重构大型密码联合查询

时间:2018-11-05 15:37:52

标签: neo4j cypher query-optimization graph-databases

我有一个很长的密码查询,它使用并集,但是,在两个查询中都有一些共同的语句(粗体)。有什么方法可以分解出,甚至可以存储公共语句的结果集,然后在以后进行分支和合并?我已经研究过使用,匹配和可选匹配,但无济于事。

MATCH(s:主题),(p:Programme)
WHERE s.name在['A','B','C']中
以subs,p的形式收集
以p,subs的形式,将SIZE(FILTER(c在subs的位置,C.level =“ CSEC”))作为csecs,SIZE(FILTER(c在subs的位置,WHERE) c.level =“ CAPE”))作为斗篷
p.csec_passes <= csecs和p.cape_passes <= capes
MATCH(p:Programme)-[:requires]->(s:Subject)

p,subs和COLLECT作为强制条件在哪里(n个强制条件在n IN subs中)不(p)->(:Combo)
返回p

UNION

MATCH(s:主题),(p:Programme)
WHERE s.name在['A','B','C']中
以subs,p的形式收集
以p,subs的形式,将SIZE(FILTER(c在subs的位置,C.level =“ CSEC”))作为csecs,SIZE(FILTER(c在subs的位置,WHERE) c.level =“ CAPE”))作为斗篷
p.csec_passes <= csecs和p.cape_passes <= capes
MATCH(p:Programme)-[:requires]->(s:Subject)

其中p,sub和COLLECT作为必需项(n个强制项,n IN个子项)
匹配(p)-[:需要]->(c:组合)-[:包含]->(s:主题)
与p,c,subs,collects一起作为列表
与p,subs,collect({amt:c.amt,set:list})作为连击
WHERE ALL(组合中的组合,其中combo.amt <= size(apoc.coll.intersection(subs,combo.set))) 返回p

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

1 个答案:

答案 0 :(得分:3)

首先,要注意几个重要事项。

  1. 密码并不指示如何检索信息。 Cypher计划人员应该处理这种优化(现在还不行,但是将来可能会改变)

  2. Cypher并行运行UNION查询,这意味着,除非您将Neo4j服务器推到极限,否则查询时间与仅运行两个查询中较昂贵的查询的时间应该是没有区别的。 (请注意,由于内存缓存的原因,重复运行可能会更快),因此,如果时间是您的问题,那不应该。如果现在不是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