Neo4j 查询与节点之间的布尔运算

Neo4j query with Boolean operations between Nodes

我们有一个带有节点的图形数据库:Resume 和 Skill。 技能节点包含 属性 - "Name"。它具有 Java、C ...

等示例值

并且我们在简历和技能节点之间创建了关系 has_skill。并且每个 Resume 节点可以包含任意数量的具有关系 has_skill.

的 Skill 节点

现在,我们要检索满足以下布尔条件的所有简历节点。

((JAVA AND MYSQL) OR (C AND MSSQL)) AND HTML 

我们尝试了以下查询

match(n:Skill)--(n1:PannaResume)
where ((n.name contains "java" AND n.name contains "mysql")
OR (n.name contains "c" AND n.name contains "mssql")) 
AND n.name contains "html"
return n1

但这并没有给出预期的结果。请提出正确的查询。

您可以尝试通过模式是否存在来检查:

match (n1:PannaResume)-[:has_skill]->(n:Skill)
where ((n1)-[:has_skill]->(:Skill {name : "java"}) and (n1)-[:has_skill]->(:Skill {name : "mysql"}))
OR ((n1)-[:has_skill]->(:Skill {name : "c"}) and (n1)-[:has_skill]->(:Skill {name : "mssql"}))
AND (n1)-[:has_skill]->(:Skill {name : "HTML"})
RETURN n1

您的查询失败是因为您要求 Neo4j 查找具有技能的人,该技能至少同时具有 3 项技能。显然,给定技能 n,它永远不会是 java 和 mysql 和 html。 (我假设您使用的是包含 'strip' 他们可能已添加到技能名称中的任何版本信息,例如 javaee 或 java 8。请记住,这也会匹配 javascript。不过,我建议您将此数据输入 Neo4j,让它将通用名称解析为一个单独的字段。如果您将名称视为技能数组,那么您可能没有匹配项,并且您的方案太做任何有用的事情都很脆弱)

我会推荐这种匹配模式,以便 Neo4j 检查任何有效的技能节点

MATCH (n:Skill)<--(n1:PannaResume)
WITH n1, COLLECT(n.name) as skills
WHERE ANY(name in skills WHERE name contains "html")
AND (ANY(name in skills WHERE name contains "java")
 OR ANY(name in skills WHERE name contains "mysql")
)
OR (
(ANY(name in skills WHERE name contains "c")
 OR ANY(name in skills WHERE name contains "mssql")
))

当然,越明确越好。这样效果会好很多。

MATCH (n1:PannaResume)-[:has_skill]->(:Skill {name : "html"})
WHERE ((n1)-[:has_skill]->(:Skill {name : "java"}) AND (n1)-[:has_skill]->(:Skill {name : "mysql"}))
OR ((n1)-[:has_skill]->(:Skill {name : "c"}) AND (n1)-[:has_skill]->(:Skill {name : "mssql"}))
RETURN n1

但是您也可以通过将查询分成一组不同的查询并像这样合并结果来稍微简化(并使其在 Cypher 规划器上更容易)

MATCH (n1:PannaResume)-[:has_skill]->(:Skill {name : "html"})
WHERE (n1)-[:has_skill]->(:Skill {name : "java"}) 
  AND (n1)-[:has_skill]->(:Skill {name : "mysql"})
RETURN n1
UNION
MATCH (n1:PannaResume)-[:has_skill]->(:Skill {name : "html"})
WHERE (n1)-[:has_skill]->(:Skill {name : "c"})
  AND (n1)-[:has_skill]->(:Skill {name : "mssql"})
RETURN n1

这应该等同于之前的查询,但根据您的 doing/Neo4j 版本,效率可能更高。 WITH 会将查询分成不同的阶段,因此将强制 Cypher 规划器首先找到技能节点,然后找到与这些技能相关联的人。

MATCH (html:Skill {name : "html"}), (java:Skill {name : "java"}), (c:Skill {name : "c"}), (mysql:Skill {name : "mysql"}), (mssql:Skill {name : "mssql"})
WITH *
MATCH (n1:PannaResume)-[:has_skill]->(html)
WHERE ((n1)-[:has_skill]->(java) AND (n1)-[:has_skill]->(mysql)
OR ((n1)-[:has_skill]->(c) AND (n1)-[:has_skill]->(mssql)
RETURN n1

如果您需要担心效率,您应该使用 PROFILE 关键字尝试不同的查询。您要做的关键是尽量减少每个 'stage' 查询生成的行数,因为此后的每个阶段都将乘以前一阶段生成的行数。 (Cypher 规划器可以选择并行执行多个查询,然后找到它们的交集。因此,对 Cypher 的更改将如何影响计划并不明显。PROFILE 将向您显示查询效率低下,因此请注意以下步骤高 dbhits 或导致很多行。)