如何编写查询来比较不同路径上的节点和边缘?

时间:2014-06-17 12:05:57

标签: neo4j cypher

我们是Neo4j的新手(也很兴奋!)我试图将Cypher应用于我们的问题。我们有一个匹配路径的查询,但需要删除涉及在源自同一节点或边缘的任何其他路径上遍历的任何节点或边的路径。这是一个测试用例:

CREATE (a1:A {name: 'a1'})-[ab1:AB {name: 'ab1'}]->(b1:B {name: 'b1'}),
       (a1)-[ab2:AB {name: 'ab2'}]->(b2:B {name: 'b2'}),
       (a2:A {name: 'a2'})-[ab3:AB {name: 'ab3'}]->(b1),
       (a2)-[ab5:AB {name: 'ab5'}]->(b3:B {name: 'b3'}),
       (a3:A {name: 'a3'})-[ab4:AB {name: 'ab4'}]->(b2),
       (a3)-[ab6:AB {name: 'ab6'}]->(b3),
       (a4:A {name: 'a4'})-[ab7:AB {name: 'ab7'}]->(b3);

为便于阅读而格式化:

a1-[ab1]->b1
a1-[ab2]->b2
a2-[ab3]->b1
a2-[ab5]->b3
a3-[ab4]->b2
a3-[ab6]->b3
a4-[ab7]->b3

我们希望找到这些路径:[A,AB,B,AB,A,AB,B,AB,A](四个步骤)。 (注意:我们并不关心边缘方向性。)这是我的第一次尝试(我们的术语:i_id =' initial'和t_id =' terminal')。

MATCH p = (i_id:A)-[ab1:AB]-(b1:B)-[ab2:AB]-(a1:A)-[ab3:AB]-(b2:B)-[ab4:AB]-(t_id:A)
RETURN i_id.name, ab1.name, b1.name, ab2.name, a1.name, ab3.name, b2.name, ab4.name, t_id.name
ORDER BY i_id.name;

鉴于Cypher的Uniqueness功能,结果是合理的:

+-------------------------------------------------------------------------------------------------+
| i_id.name | ab1.name | b1.name | ab2.name | a1.name | ab3.name | b2.name | ab4.name | t_id.name |
+-------------------------------------------------------------------------------------------------+
| "a1"      | "ab1"    | "b1"    | "ab3"    | "a2"    | "ab5"    | "b3"    | "ab6"    | "a3"      |
| "a1"      | "ab1"    | "b1"    | "ab3"    | "a2"    | "ab5"    | "b3"    | "ab7"    | "a4"      |
| "a1"      | "ab2"    | "b2"    | "ab4"    | "a3"    | "ab6"    | "b3"    | "ab5"    | "a2"      |
| "a1"      | "ab2"    | "b2"    | "ab4"    | "a3"    | "ab6"    | "b3"    | "ab7"    | "a4"      |
| "a2"      | "ab3"    | "b1"    | "ab1"    | "a1"    | "ab2"    | "b2"    | "ab4"    | "a3"      |
| "a2"      | "ab5"    | "b3"    | "ab6"    | "a3"    | "ab4"    | "b2"    | "ab2"    | "a1"      |
| "a3"      | "ab4"    | "b2"    | "ab2"    | "a1"    | "ab1"    | "b1"    | "ab3"    | "a2"      |
| "a3"      | "ab6"    | "b3"    | "ab5"    | "a2"    | "ab3"    | "b1"    | "ab1"    | "a1"      |
| "a4"      | "ab7"    | "b3"    | "ab5"    | "a2"    | "ab3"    | "b1"    | "ab1"    | "a1"      |
| "a4"      | "ab7"    | "b3"    | "ab6"    | "a3"    | "ab4"    | "b2"    | "ab2"    | "a1"      |
+-------------------------------------------------------------------------------------------------+

但是,我们想要额外的过滤。考虑WHERE i_id.name =' a2':

+-------------------------------------------------------------------------------------------------+
| i_id.name | ab1.name | b1.name | ab2.name | a1.name | ab3.name | b2.name | ab4.name | t_id.name |
+-------------------------------------------------------------------------------------------------+
| "a2"      | "ab3"    | "b1"    | "ab1"    | "a1"    | "ab2"    | "b2"    | "ab4"    | "a3"      |
| "a2"      | "ab5"    | "b3"    | "ab6"    | "a3"    | "ab4"    | "b2"    | "ab2"    | "a1"      |
+-------------------------------------------------------------------------------------------------+

注意第一条路径如何包含ab4.name =" ab4",在第二条路径上也可以找到ab3.name。相反," ab2"在第二个路径上找到ab4.name,在第一个路径上找到ab3.name。在我们的应用程序中,我们希望这两个消除'这样查询就不会返回a2的匹配项。

最后,我的问题是:你如何在Cypher中做到这一点?只要它们快速执行,多个查询就可以了:-)我是Cypher的新手,但我认为可能有用的一些东西是(稻草紧抓,这里: - )

  • 将路径比较为集合(类似于WHERE ab4.name NOT IN ...?)
  • 标记/向项目添加属性表示i_id和 他们位于?
  • 的路径
  • FOR EACH?
  • UNWIND?
  • GROUP BY?

我们希望尽可能多地在Cypher做,但如果答案是"你不能这样做,"然后我们将上面的候选结果拉入内存并在那里完成处理。非常感谢!

1 个答案:

答案 0 :(得分:2)

所以我使用你的第二个建议制定了一个解决方案,即在关系中添加属性以指示它们是否在两个或更多路径上。

首先,在每个traversed关系上创建一个AB属性,并将其设置为0

MATCH ()-[ab:AB]-()
SET ab.traversed = 0

现在我将使用a2作为示例的起始节点。此查询将查找从a2到标签为A的另一个节点的所有路径,该路径长度为四步。每个关系的traversed属性设置为在路径上遇到关系的次数。

MATCH p = (a2:A {name:'a2'})-[:AB*4]-(:A)
UNWIND RELATIONSHIPS(p) AS r
WITH r, COUNT(*) AS times_traversed
SET r.traversed = times_traversed
RETURN r.name, r.traversed
ORDER BY r.name

我们得到以下输出:

enter image description here

正如您在示例中说明的那样,ab2ab4位于两个路径上,因此traversed属性为2

在每个关系上设置这些属性后,您可以将路径过滤到仅traversed属性总和等于路径长度的路径,在您的情况下为4。

MATCH p = (a2:A {name:'a2'})-[:AB*4]-(:A)
WHERE REDUCE(traversal = 0, r IN RELATIONSHIPS(p) | traversal + r.traversed) = LENGTH(p)
RETURN p

这不会返回任何路径,因为两个路径的traversed属性总和为6,而不是所需的4

但就像我说的那样,这是非常不优雅的,并且可能有更好的方法来做到这一点。