Reputation: 13
I currently have these three tables:
create_table "cocktail_ingredients", force: :cascade do |t|
t.integer "cocktail_id"
t.integer "ingredient_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["cocktail_id"], name: "index_cocktail_ingredients_on_cocktail_id"
t.index ["ingredient_id"], name: "index_cocktail_ingredients_on_ingredient_id"
create_table "cocktails", force: :cascade do |t|
t.string "title"
t.string "ingredients"
t.text "method"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
create_table "ingredients", force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
I'm trying to do a seach that returns all the cocktails that contain ALL of the ingredients in my search. So far i've managed to come up with this:
CocktailIngredient.where(ingredient_id: [1, 4]).map(&:cocktail).uniq
Which returns all the cocktails that include the ingredient_id of 1 or 4. I am trying to return only the cocktails that contain BOTH ingredients with the id 1 AND 4.
Any help would be much appreciated.
Upvotes: 1
Views: 95
Reputation: 317
Here is a generalised version, if you have an ingredient_ids
array:
cocktail_ingredients = [1, 4]
cocktail_ingredients = CocktailIngredient.where(ingredient_id: ingredient_ids).select(:cocktail_id)
cocktail_ingredients = cocktail_ingredients.group(:cocktail_id).having('COUNT(ingredient_id) >= ?', ingredient_ids.count)
cocktails = Cocktail.where(id: cocktail_ingredients.select(:cocktail_id))
Here is a tested scope for your Cocktail
model (with Rspec and FactoryGirl). You'll just have to call Cocktail.with_ingredients([1,4])
.
In cocktail.rb:
scope :with_ingredients, (lambda do |ingredient_ids|
cocktail_ingredients = CocktailIngredient.where(ingredient_id: ingredient_ids).select(:cocktail_id)
cocktail_ingredients = cocktail_ingredients.group(:cocktail_id).having('COUNT(ingredient_id) >= ?', ingredient_ids.count)
where(id: cocktail_ingredients.select(:cocktail_id))
end)
And cocktail_spec.rb: https://gist.github.com/ArnoHolo/54b9259fbaa067d7abbf04a73d94ec40
Upvotes: 1
Reputation: 1784
The best I found for now is:
Cocktail.joins(:cocktail_ingredients).where(cocktail_ingredients: { ingredient_id: 1, cocktail_id: CocktailIngredient.where(ingredient_id: 4).select(:cocktail_id) })
I don't think it's the best we can do, but it's MUCH MORE optimized then previous solution
Upvotes: 1