rails active record traversing rows - ruby-on-rails

I am accessing a database table with the .where() method. This should return many rows. How can I view the first row, or the second row. I know I can use the .each method to traverse all rows but what if I just want to access a certain row. I am new to rails, so sorry for this simple question.

To get the first row, you can just use .first
Model.where(:state => "active").first
.last works the same way:
Model.where(:state => "active").last
To get the nth row, use [] with a 0-based index, as with any other array
Model.where(:state => "active")[1] #second result

Once you have your set of results you can just reference individual rows with the [] method.
http://www.ruby-doc.org/core-1.9.3/Array.html#method-i-5B-5D
results = YourClass.where(:foo => 'bar').all
results[0] # the first result
results[1] # the 2nd
# and so on until
results[results.length - 1] # the last item in the array
results[results.length] # out of bounds, and returns nil
# you can also use negative numbers to count backwards
results[-1] # the last element
results[-2] # the 2nd from last

Related

Rails distinct values of a value returned by a method in model

Suppose I have an Employee model, there is a method in Employee model named def fixed
def fixed
return self.cached_fixed.to_f if self.cached_fixed.present?
return (self.current_salary && self.current_salary.fixed).to_f
end
end
def current_salary
return #current_salary if #current_salary
# #current_salary = self.employee_salaries.detect{|es| es.end_date == nil}
#current_salary = self.db_current_salary
return #current_salary
end
if the fixed were a column in employee table we could have just used Employee.distinct.select(:fixed) to pull the distinct values
is there a way if it's just a method not a field in table without loading all the employees.
I am expecting to get the unique values of a column from a table , but it may not be a column as in the above table
Not for an arbitrary method, no. But you start the query from the EmployeeSalary end and fetch only the column you care about in one query using select:
EmployeeSalary
.select(:fixed)
.join(:employee)
.where(end_date: nil)
This will run a select fixed from... query and return a list of EmployeeSalary objects, but all the fields that aren't listed in the select call will be nil. Assuming the constraint of only one salary record having end_date: nil, there will be one EmployeeSalary object per employee. You can add .distinct in the method chain if you want unique values.
I'm not sure how the caching logic fits into this question. You can apply caching logic on top of that list if you like, but doing one query like this is pretty fast.

How to check how many columns are 'nil' in a single row of a table

We have Employee table. We get all columns there are in that table through:
Employee.columns.map(&:name)
Next, we want to know how many fields are nil or empty in a single row.
How can we find it?
You can count empty columns in a single row using:
Employee.find(123).attributes.values.count { |v| v.nil? }
(just change 123 to your employee id)
You can use this to find the number of columns with nil value in a row:
id = 1 // or params[:id]
Employee.find(id).attributes.values.select(&:nil?).count

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.

Combine two ActiveRecord::Relation with OR, not AND, returning a Relation and not an Array to be able to paginate later

a and b are ActiveRecord::Relation objects which return the same type of objects(Micropost objects in this case)
a.class
=> ActiveRecord::Relation
b.class
=> ActiveRecord::Relation
a.first
=> Micropost(...)
b.first
=> Micropost(...) #They both return the same type of objects
c=a+b
c.class
=> Array #This is not what i'm looking for
c=a|b
c.class
=> Array #Not what i'm looking for either
c=(a or b)
c.class
=> ActiveRecord::Relation #But it is just a, so it's wrong
c==a
=> true
a.merge(b)
=> [] #It merges with AND which in my case results in an empty array
Is there some way to "OR" both Relation objects and return another Relation object in Rails 3.2.11? Do I need to use some gem such as squeel to do it?
If it can't be done: Why it can't be done? Can I paginate the array resulting from a|b without loading every record from the database?
If it can be done: Could anyone please write an example of how to do it?
Thanks in advance.
EDIT: I copied the wrong variables into the block of code, b was not an ActiveRecord::Relation, it was actually an array and that is why a.merge(b) returned [].
My fault.
TLDR: Question is wrong, and array can be paginated :)
For you question: Can I paginate the array resulting from a|b without loading every record from the database?
Yes, you can paginate array. Only you need to do is require 'will_paginate/array' file where your are using paginate for array. If you are using in many place then its better to add it in config/initializers. And its array so it won't load every record from database each time (i.e.) query is fired only once. To load association you can use eager load on array using include.

Rails 3.0 with "or" statement in each loop

I am trying to build a query that compares two object, and if they have the same id the record is not fetched. What I have is this:
#channels.each do |channel|
unless #guide_channels.where(:id => channel.id).exists?
#dropdown_channels = #dropdown_channels.where(:id => channel.id)
end
end
This creates the query, but puts a AND between every value which isn't what I have in mind. I want the "or" operator. Is there a 'orwhere' function I can use or is there a better way to do this with some compare function?
The point is that the .where() method of a AR::Relation objects adds the condition to a set of conditions that are then AND-ed together when the query is executed.
What you have to do is a query like NOT IN:
# select all the ids for related #guide_channels
# if #channels comes from a query (so it's a ActiveRecord::Relation):
guide_ids = #guide_channels.where(:id => #channels.pluck(:id)).pluck(:id)
# use .where(:id => #channels.map {|c| c.id}) instead if #channels is just an array
# then use the list to exclude results from the following.
#dropdown_channels = #dropdown_channels.where("id NOT IN (?)", guide_ids.to_a)
The first query will accumulate all the IDs for channels that have an entry in #guide_channels. The second one will use the result of the first to exclude the found channels from tthe results for the dropdown.
This strange behavior is due to ActiveRecord's lazy evaluation of scopes.
What happens is that the line
#dropdown_channels = #dropdown_channels.where(:id => channel.id)
Does not send a query to the DB until you actually use the value of #dropdown_channels, and when you do it concatenates all the conditions into one big query, this is why you get the AND between the conditions.
In order to force ActiveRecord to eager load the scope you can use either the all scope or the first scope, for example:
#dropdown_channels = #dropdown_channels.where(:id => channel.id).first
This will force ActiveRecord to calculate the query on spot returning the result immediately and not to accumulate the scopes for lazy evaluation.
Another approach could be to accumulate all those channels_ids and get them later in one query, instead of making a query for each one. This approach is more cost-effective relating to DB resources.
In order to achieve this :
dropdown_channels_ids = []
#channels.each do |channel|
unless #guide_channels.where(:id => channel.id).exists?
dropdown_channels_ids << channel.id
end
end
#dropdown_channels = #dropdown_channels.where(:id => dropdown_channels_ids)

Resources