ActiveRecord query ordering - ruby-on-rails

Suppose I have the following models:
class Car < ActiveRecord::Base
belongs_to :seat
...
end
class Seat < ActiveRecord::Base
belongs_to :color
...
end
class Color < ActiveRecord::Base
attr_reader :name
...
end
If I have get a list of Cars, and I want to order the Cars by color.name, how to write the order query?
class Car < ActiveRecord::Base
belongs_to :seat
...
def cars_order_by_color(car_ids)
where(:id=>car_ids).order(?????) #HOW TO ORDER BY COLOR.name
end
end

If you use a joins on your query, you can then sort by the joined tables (either seats or colors):
Car.joins(:seat => :color).order("colors.name")

To retrieve records from the database in a specific order, you can specify the :order option to the find call.
Car.order("color")
You could specify ASC or DESC as well:
Car.order("color DESC")
For more help in query look here: active_record_querying
Hope this helps.
Edit
You can use find_by_sql:
Car.find_by_sql("SELECT * FROM clients
INNER JOIN orders ON clients.id = orders.client_id
ORDER clients.created_at desc")
Write appropriate query.

Related

How do you sort a collection by distant relationships?

I have a tree-like relationship model with a fixed depth, and each level has a code attribute - similar to this;
class Category < ActiveRecord::Base
has_many :sub_categories
default_scope order(:code)
end
class SubCategory < ActiveRecord::Base
belongs_to :category
has_many :items
def self.sorted
self.joins(:category).order('"categories".code ASC, "sub_categories".code')
end
end
class Item < ActiveRecord::Base
belongs_to :sub_category
def self.sorted
# what goes here?
end
end
Category.all gets all the the categories ordered by categories.code.
SubCategory.sorted gets all the sub_categories ordered by categories.code, sub_categories.code. I used this approach because default_scope : joins(:categories).order('categories.code, sub_categories.code') makes .find return read-only records.
I would like to call Items.sorted and get the all items ordered by categories.code, sub_categories.code, items.code but I can't figure out how. I imagine I need a second .joins, but I don't have a relationship name to supply.
Try this:
class Item < ActiveRecord::Base
belongs_to :sub_category
def self.sorted
# do not need self here as that is implied
joins(sub_category: :category).
order('"categories".code ASC, "sub_categories".code, "items".code')
end
end
See the docs for joining nested assoications here
This works, but it seems like there should be a better way;
def self.sorted
joins(:sub_category).
joins('INNER JOIN "categories" on "categories".id = "sub_categories".category_id').
order('"categories".code ASC, "sub_categories".code ASC, "items".number ASC')
end

Ruby on Rails - maximum count of associated objects?

I need help with a query. I have multiple Canteens, where each has multiple Meals, where each meal has multiple MealPicks.
Although I don't know if this MealPick model is good idea, because I need to display how many times has the meal been picked TODAY, so I needed the timestamp to make this query.
class Meal < ActiveRecord::Base
def todays_picks
meal_picks.where(["created_at >= ? AND created_at < ?", Date.today.beginning_of_day, Date.today.end_of_day])
end
end
Before I had just a meal_picked_count counter in Meal which I incremented by increment_counter method.
Okay so, now I need to display for each Canteen the Meal that has the most MealPicks, I played around in the console and tried something like Canteen.find(1).meals.maximum("meal_picks.count") but that obviously does not work as it is not a column.
Any ideas?
You can do this:
MealPick.joins(:meal => :canteen)
.where("canteens.id = ?", 1)
.order("count_all DESC")
.group(:meal_id)
.count
That will return an ordered hash like this:
{ 200 => 25 }
Where 200 would be the meal id and 25 would be the count.
Update
For anyone interested, I started playing around with this to see if I could use subqueries with ActiveRecord to give me meaningful information than what I came up with before. Here's what I have:
class Meal < ActiveRecord::Base
belongs_to :canteen
has_many :meal_picks
attr_accessible :name, :price
scope :with_grouped_picks, ->() {
query = <<-QUERY
INNER JOIN (#{Arel.sql(MealPick.counted_by_meal.to_sql)}) as top_picks
ON meals.id = top_picks.meal_id
QUERY
joins(query)
}
scope :top_picks, with_grouped_picks.order("top_picks.number_of_picks DESC")
scope :top_pick, top_picks.limit(1)
end
class MealPick < ActiveRecord::Base
belongs_to :meal
attr_accessible :user
scope :counted_by_meal, group(:meal_id).select("meal_id, count(*) as number_of_picks")
scope :top_picks, counted_by_meal.order("number_of_picks DESC")
scope :top_pick, counted_by_meal.order("number_of_picks DESC").limit(1)
end
class Canteen < ActiveRecord::Base
attr_accessible :name
has_many :meals
has_many :meal_picks, through: :meals
def top_picks
#top_picks ||= meals.top_picks
end
def top_pick
#top_pick ||= top_picks.first
end
end
This allows me to do this:
c = Canteen.first
c.top_picks #Returns their meals ordered by the number of picks
c.top_pick #Returns the one with the top number of picks
Let's say that I wanted to order all meals by the number of picks. I could do this:
Meal.includes(:canteen).top_picks #Returns all meals for all canteens ordered by number of picks.
Meal.includes(:canteen).where("canteens.id = ?", some_id).top_picks #Top picks for a particular canteen
Meal.includes(:canteen).where("canteens.location = ?", some_location) #Return top picks for a canteens in a given location
Since we are using joins, grouping, and server-side counts, the whole collection need not be loaded to determine the pick count. This is a bit more flexible and probably more efficient.
canteen.meals.max {|m| m.meal_picked_count}

How do I count records that satisfy a condition in a associated model?

Okay, what I've got is two models...
Jiraissue:
class Jiraissue < ActiveRecord::Base
# JIRA uses a singular table name for this model
set_table_name 'jiraissue'
has_one :severity
end
Severity:
class Severity < ActiveRecord::Base
belongs_to :jiraissue
end
What I'm trying to do is get a count of all Jiraissues for which jiraissue.severity = "S1"
Now it turns out that the jiraissue table has a column for priority so I can pull this trick in the model...
Jiraissue:
class Jiraissue < ActiveRecord::Base
# JIRA uses a singular table name for this model
set_table_name 'jiraissue'
has_one :severity
def self.count_priority(priority)
where("PRIORITY = ?",priority).count()
end
end
And then in the view do something like...
<%= (1..4).map {
|priority| Jiraissue.biit.bugs.recent.count_priority(priority)
}.inspect %>
How do I do something similar for Jiraissue to get a count_severity method?
This just doesn't work (nor would I expect it to)...
def self.count_severity(severity)
where("severity = ?",severity).count()
end
But I'm totally confused.
Jiraissue.joins(:severities).where(:severities => {:severity => "S1"}).count
model
def self.count_priority(priority)
where("PRIORITY = ?",priority).size
end
controller
def index
#jiraissues = Jiraissue.count_priority('S1')
end
Doesn't it work?

Finding multiple records through has_one association

My Customer and Person models looks like this:
class Customer < ActiveRecord::Base
belongs_to :person
belongs_to :company
end
class Person < ActiveRecord::Base
has_one :customer
end
How can I get all Person records that have an association with a Customer?
with sql it might be something like
Customer.where("customers.person_id IS NOT NULL")
to get Person record you can use join
Person.joins( :customers ).where("customers.person_id IS NOT NULL")
I'm not sue either where is necessary here (I believe no) so try Person.joins( :customers ) first
person_array = []
Person.all.each do |p|
unless p.customer.nil?
person_array << p
end
end
I don't think it's the fastest query but:
Customer.where('person_id IS NOT NULL').map(&:person)
rails 2.3.x
Customer.all(:include => :person).map(&:person).compact

Rails: order using a has_many/belongs_to relationship

I was wondering if it was possible to use the find method to order the results based on a class's has_many relationship with another class. e.g.
# has the columns id, name
class Dog < ActiveRecord::Base
has_many :dog_tags
end
# has the columns id, color, dog_id
class DogTags < ActiveRecord::Base
belongs_to :dog
end
and I would like to do something like this:
#result = DogTag.find(:all, :order => dog.name)
thank you.
In Rails 4 it should be done this way:
#result = DogTag.joins(:dog).order('dogs.name')
or with scope:
class DogTags < ActiveRecord::Base
belongs_to :dog
scope :ordered_by_dog_name, -> { joins(:dog).order('dogs.name') }
end
#result = DogTags.ordered_by_dog_name
The second is easier to mock in tests as controller doesn't have to know about model details.
You need to join the related table to the request.
#result = DogTag.find(:all, :joins => :dog, :order => 'dogs.name')
Note that dogs is plural in the :order statement.

Resources