Cypher:更新特定节点列表中所有节点对之间的关系
我正在使用Neo4j,对如何在Cypher中进行一些查询以保持高性能有些担心。
我有一个图,里面的节点都有一个标签叫“Thing”。每个节点都有一个属性pk(整数类型)。
现在,我有一个像[123, 34, 125]这样的pk列表,我想创建/更新(如果已经存在的话)所有可能的节点对之间的关系。
比如说要创建(123, 34)、(123, 125)、(34, 125)这样的关系,并且对于每一对节点之间的关系r.weight = 0(如果是新创建的关系),或者r.weight += 1(如果关系已经存在)。
目前,我在Python中使用itertools.combinations(pks, 2)来生成组合,然后对每一对c1, c2进行处理:
'MATCH (c1:Thing {pk: %(c1_pk)s}), (c2:Thing {pk: %(c2_pk)s}) ' \
'CREATE UNIQUE c1-[r:KNOWS]-c2 ' \
'SET r.weight=coalesce(r.weight, 0)+1 RETURN c1, r.weight, c2' % {'c1_pk': c1_pk,
'c2_pk': c2_pk}
但是我不喜欢对每一对都调用查询,因为当pk列表很长时,性能会很差,因为组合和查询的次数是length(pks)*(length(pks)-1)。
我该怎么做呢?
编辑: 现在大约有15000个“Thing”节点,并且这个数量将始终保持不变。 还有一个约束条件是pk属性必须唯一(t:Thing t.pk是唯一的)。
1 个回答
你在创建这些成对关系时没有任何限制,所以你想用笛卡尔积的方式来做。如果节点很多,这样会很慢。
不过,我看到你查询中的一个错误是,你试图创建每个关系两次;比如说你想从 X 指向 Y。你首先创建了 X 指向 Y,然后又创建了 Y 指向 X。
你可以通过只创建一次来把这个数量减半,像这样:
MATCH (c1:Thing {pk: %(c1_pk)s}), (c2:Thing {pk: %(c2_pk)s})
CREATE UNIQUE c1-[r:KNOWS]-c2
SET r.weight=coalesce(r.weight, 0)+1 RETURN c1, r.weight, c2' % {'c1_pk': c1_pk,
'c2_pk': c2_pk}
WHERE c1.id < c2.id
注意我们是按 ID 来排序节点的。因为它们的 ID 都不一样,所以你只会做 X 指向 Y,而不会做 Y 指向 X。
这样,你的整体处理过程就从 length(pks)*length(pks)-1 变成了: ( length(pks)*length(pks)-1 ) / 2。
不过,想要避免的事情是,你正在做的事情是 O(n^2) 的复杂度。