Cypher查询在查找多条记录时不会返回关系

时间:2016-11-26 16:54:23

标签: neo4j cypher

我正在编写一个搜索食谱的查询。我想使用与查询匹配的配方返回当前用户的交互(喜欢等)。

此查询正确返回当前用户的所有交互:

MATCH (recipe)<-[reaction:REACTS]-(beholder:User {cuid: 'some-id'})
RETURN reaction

但是,当我将匹配添加到现有搜索查询时,reaction变量对于每条记录都为空:

MATCH (recipe:Recipe)
OPTIONAL MATCH (recipe)-[a:CONTAINS]->(i:Ingredient)
OPTIONAL MATCH (recipe)-[:IS]->(c:Category)
OPTIONAL MATCH (recipe)<-[:AUTHORED]-(u:User)
OPTIONAL MATCH (recipe)<-[reaction:REACTS]-(beholder:User {cuid: 'some-id'})
WHERE ALL(
  ingredient IN ['tomato', 'banana']
  WHERE (recipe)-[:CONTAINS]->(:Ingredient {name: ingredient})
)
AND ALL(
  category IN ['smoothie']
  WHERE (recipe)-[:IS]->(:Category {name: category})
)
RETURN DISTINCT recipe,
{username: u.username, cuid: u.cuid} AS author,
{love: reaction.love, favorite: reaction.favourite} AS interactions,
collect( DISTINCT {name: i.name, amount: a.amount}) AS ingredients,
collect( DISTINCT {name: c.name}) AS categories

我用来获取id的单个食谱的查询可以正常工作:

MATCH (recipe:Recipe {cuid: {recipeCuid}})
OPTIONAL MATCH (recipe)-[a:CONTAINS]->(i:Ingredient)
OPTIONAL MATCH (recipe)-[:IS]->(c:Category)
OPTIONAL MATCH (recipe)<-[:AUTHORED]-(u:User)
OPTIONAL MATCH (recipe)<-[reaction:REACTS]-(beholder:User {cuid: {beholderCuid}})
RETURN recipe,
{username: u.username, cuid: u.cuid} AS author,
{love: reaction.love, favorite: reaction.favorite} AS interactions,
collect( DISTINCT {name: i.name, amount: a.amount}) AS ingredients,
collect( DISTINCT {name: c.name}) AS categories

指出我做错了什么?

2 个答案:

答案 0 :(得分:3)

Gabor的查询是一个很好的进步,因为它将你的WHERE移动到WITH之后而不是保持它与OPTIONAL MATCH ...这是你无法获得正确结果的主要原因。

但是,查询仍需要提高效率。对于一个,连续的多个MATCH或OPTIONAL MATCH,特别是那些将返回多行(成分,类别)的MATCH,如果不立即运行聚合,将影响其余MATCH或OPTIONAL MATCH的效率。

例如,对于具有3个成分和2个类别的单个配方,在前两个OPTIONAL MATCH完成时发出的3 x 2 = 6行,这意味着其他可选匹配需要在所有6个中执行那些行,但你的意图是每个食谱只执行一次,而不是多次。

这就是为什么尽快聚合是有用的,因为它可以将每个配方的行数减少到一个,而不是多个(配方与单一成分和单一类别,对于配方和成分和类别的每个组合)

此外,只有在匹配完所有内容之后,才会过滤掉(基于成分和类别),这意味着您在行上运行了许多可选匹配,肯定会被过滤掉。这是浪费的工作和浪费db命中。最好尽快进行过滤,然后在您知道将要返回的行上运行所需的其他可选匹配。

最后,由于您似乎只想要使用某些成分和仅某些类别的食谱行,我们应该使用MATCH来分析成分和类别,而不是选择性匹配。

我建议使用类似的方法来改进查询:

MATCH (cat:Category) 
WHERE cat.name IN ['smoothie']
WITH COLLECT(cat) as desiredCategories
MATCH (i:Ingredient)
WHERE i.name IN ['tomato', 'banana']
WITH desiredCategories, COLLECT(i) as desiredIngredients
MATCH (recipe:Recipe)
WHERE ALL(
  category IN desiredCategories
  WHERE (recipe)-[:IS]->(category)
) 
AND ALL(
  ingredient IN desiredIngredients
  WHERE (recipe)-[:CONTAINS]->(ingredient)
)
WITH recipe
MATCH (recipe)-[:IS]->(c:Category)
WITH recipe, COLLECT(c) as categories
MATCH (recipe)-[a:CONTAINS]->(i:Ingredient)
WITH recipe, categories, COLLECT({name: i.name, amount: a.amount}) as ingredients
OPTIONAL MATCH (recipe)<-[:AUTHORED]-(u:User)
// only one author, so okay to use optional matches back to back
OPTIONAL MATCH (recipe)<-[reaction:REACTS]-(beholder:User {cuid: 'some-id'})
RETURN recipe,
  {username: u.username, cuid: u.cuid} AS author,
  {love: reaction.love, favorite: reaction.favourite} AS interactions,
  ingredients,
  categories

你应该能够看到通过在我们执行匹配返回多行后立即运行我们的COLLECTS(),我们将每个配方的构建行与集合保持为1(集合是单行,vs未收集时有多行。

您还应该能够看到,因为我们过滤掉了早期没有所需类别或成分的配方,最后作者和反应的可选匹配仅适用于具有所需类别和成分的配方而不是无用地运行将在以后过滤掉的食谱。

修改

我注意到,最近,您检查类别和成分的方式存在问题,您无法直接将节点与数组中的名称进行比较。您可能有name属性:成分和:类别节点,我们应该使用它来匹配必要的成分和类别,然后在开始附近过滤食谱,所以我们只使用食谱与这些类别和成分。这也让我们避免匹配和收集类别和成分,直到我们完成过滤。我已相应更新了查询。

答案 1 :(得分:2)

尝试收集集合的成分和类别,并对这些集合执行检查。 RETURN子句有点简化,但让我们先看看它是否有效。

MATCH (recipe:Recipe)
OPTIONAL MATCH (recipe)-[a:CONTAINS]->(i:Ingredient)
OPTIONAL MATCH (recipe)-[:IS]->(c:Category)
OPTIONAL MATCH (recipe)<-[:AUTHORED]-(u:User)
OPTIONAL MATCH (recipe)<-[reaction:REACTS]-(beholder:User {cuid: 'some-id'})
WITH recipe, collect(i) AS ingredients, collect(c) AS categories, u, reaction, beholder
WHERE ALL(
  ingredient IN ['tomato', 'banana']
  WHERE ingredient in ingredients
)
AND ALL(
  category IN ['smoothie']
  WHERE category in categories
)
RETURN DISTINCT recipe,
  {username: u.username, cuid: u.cuid} AS author,
  {love: reaction.love, favorite: reaction.favourite} AS interactions,
  ingredients,
  categories