如何计算neo4j中树中根节点的总数?

问题描述 投票:1回答:1

我正在学习cypher,并且遇到了一个我已经解决的问题,但是想知道是否有更好的方法来编写cypher查询。

我有一个任意深度的层次结构(树),包括公司及其子公司和子公司的子公司等。

每个公司/子公司都是一个节点,每个节点上的属性是该特定公司/子公司获得的收入。

我想计算根节点的总收入。那就是我需要计算顶级公司的总收入,即其自身收入加上其下所有子公司的收入。

我提出的查询计算每个迷你树(父母及其直接子公司)的所有子总计。查询似乎从树的底部开始并向上运行。

查询第一部分的输出是所有节点(叶子除外)的列表,其中包含所有节点的总和。

接下来,我计算所有根节点,并将此根节点列表“加入”到先前的结果。

这将返回我需要的答案。然而,它似乎相当复杂 - 因此我的问题是,有没有办法更优雅地做到这一点 - 也许只有一个匹配条款?

以下是一些示例数据和我到目前为止所写的查询。

create (a:Company {revenue: 10, cid: "a"})
create (b:Company {revenue: 10, cid: "b"})
create (c:Company {revenue: 20, cid: "c"})
create (d:Company {revenue: 15, cid: "d"})
create (e:Company {revenue: 20, cid: "e"})
create (f:Company {revenue: 25, cid: "f"})
create (g:Company {revenue: 30, cid: "g"})
create (h:Company {revenue: 10, cid: "h"})
create (i:Company {revenue: 20, cid: "i"})
create (j:Company {revenue: 20, cid: "j"})
create (k:Company {revenue: 40, cid: "k"})
create (l:Company {revenue: 10, cid: "l"})
create (m:Company {revenue:  5, cid: "m"})

create (b)-[:REPORTS_TO]->(a)
create (c)-[:REPORTS_TO]->(a)
create (d)-[:REPORTS_TO]->(b)
create (e)-[:REPORTS_TO]->(c)
create (f)-[:REPORTS_TO]->(c)

create (h)-[:REPORTS_TO]->(g)
create (i)-[:REPORTS_TO]->(g)
create (j)-[:REPORTS_TO]->(h)
create (k)-[:REPORTS_TO]->(h)
create (l)-[:REPORTS_TO]->(i)
create (m)-[:REPORTS_TO]->(i)
;

这是我创建的查询:

// First Calculate total revenue for each company in the tree with subsidiaries.
// This will include top level and intermediate level companies.
match (c: Company)<-[:REPORTS_TO*]-(s:Company)
  with c.cid as r_cid, sum (s.revenue) + c.revenue as tot_revenue

// Next, Determine the root nodes
// "join" the list of root nodes to the totals for each company.
// The result is the root node companies with their total revenues.
  match (c)
  where not ()<-[:REPORTS_TO]-(c) AND
      c.cid = r_cid
      // Return the root company id and the revenue for it.
  return c.cid, tot_revenue;

以上返回我期望的结果:

+---------------------+
| c.cid | tot_revenue |
+---------------------+
| "g"   | 135         |
| "a"   | 100         |
+---------------------+

同样,这个问题是关于是否有更好的方法来编写密码查询而不是我提出的解决方案?

neo4j tree cypher
1个回答
1
投票

是的,有一些方法可以让您的Cypher查询更好。

您在查询中执行的一些不需要或可以改进的事情:

  1. 第二次扫描所有节点,然后通过将当前节点的WHERE与这些节点进行匹配来过滤cid以获取您已拥有的节点。
  2. 计算所有公司的total revenue。您可以避免子公司的总收入计算,因为您没有在任何地方使用它。

要使查询有效运行,您需要最小化总数据库调用(AKA db命中)。您可以通过分析查询来检查db命中。这将显示一个查询计划以及哪些操作员正在完成大部分工作。您需要在开头添加PROFILE来运行查询。

我为您的查询进行了分析。查询的总db命中数为311。

Let's make changes to your query step by step:

删除不必要的比较:db命中总数减少到131

PROFILE 
MATCH (c:Company)<-[:REPORTS_TO*]-(s:Company)
WITH c, sum(s.revenue) + c.revenue AS tot_revenue
MATCH (c)
WHERE  NOT ()<-[:REPORTS_TO]-(c)
RETURN c.cid, tot_revenue;

通过在计算之前过滤根公司,避免计算子公司的总收入。 db的总命中率降至108

PROFILE 
MATCH (c:Company)<-[:REPORTS_TO*]-(s:Company)
WHERE  NOT ()<-[:REPORTS_TO]-(c)
WITH c.cid AS r_cid, sum(s.revenue) + c.revenue AS tot_revenue
RETURN r_cid, tot_revenue;

将别名和公司收入与汇总分开。 db的总命中率降至90

PROFILE 
MATCH (c:Company)<-[:REPORTS_TO*]-(s:Company)
WHERE  NOT ()<-[:REPORTS_TO]-(c)
WITH c, sum(s.revenue) AS sub_tot_revenue
RETURN c.cid AS cid, sub_tot_revenue + c.revenue AS tot_revenue;

这些是改善解决方案的一些方法。您可以阅读更多关于query tuning in Neo4j documentation.的信息

© www.soinside.com 2019 - 2024. All rights reserved.