Ruby sort_by with null values - ruby-on-rails

Using Ruby on Rails. I'm trying to sort a query by number (saved as string) and letter. However, my letter column can contain "NULL" values. This crashes when there are Null values in the letter column:
#fences = Fence.find(:all,
:conditions => ["coursewalk_id = #{#coursewalk.id}"]).sort_by { |a| [+(a.number.to_i), a.letter] }
How can I fix this?

a.letter.to_s should do the trick.

Related

Rails 4 Using Ruby Enumerable to query arrays using arrays

I have a model that stores an array in one of the table columns called 'attributes'. So 3 separate records might look like this:
Record 1
MyModel.attributes = {Red, Furry, Stinky}
Record 2
MyModel.attributes = {Red}
Record 3
MyModel.attributes = nil
Record 4
MyModel.attributes = {Blue, Furry, Sweet}
I'd like to query this array for any of another array, including nil. The results should return any records that have any of the attributes in the query array and any records where the attributes column is nil.
query_array = [Blue, Furry]
The answer to this query should provide Record 1, Record 3 and Record 4 -- again, it's not looking for ALL the
currently, I can do this if I just query
MyModel.all.select {|m| m.attributes["Furry"] or m.attributes["Blue"] }
But I want to be able to create the array dynamically and not handcode the m.attributes["attribute"]. I can't quite figure out how to do this without requiring all of the array items, I just want ANY of the array items and records with no attributes.
You probably should not call the column attributes as this name is already used for the attributes hash accessors in rails models. For the examples below i renamed this to tags
A simple solution would be to check for nil (always include those records) and check if the intersection of tags has any tags in it:
model_1.tags # => ['red', 'furry', 'stinky']
model_2.tags # => ['red']
model_3.tags # => nil
model_4.tags # => ['blue', 'furry', 'stinky']
search_tags = ['red', 'blue']
MyModel.all.select do |model|
model.tags.nil? || (model.tags & search_tags).any?
end
You could also write it as a nested loop:
search_tags = ['red', 'blue']
MyModel.all.select do |model|
model.tags.nil? || model.tags.any? { |tag| search_tags.include?(tag) }
end
This is all done in memory, in ruby itself. If you have 100_000 or 1_000_000 records, then all of them are fetched from DB, instantiated and then filtered.
So depending on your exact requirements and what DB you are using you could find an easier/more performant solution. Some ideas:
Store the tags in a separate table
Store the tags as a comma separated string and use a 'like' query
Use postgres JSON datatype and the query features postgres provides
You can try this
query_array = [Furry, Blue]
query_string = query_array.map{|query| "m.attributes[#{query}]" }.join(" || ")
#=> "m.attributes[Furry] || m.attributes[Blue]"
MyModel.all.select {|m| eval(query_string) }
Now, all you have to do is add more items into the query_array.

Remove all letters from a string column in Rails

I'm writing a migration to change a column type from string to integer.
def change
change_column :emails, :object_id, :integer
end
This migration is failed because this column already contains string values. Before executing this migration I'm trying to remove all letters from this column so that i can get only integer values. Existing values are like
"AB12345"
"A12345X"
"789X26A"
What script i should execute before migration to remove all letters and achieve only integer values like this?
"12345"
"12345"
"78926"
Thanks
If you have more than say 10,000 records do the conversion in the database itself. For postgres, that'd be something like:
select regexp_replace('1231ASDF12', '[^0-9]', '', 'g')
You can use execute to run raw sql in a migration:
update table set col = regexp_replace(col, '[^0-9]', '', 'g')
Keep in mind if you're intending object_id to be a foreign key, you'll need to update whatever table is referenced and also ensure you haven't inadvertently broken anything (e.g., if there was AB123 and BC123 in the dataset).
I think you could use the trim function but the folloing line would do just fine as well.
result = Replace("Some sentence containing Avenue in it.", "Avenue", "Ave")
Example from Access VBA | How to replace parts of a string with another string
You could change "A" into ""
"B" into "" ect.
You would end up whit a code like this
Do While ActiveCell.Value <>""
ActiveCell.Value = Replace(ActiveCell.Value, "A", "")
ActiveCell.Value = Replace(ActiveCell.Value, "B", "")
ActiveCell.Value = Replace(ActiveCell.Value, "C", "")
ect...
ActiveCell.Offset (-1,0).Select
Loop

Postgresql syntax for select a record (row) with two of three fields

I have a table QualifyingEvents. I am attempting to search the table for records created on a particular date with their course_id and / or their sponsor_name. I'm following the examples in RailsGuides Active Record Query Interface: http://guides.rubyonrails.org/active_record_querying.html
I couldn't get a query of this form to work:
QualifyingEvent.find([:date = 'x', :course_id = 'x', :sponsor_name ='x'])
Nor this form:
QualifyingEvent.where(":course_id = ?", x )
However, this works - with the commas being interpreted as "and":
QualifyingEvent.find_by date: '2014', course_id: '96789', sponsor_name: 'Maggio and Sons'
Now, how do I get a record if I want a match on two of the three fields? I've tried using && and || with no luck. What is the correct syntax? Also, why does "find_by" work when "find" doesn't?
Thank you, in advance, for your help.
To match just two fields, one way is:
QualifyingEvent.where(field1: field1_val, field2: field2_val).first
OR
QualifyingEvent.find_by(field1: field1_val, field2: field2_val)
That hash provided as an argument to where and find_by can be passed in different forms e.g.
"field1 = ? AND field2 = ?", field1_val, field2_val
OR
"field1 = :f1 AND field2 = :f2", {f1: field1_val, f2: field2_val}
OR
"field1 = #{field1_val} AND field2 = #{field2_val}"
That last one isn't advised though mainly because of the risk of SQL injection.
But if you meant matching any two of the three field values you gave, then you'll have to provide all three match-scenarios yourself and specify the "OR" between them.
QualifyingEvent.where("
(date = :date AND course_id = :course_id)
OR
(date = :date AND sponsor_name = :sponsor_name)
OR
(course_id = :course_id AND sponsor_name = :sponsor_name)
",
{date: date_val, course_id: course_val, sponsor_name: sponsor_val}
).first
Again, the above code can be written in different ways.
And find_by is for certain conditions that you can pass as a hash whereas find is for finding by ID or array of IDs. Shortcut for find_by(id: some_value) i.e.
find_by(id: 1) == find(1)
Try something like this:
QualifyingEvent.where("course_id = ? and some_other_col = ? and some_more_col = ?", x,y,z)
Or
QualifyingEvent.where(:course_id => 'x', :some_other_col => 'y', :some_more_col => 'z')
Any one of them will fetch you the result.

ActiveRecord/Rails: most efficient way to count results of multiple queries

I'm new to ActiveRecord. I'm trying to optimize a database query.
I've got a Person model with gender column. A person's gender can have one of three values: male, female, or transgendered. I want to know the number of people of each gender.
I'm currently doing this with three separate database queries.
numMale = Person.where(:gender => :male).count
numFemale = Person.where(:gender => :female).count
numTrans = Person.where(:gender => :transgendered).count
Could these be made more efficient? Is there a way to combine them into a single database call?
Take a look at ActiveRecord::Calculations#count. The syntax depends on your Rails on Rails version.
# Rails 3.2
Person.count(:group => :gender)
# Rails 4+
Person.group(:gender).count
count returns a Hash like { 'male' => 12, 'female' ... }
You can do a group by query to aggregate all the data:
Person.all(:group => "gender", :select => "gender, COUNT(*) as count")
Then you can split out the values and assign to your variables.
Person.select('gender,count(*)').where(:gender => [:male,:female,:transgendered]).group(:gender)
would return
SELECT gender,count(*) FROM `people` WHERE `people`.`gender` IN ('male', 'female', 'transgendered') GROUP BY gender
You can try this:
Person.where(gender: [:male, :female, :transgendered]).count

Compare separate columns in Mongoid

I'm looking for the mongoid equivalent to:
How to select the comparison of two columns as one column in Oracle
I can't seem to find any documentation or examples comparing columns within the same query. Is this just not possible in Mongoid?
Nope, you need to drop down to the mongodb ruby driver to do this and it will potentially be very slow as it is a javascript query that will not use an index:
Model.collection.find_one({"$where" => 'this.name == this.name2'})
Which is equivalent to third shell command here.:
> db.collection.insert({name: "awesome", name2: "awesome"})
> db.collection.insert({name: "awesome", name2: "awesome2"})
> db.collection.find('this.name == this.name2')
{ "_id" : ObjectId("xxx"), "name" : "awesome", "name2" : "awesome" }
> (line shown to signify end of results)
Note: if a document does not have key name and that same document also does not have key name2 that this will return true because null == null.

Resources