Select descendants from has_many association in Rails 4 - ruby-on-rails

So I have a list of sources and have entries. Entries have a field source_id to reference the Source. Now I need to get the count of entries for a number of sources, via the user_id field which is a field in sources.
So basically I need to select all source IDs from a certain user_id and then through those IDs, get a number of entries. I tried the following:
entries_count = Source.where('user_id = ?', user_id).entries.count
But this obviously didn't work.
What would be the way to achieve this?

entries_count = Sources.where('user_id = ?', user_id) will return a collection, not an object. So you have to get each collection element's entries first, and count after:
entries_count = Sources.where('user_id = ?', user_id).map(&:entries).flatten.count
Unrelated thing: is you model really called Sources or it is a typo and it's Source in fact?

I would go from entries to sources using
Entry.joins(:source).where(sources: {user_id: user_id}).count

Related

Ruby on Rails: where returning nil

In my app I'm obtaining a certain category, and I'm filtering the associated items based on their name.
The following code should be pretty clear:
categories = Category.where(:id => params[:category_id]).includes(:items).where("lower(items.name) like ?", "%#{params[:keywords].downcase}%")
However, if the name filter excludes all the items, the categories object returned by where is nil. Is this the expected behaviour? How can I get the category even either items exist or not?
The easiest way might be to just split the query:
#category = Category.find(params[:category_id])
#items = #category.items.where("lower(items.name) like ?", "%#{params[:keywords].downcase}%")
Based on your code it seems like category_id references only 1 category so I've changed it to singular.
You should look into doing an OUTER JOIN against the items table which will give you categories regardless of whether or not their items meet the name filter.

How Do I Use Joins for Three Tables in Ruby on Rails?

I am working on a Ruby on Rails application which already has logic for text searching using pg_search and two other fields on a model. The logic creates an 'array' of rows from the search result. I do not remember the actual name of this since technically this is not an array. It is an instance variable of selected rows. I want to add search criteria from two additional models. My database is PostgreSQL.
Here is a subset of all three model definitions:
MediaLibrary: name, media_creator_id, media_type_id (fields that are being used in current search; has many media_topics and has many media_targets)
MediaTopic: media_library_id, topic_id (want to search for topic_id; belongs to media_library; topic_id being searched is coming from a Topic model (id, name))
MediaTarget: media_library_id, target_id (want to search for target_id; belongs to media_library; target_id being searched is coming from a Target model (id, name))
I'm thinking that I should be able to do something like this if both topic and target are being searched along with the other three search criteria. I will also need to have topic_id and target_id in my results so I can display Topic.name and Target.name on my view.
#media_libraries = MediaLibrary.text_search(params[:query]).where("media_creator_id = ? AND media_type_id = ?", params[:media_creator_id].to_i, params[:media_type_id].to_i).joins(:media_topics.where("media_library_id = ? and topic_id = ?", id_from_media_library_row, params[:topic_id].to_i).joins(:media_targets.where("media_library_id = ? and target_id = ?", id_from_media_library_row, params[:target_id].to_i)
I have searched on postgresql.org and Stack Overflow but have not found anything joining three tables using Ruby on Rails that was answered by anyone.
You can pass a SQL join statement into #joins. I'm not sure what it'd be in your case but you can do something like:
#media_libraries = MediaLibrary.joins(%q(
JOIN media_targets
ON media_targets.media_library_id = media_libraries.id
JOIN media_topics
ON media_topics.media_library_id = media_libraries.id
)).text_search(params[:query])
.where(
media_libraries: {
media_creator_id: params[:media_creator_id],
media_type_id: params[:media_type_id]
},
media_topics: { id: params[:topic_id] },
)

Trying to return AREL not Array in Rails

I have two models: Products and Tags through a products_tags join in a HABTM relationship.
I am currently defining my controller index as:
#stats = Product.all(:include => :tags).uniq
which returns an array. How can I return an Active Rel object? I tried added scoped, however received a no method error.
I need to find and list a unique list of tags, and be able to view what product each specif tag belongs to.
Try #stats = Product.includes(:tags).uniq.
Note that uniq turns the Relation into an Array; if you want to do something like SELECT DISTINCT you'll want to use Product.includes(:tags).select('DISTINCT whatever').

How to Average Multiple Columns in Rails

I have the following objects: Products, Ratings, and Users. I am trying to filter and select a number of Products that the User owns (through a has_many :through relationship with UserProducts) and average a certain column the Ratings table that matches their User ID and the correct Product ID.
So, my function is something along these lines:
def find_rating(criteria)
product = self.products.find(:all, :conditions => ["criteria = ?", criteria])
rating = self.ratings.where("product_id = ?", product).average(:overall)
end
I think that I'm going about this the wrong way, because I'm trying to find a product_id by passing an entire array of data consisting of multiple products. But, I think of using a more traditional loop and that seems convoluted. Can someone point me in the right direction for solving this problem? Thanks!
If product is a single entry, as it appears to be in your code, I would do this:
rating = self.products.find_by_criteria(criteria).ratings.average(:overall)
If it's an array of products, this method may help you: http://apidock.com/rails/ActiveRecord/Batches/ClassMethods/find_each

rails where() sql query on array

I'll explain this as best as possible. I have a query on user posts:
#selected_posts = Posts.where(:category => "Baseball")
I would like to write the following statement. Here it is in pseudo terms:
User.where(user has a post in #selected_posts)
Keep in mind that I have a many to many relationship setup so post.user is usable.
Any ideas?
/EDIT
#posts_matches = User.includes(#selected_posts).map{ |user|
[user.company_name, user.posts.count, user.username]
}.sort
Basically, I need the above to work so that it uses the users that HAVE posts in selected_posts and not EVERY user we have in our database.
Try this:
user.posts.where("posts.category = ?", "Baseball")
Edit 1:
user.posts.where("posts.id IN (?)", #selected_posts)
Edit 2:
User.select("users.company_name, count(posts.id) userpost_count, user.username").
joins(:posts).
where("posts.id IN (?)", #selected_posts).
order("users.company_name, userpost_count, user.username")
Just use the following:
User.find(#selected_posts.map(&:user_id).uniq)
This takes the user ids from all the selected posts, turns them into an array, and removes any duplicates. Passing an array to user will just find all the users with matching ids. Problem solved.
To combine this with what you showed in your question, you could write:
#posts_matches = User.find(#selected_posts.map(&:user_id).uniq).map{ |user|
[user.company_name, user.posts.size, user.username]
}
Use size to count a relation instead of count because Rails caches the size method and automatically won't look it up more than once. This is better for performance.
Not sure what you were trying to accomplish with Array#sort at the end of your query, but you could always do something like:
#users_with_posts_in_selected = User.find(#selected_posts.map(&:user_id).uniq).order('username DESC')
I don't understand your question but you can pass an array to the where method like this:
where(:id => #selected_posts.map(&:id))
and it will create a SQL query like WHERE id IN (1,2,3,4)
By virtue of your associations your selected posts already have the users:
#selected_posts = Posts.where("posts.category =?", "Baseball")
#users = #selected_posts.collect(&:user);
You'll probably want to remove duplicate users from #users.

Resources