neo4j - 查找多个记录时,Cypher 查询不返回关系

标签 neo4j cypher

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

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

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

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

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

有什么指示可以指出我做错了什么吗?

最佳答案

Gabor 的查询向前迈出了一大步,因为它将 WHERE 移到了 WITH 之后,而不是与 OPTIONAL MATCH 一起保留......这是您无法获得正确结果的主要原因。

但是,查询仍然需要一些效率改进。就其一而言,如果您不立即运行聚合,则连续的多个 MATCH 或 OPTIONAL MATCH,尤其是那些将返回多行(成分、类别)的 MATCH 或 OPTIONAL MATCH,将影响其余 MATCH 或 OPTIONAL MATCH 的效率。

例如,对于包含 3 种成分和 2 个类别的单个食谱,在前两个可选匹配完成时会发出 3 x 2 = 6 行,这意味着其余的可选匹配需要在所有 6 个选项中执行这些行,但您的意图是让它们仅在每个配方中执行一次,而不是多次。

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

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

最后,由于您似乎只想返回包含某些成分和某些类别的食谱的行,因此我们应该对成分和类别使用 MATCH,而不是 OPTIONAL 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(集合是单行,而不是未收集时为多行)。

您还应该能够看到,由于我们提前过滤掉了没有所需类别或成分的食谱,因此作者和 react 末尾的可选匹配将仅适用于具有所需类别和成分的食谱,而不是无用地运行稍后将被过滤掉的食谱。

编辑

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

关于neo4j - 查找多个记录时,Cypher 查询不返回关系,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40820717/

相关文章:

python - 模块未找到错误: No module named 'neo4j.addressing' and ModuleNotFoundError: No module named 'neo4j'

neo4j - 如何隐藏 Neo4j 中节点之间不需要的关系

csv - neo4j:带有 uuid 的 Cypher LOAD CSV

json - Neo4j/Cypher - 返回带有链接节点的嵌套 JSON 对象

nosql - 用于创建多个节点和关系的 Cypher 查询

database-design - Neo4j 设计 : When to use Properties for Relationships

database - 为什么 Neo4j 中的这些查询会返回不同的结果?

neo4j - Cypher/Neo4J 中的集合是什么?

neo4j - 不能与空值合并; Neo4j 中的 'Cannot merge node using null property value'

neo4j - 从 cypher 中的多个特定路径查询节点