NHibernate:过滤一对多连接的结果
NHibernate: Filter result on one-to-many join
我正在尝试根据左侧联接中所有对象的值来过滤查询。我的对象定义如下:
public class Recipe
{
public virtual long Id {get;set;}
public virtual ISet<Ingredient> Ingredients { get; set; } = new HashSet<Ingredient>();
}
public class Ingredient
{
public virtual long Id{get;set;}
public virtual string Ingredient {get;set;}
public virtual decimal Amount {get;set;}
}
它们是这样映射的:
<hibernate-mapping>
<class name="Recipe" table="Recipe">
<set name="Ingredients" inverse="true">
<key column="RecipeId" />
<one-to-many class="Recipe" />
</set>
</class>
</hibernate-mapping>
<hibernate-mapping>
<class name="Ingredient" dynamic-update="true" table="Ingredient" lazy="false">
<id name="Id" column="Id" type="long" unsaved-value="0">
<generator class="native" />
</id>
<property name="Ingredient" not-null="true" lazy="false"/>
<property name= "Amount" column= "Amount" type="decimal" />
</class>
</hibernate-mapping>
我想做的是能够根据成分搜索食谱。例如,假设我想找到成分为
的所有食谱
Ingredient | Amount
Sugar | 100
Flour | 200
Eggs | 2
我什至不确定在 SQL 中我会怎么做。我试过这样的事情:
public Recipe FindRecipeByIngredients(ISet<Ingredient> ingredients)
{
return this.Session.QueryOver<Recipe>()
.Where(r => r.Ingredients.All(c => ingredients.Any(i => i.Ingredient == r.Ingredient && i.Amount == r.ConcentrAmountation))).SingleOrDefault();
}
但 Nhibernate 不知道如何将 All 转换为 SQL。
我在想也许可以反向搜索,在成分 table 中搜索具有正确成分和数量的所有记录,但随后我会想出包含这些成分和其他成分的所有食谱.
这可以使用 SQL 查询来完成,还是我必须接受这样一个事实,即我必须提取所有记录(使用最少的过滤)然后在代码中过滤它们?
(作为奖励,我还必须在未来过滤类似的食谱,因此数量几乎相等的食谱可能数量 <1.05refAmount && 数量 >0.95refAmount)
好的,所以我设法找到了一个相当好的解决方案。我需要 运行 的 sql 查询是:
SELECT RecipeId
FROM Ingredient
GROUP BY RecipeId
HAVING SUM(CASE WHEN Ingredient = 'Ingredient1' AND Amount = 200 THEN 1 ELSE 0 END) = 1
AND SUM(CASE WHEN Ingredient = 'Ingredient2' AND Amount = 300 THEN 1 ELSE 0 END) = 1
AND ...
我在将它转换成 nhibernate 可以正确构造的东西时遇到了一些问题,结果是这样的:
ICriteria criteria = Session.CreateCriteria<Ingredients>()
.SetProjection(Projections.GroupProperty(Projections.Property<Ingredient>(i => i.Recipe.Id)));
foreach (Ingredient ingredient in recipe.Ingredients)
{
criteria.Add(
Restrictions.Eq(
Projections.Sum(
Projections.Conditional(
Restrictions.Where<Ingredient>(i => i.Ingredient == ingredient.Ingredient && i.Amount == ingredient.Amount)
Projections.Constant(1),
Projections.Constant(0)
)
),
1));
}
其中returns上面的查询。我尝试使用 Restrictions.Conjuntction 或使用 QueryOver 来执行此操作,但条件查询最终在 GROUP BY 之前的 WHERE 条件而不是 GROUP BY 之后的 HAVING 条件导致不正确的 sql 查询。这可能是 Nhibernate 中的一个错误(类似于 NH-2863),但我不确定。
如果有人找到更有效的方法来解决这个问题,我很乐意更新它。
答案也是基于 SO 上的这些答案:
我正在尝试根据左侧联接中所有对象的值来过滤查询。我的对象定义如下:
public class Recipe
{
public virtual long Id {get;set;}
public virtual ISet<Ingredient> Ingredients { get; set; } = new HashSet<Ingredient>();
}
public class Ingredient
{
public virtual long Id{get;set;}
public virtual string Ingredient {get;set;}
public virtual decimal Amount {get;set;}
}
它们是这样映射的:
<hibernate-mapping>
<class name="Recipe" table="Recipe">
<set name="Ingredients" inverse="true">
<key column="RecipeId" />
<one-to-many class="Recipe" />
</set>
</class>
</hibernate-mapping>
<hibernate-mapping>
<class name="Ingredient" dynamic-update="true" table="Ingredient" lazy="false">
<id name="Id" column="Id" type="long" unsaved-value="0">
<generator class="native" />
</id>
<property name="Ingredient" not-null="true" lazy="false"/>
<property name= "Amount" column= "Amount" type="decimal" />
</class>
</hibernate-mapping>
我想做的是能够根据成分搜索食谱。例如,假设我想找到成分为
的所有食谱Ingredient | Amount
Sugar | 100
Flour | 200
Eggs | 2
我什至不确定在 SQL 中我会怎么做。我试过这样的事情:
public Recipe FindRecipeByIngredients(ISet<Ingredient> ingredients)
{
return this.Session.QueryOver<Recipe>()
.Where(r => r.Ingredients.All(c => ingredients.Any(i => i.Ingredient == r.Ingredient && i.Amount == r.ConcentrAmountation))).SingleOrDefault();
}
但 Nhibernate 不知道如何将 All 转换为 SQL。 我在想也许可以反向搜索,在成分 table 中搜索具有正确成分和数量的所有记录,但随后我会想出包含这些成分和其他成分的所有食谱. 这可以使用 SQL 查询来完成,还是我必须接受这样一个事实,即我必须提取所有记录(使用最少的过滤)然后在代码中过滤它们?
(作为奖励,我还必须在未来过滤类似的食谱,因此数量几乎相等的食谱可能数量 <1.05refAmount && 数量 >0.95refAmount)
好的,所以我设法找到了一个相当好的解决方案。我需要 运行 的 sql 查询是:
SELECT RecipeId
FROM Ingredient
GROUP BY RecipeId
HAVING SUM(CASE WHEN Ingredient = 'Ingredient1' AND Amount = 200 THEN 1 ELSE 0 END) = 1
AND SUM(CASE WHEN Ingredient = 'Ingredient2' AND Amount = 300 THEN 1 ELSE 0 END) = 1
AND ...
我在将它转换成 nhibernate 可以正确构造的东西时遇到了一些问题,结果是这样的:
ICriteria criteria = Session.CreateCriteria<Ingredients>()
.SetProjection(Projections.GroupProperty(Projections.Property<Ingredient>(i => i.Recipe.Id)));
foreach (Ingredient ingredient in recipe.Ingredients)
{
criteria.Add(
Restrictions.Eq(
Projections.Sum(
Projections.Conditional(
Restrictions.Where<Ingredient>(i => i.Ingredient == ingredient.Ingredient && i.Amount == ingredient.Amount)
Projections.Constant(1),
Projections.Constant(0)
)
),
1));
}
其中returns上面的查询。我尝试使用 Restrictions.Conjuntction 或使用 QueryOver 来执行此操作,但条件查询最终在 GROUP BY 之前的 WHERE 条件而不是 GROUP BY 之后的 HAVING 条件导致不正确的 sql 查询。这可能是 Nhibernate 中的一个错误(类似于 NH-2863),但我不确定。 如果有人找到更有效的方法来解决这个问题,我很乐意更新它。 答案也是基于 SO 上的这些答案: