查找具有共同二级关系的节点的所有子树

时间:2018-07-21 03:49:34

标签: neo4j tree cypher

我正在使用Neo4J数据库中的物料清单(BOM)和零件数据。

我的图中有3种类型的节点:

  • (ItemUsageInstance)这些是物料清单树的元素
  • (Item)对于BOM表树上的每个唯一项都存在一个
  • (Material)

这些关系是:

  • (ItemUsageInstance)-[CHILD_OF]->(ItemUsageInstance)
  • (ItemUsageInstance)-[INSTANCE_OF]->(Item)
  • (Item)-[MADE_FROM]->(Material)

该架构如下图所示:

schema

这是数据的简化图片。 (已重新放置节点以增强可见性的图):

enter image description here

我想做的是找到其ItemUsageInstance都是由相同的Items组成的相邻Material的子树

到目前为止,我的查询是:

MATCH (m:Material)
WITH m AS m
MATCH (m)<-[:MADE_FROM]-(i1:Item)<-[]-(iui1:ItemUsageInstance)-[:CHILD_OF]->(iui2:ItemUsageInstance)-[]->(i2:Item)-[:MADE_FROM]->(m) RETURN iui1, i1, iui2, i2, m

但是,这仅返回一个这样的子树,即图形中间的相邻节点,它们的公共Material为“ M0002”。同样,结果的行是单独的条目,子树中的每个父子对都一个:

╒══════════════════════════╤══════════════════════╤══════════════════════════╤══════════════════════╤═══════════════════════╕
│"iui1"                    │"i1"                  │"iui2"                    │"i2"                  │"m"                    │
╞══════════════════════════╪══════════════════════╪══════════════════════════╪══════════════════════╪═══════════════════════╡
│{"instance_id":"inst5002"}│{"part_number":"p003"}│{"instance_id":"inst7003"}│{"part_number":"p004"}│{"material_id":"M0002"}│
├──────────────────────────┼──────────────────────┼──────────────────────────┼──────────────────────┼───────────────────────┤
│{"instance_id":"inst7002"}│{"part_number":"p003"}│{"instance_id":"inst7003"}│{"part_number":"p004"}│{"material_id":"M0002"}│
├──────────────────────────┼──────────────────────┼──────────────────────────┼──────────────────────┼───────────────────────┤
│{"instance_id":"inst7001"}│{"part_number":"p002"}│{"instance_id":"inst7002"}│{"part_number":"p003"}│{"material_id":"M0002"}│
└──────────────────────────┴──────────────────────┴──────────────────────────┴──────────────────────┴───────────────────────┘

我期望包含第二个子树,它恰好也是一个链表。第二个子树由位于图形最右边的ItemUsageInstances inst7006,inst7007和inst7008组成。就其价值而言,这些相邻实例不仅由相同的Material组成,而且都是相同的Item的实例。

我确认每个ItemUsageInstance节点与一个[INSTANCE_OF]节点都有Item关系:

MATCH (iui:ItemUsageInstance) WHERE NOT (iui)-[:INSTANCE_OF]->(:Item) RETURN iui 

(返回0条记录)。

还确认每个Item节点与一个[MADE_FROM]节点具有Material关系:

MATCH (i:Item) WHERE NOT (i)-[:MADE_FROM]->(:Material) RETURN i 

(返回0条记录)。

确认inst7008是唯一没有外向ItemUsageInstance关系的[CHILD_OF]

MATCH (iui:ItemUsageInstance) WHERE NOT (iui)-[:CHILD_OF]->(:ItemUsageInstance) RETURN iui

(返回1条记录:{"instance_id":"inst7008"}

inst5000inst7001是仅有的ItemUsageInstances,没有传入的[CHILD_OF]关系

MATCH (iui:ItemUsageInstance) WHERE NOT (iui)<-[:CHILD_OF]-(:ItemUsageInstance) RETURN iui

(返回2条记录:{"instance_id":"inst7001"}{"instance_id":"inst5000"}

我想收集/汇总结果,以便每一行都是一个子树。我了解了collect()的使用方法this example,并使用了数组方法。但是它中仍然有重复的ItemUsageInstance。 (在那里讨论的“项目图”完全失败了……)

关于我的查询为什么只查找具有相同材料的相邻项目用法实例的一个子树的任何见解?

按子树汇总结果的最佳方法是什么?

3 个答案:

答案 0 :(得分:1)

找到根源很容易。 MATCH (root:ItemUsageInstance) WHERE NOT ()-[:CHILD_OF]->(root)

对于孩子,您可以通过指定最小距离0(默认值为1)来包含根。

MATCH p=(root)-[:CHILD_OF*0..25]->(ins), (m:Material)<-[:MADE_FROM]-(:Item)<-[:INSTANCE_OF]-(ins)

然后假设每个实例仅一个物料,则基于物料聚合所有内容(您不能聚合到一个聚合中,因此在使用节点收集深度之前,请使用WITH获取深度)

WITH ins, SIZE(NODES(p)) as depth, m RETURN COLLECT({node:ins, depth:depth}) as instances, m as material


所以,在一起

MATCH (root:ItemUsageInstance),
      p=(root)<-[:CHILD_OF*0..25]-(ins),
      (m:Material)<-[:MADE_FROM]-(:Item)<-[:INSTANCE_OF]-(ins)
WHERE NOT ()<-[:CHILD_OF]-(root)
  AND NOT (m:Material)<-[:MADE_FROM]-(:Item)<-[:INSTANCE_OF]-()<-[:CHILD_OF]-(ins)
MATCH p2=(ins)<-[:CHILD_OF*1..25]-(cins)
WHERE ALL(n in NODES(p2) WHERE (m)<-[:MADE_FROM]-(:Item)<-[:INSTANCE_OF]-(n))
WITH ins, cins, SIZE(NODES(p2)) as depth, m ORDER BY depth ASC
RETURN ins as collection_head, ins+COLLECT(cins) as instances, m as material

答案 1 :(得分:0)

在您的模式中,您不会考虑inst_5001与inst_7001之间的链接之类的情况。 Inst_5001没有任何零件用法的链接,但是您的匹配模式要求这两种用法都具有这样的链接。我认为这是您偏离轨道的地方。您找到的inst_5002树恰好是您的模式所需要的用法的链接。

就“按子树聚合”而言,我将返回树根的ID(例如,id(iui1),然后再返回count(*),以显示给定根参与多少个子树)

答案 2 :(得分:0)

这是我经过大量修改的查询:

MATCH path = (cinst:ItemUsageInstance)-[:CHILD_OF*1..]->(pinst:ItemUsageInstance), (m:Material)<-[:MADE_FROM]-(:Item)<-[:INSTANCE_OF]-(pinst)
WHERE ID(cinst) <> ID(pinst) AND ALL (x in nodes(path) WHERE ((x)-[:INSTANCE_OF]->(:Item)-[:MADE_FROM]->(m)))
WITH nodes(path) as insts, m
UNWIND insts AS instance
WITH DISTINCT instance, m
RETURN collect(instance), m

它返回我的期望:

╒═════════════════════════════════════════════════════════════════════════════════════════════════════════════╤═══════════════════════╕
│"collect(instance)"                                                                                          │"m"                    │
╞═════════════════════════════════════════════════════════════════════════════════════════════════════════════╪═══════════════════════╡
│[{"instance_id":"inst7002"},{"instance_id":"inst7003"},{"instance_id":"inst7001"},{"instance_id":"inst5002"}]│{"material_id":"M0002"}│
├─────────────────────────────────────────────────────────────────────────────────────────────────────────────┼───────────────────────┤
│[{"instance_id":"inst7007"},{"instance_id":"inst7008"},{"instance_id":"inst7006"}]                           │{"material_id":"M0001"}│
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────┴───────────────────────┘

一个限制是它不能将子树的根与子树区分开。理想情况下,{"instance_id"}的列表应按树中的深度排序。