views:

33

answers:

2

Hi all,

Need a little help with a SQL / ActiveRecord query. Let's say I have this:

Article < ActiveRecord::Base
  has_many :comments
end

Comment < ActiveRecord::Base
  belongs_to :article
end

Now I want to display a list of "Recently Discussed" articles - meaning I want to pull all articles and include the last comment that was added to each of them. Then I want to sort this list of articles by the created_at attribute of the comment.

I have watched the Railscast on include /joins - very good, but still a little stumped.

I think I want to use a named_scope, something to this effect:

Article < ActiveRecord::Base
  has_many :comments

  named_scope :recently_commented, :include => :comments, :conditions => {   some_way_to_limit_just_last_comment_added }, :order => "comments.created_at DESC"
end

Using MySQL, Rails 2.3.4, Ruby 1.8.7

Any suggestions? :)

A: 

You have two solutions for this.

1) You treat n recent as n last. Then you don't need anything fancy:

Article < ActiveRecord::Base   
  has_many :comments

  named_scope :recently_commented, :include => :comments, 
                :order => "comments.created_at DESC",
                :limit => 100 
end

Article.recently_commented # will return last 100 comments

2) You treat recent as in last x duration.

For the sake of clarity let's define recent as anything added in last 2 hours.

Article < ActiveRecord::Base   
  has_many :comments

  named_scope :recently_commented, lambda { { 
                    :include => :comments, 
                    :conditions => ["comments.created_at >= ?", 2.hours.ago]
                    :order => "comments.created_at DESC",
                    :limit => 100 }} 

end

Article.recently_commented # will return last 100 comments in 2 hours

Note Code above will eager load the comments associated with each selected article. Use :joins instead of :include if you don't need eager loading.

KandadaBoggu
Sorry I should be more clear here - what I am looking for is not to display an article with most recent comments. I am looking to display a list of all articles ordered by the date of their latest or newest comment. Perhaps this can be edited in a away to meet this? Appreciate your time and help!
Betsy
It might be something like this:named_scope :recently_commented, , :include => {:comments, {:order => "comments.created_at DESC", :limit => 1}}, :order => "comments.created_at DESC"This obviously does not work - especially since I am still trying to understand proper nesting.
Betsy
That was type from my part. The scope 'Article.recently_commented` returns the recently commented articles.
KandadaBoggu
You are correct - my mistake!! I did something similar earlier and thought it was not working correctly. My problem turned out to be the created_at column not being set correctly in my Factory (using factory girl). The created_at was the same for all test comments.
Betsy
A: 

You're gonna have to do some extra SQL for this:

named_scope :recently_commented, lambda {{
  :select => "articles.*, IFNULL(MAX(comments.created_at), articles.created_at) AS last_comment_datetime",
  :joins => "LEFT JOIN comments ON comments.article_id = articles.id",
  :group => "articles.id",
  :conditions => ["last_comment_datetime > ?", 24.hours.ago],
  :order => "last_comment_datetime DESC" }}

You need to use :joins instead of :include otherwise Rails will ignore your :select option. Also don't forget to use the :group option to avoid duplicate records. Your results will have the #last_comment_datetime accessor that will return the datetime of the last comment. If the Article had no comments, it will return the Article's created_at.

Edit: Named scope now uses lambda

Jose Fernandez
In your code, `24.hours.ago` will be set to the time model is loaded for the first time. You have to use a `lambda` for this.
KandadaBoggu
+1 for very helpful SQL! But I can't b/c I am a newb. Can someone share the love until I get to 15 points?
Betsy
Dont be afraid of the SQL :)
Jose Fernandez
Just out of curiosity, is there any benefit of writing out the JOIN statement instead of just doing `:joins => :comments`?
Beerlington
I believe using the symbol notation will always use an INNER JOIN, which will remove any Articles that don't have Comments - hence why I explicitly tell it to use a LEFT JOIN.
Jose Fernandez
This actually turned out to be the better solution for me - unfortunately I can't give you credit too- thanks for your help though!
Betsy