rails association, single record only - ruby-on-rails-3

I have an association like so
has_many :comments, :dependent => :delete_all, :order => "created_at ASC", :include => [:user]
this is returning only the first comment. while it should be returning all...
EDIT from comment :
a = Activity.find(1) a.comments a.comments has only one record entry, while Comment.find_all_by_activity_id(1) has 4
EDIT after trials...
It seems that using :include => [:user, :comments] is limiting the result to one comment for every activity.
any idea why? and more importantly how to fix it? I could remove it from include, but i'd like to avoid n+1 queries...

I'd look at the SQL that's being generated for each call, either in the console output or database log file.
Are the generated statements the same?

Related

Rails sort collection by association count

I'm working in Rails 4, and have two relevant models:
Account Model
has_many :agent_recalls, primary_key: "id", :foreign_key => "pickup_agent_id", class_name: "Booking"
Hence, queries like Account.find(10).agent_recalls would work.
What I want to do is sort the entire Account collection by this agent_recalls association.
Ideally it'd look something like (but obviously not):
#agents = Account.where(agent: true).order(:agent_recalls)
Question: What's the correct query to output an ordered list, by this agent_recall count?
Well to accomplish what you are looking for you have 2 options:
first, only a query, but it will implied a join, so there will be lost the Accounts that doesn't have any agent_recalls, so i will discard this option
second, i think this one is more appropriate for what you are trying to do
Account.find(:all, :conditions => { :agent => true }, :include => :agent_recalls).sort_by {|a| a. agent_recalls.size}
As you can see is a mix between a query and ruby, hope it helps :)

Limit the amount of results in associated queries with Rails 3.2

I have the following query:
#books = Books.includes(:author, :pages)
.find(:all,
:order => 'created_at DESC')
Let's assume my "Pages" table has fields "words, pictures". For blank pages, field "words" is NULL. There are many "Pages" records per book.
The problem with the above query, is that it retrieves ALL the pages for each book. I would like to retrieve only 1 page record for example with the condition "NOT NULL" on the "words" field. However, I don't want to exclude from the query results the Books that do not match the pages query (I have 10 books in my table and I want 10 books to be retrieved. The book.page association should be "nil" for the books where the condition does not match.)
I hope this makes sense.
Check this SO question:
Rails 3 - Eager loading with conditions
It looks like what you want
class Category
has_many :children, :class_name => "Category",
:foreign_key => "parent_id"
has_many :published_pages, :class_name => "Page",
:conditions => { :is_published => true }
end
If you only want a single blank page to be returned then you could add an association:
has_one :first_blank_page, -> {merge(Page.blanks).limit(1)}, :class_name => "Page"
... where in page.rb ...
def blanks
where(:words => nil)
end
Then you can:
#books = Books.includes(:author, :first_blank_page).order('created_at desc')
... and subsequently reading first_blank_page would be very efficient.
The limit will not be used if you eager load, though, as the SQL syntax for that sort of this would be very complex to execute as one query, so you'd want to consider whether you want to eager load all of the pages per book and then just use one per book. It's a tricky trade-off.

putting a condition on an includes

I have the following relationships:
Category has_many :posts
Post has_many :comments
Post has_many :commenters, :through => :comments
I have the following eager load, giving me posts, comments and commenters (note that I need all 3, and hence the includes as opposed to joins)
category.posts.includes(:comments, :commenters)
However, I'd like to limit comments (and if possible commenters) to only those created in the past two weeks while still returning the same set of posts. Initially I thought I could specify a condition on the includes:
category.posts.includes(:comments, :commenters).where("comments.created_at > ?", 2.weeks.ago)
But found that this returns only the posts that meet the condition. I'm thinking that I may need to do something like performing a subquery on comments and then performing a join. Is there an easy way to do this with AR of would I be better off doing this with sql?
Finally managed to figure this out from reading this page:
http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html
I simply needed to create an association in my Post model like:
Post has_many :recent_comments, :class_name = 'Comment', :conditions => ["created_at > ?", 2.weeks.ago]
Then I could do the following to get the desired ActiveRecord::Association object:
category.posts.includes(:recent_comments => :commenters)
There was also a suggestion of doing this by using a scope on a model. However, I read somewhere (I think it was on SO) that scopes are on their way out and that ARel has taken their place so I decided to do this without scopes.
Try :
category.posts.all(:includes => {:comments =>:commenters}, :conditions => ["comments.created_at = ? AND commenters.created_at = ?", 2.weeks.ago, 2.weeks.ago]

Finding a node with no children in a tree like structure with Rails

I have a tree like structure roughly like this:
class Node < ActiveRecord::Base
belongs_to :parent, :class_name => self.to_s, :foreign_key => 'parent_id'
has_many :children, :class_name => self.to_s, :foreign_key => 'parent_id', :dependent => :destroy
...
end
I can load all nodes that don't have a parent with this scope:
named_scope :not_child, :conditions => 'parent_id IS NULL'
But I also need to find nodes that don't have children but can have a parent and I am having a hard time with it. I guess I have to include children_events but then I am lost, I cannot use:
named_scope, :faulty_not_parent, :include => :children, :conditions => "node.parent_id IS NULL"
Got it:
named_scope :not_parent, :conditions => "id NOT IN (SELECT DISTINCT parent_id FROM nodes WHERE parent_id IS NOT NULL)"
I highly recommend you use a plugin for this. There are a few, acts_as_tree, awesome_nested_set and my personal recommendation, ancestry which uses only one column to map the structure rather than three as in nested set structures.
I'm not a SQL guru, so I couldnt' figure out a good way to do this in pure SQL. Of course, this means I couldn't find a way to do it with named_scopes, either. But you can find parents, and subtract that from all nodes, like so:
def childless
#childless ||= self.all - self.all(:joins => :children)
end
This isn't as elegant as I'd like, since it requires loading a lot in memory. But it only takes 2 select statements, which is good. I'll be interested to see the other answers.

Ruby-on-Rails: How to pull out most recent entries from a limited subset of a database table

Imagine something like a model User who has many Friends, each of who has many Comments, where I'm trying to display to the user the latest 100 comments by his friends.
Is it possible to draw out the latest 100 in a single SQL query, or am I going to have to use Ruby application logic to parse a bigger list or make multiple queries?
I see two ways of going about this:
starting at User.find and use some complex combination of :join and :limit. This method seems promising, but unfortunately, would return me users and not comments, and once I get those back, I'd have lots of models taking up memory (for each Friend and the User), lots of unnecessary fields being transferred (everything for the User, and everything about the name row for the Friends), and I'd still have to step through somehow to collect and sort all the comments in application logic.
starting at the Comments and using some sort of find_by_sql, but I just can't seem to figure out what I'd need to put in. I don't know how you could have the necessary information to pass in with this to limit it to only looking at comments made by friends.
Edit: I'm having some difficult getting EmFi's solution to work, and would appreciate any insight anyone can provide.
Friends are a cyclic association through a join table.
has_many :friendships
has_many :friends,
:through => :friendships,
:conditions => "status = #{Friendship::FULL}"
This is the error I'm getting in relevant part:
ERROR: column users.user_id does not exist
: SELECT "comments".* FROM "comments" INNER JOIN "users" ON "comments".user_id = "users".id WHERE (("users".user_id = 1) AND ((status = 2)))
When I just enter user.friends, and it works, this is the query it executes:
: SELECT "users".* FROM "users" INNER JOIN "friendships" ON "users".id = "friendships".friend_id WHERE (("friendships".user_id = 1) AND ((status = 2)))
So it seems like it's mangling the :through to have two :through's in one query.
Given the following relationships:
class User < ActiveRecord::Base
has_many :friends
has_many :comments
has_many :friends_comments, :through => :friends, :source => :comments
end
This statement will execute a single SQL statement. Associations essentially create named scopes for you that aren't evaluated until the end of the chain.
#user.friends_comments.find(:limit => 100, :order => 'created_at DESC')
If this is a common query, the find can be simplified into its own scope.
class Comments < ActiveRecord::Base
belongs_to :user
#named_scope was renamed to scope in Rails 3.2. If you're working
#if you're working in a previous version uncomment the following line.
#named_scope :recent, :limit => 100, : order => 'created at DESC'
scope :recent, :limit => 100, :order => 'created_at DESC'
end
So now you can do:
#user.friends_comments.recent
N.B.: The friends association on user may be a cyclical one through a join table, but that's not important to this solution. As long as friends is a working association on User, the preceding will work.