How to sort authors by their book count with ActiveRecord?
As Kevin has suggested, counter_cache is the easiest option, definitely what I would use.
class Author < ActiveRecord::Base
has_many :books, :counter_cache => true
end
class Book < ActiveRecord::Base
belongs_to :author
end
And if you are using Rails 2.3 and you would like this to be the default ordering you could use the new default_scope method:
class Author < ActiveRecord::Base
has_many :books, :counter_cache => true
default_scope :order => "books_count DESC"
end
books_count is the field that performs the counter caching behaviour, and there is probably a better way than using it directly in the default scope, but it gives you the idea and will get the job done.
EDIT:
In response to the comment asking if counter_cache will work if a non rails app alters the data, well it can, but not in the default way as Rails increments and decrements the counter at save time. What you could do is write your own implementation in an after_save callback.
class Author < ActiveRecord::Base
has_many :books
after_save :update_counter_cache
private
def update_counter_cache
update_attribute(:books_count, self.books.length) unless self.books.length == self.books_count
end
end
Now you don't have a counter_cache installed, but if you name the field in the database books_count as per the counter_cache convention then when you look up:
@Author = Author.find(1)
puts @author.books.size
It will still use the counter cached number instead of performing a database lookup. Of course this will only work when the rails app updates the table, so if another app does something then your numbers may be out of sync until the rails application comes back an has to save. The only way around this that I can think of is a cron job to sync numbers if your rails app doesn't do lookup up things often enough to make it not matter.
rails order by association through another association?
Firstly, let's have all associations available on the Author class itself to keep the query code simple.
class Author < AR::Base
has_many :books
has_many :orders, :through => :books
has_many :sales, :through => :orders
end
The simplest approach would be for you to use group
with count
, which gets you a hash in the form {author-id: count}
:
author_counts = Author.joins(:sales).group("authors.id").count
=> {1 => 3, 2 => 5, ... }
You can now sort your authors and lookup the count using the author_counts
hash (authors with no sales will return nil
):
<% Author.all.sort_by{|a| author_counts[a.id] || 0}.reverse.each do |author| %>
<%= author.name %>: <%= author_counts[author.id] || 0 %>
<% end %>
UPDATE
An alternative approach would be to use the ar_outer_joins gem that allows you get around the limitations of using includes
to generate a LEFT JOIN:
authors = Author.outer_joins(:sales).
group(Author.column_names.map{|c| "authors.#{c}").
select("authors.*, COUNT(sales.id) as sales_count").
order("COUNT(sales.id) DESC")
Now your view can just look like this:
<% authors.each do |author| %>
<%= author.name %>: <%= author.sales_count %>
<% end %>
This example demonstrates how useful a LEFT JOIN can be where you can't (or specifically don't want to) eager load the other associations. I have no idea why outer_joins
isn't included in ActiveRecord by default.
Rails ActiveRecord sort by count of join table associations
Try the following:
@resources = Resouce.select("resources.*, COUNT(votes.id) vote_count")
.joins(:votes)
.where(language_id: "ruby")
.group("resources.id")
.order("vote_count DESC")
@resources.each { |r| puts "#{r.whatever} #{r.vote_count}" }
To include resources with 0 votes, use an outer join. If the example below doesn't work as is you'll have to alter the joins statement to join across the correct relations.
@resources = Resource.select("resources.*, COUNT(votes.id) vote_count")
.joins("LEFT OUTER JOIN votes ON votes.votable_id = resources.id AND votes.votable_type = 'Resource'")
.where(language_id: "ruby")
.group("resources.id")
.order("vote_count DESC")
Order by count using pluck for associated table ruby
You can read SO question about ORDER BY the IN value list
. Only available option, without join and without creating additional Postgres functions, is use order like this:
ORDER BY id=1 DESC, id=3 DESC, id=2 DESC, id=4 DESC
You may build such a query in Rails:
ids = Book.group(:author_id).order("COUNT(*) DESC").pluck(:author_id)
order_by = ids.map { |id| "id=#{id} DESC" }.join(", ")
Author.order(order_by)
This will work. Though in case when Author table contains a lot of records better to use a counter_cache column (as recommended by @tegon) for performance reasons.
Rails selecting objects with conditions on associations
You can do it with sql:
Author.where(:active => true).joins(:books).group("author_id HAVING count(books.id) > 0")
How can I implement sorting with Active Model Serializer?
In my experience AMS doesn't really concern itself with sorting. This seems like something you'd do before serializing. Perhaps in the controller or better yet in the model.
UPDATE
@CalebSayre if your sort parameter is just a simple list of fields to sort by you could probably get away with something as simple as this.
render json: Author.order(params[:sort]), serializer: AuthorSerializer
If no sort params are passed in it'll just skip the order clause.
Rails order Post by def in model
You should try counter cache.
You can read more about it from the following links -
How to sort authors by their book count with ActiveRecord?
http://hiteshrawal.blogspot.com/2011/12/rails-counter-cache.html
http://railscasts.com/episodes/23-counter-cache-column
Counter cache only works inside rails. If you updated from outside application you might have to do some work around.
Related Topics
Handling Namespace Models (Classes) in Namespace
How to Share Variables Across My .Rb Files
Param Is Missing or the Value Is Empty: User Rails 4
Differences Between Proc and Lambda
Failed to Build Gem Native Extension
How to Spawn an Eventmachine "Inside" a Rails App
Eager Loading: the Right Way to Do Things
How to Convert PDF Files to Images Using Rmagick and Ruby
How to Check If a Variable Is a Number or a String
Get Class Location from Class Object
Sum 2 Hashes Attributes with the Same Key
Rails Link_To External Site, Url Is Attribute of User Table, Like: @Users.Website
Sidekiq Not Deallocating Memory After Workers Have Finished
How to Backreference in Ruby Regular Expression (Regex) with Gsub When I Use Grouping
Private Class (Not Class Method) in a Ruby Module
Active Admin - Refresh Second Drop Down Based on First Drop Down, Ruby on Rails