【问题标题】:Performing aggregation across unique nodes with multiple relationships or multiple labels跨具有多个关系或多个标签的唯一节点执行聚合
【发布时间】:2016-10-18 10:21:07
【问题描述】:

有什么建议可以通过改进图形结构或查询以下内容来提高性能?理想情况下,我想让它达到 1 秒以下。现在我能得到的最好的结果是大约 8 秒,在大约 2M 节点和 10M rels 上有 7M db 点击。

我有图结构在哪里

(co:Company)<-[:HAS_PROVIDER]-(c:Customer)-[:HAS_CLAIM]->(c:Claim)

  • 每个声明都有一个布尔属性“direct”以及一个名为“amount”的属性
  • 每家公司都有一个名字
  • 客户可以有多个索赔,但只有一个公司

我希望能够参加演出:

  • 不同的公司名称
  • 公司的客户数量
  • direct=true 声明计数
  • direct=true 索赔的金额总和
  • direct=false 声明计数
  • direct=false 索赔的金额总和

为此,我使用了两种方法:

a) 创建了从客户到索赔的关系,其中 direct=true 为 :IS_DIRECT,direct=false 为 :IS_INDIRECT

b) 将每个 direct=true 声明标记为 :DirectClaim 节点,将 direct=false 声明标记为 :InDirectClaim 节点

使用 (a) 允许我通过 rels TYPE 上的过滤器获取公司名称、客户数量、(IS_DIRECT) 的大小和大小 (IS_INDIRECT)。但是,无论配置如何,都可以使用提取、过滤、减少超时的组合来获取总和(数量)。

使用 (b) 有效,但需要大约 10 秒

编辑:

查询 (a) 看起来像(加上 @cybersam 的帽子,现在是 ~6s)

MATCH (co:Company)<-[:HAS_PROVIDER]-(c)-[r:IS_DIRECT|IS_INDIRECT]->(cl)
WITH distinct co, collect(r) as rels, count (distinct c) as cntc, collect(mc) as claims
WITH co, cntc, 
size(filter(r in rels WHERE TYPE(r) = 'IS_DIRECT')) as dcls, 
size(filter(r in rels WHERE TYPE(r) = 'IS_INDIRECT')) as indcls,
REDUCE
  (s = {dclsamt: 0, indclsamt: 0}, x IN claims | 
  CASE WHEN x.direct 
  THEN {dclsamt: s.dclsamt + x.amount, indclsamt: s.indclsamt} 
  ELSE {dclsamt: s.dclsamt, indclsamt: s.indclsamt + x.amount} 
  END) 
  AS data
RETURN co.name  as name,cntc, dcls,indcls, data
ORDER BY dcls desc

查询 (b) 看起来像:

MATCH (co:Company)<-[:HAS_PROVIDER]-(c)-[:HAS_CLAIM]->(cl)
WITH distinct co, count (distinct c) as cntc, COLLECT(cl) as cls
WITH co,cntc, 
FILTER(n in cls WHERE 'DirectClaim' IN IN LABELS(n)) as dcls,
FILTER(n in cls WHERE 'InDirectClaim' IN LABELS(n)) as indcls
WITH co,cntc, size(dcls) as dclsct, size(indcls) as indclsct, 
REDUCE(s = 0 , x IN dcls | s + x.amount) as dclsamt, 
REDUCE(s = 0 , x IN indcls | s + x.amount) as indclsamt
RETURN co.name as name, cntc, dclsct, dclsamt, indclsct, indclsamt

【问题讨论】:

  • 您能否提供您当前用于方法 a 和 b 的查询?
  • @InverseFalcon 添加了查询

标签: neo4j cypher


【解决方案1】:

无需向数据模型添加额外数据(如冗余关系或标签)。

此查询显示了一种返回所需结果的方法(在返回的 data 映射中,ttrue 数量的总和,tctrue 数量的计数; ffc 类似):

MATCH (co:Company)<-[:HAS_PROVIDER]-(cu:Customer)-[:HAS_CLAIM]->(cl:Claim)
WITH co.name as comp_name, COUNT(DISTINCT cu) AS cust_count,
  REDUCE(s = {t: 0, tc: 0, f: 0, fc: 0}, x IN COLLECT(cl) |
    CASE WHEN x.direct
      THEN {t: s.t + x.amount, tc: s.tc + 1, f: s.f, fc: s.fc}
      ELSE {t: s.t, tc: s.tc, f: s.f + x.amount, fc: s.fc + 1}
    END) AS data
RETURN comp_name, cust_count, data

【讨论】:

  • 这个查询是迄今为止最快的。本地约 6 秒,远程实例约 10 秒
【解决方案2】:

您当前的数据模型足以生成您想要的结果。

MATCH (co:Company)<-[:HAS_PROVIDER]-(cust:Customer)-[:HAS_CLAIM]->(claim:Claim)
WITH co, COUNT(DISTINCT cust) AS custs, COLLECT(DISTINCT claim) AS claims
WITH co,
custs,
[x IN claims WHERE x.direct|x.amount] AS direct_amts,
[x IN claims WHERE NOT x.direct|x.amount] AS indirect_amts
RETURN co,
custs,
SIZE(direct_amts) AS direct_count,
REDUCE(s=0, x IN direct_amts| s+x) AS direct_amt_total,
SIZE(indirect_amts) AS indirect_count,
REDUCE(s=0, x IN indirect_amts| s+x) AS indirect_amt_total

如果您真的需要速度,请确保您在 :Claim(direct):Claim(amount) 上有一个索引,这会非常令人尖叫。或者,将布尔属性转换为第二个标签(即(claim:Claim:Direct)),您可以为自己保存一个索引。

更新:根据您所引用的内容,您唯一真正的改进途径将取决于您的使用情况。如果此图是“活动的”并且会不断更新,那么无论何时添加、删除或更改 :Claim,您都可以始终在 :Customer 节点上缓存计数和总量。这就是图表大放异彩的地方,涉及数据库子集的小频繁查询。因此,当您对声明执行任何操作时,只需为相应的 :Customer 重新运行聚合,将结果存储在 Customer 上作为属性,然后对于您的大报告,只需直接从(数量少得多)中获取这些属性) :Customer 节点。

【讨论】:

  • 谢谢。您的第二个查询确实描述了我需要什么,并且我已经在之前的几次尝试中一次性使用了提取/过滤器。此外,我为“直接”FYI 添加了一个索引,我在原始问题中添加了我的“最佳”查询尝试。所有这些查询仍然不超过 ~7s
猜你喜欢
  • 2021-11-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多