Rails 3: Fastest way to determine model popularity by counting association - ruby-on-rails

Suppose I have a Post model which has_many Comments. I want to get the top 10 most popular posts based on those who have the most comments. Assuming I have hundreds of thousands of posts, what's the most efficient way of getting those 10 top posts?
Also, how do I cache that query?
Thanks!

I'd suggest you add a counter-cache column on Post called comments_count. Add an index on this column, and then you can select the most popular posts by:
# app/models/post.rb
scope :popular, lambda { order("comments_count DESC").limit(10) }
Check out the ActiveRecord associations class methods document for more info on counter-caches.

Related

Rails: Order activerecord object by attribute under has_many relation

class Post
has_many :commments
end
class Comment
belongs_to :post
end
I wish to display a list of posts ordered by date of post creation (submitted_at). I also want some post xyz to appear at the top if it has some new comment posted and yet to be reviewed by moderator. We will determine this by a boolean attribute/field at comments level (moderated = 1/0)
I tried
Posts.join(:comments)
.distinct
.order("submitted_at DESC, comments.moderated")
but this excludes posts that have no comments and results aren't sorted as expected. I am sure that we can do this at ruby level, but looking for a way to do this using AR.
For the join, use this:
Posts.join("LEFT JOIN comments ON comments.post_id = posts.id")
Which will include the ones with no comments.
Your sorting seems to suggest you want a count of moderated comments, in which case, try this:
.order("submitted_at DESC, COUNT(comments.moderated)")
Although, you may need to use group in some way too.

Rails: Order array by number of child objects

I have an array of posts. I want to order this array of posts by number of comments they have (First object in the array being most comments, last being the least). A Post has many comments. This seems like a simple problem, but I can't figure out how to order them. I've tried and I don't believe it's possible to achieve this via the order method. Is there a Rails method I don't know about? Or will I have to home roll it? Thanks in advance!
PS: My relation of comments to posts is polymorphic Comment - (belongs_to :threadable)
You can do this with a complicated active record query, however, the easiest thing by far is to just add a :counter_cache on association. You just add a field on your Post model called comments_count and then in your Comment model:
belongs_to :post, counter_cache: true
Then since it's just a column like all your other columns you can order by it.
See: http://guides.rubyonrails.org/association_basics.html
The easiest way to do this would probably be using the counter_cache option
see:
http://guides.rubyonrails.org/association_basics.html#counter-cache
http://railscasts.com/episodes/23-counter-cache-column
-
class Comment
belongs_to :post, counter_cache: true
end
add the column comments_count to the posts database table, then order by that
The best way to achieve it is to use counter cache feature. You need to add integer column to your posts table called comments_count. Then on your comments model you need to change your post association to read:
belongs_to :post, counter_cache: true
Rails will handle comments_count column to show how many comments are associated with given post. You can then use this column for ordering.

Rails query for models having has_many and belongs_to relationship

I am fairly new to rails. I have the following models
class Question < ActiveRecord::Base
has_many :options
has_many :response_parts
end
class ResponsePart < ActiveRecord::Base
belongs_to :question
end
The corresponding scaffolds are
rails g scaffold Question qorder:string qtext:text qtype:string
rails g scaffold ResponsePart answer:string question:belongs_to
Now I want all the response parts where qtype is 'mobile'. I have tried a few ways but could not query successfully. Can someone tell a way to make such query. Thanks in advance.
You can include the relation between the two model and add a constraint on it:
ResponsePart.includes(:question).where(questions: { qtype: 'mobile' })
This will retrieve all the ResponsePart objects from the DB having a question which match "qtype == 'mobile'"
This is also the most efficient way to retrieve these records.
Question.where(qtype: 'mobile').collect(&:response_parts)
This will query the DB to get the corresponding response_parts of each question having "qtype == 'mobile'"
Example: If you have 6 questions with "qtype == 'mobile'", it will create 6 SQL queries for each Question.
Question.where(qtype: "mobile").first.response_parts
This just retrieves the ResponsePart objects related to the first question matching the condition "qtype == 'mobile'"
try this
Question.where(qtype: "mobile").first.response_parts
Try:
Question.where(qtype: 'mobile').collect(&:response_parts)
This will give you all the response_parts for all the questions with qtype = 'mobile'
Update: (Avoiding N+1 queries)
Question.where(qtype: 'mobile').collect(&:response_parts)
This will execute a select query on each response_parts for each question leading to the "N+1" queries.
In order to avoid "N+1 queries" i.e. one Query to retrieve question and n queries to retrieve resposne_parts, you can add includes(:join_relation) (where :join_relation is response_parts in your case) as follows:
Question.includes(:response_parts).where(qtype: 'mobile').collect(&:response_parts)

How to get highest count of associated model (Rails)?

A User has_many Solutions
How do I order Users by those with the most Solutions?
I'm trying to find the top ten users but I'm not sure how the most tidy or efficient way to do this?
Does anyone have an example that isn't too computationally expensive?
User
.joins(:solutions)
.select("users.*, count(solutions.id) as scount")
.group("users.id")
.order("scount DESC")
If you really want a fast way of doing it, put a counter_cache on a users' solutions (have a solutions_count column in your User) and order by that column. You don't need to manage that counter, because rails does it for you. You can read more about counter_cache in the Rails Guides
Assuming the following models
class User
has_many :solutions
end
class Solution
belongs_to :user
end
Then the best way is to counter_cache the solutions_count and order by it. more on counter_cache
The query will then be
User.order("users.solutions_count DESC").limit(10)
Don't forget to index the solutions_count column.

Rails way to COUNT the nr of records in my case

I am using Rails v2.3.2.
I have a model called UsersCar:
class UsersCar < ActiveRecord::Base
belongs_to :car
belongs_to :user
end
This model mapped to a database table users_cars, which only contains two columns : user_id, car_id.
I would like to use Rails way to count the number of car_id where user_id=3. I konw in plain SQL query I can achieve this by:
SELECT COUNT(*) FROM users_cars WHERE user_id=3;
Now, I would like to get it by Rails way, I know I can do:
UsersCar.count()
but how can I put the ...where user_id=3 clause in Rails way?
According to the Ruby on Rails Guides, you can pass conditions to the count() method. For example:
UsersCar.count(:conditions => ["user_id = ?", 3])
will generates:
SELECT count(*) AS count_all FROM users_cars WHERE (user_id = 3)
If you have the User object, you could do
user.cars.size
or
user.cars.count
Another way would be to do:
UserCar.find(:user_id => 3).size
And the last way that I can think of is the one mentioned above, i.e. 'UserCar.count(conditions)'.
With the belogngs to association, you get several "magic" methods on the parent item to reference its children.
In your case:
users_car = UsersCar.find(1) #=>one record of users_car with id = 1.
users_car.users #=>a list of associated users.
users_car.users.count #=>the amount of associated users.
However, I think you are understanding the associations wrong, based on the fact that your UsersCar is named awkwardly.
It seems you want
User has_and_belongs_to_many :cars
Car has_and_belongs_to_manu :users
Please read abovementioned guide on associations if you want to know more about many-to-many associations in Rails.
I managed to find the way to count with condition:
UsersCar.count(:condition=>"user_id=3")

Resources