putting a condition on an includes - ruby-on-rails-3

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]

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 :)

How to retrieve both "associated" records and "associated through" records in a performant way?

I am using Ruby on Rails 3.1 and I am trying to improve an SQL query in order to retrieve both "associated" records and "associated through" records (ActiveRecord::Associations) in a performant way so to avoid the "N + 1 query problem". That is, I have:
class Article < ActiveRecord::Base
has_many :category_relationships
has_many :categories,
:through => :category_relationships
end
class Category < ActiveRecord::Base
has_many :article_relationships
has_many :articles,
:through => :article_relationships
end
In a couple of SQL queries (that is, in a "performant way", maybe by using the Ruby on Rails includes() method) I would like to retrieve both categories and category_relationships, or both articles and article_relationships.
How can I make that?
P.S.: I am improve queries like the followings:
#category = Category.first
articles = #category.articles.where(:user_id => #current_user.id)
articles.each do |article|
# Note: In this example the 'inspect' method is just a method to "trigger" the
# "eager loading" functionalities
article.category_relationships.inspect
end
You can do
Article.includes(:category_relationships => :categories).find(1)
Which will reduce this to 3 queries (1 for each table). For performance, also make sure your foreign keys have an index.
But in general, I'm curious why the "category_relationships" entity exists at all, and why this isn't a has_and_belongs_to sort of situation?
Updated
As per your changed question, you can still do
Category.includes(:article_relationships => :articles).first
If you watch the console (or tail log/development) you'll see that when you call the associations, it'll hit the cached values and you're golden.
But I am still curious why you're not using a Has and Belongs To Many association.

Rails Model and foreign keys

Sorry if this has been asked before but I've tried searching and can't find exactly what I'm looking for.
I have two tables, one is called users and has a user_id field, the second is called users_friends and only has two fields, user_id and friends_id
I've spent the entire day trying many different variations and cannot get it to pull back the friends as user objects like I'm expecting.
The entire contents of my UsersFriends model is
belongs_to :user, :class_name => "User", :foreign_key => "user_id"
has_one :friends, :class_name => "User", :foreign_key => "id"
Thanks in advance,
J
I got a feeling that you do not understand how associations work. Please refer to: http://guides.rubyonrails.org/association_basics.html
Also you might find reading github page and/or code of acts_as_network plugin quite helpful and inspiring for building your "friends" feature. Please have a look:
https://github.com/sjlombardo/acts_as_network

rails association, single record only

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?

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.