Cypher:查询之间的联合,并带有初始节点检索

时间:2019-05-27 15:11:32

标签: neo4j cypher

我正在尝试编写一个Cypher查询,它是一种“联盟”查询,基本上是这样的:

MATCH (myNodeUsedInAllSubQueries)
WITH myNodeUsedInAllSubQueries
MATCH (a)-[...]->(myNodeUsedInAllSubQueries)
RETURN a
UNION
MATCH (b)-[...]->(myNodeUsedInAllSubQueries)
RETURN b
...

我想首先匹配(myNodeUsedInAllSubQueries) 一次,因为此节点在所有子查询中都使用。 当然,在我的示例中,此节点的检索不仅如此简单(它涉及关系和一个WHERE子句,但在这里我将使其保持简单)。

我将尝试通过一个简单的示例以及到目前为止我已经尝试过的内容进行解释。

示例

在图形上,图形结构如下:

(John: Person) ----------[LIKES]----------> (pizza: Food)
                                               ^
        (Developers: Group) ------[LIKES]------┘ 

(Jim: Person) --[BELONGS_TO]--> (Business: Group) --[LIKES]--> (apple: Food)

该示例可访问here in Neo4J console。导入脚本在这里(如果您想在本地重现问题):

CREATE (john: Person{name: "John"});
CREATE (jim: Person{name: "Jim"});

CREATE (devs: Group{name: "Developers"});
CREATE (business: Group{name: "Business"});

CREATE (pizza: Food{name: "pizza"});
CREATE (apple: Food{name: "apple"});

MATCH (john: Person{name: "John"}), (pizza: Food{name: "pizza"})
CREATE (john)-[:LIKES]->(pizza);

MATCH (jim: Person{name: "Jim"}), (business: Group{name: "Business"})
CREATE (jim)-[:BELONGS_TO]->(business);

MATCH (devs: Group{name: "Developers"}), (pizza: Food{name: "pizza"})
CREATE (devs)-[:LIKES]->(pizza);

MATCH (business: Group{name: "Business"}), (apple: Food{name: "apple"})
CREATE (business)-[:LIKES]->(apple);

对于该特定示例,我的问题是:如何检索所有喜欢披萨的Person节点?知道这一点:

  • Person可以喜欢Food(直接关系)
  • Group可以喜欢Food,如果某人A BELONGS_TO是一个团体,则可以传递, 他LIKES也是LIKES团伙的食物(但没有直接关系 在这种情况下,PersonFood之间)

我也想一次检索pizza节点。正如我在简介中所述,在我的真实示例中,pizza的检索可能非常复杂,因此我不想在每种情况下都复制MATCH

我尝试过的

使用UNION

MATCH (pizza: Food{name: "pizza"})
WITH pizza
MATCH (p: Person)-[:LIKES]->(pizza)
RETURN p
UNION
MATCH (p: Person)-[:BELONGS_TO]->(g: Group)-[:LIKES]->(pizza)
RETURN p

此查询令人惊讶地返回2个节点:JohnJim。经过调查,我了解到 这是因为第二个(pizza)子句中的UNION并不指向pizza节点 由第一个MATCH(而不是任何节点)返回。我们可以通过运行来确认:

MATCH (pizza: Food{name: "pizza"})
WITH pizza
MATCH (p: Person)-[:LIKES]->(pizza)
RETURN p
UNION
MATCH (p: Person)-[:BELONGS_TO]->(g: Group)-[:LIKES]->(anything)
WHERE anything = pizza
RETURN p

这将返回错误:

Neo.ClientError.Statement.SyntaxError: Variable `pizza` not defined (line 7, column 18 (offset: 184))
"WHERE anything = pizza"

,验证pizza节点未传播到第二个MATCH的事实。

使用WITHCOLLECT

这解决了pizza节点无法从第一个MATCH传递到第三个MATCH的问题。

MATCH (pizza: Food{name: "pizza"})
WITH pizza
MATCH (p: Person)-[:LIKES]->(pizza)
WITH pizza, COLLECT(p) AS people
MATCH (p: Person)-[:BELONGS_TO]->(g: Group)-[:LIKES]->(pizza)
RETURN people + COLLECT(p) AS people

但是此查询不返回任何内容:这是因为第三个MATCH不返回任何内容 (在Person Group中,LIKES中没有pizza)。

如果我在Person Developers中添加Group

MATCH (devs: Group{name: "Developers"})
CREATE (emma: Person{name: "Emma"})-[:BELONGS_TO]->(devs)

上一个查询成功返回John和Emma,因为最后一个MATCH返回至少一个Person。 但就我而言,这个MATCH可能不返回任何内容,因此不起作用。

我希望这个例子简单明了。在这种情况下,我要查找的查询应该仅返回John,因为他是这里唯一喜欢Person的{​​{1}}。

2 个答案:

答案 0 :(得分:1)

[已更新]

这应该适用于您的简单用例:

MATCH (p:Person)-[:BELONGS_TO|LIKES*..2]->(food:Food {name:"pizza"})
RETURN food, COLLECT(p) AS people;

答案 1 :(得分:1)

作为Cyber​​sam答案的替代方法,如果您的图形结构无法使MATCH (p:Person)-[:BELONGS_TO|LIKES*..2]->(food:Food)有效(例如,如果您正在制作人们可以彼此喜欢的图形,并且您不想要返回一个喜欢另一个喜欢披萨的人(将遵循您的模式),您可以使用0..1长度关系来表示该模式中的可选步骤:

MATCH (p:Person)-[:BELONGS_TO*0..1]->()-[:LIKES]->(food:Food)
WHERE food.name = "pizza"
RETURN COLLECT(p) AS people;

这允许同时匹配这两种模式:

(p:Person)-[:LIKES]->(food:Food)

(p:Person)-[:BELONGS_TO]->()-[:LIKES]->(food:Food)

我已经用这种方法写了knowledge base article