【问题标题】:Neo4j trouble optimizing query with multiple optional matchesNeo4j 使用多个可选匹配优化查询时遇到问题
【发布时间】:2022-01-09 07:20:20
【问题描述】:

我以为我掌握了 neo4j。事实证明我不是。我正在运行一个很长的查询。当我使用任何 2 个可选匹配运行时,它会在 20 秒内运行。但是如果我有任何第三场可选比赛(似乎并不重要),它将需要将近 15 分钟才能运行。我不太明白。我(在某种程度上)理解可选匹配顺序很重要,因为它们将所有已经匹配的东西放在它们之前,并使用这些“行”来检查可选匹配,从而使每个匹配的成本成倍增加。我想如果我在每个语句之间小心地添加“with”语句,我可以尝试仅将必要的内容过滤到每个语句中。

我的可选匹配项之间并没有太大的关系。我实际上会做 3-4 个不同的 neo4j 查询,但我的老板希望我在一个查询中完成所有操作。如果事实证明性能好得多,我可能最终会违背他的意愿。我将为您提供完整的查询,其中更改了一些名称。它不会影响查询或任何事情,我的工作在技术上是开源的,但我仍然不应该分享任何可识别的内容。

我还运行“个人资料”来显示完整的树。

profile
match (ds:Analysis)<-[:OUTPUT]-(a)<-[:INPUT]-(firstSample:Sample)<-[*]-(source:Source)
with ds, firstSample

optional match (ds)<-[*]-(othersample:Sample)
with ds, othersample, firstSample
where not othersample.location is null and not trim(othersample.location) = ''

optional match (source)-[:INPUT]->(oa)-[:OUTPUT]->(specialsample:Sample {sample_type:'protein'})-[*]->(ds)
with ds, othersample, firstSample, source, specialsample

optional match (ds)<-[*]-(finalsample:Sample)
with ds, othersample, firstSample, source, specialsample, finalsample
where not finalsample.metadata is null and not trim(finalsample.metadata) = ''


return ds.id, collect(distinct firstSample), collect(distinct source), collect(distinct othersample), collect(distinct specialsample), ds.alt_id, ds.status, ds.group_name, ds.group_uuid, 
ds.created_timestamp, ds.created_email, ds.last_modified_timestamp, ds.last_modified_email, ds.lab_id, ds.data_types, collect(distinct finalsample)

这是挂钩到一个已经编写好的 python 脚本,所以我对输出甚至返回的顺序都没有灵活性,但如果有必要我可以做点什么。

任何建议将不胜感激。 https://i.stack.imgur.com/9psgT.png

【问题讨论】:

  • 这是您在此处发布的完整查询还是您删除了部分查询?
  • 他们的查询是完整的。我为某些标签提供了一些别名,但这是我理解的完整查询。

标签: python database optimization neo4j cql


【解决方案1】:

我会尝试几件事。

  1. 在查询的早期进行聚合,而不是等到结束。

  2. 尽可能使用pattern comprehensions,而不是OPTIONAL MATCH

这可能会让你开始。

match (ds:Analysis)<-[:OUTPUT]-(a)<-[:INPUT]-(firstSample:Sample)<-[*]-(source:Source)

WITH ds, 
collect(distinct firstSample) as firstSamples, 
collect(distinct source) as sources

UNWIND sources as source

OPTIONAL MATCH (source)-[:INPUT]->(oa)-[:OUTPUT]->(specialsample:Sample {sample_type:'protein'})-[*]->(ds)

WITH ds, 
firstSamples, 
collect(distinct source) AS sources, 
collect(distinct specialsample) AS specialSamples

RETURN ds.id, ds.alt_id, ds.status, ds.group_name, ds.group_uuid, 
ds.created_timestamp, ds.created_email, ds.last_modified_timestamp, 
ds.last_modified_email, ds.lab_id, ds.data_types, 
firstSamples, 
sources,
specialSamples,

apoc.coll.toSet([(ds)<-[*]-(othersample:Sample) 
where not othersample.location is null 
and not trim(othersample.location) = '' | othersample]) 
AS otherSamples,

apoc.coll.toSet([(ds)<-[*]-(finalsample:Sample) 
where not finalsample.metadata is null 
and not trim(finalsample.metadata) = '' | finalsample]) 
AS finalSamples

您可以在配置文件跟踪中看到查询的 finalsamples 部分涉及很多行。你确定你的逻辑是正确的吗?

【讨论】:

  • 好东西。我对这里发生的一些事情有点好奇。你能告诉我收集资源的目的是什么,然后立即展开,然后再次收集它是什么?此外,您似乎正在使用 apoc.coll 替换后两个可选匹配项。您能否详细说明一下您为什么能够做到这一点?
  • 我只想为 source 和 ds 的每个组合搜索一次 specialsample 模式。在第 4 行中使用collect(distinct source) 只为我们提供了每个 ds 值的唯一源值。我们可能已经找到了与 source-ds 对一起使用的多个 specialsample 值,因此我在搜索 specialsamples 后再次使用了collect(distinct source)。 (顺便说一句,我注意到第一个 count(distinct sample) 中有一个额外的冒号,这是一个错字。我进行了编辑以将其删除。)
  • 模式推导搜索该模式的所有匹配项。在从 ds 到 othersample 或 finalsample 的路径不止一条的情况下,您最终会得到 othersample 或 finalsample 的重复值。对 apoc.coll.toSet() 的调用将删除一行中的重复项。你也可以展开列表,做不同的,然后再次收集。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-08-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多