结合Neo4j cypher查询(UNION的替代方案)的最佳方式?

时间:2016-09-18 11:17:01

标签: neo4j cypher

给定模型

有一个模型,它们之间有类别和关系。 对于关系,如果它们绑定到特定的开始或结束类别,则可以指定它。

有四种关系:

  • 仅指定了起始类别(例如:“外向”)
  • 仅指定结束类别(例如:“Incoming”)
  • 指定了开始和结束类别(例如:“传出和传入”)
  • 未指定开始类别或结束类别(例如:“未绑定”)

代码:

MERGE (cat:ModelCategory {title:'Cat'})
MERGE (rel1:ModelRelation {title:'Outgoing'})
MERGE (rel2:ModelRelation {title:'Incoming'})
MERGE (rel3:ModelRelation {title:'Outgoing and Incoming'})
MERGE (rel4:ModelRelation {title:'Unbound'})
MERGE (rel1)-[:STARTS_AT]->(cat)
MERGE (rel2)-[:ENDS_AT]->(cat)
MERGE (rel3)-[:STARTS_AT]->(cat)
MERGE (rel3)-[:ENDS_AT]->(cat)

Neo4j Browser Screenshot

单次查询

如果选择“Cat”作为起始节点并想知道可以创建哪些关系和生成的结束节点,则可以使用单个查询:

// Relations with current source and a target
// Returns relation "Outgoing and Incoming"
MATCH (relation:ModelRelation)-[STARTS_AT]->(:ModelCategory{title:"Cat"}),
(relation)-[ENDS_AT]->(target)
RETURN DISTINCT relation, target

// Relations with current source and without target
// Returns relation "Outgoing"
MATCH (relation:ModelRelation)-[STARTS_AT]->(:ModelCategory{title:"Cat"})
WHERE NOT (relation)-[:ENDS_AT]->()
MATCH (allCategories:ModelCategory)
RETURN relation, allCategories as target

// Relations with target, without source
// Returns relation "Incoming"
MATCH (relation:ModelRelation)-[ENDS_AT]->(target)
WHERE NOT (relation)-[:STARTS_AT]->()
RETURN relation, target

// Relations without source or target
// Returns relation "Unbound"
MATCH (relation:ModelRelation)
WHERE NOT (relation)-[:STARTS_AT]->() AND NOT (relation)-[:ENDS_AT]->()
MATCH (allCategories:ModelCategory)
RETURN relation, allCategories as target

问题

合并四个查询的最佳方式是什么?
最简单的解决方案是在语句之​​间添加UNION。
也许最好首先获取标签ModelRelation和ModelCategory的所有节点,然后在子图上执行进一步的查询?

更新

更好的解决方案是在有或没有指定目标的情况下消除关系。 (导致一个类别或所有类别。)一个UNION仍然是必需的,两个子查询的第一部分是相同的。

// Relations which start at selected category or have no specified start
MATCH (relation:ModelRelation)
WHERE (relation)-[:STARTS_AT]->(:ModelCategory{title:"Cat"}) OR
      NOT (relation)-[:STARTS_AT]->()

// Relations with specified targets
Match (relation)-[ENDS_AT]->(target) 
RETURN relation, target

UNION

// Relations which start at selected category or have no specified start
MATCH (relation:ModelRelation)
WHERE (relation)-[:STARTS_AT]->(:ModelCategory{title:"Cat"}) OR
      NOT (relation)-[:STARTS_AT]->()

// Relations without specified targets
MATCH (relation)
WHERE NOT (relation)-[:ENDS_AT]->()
MATCH (allCategories:ModelCategory)
RETURN relation, allCategories as target

1 个答案:

答案 0 :(得分:1)

使用过滤器表示法和可变长度路径在一个查询中捕获所有这些,然后使用CASE语句将空值替换为allCategories

MATCH (m:ModelCategory)
WITH COLLECT(m) AS allCategories
MATCH path = (:ModelCategory) <- [:STARTS_AT*0..1] - (:ModelRelation) - [:ENDS_AT*0..1] -> (:ModelCategory)
WITH CASE WHEN ANY(x in RELATIONSHIPS(path) WHERE TYPE(x) = 'STARTS_AT') THEN NODES(path)[0] ELSE allCategories END AS start,
[x IN NODES(path) WHERE x:ModelRelation][0] as relation,
CASE WHEN ANY(x IN RELATIONSHIPS(path) WHERE TYPE(x) = 'ENDS_AT') THEN LAST(NODES(path)) ELSE allCategories END AS end
RETURN start, relation, end

通常,您始终可以使用UNIONCOLLECTCASE或0长度路径的正确组合替换OPTIONAL MATCH个查询。通过扭曲是值得的,因为您可以在结果上运行聚合,而不是在每个查询中返回相同的列布局。

编辑:一个更简单的版本,每个关系返回一行。

MATCH (m:ModelCategory)
WITH COLLECT(m) AS allCategories
MATCH (relation:ModelRelation)
OPTIONAL MATCH (relation) - [:STARTS_AT] -> (start:ModelCategory)
OPTIONAL MATCH (relation) - [:ENDS_AT] -> (end:ModelCategory)
WITH COLLECT(start) AS starts, relation, COLLECT(end) AS ends, allCategories
RETURN
CASE starts WHEN [] THEN allCategories ELSE starts END AS relationStarts,
relation,
CASE ends WHEN [] THEN allCategories ELSE ends END AS relationEnds