2017-10-13 2 views
1

J'ai actuellement ces trois tableaux:Comment utiliser et condition dans une requête ActiveRecord

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 

Je suis en train de faire un seach qui retourne tous les cocktails qui contiennent tous les ingrédients dans ma recherche. Jusqu'à présent, je suis parvenu à trouver ceci:

CocktailIngredient.where (ingredient_id: [1, 4]) carte. (&: cocktail) .uniq

qui retourne tous les cocktails qui incluent le ingrédient_id de 1 ou 4. J'essaie de retourner seulement les cocktails qui contiennent les deux ingrédients avec les id 1 et 4.

Toute aide serait grandement appréciée.

+0

pendant que je recherche votre problème ... quel est le but du champ «ingridients» dans la table de cocktails? – AntonTkachov

+0

Pour afficher facilement les ingrédients du cocktail à l'utilisateur par la suite – pfrasdog

Répondre

1

Voici une version généralisée, si vous avez un tableau ingredient_ids:

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

Voici une portée testée pour votre modèle Cocktail (avec Rspec et factorygirl). Vous devrez simplement appeler le Cocktail.with_ingredients([1,4]).

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

Et cocktail_spec.rb: https://gist.github.com/ArnoHolo/54b9259fbaa067d7abbf04a73d94ec40

+0

Je pense que tel cas où le chaînage se traduira toujours en tableau vide, parce que ces conditions se contredisent. – AntonTkachov

+0

Oui, il retourne un tableau vide lors de l'utilisation de la chaîne .where – pfrasdog

+0

C'est vrai, j'ai édité ma réponse! –

1

Le meilleur que je trouve pour maintenant:

Cocktail.joins(:cocktail_ingredients).where(cocktail_ingredients: { ingredient_id: 1, cocktail_id: CocktailIngredient.where(ingredient_id: 4).select(:cocktail_id) })

Je ne pense pas que ce soit la mieux que nous pouvons faire, mais c'est BEAUCOUP PLUS optimisé que la solution précédente