Grails many-to-many - 查找包含特定对象列表的所有对象

时间:2016-07-24 18:33:50

标签: grails gorm

我有以下域名模型:

class Recipe {
    String title
    static hasMany = [ ingredients : Ingredient ]
}

class Ingredient {
    String ingredient
    static hasMany = [ recipes : Recipe ]
    static belongsTo = Recipe
}

Grails使用成分ID和配方ID创建了表RECIPE_INGREDIENTS。

如何通过传递成分列表获得食谱清单?

def egg = new Ingredient(ingredient:"Egg")
def milk = new Ingredient(ingredient:"Milk")
def flour = new Ingredient(ingredient:"Flour")
def apple = new Ingredient(ingredient:"Apple")
def banana = new Ingredient(ingredient:"Banana")
def mango = new Ingredient(ingredient:"Mango")


def pizza = new Recipe(title:"Pizza")
pizza.addToIngredients(egg)
pizza.addToIngredients(milk)
pizza.addToIngredients(flour) 
pizza.save()

def salad = new Recipe(title:"Fruit Salad with milk")
salad.addToIngredients(apple)
salad.addToIngredients(banana)
salad.addToIngredients(mango)
salad.addToIngredients(milk)
salad.save()

例如:

[mango, milk] return me salad
[milk] return me salad and pizza
[milk, flour] return me pizza

1 个答案:

答案 0 :(得分:0)

您需要检查属于ingredients的一组Recipe是否包含您要传递的ingredients的子集。我无法想到使用GORMCriteira执行此操作的直接方法。使用HQL可以解决这个问题:

public List<Recipe> fetchRecipe(List<String> ingredients){
    ingredients = ingredients.unique()
    Recipe.executeQuery('''
        SELECT recipe FROM Recipe AS recipe
        JOIN recipe.ingredients as ingredients
        WHERE ingredients.ingredient in :ingredients
        GROUP BY recipe
        HAVING COUNT(recipe) = :count
    ''', [ingredients: ingredients, count: ingredients.size().toLong()])
}

所以当你用:

执行这个方法时
println fetchRecipe(['milk'])*.title
println fetchRecipe(['milk', 'banana'])*.title
println fetchRecipe(['milk', 'egg'])*.title

将输出:

[Pizza, Fruit Salad with milk]
[Fruit Salad with milk]
[Pizza]

工作原理

  • 查询首先选择配料列表中包含任何配料的所有食谱。只要配方中含有至少一种成分,它就会被退回。

  • 这会产生为每个匹配成分列出食谱的副作用。例如,如果食谱中有两种成分,则食谱会返回两次。 GROUP BY子句使查询过滤掉那些重复的列表。

  • 然而,这些重复是这个黑客的关键:如果成分列表是一个唯一的列表,并且食谱没有多次相同的成分,那么配方具有所有必需的成分,如果数量重复数量等于成分数量。这就是HAVING子句通过计算食谱数量所做的事情。