Get children of a collection of parents with ActiveRecord - ruby-on-rails

I have a Category model and Post model with a one-to-many relationship.
I'd like to have all posts for a certain set of categories.
I want the result to be an ActiveRecord object to be able to do further queries.
Right now I'm using .map like so
categories.map{|c| c.posts.order(position: :asc)}

Use embedded query to Posts, as follows:
Post.where(category_id: Category.all.pluck(:id)).order(position: :asc)

First find all the categories that you are interested and get the ids:
category_ids = Category.where('name like ?', '%foo%').pluck(:id)
Then just query for the Posts where the category_id is included in this list of ids:
posts = Post.where(category_id: category_ids)
This is a AR object, so you can keep adding order or where and so on:
posts.order(position: :asc)

Related

Only return associative records that include all IDs in array

Say I have the following tables and relationships:
class Book
has_many :book_genres
end
class Genre
has_many :book_genres
end
class BookGenre
belongs_to :book
belongs_to :genre
end
If I wanted to only find the books that have two specific genre IDs (13005 and 9190),
I was thinking I could do something like:
Book.where(book_genres: {genre_id: [13005, 9190]})
But it's returning books that have either Genres of ID 13005 OR 9190.
I also thought maybe I could filter it down by doing something like:
Book.where(book_genres: {genre_id: [13005]}).where(book_genres: {genre_id: [9190]})
But that's not working either.
How would I only find the Books that contain Genres with both IDs?
Notes:
From this post (https://stackoverflow.com/a/3416838/1778314), I tried doing this:
Book.joins(:book_genres).where(book_genres: { genre_id: [13005,9190]}).group("books.id").having("count(book_genres.id) = #{[13005, 9190].count}")
But this doesn't work either (returns both sets).
Additionally, there's a similar post using Recipes and Ingredients with no working solution: Ruby On Rails 5, activerecord query where model association ids includes ALL ids in array
Books that have two specific genre IDs (13005 and 9190),
book_ids_either_have_two_ids = Book. includes(:book_genres).where("book_genres.id IN ?= [3005 9190]")distinct.pluck(:id)
Book.where.not("id IN ?", book_ids_either_have_two_ids)
I know following is not proficient way to do but can solve your problem,
Book.includes(:book_genres).select { |book| book.book_genres.map(&:genre_id).uniq.sort == [9190, 13005] }
Above will return ouput in Array type instead of ActiveRecord::Relation but it will work as per your requirement.
This worked for me:
Book.joins(:book_genres).where(book_genres: { genre_id: [13005, 205580]}).group("books.id").having('count(books.id) >= ?', [13005, 205580].size)

Retrieving new mongoid criteria based on filter in parent model

I have a schema where product has_many articles
I am retrieving a mongoid criteria based on scopes I created on the article model :
criteria = Article.published.with_image
From this criteria, I would like now to find all articles for which their products have a certain subject_id (or a subset of subject_ids).
I tried to writte :
criteria = criteria.in('product.subject_ids': data[:subjects])
where data[:subjects] is an array of subject_ids but this doesn't work.
Is there a clean way to do this with mongoid without having to loop over all articles from the first criteria or pluck all product_ids from the first criteria ?
How about any of these?
Project.where(:subject_id.in => data[:subject_id], :article_ids.in => criteria.pluck(:id))
criteria = Article.eagerload(:products).published.with_image
criterial.map {|art| return art.product if data[:subjects].any? {|subjects| art.product.subject_ids.include?(id) }

How to use active record reputation gem with many to many relationship

I am using the active record reputation system gem.
My starting point was to display a list of businesses with their associated votes:
#businesses = Business.find_with_reputation(:votes, :all, order: "votes desc")
Now I need to group the businesses by category and estate.
I want to display a list of businesses from a certain category inside an estate with their associated votes.
So I have the following models (many to many)
Business has many Categories through Categorizations
Category has many Businesses through Categorizations
Business has many Estates through Localizations
Estate has many Businesses through Localizations
I can get the grouped businesses
#businesses = #estate.businesses.joins(:categories).where(categories: {id: #category.id})
but I don't know how to get the associated votes. Any ideas?
The two might be chainable:
#businesses = #estate.
businesses.
joins(:categories).
where(categories: {id: #category.id}).
find_with_reputation(:votes, :all, order: "votes desc")
Why could this work?: Most queries in ActiveRecord actually don't return the objects, but an ActiveRecord::Relation object that you can append queries to and use for further chained queries.

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 do I select only the associated objects in a Rails "where" query?

I have a model Category, which has_many Products, and a Product in turn has_many Categories. When a user searches for a Category, I'd like to return the products of the matching Categories without losing my Arel object. Here's what I have so far:
Category.where("upper(title) like ?", search_term.upcase).map {|category| category.products}.flatten
This does the trick of returning the products, but of course what I have is an array and not Arel. I can get as far as adding an :includes(:products) clause, so I do indeed get the products back but I still have them attached to their categories. How do I adjust my query so that all I get back is an Arel that only addresses products?
If it is products that you want then you should probably start with the Product object when you are searching. For example ,you could do it like this:
Product.joins(:categories).where("upper(categories.title) like ?", search_term.upcase)
The reason I use joins instead of includes is that joins will perform an INNER JOIN instead of LEFT OUTER JOIN which is what you need to only return the products that are actually associated with the found categories.
To make it a little more elegant you could wrap it all up in a scope in your Product model like this:
# In Product.rb
scope :in_categories_like, Proc.new{ |search_term|
joins(:categories).where("upper(categories.title) like ?", search_term.upcase)
}
# In use
#products = Product.in_categories_like(params[:search_term])

Resources