重组大型密码联合查询

Restructuring a large cypher union query

我有一个非常长的使用联合的密码查询,但是,有一些公共语句(粗体)在两个查询中重复出现。有没有一种方法可以分解,甚至存储公共语句的结果集,然后在以后对它们进行分支和合并?我调查了使用 with、collect 和可选匹配,但无济于事。

MATCH (s:Subject), (p:Programme)
WHERE s.name in ['A', 'B', 'C']
WITH collect(s) as subs, p
WITH p, subs, SIZE(FILTER(c in subs WHERE c.level ="CSEC")) as csecs, SIZE( FILTER(c in subs WHERE c.level ="CAPE")) as cape
WHERE p.csec_passes <= csecs AND p.cape_passes <= capes
匹配 (p:Programme)-[:requires]->(s:Subject)

WITH p, subs, COLLECT(s) AS mandatories WHERE ALL(n IN mandatories WHERE n IN subs) AND NOT (p)-->(:Combo)
RETURNp

联盟

MATCH (s:Subject), (p:Programme)
WHERE s.name in ['A', 'B', 'C']
WITH collect(s) as subs, p
WITH p, subs, SIZE(FILTER(c in subs WHERE c.level ="CSEC")) as csecs, SIZE( FILTER(c in subs WHERE c.level ="CAPE")) as cape
WHERE p.csec_passes <= csecs AND p.cape_passes <= capes
匹配 (p:Programme)-[:requires]->(s:Subject)

WITH p, subs, COLLECT(s) AS mandatories WHERE ALL(n IN mandatories WHERE n IN subs)
匹配 (p)-[:requires]->(c:Combo)-[:contains]->(s:Subject)
WITH p, c, subs, collect(s) 作为列表
WITH p, subs, collect({amt:c.amt, set:list}) 作为组合
WHERE ALL(combo in combos where combo.amt <= size(apoc.coll.intersection(subs, combo.set))) RETURNp

一些额外的上下文;所有程序节点都连接到至少 1 个称为强制性主题节点。此外,一些程序节点还连接到一个或多个组合节点。在这种情况下,需要对程序进行更多检查,我将对组合和非组合这两种类型的查询进行合并。

首先,有几点重要说明。

  1. Cypher 不规定如何检索信息。这种优化是Cypher规划器应该处理的事情(现在没有,但将来可能会改变)

  2. Cypher 运行s UNION 并行查询,这意味着除非您将 Neo4j 服务器推到极限,否则查询时间应该与只有运行 两个查询中成本较高的一个。 (请注意,由于内存缓存,重复 运行s 可能会更快)因此,如果时间是您的问题,那不应该是。如果 DBHits 是问题,那么现在您不应该使用 UNION。


也就是说,我可以通过添加 OPTIONALSIZE(combo.set)=0 OR 来组合这两个查询。添加评论以解释逻辑

MATCH (s:Subject), (p:Programme) 
WHERE s.name in ['A', 'B', 'C'] 
WITH collect(s) as subs, p 
WITH p, subs, SIZE(FILTER(c in subs WHERE c.level ="CSEC")) as csecs, SIZE(FILTER(c in subs WHERE c.level ="CAPE")) as capes 
WHERE p.csec_passes <= csecs AND p.cape_passes <= capes 
MATCH (p:Programme)-[:requires]->(s:Subject)

WITH p, subs, COLLECT(s) AS mandatories WHERE ALL(n IN mandatories WHERE n IN subs)
OPTIONAL MATCH (p)-[:requires]->(c:Combo)-[:contains]->(s:Subject)
// Where c is null, list is empty
WITH p, c, subs, collect(s) as list
// If c is null, combos is a list of empty lists
WITH p, subs, collect({amt:c.amt, set:list}) as combos
// SIZE(combo.set)=0 is true if the list is null or an empty list
WHERE ALL(combo in combos where SIZE(combo.set)=0 OR combo.amt <= size(apoc.coll.intersection(subs, combo.set))) RETURN p