get most discussed forums in rails - ruby-on-rails

I got two classes: Forum and Topic. Forum has_many Topics.
How do you get the (lets suppose) 5 most disscused forums?
How do you limit it by Date? for example, most disscused this day/week etc.
thanks

1. How do you get the (lets suppose) 5 most disscused forums?
You will probably be able to do something like this:
Forum.where('id IN (?)', Topic.limit(5).count(:group => 'forum_id').map {|key, value| key })
2. How do you limit it by Date? for example, most disscused this day/week etc.
Forum.where('id IN (?)', Topic.where('date > ', 1.week.ago.to_s(:db)).limit(5).count(:group => 'forum_id').map {|key, value| key })

If the most discussed forum is the one with the most topics, the query would be:
Forum.joins(:topics).group("forums.id").order("COUNT(*) DESC").limit(5)
The five forums with the largest number of opened topics in the last week:
Forum.joins(:topics)
.where("topics.created_at > ?", 1.week.ago)
.group("forums.id")
.order("COUNT(*) DESC")
.limit(5)

The best way is to keep timestamps (created_at and updated_at) in both tables. And update them whenever some activity occurs on the topic.
Steps:
Keep timestamps in both the tables.
Add an after_save callback
in Topic model, to update updated_at of the parent forum.
Get the list of forums order by updated_at desc.
class Topic
def after_save
self.forum.touch
end
end
Then run a query like
Forum.order("updated_at desc").limit(5)

Related

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.

Sort records by a related model count, and then sort chronologically

Posts have_many upvotes. A post can also be featured or not with the column is_featured.
I want to get all featured posts, ordered by upvotes (most to least), then created_at (newest to oldest).
Here is how I build up the query. #1 and #2 work, but for #3 I can't sort by dates and upvotes at the same time.
# 1. Get all featured posts
Posts.where(is_featured: true)
# 2. Get all featured posts, sort by their votes in DESC order. Maybe there is an easier way?
Posts.where(is_featured: true).sort_by{|post| post.upvotes.count}.reverse!
# 3. Get all featured posts, sort by their votes in DESC order, sort by created_at DESC (doesnt work)
Posts.where(is_featured: true).sort_by{|post| post.upvotes.count}.reverse!.sort_by{|post| post.created_at}.reverse!
What is the correct query?
I highly recommend that you implement a counter_cache for the upvotes and then simply do:
Posts.where(is_featured: true).order("upvotes_count DESC, created_at DESC")
You upvotes model should look like this
class Upvotes
belong_to :posts, :counter_cache => true
...
end
Then you will need upvotes_count:integer on your posts table.

Rails find all records with an association under certain conditions

I am having trouble finding the solution to this.
I have one model called Users which has a has_many association with the model SalaryReport. The user has to report their salary once a month, if not a reminder is to be sent out. My problem is finding the users that have not reported their salary. I can't figure out the best way to do this.
I have tried a few different approaches but nothing that has worked yet. Has anyone got any ideas?
Thanks in advance for your help!
look for Users that have the last reportet salary report befor the delimiting date:
Users.select('users.*, max(salary_reports.report_date) report_date').joins(:salary_reports).group(:id).having('max(salary_report.report_date)<?',limiting_date)
In your User model,
def self.no_salary_report
User.all.select {|u| u.salary_reports.empty? }
end
To call it, User.no_salary_report
This will get you all the users without any salary_reports. I don't exactly know your model names, so this is best I can do.
edit 1: For time range you additionally asked
def self.no_salary_reports_between(start_date, end_date)
User.all.select do |u|
u.salary_reports.select do |s|
(start_date < s.created_at) && (end_date > s.created_at)
end.empty?
end
end
Note that start_date and end_date arguments you give to this method must be DateTime object to work correctly compare the value to the created_at values of salary_reports.

show only posts created in last week

I want to be able to show posts and have them sorted by a couple criteria, first by the amount of votes they have on them and second by the date at which they were created. I don't want posts that are more than a week old being displayed so only posts in the last week. I tried doing this:
<%= render #posts.sort_by { |post| post.votes.count if post.created_at < 1.week.ago.utc }.reverse %>
but it gave me an error of comparison of NilClass with 2 failed
I know the code works by just sorting posts by vote count but I also want to limit time so could someone tell me how this can be done. I'm still new so sorry for the simplicity.
Solution by #Salil is ok, but I would suggest adding counter_cache column ( http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html ) and changing recent_post code (from this comment: https://stackoverflow.com/a/11498634/1392074 ) into:
def self.recent_posts
Post.where("created_at >= ?", 1.week.ago.utc).order("votes_count DESC, created_at DESC")
end
The code to find posts should be in Model and not on Views.
There is always a good idea that you should fetch the records which we need to display instead fetching the records and showing some of it on views.
You should do something like following
in your post.rb
def self.recent_posts
Post.select("p.*, COUNT(v.id) AS count").where("post.created_at >= 1.week.ago.utc").joins("p LEFT JOIN votes v on p.id=v.post_id").order("count, created_at DESC")
end

Ruby on Rails: how do I sort with two columns using ActiveRecord?

I want to sort by two columns, one is a DateTime (updated_at), and the other is a Decimal (Price)
I would like to be able to sort first by updated_at, then, if multiple items occur on the same day, sort by Price.
In Rails 4 you can do something similar to:
Model.order(foo: :asc, bar: :desc)
foo and bar are columns in the db.
Assuming you're using MySQL,
Model.all(:order => 'DATE(updated_at), price')
Note the distinction from the other answers. The updated_at column will be a full timestamp, so if you want to sort based on the day it was updated, you need to use a function to get just the date part from the timestamp. In MySQL, that is DATE().
Thing.find(:all, :order => "updated_at desc, price asc")
will do the trick.
Update:
Thing.all.order("updated_at DESC, price ASC")
is the Rails 3 way to go. (Thanks #cpursley)
Active Record Query Interface lets you specify as many attributes as you want to order your query:
models = Model.order(:date, :hour, price: :desc)
or if you want to get more specific (thanks #zw963 ):
models = Model.order(price: :desc, date: :desc, price: :asc)
Bonus: After the first query, you can chain other queries:
models = models.where('date >= :date', date: Time.current.to_date)
Actually there are many ways to do it using Active Record. One that has not been mentioned above would be (in various formats, all valid):
Model.order(foo: :asc).order(:bar => :desc).order(:etc)
Maybe it's more verbose, but personally I find it easier to manage.
SQL gets produced in one step only:
SELECT "models".* FROM "models" ORDER BY "models"."etc" ASC, "models"."bar" DESC, "models"."foo" ASC
Thusly, for the original question:
Model.order(:updated_at).order(:price)
You need not declare data type, ActiveRecord does this smoothly, and so does your DB Engine
Model.all(:order => 'updated_at, price')
None of these worked for me!
After exactly 2 days of looking top and bottom over the internet, I found a solution!!
lets say you have many columns in the products table including: special_price and msrp. These are the two columns we are trying to sort with.
Okay, First in your Model
add this line:
named_scope :sorted_by_special_price_asc_msrp_asc, { :order => 'special_price asc,msrp asc' }
Second, in the Product Controller, add where you need to perform the search:
#search = Product.sorted_by_special_price_asc_msrp_asc.search(search_params)

Resources