Rank posts by comments added today - ruby-on-rails

I have two models: posts and comments. I want to rank the posts by the # of comments added today. So far I only have it ranked by the number of comments and am not sure how to add the today part.
Here is the posts controller:
#posts = Post.order('posts.comments_count desc')
I have tried
#posts = Post.order('posts.comments_count desc').where("created_at >= ?", Time.zone.now.beginning_of_day)
but that returned nothing.

Your second query is giving you the number of posts that have been created today, rather than the number of comments that have been created today.
This should be what you're looking for:
#posts = Post.select('posts.*, count(comments.id) as count_comments')
.joins(:comments)
.where('comments.created_at >= ?', Time.zone.now.beginning_of_day)
.group('posts.id')
.order('count_comments desc')
If you'd like to include all posts, including ones that don't have any comments today, then you'll need a left join:
#posts = Post.select('posts.*, count(comments.id) as count_comments')
.joins("left join comments on comments.post_id = posts.id and comments.created_at >= '#{Time.zone.now.beginning_of_day}'")
.group('posts.id')
.order('count_comments desc')

Related

Pagination breaks when I add a join and group statement to a query?

I have an index page with Subscriptions. A Subscription has_many SubscriptionVersions and I need to order them based on the latest SubscriptionVersions authorized_at date.
This is my controller action
def index
#subscriptions = current_account.subscriptions
.includes(:plan, :user)
.joins("INNER JOIN subscription_versions
ON subscription_versions.subscription_id = subscriptions.id")
.group("plans.id, subscriptions.id, users.id")
.order("MAX(subscription_versions.authorized_at) ASC")
.paginate(page: params[:page], per_page: 20)
end
Removing the join and order statements, pagination works fine, but if I add them the results are not paginated.
What is going on and why?

With Rails 5, how to left joins with selected associations and order by association count

here are my models:
class Article
has_many :votes
end
class Vote
belongs_to :article
belongs_to :user
end
Now I am trying to order the articles, by the count of votes in the past 24 hours. Any suggestions for how to do this?
I have tried this:
Article.left_joins(:votes).group("articles.id").order("count(votes.id) DESC")
However, this is ordering by all the votes, not the votes in last 24h. Any suggestions?
One more thing is, I still need to get the articles with no votes. So I am not sure how to use the where clause here...
You need to add the date when the vote was created for an article prior to the count of votes in your order.
Try this:
Article.left_joins(:votes)
.group("articles.id")
.order("DATE(votes.created_at) DESC, count(votes.id) DESC")
Then if you only want to get the articles that has been upvoted for the past 24hr, you can chain this to your query:
.where("votes.created_at >= ?", 1.day.ago)
Finally I got it work. It turns out left_joins is not necessary. My current solution is using select clause in the order() function:
Article.order("(select count(*) from votes where votes.article_id = articles.id and votes.created_at >= NOW() - '1 day'::INTERVAL ) desc")
Maybe not elegant, but works well.

Rails 4: Sort by associated created_at if present, otherwise by parent's

I have an Article and Comment MVC - with the standard relationships.
I want to sort the articles in order based on the Article created_at, OR if the Article has Comments, then sort by the Comments created_at.
So far, I have:
#articles = Article.includes(:comments).order("comments.created_at desc")
What would I need to add to order the Article created_at, but only if the Article has no comments?
You can use coalesce:
#articles = Article.includes(:comments)
.order('coalesce(comments.created_at, articles.created_at) desc')
.references(:comments)
Take two different objects, one which have comments, the other which doesn't.
#articles_one = Article.includes(:comments).where('comments.id IS NOT NULL').order("comments.created_at desc")
#articles_two = Article.includes(:comments).where('comments.id IS NULL').order("articles.created_at desc")
Then, add the two objects to get the final required result.
#articles_final = #articles_one + #articles_two
You can simply achieve by this single query:
#articles = Article.joins("LEFT OUTER JOIN ( SELECT article_id, MAX(created_at) AS max_created_at
FROM comments
GROUP BY article_id
) AS temp
ON temp.article_id = articles.id
").order("CASE WHEN temp.max_created_at IS NULL THEN 1 ELSE 2 END, articles.created_at")
Explanation:
Sub query temp as comments was grouped by article_id
Article left outer join with temp on article_id, so that means: article which doesn't have any comment, will have max_created_at = NULL
We do order by CASE WHEN temp.max_created_at IS NULL THEN 2 ELSE 1 END, this means article has comments will be ordered by max_created_at, then articles haven't any comment will be ordered by articles.created_at

Multiple order values for find_with_reputation

The following function sorts the businesses (relation) by votes descending (using the active record reputation system link to gem).
#businesses = Business.find_with_reputation(:votes, :all, order: "votes desc")
How would I add a secondary order value so that it sorts by votes but if votes are equal it sorts by created_at (oldest at the top)?
Is it simply:
#businesses = Business.find_with_reputation(:votes, :all, order: "votes desc, created_at desc")
I suspect that your proposed answer is going to result in a SQL error because 'created_at' probably occurs in both the reputation table and the Business table. To get around that, I'd suggest being specific on created_at.
Business.find_with_reputation(:votes, :all, order: "votes desc, businesses.created_at desc")
However, I'd suggest instead that you use the id column. DBs will be faster with INTs and the sort sequence should be the same.
Business.find_with_reputation(:votes, :all, order: "votes desc, businesses.id desc")

thumbs_up gem to sort liked posted - rails

I'm using the thumbs_up gem to allow users to like designs and products. In the users profile, i have it showing all of the designs and products that the user has liked. Right now, i have concatenated the designs and products into one view but they are ordered 'created_at DESC' for the designs+products. I want to order it based on when the user voted so when a user votes on a design, it's first on their profile. Here is the code i have
in the model
def favorites
Design.joins(:votes).where('voter_id = ?', self.id).where('voteable_type = ?', 'Design').where('vote = ?', true)
end
def favs
Product.joins(:votes).where('voter_id = ?', self.id).where('voteable_type = ?', 'Product').where('vote = ?', true)
end
in the controller
#user = User.find_by_username(params[:id])
#designs = #user.favorites
#products = #user.favs
#favorites = #designs.to_a.concat #products.to_a
#favorites.sort! {|t1, t2| t2.created_at <=> t1.created_at}
I've even removed the default scope for designs and products in case that was the issue but it didn't resolve it.
i've also tried this with no luck
Design.joins(:votes).where('voter_id = ?', self.id).where('voteable_type = ?', 'Design').where('vote = ?', true).order('created_at DESC')
there is a timestamp 'created_at' that belongs to each vote. so i know this is possible.
If a user has_many :votes, votes belong_to :voteable and every user only votes on a specific voteable once, I would try something like this:
#user.votes.includes(:voteable).order('created_at DESC')
This will return collection of votes ordered by their create date. Then you can iterate over them and display their voteable attribute however you want. If you only want designs and not products then you will need to do a join instead of includes and filter by voteable_type

Resources