具有子图聚合的递归查询(任意深度)

Ned*_*nov 6 recursion neo4j cypher

我问一个问题前面关于沿着图形聚集量.提供的两个答案运作良好,但现在我正在尝试将Cypher查询扩展到可变深度的图表.

总结一下,我们从一堆叶子商店开始,这些叶子商店都与特定供应商相关联,这是Store节点上的一个属性.然后将库存移至其他商店,每个供应商的比例对应于他们对原始商店的贡献.

所以对于节点B02,S2贡献750/1250 = 60%S3贡献40%.然后,我们600台我们对B02其中60%属于S240%S3等.

在此输入图像描述

我们想知道最终700个单位的百分比D01属于每个供应商.供应商名称相同的供应商.因此,对于上图,我们期望:

S1,38.09
S2,27.61
S3,34.28

我使用这个Cypher脚本编写了一个图表:

CREATE (A01:Store {Name: 'A01', Supplier: 'S1'})
CREATE (A02:Store {Name: 'A02', Supplier: 'S1'})
CREATE (A03:Store {Name: 'A03', Supplier: 'S2'})
CREATE (A04:Store {Name: 'A04', Supplier: 'S3'})
CREATE (A05:Store {Name: 'A05', Supplier: 'S1'})
CREATE (A06:Store {Name: 'A06', Supplier: 'S1'})
CREATE (A07:Store {Name: 'A07', Supplier: 'S2'})
CREATE (A08:Store {Name: 'A08', Supplier: 'S3'})

CREATE (B01:Store {Name: 'B01'})
CREATE (B02:Store {Name: 'B02'})
CREATE (B03:Store {Name: 'B03'})
CREATE (B04:Store {Name: 'B04'})

CREATE (C01:Store {Name: 'C01'})
CREATE (C02:Store {Name: 'C02'})

CREATE (D01:Store {Name: 'D01'})

CREATE (A01)-[:MOVE_TO {Quantity: 750}]->(B01)
CREATE (A02)-[:MOVE_TO {Quantity: 500}]->(B01)
CREATE (A03)-[:MOVE_TO {Quantity: 750}]->(B02)
CREATE (A04)-[:MOVE_TO {Quantity: 500}]->(B02)
CREATE (A05)-[:MOVE_TO {Quantity: 100}]->(B03)
CREATE (A06)-[:MOVE_TO {Quantity: 200}]->(B03)
CREATE (A07)-[:MOVE_TO {Quantity: 50}]->(B04)
CREATE (A08)-[:MOVE_TO {Quantity: 450}]->(B04)

CREATE (B01)-[:MOVE_TO {Quantity: 400}]->(C01)
CREATE (B02)-[:MOVE_TO {Quantity: 600}]->(C01)
CREATE (B03)-[:MOVE_TO {Quantity: 100}]->(C02)
CREATE (B04)-[:MOVE_TO {Quantity: 200}]->(C02)

CREATE (C01)-[:MOVE_TO {Quantity: 500}]->(D01)
CREATE (C02)-[:MOVE_TO {Quantity: 200}]->(D01)
Run Code Online (Sandbox Code Playgroud)

目前的查询是这样的:

MATCH (s:Store { Name:'D01' })
MATCH (s)<-[t:MOVE_TO]-()<-[r:MOVE_TO]-(supp)
WITH t.Quantity as total, collect(r) as movements
WITH total, movements, reduce(totalSupplier = 0, r IN movements | totalSupplier + r.Quantity) as supCount
UNWIND movements as movement
RETURN startNode(movement).Supplier as Supplier, round(100.0*movement.Quantity/supCount) as pct
Run Code Online (Sandbox Code Playgroud)

我正在尝试使用递归关系,类似于以下内容:

MATCH (s)<-[t:MOVE_TO]-()<-[r:MOVE_TO*]-(supp)
Run Code Online (Sandbox Code Playgroud)

但是,它提供了到终端节点的多条路径,我需要在每个节点聚合库存.

cyb*_*sam 2

此查询为符合问题中描述的模型的任意图形生成正确的结果。(当Storex 将商品移动到Storey 时,假设Supplier移动商品的百分比与 x 相同Store。)

然而,该解决方案不仅仅包含单个 Cypher 查询(因为这可能是不可能的)。相反,它涉及多个查询,其中一个查询必须迭代,直到计算级联通过整个节点图Store。该迭代查询将清楚地告诉您何时停止迭代。需要其他 Cypher 查询来:准备迭代图、报告“最终”节点的供应商百分比以及清理图(以便将其恢复到下面步骤 1 之前的状态) 。

这些查询可能可以进一步优化。

以下是所需的步骤:

  1. 为迭代查询准备图形(初始化pcts所有起始Store节点的临时数组)。这包括创建一个单例Suppliers节点,该节点具有一个包含所有供应商名称的数组。这用于建立临时数组元素的顺序pcts,并将这些元素映射回正确的供应商名称。

    MATCH (store:Store)
    WHERE HAS (store.Supplier)
    WITH COLLECT(store) AS stores, COLLECT(DISTINCT store.Supplier) AS csup
    CREATE (sups:Suppliers { names: csup })
    WITH stores, sups
    UNWIND stores AS store
    SET store.pcts =
      EXTRACT(i IN RANGE(0,LENGTH(sups.names)-1,1) |
        CASE WHEN store.Supplier = sups.names[i] THEN 1.0 ELSE 0.0 END)
    RETURN store.Name, store.Supplier, store.pcts;
    
    Run Code Online (Sandbox Code Playgroud)

    这是问题数据的结果:

    +---------------------------------------------+
    | store.Name | store.Supplier | store.pcts    |
    +---------------------------------------------+
    | "A01"      | "S1"           | [1.0,0.0,0.0] |
    | "A02"      | "S1"           | [1.0,0.0,0.0] |
    | "A03"      | "S2"           | [0.0,1.0,0.0] |
    | "A04"      | "S3"           | [0.0,0.0,1.0] |
    | "A05"      | "S1"           | [1.0,0.0,0.0] |
    | "A06"      | "S1"           | [1.0,0.0,0.0] |
    | "A07"      | "S2"           | [0.0,1.0,0.0] |
    | "A08"      | "S3"           | [0.0,0.0,1.0] |
    +---------------------------------------------+
    8 rows
    83 ms
    Nodes created: 1
    Properties set: 9
    
    Run Code Online (Sandbox Code Playgroud)
  2. 迭代查询(重复运行直到返回 0 行)

    MATCH p=(s1:Store)-[m:MOVE_TO]->(s2:Store)
    WHERE HAS(s1.pcts) AND NOT HAS(s2.pcts)
    SET s2.pcts = EXTRACT(i IN RANGE(1,LENGTH(s1.pcts),1) | 0)
    WITH s2, COLLECT(p) AS ps
    WITH s2, ps, REDUCE(s=0, p IN ps | s + HEAD(RELATIONSHIPS(p)).Quantity) AS total
    FOREACH(p IN ps |
      SET HEAD(RELATIONSHIPS(p)).pcts = EXTRACT(parentPct IN HEAD(NODES(p)).pcts | parentPct * HEAD(RELATIONSHIPS(p)).Quantity / total)
    )
    FOREACH(p IN ps |
      SET s2.pcts = EXTRACT(i IN RANGE(0,LENGTH(s2.pcts)-1,1) | s2.pcts[i] + HEAD(RELATIONSHIPS(p)).pcts[i])
    )
    RETURN s2.Name, s2.pcts, total, EXTRACT(p IN ps | HEAD(RELATIONSHIPS(p)).pcts) AS rel_pcts;
    
    Run Code Online (Sandbox Code Playgroud)

    迭代 1 结果:

    +-----------------------------------------------------------------------------------------------+
    | s2.Name | s2.pcts       | total | rel_pcts                                                    |
    +-----------------------------------------------------------------------------------------------+
    | "B04"   | [0.0,0.1,0.9] | 500   | [[0.0,0.1,0.0],[0.0,0.0,0.9]]                               |
    | "B01"   | [1.0,0.0,0.0] | 1250  | [[0.6,0.0,0.0],[0.4,0.0,0.0]]                               |
    | "B03"   | [1.0,0.0,0.0] | 300   | [[0.3333333333333333,0.0,0.0],[0.6666666666666666,0.0,0.0]] |
    | "B02"   | [0.0,0.6,0.4] | 1250  | [[0.0,0.6,0.0],[0.0,0.0,0.4]]                               |
    +-----------------------------------------------------------------------------------------------+
    4 rows
    288 ms
    Properties set: 24
    
    Run Code Online (Sandbox Code Playgroud)

    迭代 2 结果:

    +-------------------------------------------------------------------------------------------------------------------------------+
    | s2.Name | s2.pcts                                      | total | rel_pcts                                                     |
    +-------------------------------------------------------------------------------------------------------------------------------+
    | "C02"   | [0.3333333333333333,0.06666666666666667,0.6] | 300   | [[0.3333333333333333,0.0,0.0],[0.0,0.06666666666666667,0.6]] |
    | "C01"   | [0.4,0.36,0.24]                              | 1000  | [[0.4,0.0,0.0],[0.0,0.36,0.24]]                              |
    +-------------------------------------------------------------------------------------------------------------------------------+
    2 rows
    193 ms
    Properties set: 12
    
    Run Code Online (Sandbox Code Playgroud)

    迭代 3 结果:

    +---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
    | s2.Name | s2.pcts                                                       | total | rel_pcts                                                                                                                    |
    +---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
    | "D01"   | [0.38095238095238093,0.27619047619047615,0.34285714285714286] | 700   | [[0.2857142857142857,0.2571428571428571,0.17142857142857143],[0.09523809523809522,0.01904761904761905,0.17142857142857143]] |
    +---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
    1 row
    40 ms
    Properties set: 6
    
    Run Code Online (Sandbox Code Playgroud)

    迭代 4 结果:

    +--------------------------------------+
    | s2.Name | s2.pcts | total | rel_pcts |
    +--------------------------------------+
    +--------------------------------------+
    0 rows
    69 ms
    
    Run Code Online (Sandbox Code Playgroud)
  3. 列出结束节点Supplier的非零百分比。Store

    MATCH (store:Store), (sups:Suppliers)
    WHERE NOT (store:Store)-[:MOVE_TO]->(:Store) AND HAS(store.pcts)
    RETURN store.Name, [i IN RANGE(0,LENGTH(sups.names)-1,1) WHERE store.pcts[i] > 0 | {supplier: sups.names[i], pct: store.pcts[i] * 100}] AS pcts;
    
    Run Code Online (Sandbox Code Playgroud)

    结果:

    +----------------------------------------------------------------------------------------------------------------------------------+
    | store.Name | pcts                                                                                                                |
    +----------------------------------------------------------------------------------------------------------------------------------+
    | "D01"      | [{supplier=S1, pct=38.095238095238095},{supplier=S2, pct=27.619047619047617},{supplier=S3, pct=34.285714285714285}] |
    +----------------------------------------------------------------------------------------------------------------------------------+
    1 row
    293 ms
    
    Run Code Online (Sandbox Code Playgroud)
  4. 清理(​​删除所有临时pcts道具和Suppliers节点)。

    MATCH (s:Store), (sups:Suppliers)
    OPTIONAL MATCH (s)-[m:MOVE_TO]-()
    REMOVE m.pcts, s.pcts
    DELETE sups;
    
    Run Code Online (Sandbox Code Playgroud)

    结果:

    0 rows
    203 ms
    +-------------------+
    | No data returned. |
    +-------------------+
    Properties set: 29
    Nodes deleted: 1
    
    Run Code Online (Sandbox Code Playgroud)