【问题标题】:Neo4j query with Boolean operations between NodesNeo4j 查询与节点之间的布尔运算
【发布时间】:2018-11-01 06:08:44
【问题描述】:

我们有一个带有节点的图形数据库:简历和技能。 技能节点包含属性 - “名称”。它具有 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

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

【问题讨论】:

    标签: neo4j cypher cql boolean-operations


    【解决方案1】:

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

    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
    

    【讨论】:

    • 感谢您的回复。当我们运行这个查询时,数据库被挂起。
    【解决方案2】:

    您的查询失败是因为您要求 Neo4j 找到一个具有技能的人,即同时具有至少 3 个技能。显然,给定技能 n,它永远不会是 java 和 mysql 和 html。 (我假设您正在使用 contains 来“剥离”他们可能添加到技能名称中的任何版本信息,例如 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
    

    这应该等同于之前的查询,但可能更高效,具体取决于您正在做什么/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 关键字尝试不同的查询。您尝试做的关键事情是最小化查询的每个“阶段”产生的行数,因为之后的每个阶段都将乘以前一阶段产生的行数。 (Cypher 计划器可以选择并行执行多个查询,然后找到它们的交集。因此,Cypher 的更改将如何影响计划并不明显。PROFILE 将显示您的查询效率低下,因此请注意以下步骤高 dbhits 或导致很多行。)

    【讨论】:

    • 感谢您的回复。我尝试了上述两个查询,第一个查询在 3 秒内完成了 2932334 次数据库命中,第二个查询在 2 秒内完成了 5839613 次总数据库命中。他们都在做 NodeByLabelScans。这些查询是否可扩展且性能有效?
    • @SarathBabu 您应该在 :Skill(name) 上创建一个索引以便更快地查找,您希望在计划中查看索引搜索,而不是 nodebylabelscans。
    • @SarathBabu 不。您正在使用每个人,并(至少)乘以数据库中的每项技能。我更新了 2,以便只需要每个知道 HTML 的人并将其与技能数据库相乘。我还添加了第三个使用 Union 炫耀的第三个。在这种情况下,这可能会更快。 (正如 Falcon 所说,Skill.name 上的 Index 或更好的 Unique Constraint(也是一个索引)将极大地帮助 Neo4j 找到技能关联,因为它可以使用它来提取 5 个技能,然后做剩下的逻辑)
    • @Tezra,我知道了,感谢您的回复。现在我们已经尝试了 3 种不同的方法来解决我的问题,并在所有 3 个解决方案的详细信息下方附上了每个解决方案的查询和总 DB 命中。现在,我们正在从这 3 个解决方案中寻找有关可扩展且性能有效的解决方案的建议。
    • 解决方案1:将所有Skill节点数据转换为逗号分隔的String,并将其添加为PannaResume节点下的属性。匹配 (n1:PannaResume)--(p:Skill) 与 n1,collect (p.name) as x 与 reduce(s=head(x),n in tail(x) | s+','+n) 作为结果,n1 match (n1) set n1.skillName=result return n1.skillName Then did Query using below match (n1:PannaResume)--(p:Skill) where ((n1.skillName contains "java" OR n1.skillName contains " c") AND (n1.skillName contains "mysql" OR n1.skillName contains "oracle")) return distinct(n1) 结果: 1596247 DB Hits in 1462ms
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-03-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多