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

时间:2019-04-28 03:03:19

标签: neo4j tree cypher

我正在学习密码,但遇到了一个我实际上已经解决的问题,但是想知道是否有更好的编写密码查询的方法。

我有一个任意深度的层次结构(树),由公司及其子公司以及子公司的子公司等组成。

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

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

我提出的查询将计算每个小树(父级及其直接子级)的所有子总计。该查询似乎从树的底部开始,然后一直向上。

查询的第一部分的输出是所有节点(叶子除外)的列表及其下所有节点的总数。

下一步,我计算所有根节点,并将该根节点列表“加入”到先前的结果中。

这将返回我需要的答案。但是,这似乎很令人费解-因此,我的问题是,有没有办法更优雅地做到这一点-也许只有一个match子句?

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

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         |
+---------------------+

再次,这个问题是关于写密码查询的方法是否比我想出的解决方案更好的方法?

1 个答案:

答案 0 :(得分:1)

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

您在查询中所做的一些不需要或无法改进的事情:

  1. 第二次扫描所有节点,然后通过将当前节点的WHERE与这些节点进行匹配来过滤cid,以获取您已经拥有的节点

  2. 为所有公司计算total revenue。您可以避免为子公司计算总收入,因为您没有在任何地方使用它。

要使查询有效运行,您需要使数据库调用总数(AKA数据库命中数)最小化。您可以通过对查询进行概要分析来查看数据库匹配。这将向您显示查询计划以及哪些操作员正在执行大部分工作。 您需要通过在开头添加PROFILE来运行查询。

  

我为您的查询进行了性能分析。您查询的数据库总点击数为   311。

让我们逐步更改您的查询:

  

删除不必要的比较:数据库总点击数减少到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;
  

避免通过在计算之前过滤根公司来计算子公司的总收入。总数据库点击量减少到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;
  

从别名中分离别名和增加公司收入。总数据库点击量减少到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.

的更多信息
相关问题