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

Recursion 具有子图聚合的递归查询(任意深度),recursion,neo4j,cypher,Recursion,Neo4j,Cypher,我问了一个关于沿图表聚合数量的问题。提供的两个答案都很有效,但现在我尝试将Cypher查询扩展到一个可变深度的图 总而言之,我们从一堆叶存储开始,它们都与特定的供应商关联,这是Store节点上的一个属性。然后,库存被转移到其他商店,每个供应商的比例对应于他们对原始商店的贡献 因此,对于节点B02,S2贡献750/1250=60%和S3贡献40%。然后,我们将600个B02单元移动到S3,其中60%属于S2,而40%属于S3,依此类推 我们想知道的是,在最后700个单元中,D01属于每个供应商的

我问了一个关于沿图表聚合数量的问题。提供的两个答案都很有效,但现在我尝试将Cypher查询扩展到一个可变深度的图

总而言之,我们从一堆叶存储开始,它们都与特定的供应商关联,这是
Store
节点上的一个属性。然后,库存被转移到其他商店,每个供应商的比例对应于他们对原始商店的贡献

因此,对于节点
B02
S2
贡献
750/1250=60%
S3
贡献
40%
。然后,我们将600个
B02
单元移动到
S3
,其中
60%
属于
S2
,而
40%
属于
S3
,依此类推

我们想知道的是,在最后700个单元中,
D01
属于每个供应商的百分比。相同名称的供应商为同一供应商。因此,对于上述图表,我们预计:

S1,38.09
S2,27.61
S3,34.28

我用这个密码脚本准备了一个图表:

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)
当前查询如下:

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
MATCH(s:Store{Name:'D01})

MATCH(s)我不能在纯密码中思考解决方案,因为我认为在密码中不能像这样进行递归。但是,您可以使用cypher以一种简单的方式返回树中的所有数据,这样您就可以用自己喜欢的编程语言进行计算。大概是这样的:

MATCH path=(source:Store)-[move:MOVE_TO*]->(target:Store {Name: 'D01'})
WHERE source.Supplier IS NOT NULL
RETURN
  source.Supplier,
  reduce(a=[], move IN relationships(path)| a + [{id: ID(move), Quantity: move.Quantity}])

这将返回每个路径上每个关系的id和数量。然后您可以处理该客户端(可能首先将其转换为嵌套的数据结构?

正如我之前所说,我喜欢这个问题。我知道您已经接受了答案,但我决定发布我的最终回复,因为它也会返回百分位数,而无需客户端的努力(这意味着您也可以在需要时在节点上进行设置以更新数据库中的值),当然,如果出于任何其他原因,我可以返回:) 这里是链接到

它返回一行,其中包含门店名称、从所有供应商移动到该行的金额以及每个供应商的百分比

MATCH p =s<-[:MOVE_TO*]-sup
WHERE HAS (sup.Supplier) AND NOT HAS (s.Supplier)
WITH s,sup,reduce(totalSupplier = 0, r IN relationships(p)| totalSupplier + r.Quantity) AS TotalAmountMoved
WITH sum(TotalAmountMoved) AS sumMoved, collect(DISTINCT ([sup.Supplier, TotalAmountMoved])) AS MyDataPart1,s
WITH reduce(b=[], c IN MyDataPart1| b +[{ Supplier: c[0], Quantity: c[1], Percentile: ((c[1]*1.00))/(sumMoved*1.00)*100.00 }]) AS MyData, s, sumMoved
RETURN s.Name, sumMoved, MyData

MATCH p=s此查询为符合问题中描述的模型的任意图形生成正确的结果。(当
Store
x将商品移动到
Store
y时,假定
供应商
移动商品的百分比与
Store
x相同。)

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

这些查询可能会进一步优化

以下是所需的步骤:

  • 为迭代查询准备图表(为所有开始的
    存储
    节点初始化临时
    pcts
    数组)。这包括创建一个包含所有供应商名称的数组的singleton
    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;
    
    以下是问题数据的结果:

    +---------------------------------------------+
    | 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
    
  • 迭代查询(重复运行,直到返回0行)

    迭代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
    
    迭代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
    
    迭代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
    
    迭代4结果:

    +--------------------------------------+
    | s2.Name | s2.pcts | total | rel_pcts |
    +--------------------------------------+
    +--------------------------------------+
    0 rows
    69 ms
    
  • 列出结束<代码>门店
  • 节点的非零<代码>供应商
    百分比

    结果:

    +----------------------------------------------------------------------------------------------------------------------------------+
    | store.Name | pcts                                                                                                                |
    +----------------------------------------------------------------------------------------------------------------------------------+
    | "D01"      | [{supplier=S1, pct=38.095238095238095},{supplier=S2, pct=27.619047619047617},{supplier=S3, pct=34.285714285714285}] |
    +----------------------------------------------------------------------------------------------------------------------------------+
    1 row
    293 ms
    
    0 rows
    203 ms
    +-------------------+
    | No data returned. |
    +-------------------+
    Properties set: 29
    Nodes deleted: 1
    
  • 清理(删除所有临时的
    PCT
    道具和
    供应商
    节点)

    结果:

    +----------------------------------------------------------------------------------------------------------------------------------+
    | store.Name | pcts                                                                                                                |
    +----------------------------------------------------------------------------------------------------------------------------------+
    | "D01"      | [{supplier=S1, pct=38.095238095238095},{supplier=S2, pct=27.619047619047617},{supplier=S3, pct=34.285714285714285}] |
    +----------------------------------------------------------------------------------------------------------------------------------+
    1 row
    293 ms
    
    0 rows
    203 ms
    +-------------------+
    | No data returned. |
    +-------------------+
    Properties set: 29
    Nodes deleted: 1
    

  • 我在考虑这个问题,但问题是我不认为cypher真的做递归。Cypher一次计算一个子图的
    MATCH
    ,在本例中,它是树深度上的一条路径。但是,您还需要将路径相互比较,如果您只需要从商店到原始供应商节点的路径,那么除了Brians的建议之外,您还需要类似于
    MATCH(target:store{Name:'D01')的路径,类似地,您可以使用
    WHERE NOT(source)我添加了一个说明如何正确计算答案的答案(对于具有相同模型的任意图形)。它涉及到一个迭代查询,这是获得正确结果的唯一方法。感谢您的回答,我喜欢您将移动累积到数组中的技术。我可能会再等一会儿,看看是否还有其他答案我当然希望看到另一个答案。另外,我不知道您使用的是什么语言,但我应该提到,如果您使用的是java或与java API集成的东西,您也可以通过neo4j java API访问您的数据库。不过,您需要在嵌入式模式下操作,这有其自身的复杂性。我们使用C#,因此理想情况下,我们希望避免编写任何有趣的java代码。这是广义的吗?如果还有一个关卡,它还能用吗?这应该可以用你想要的任何关卡。当然,您也可以通过将过滤器添加到s或sup匹配中,将其限制为存储或供应商。不幸的是,此查询使用的数学不正确。最终百分比的实际计算不包括将每个路径中的所有数量添加到
    D01
    ,然后使用总计作为百分比计算的分母。相反,您必须计算每个门店节点的百分比,然后
    MATCH (s:Store), (sups:Suppliers)
    OPTIONAL MATCH (s)-[m:MOVE_TO]-()
    REMOVE m.pcts, s.pcts
    DELETE sups;
    
    0 rows
    203 ms
    +-------------------+
    | No data returned. |
    +-------------------+
    Properties set: 29
    Nodes deleted: 1