关系与高级过滤
本页展示如何添加关系和更高级的过滤器,以及如何为投影结果起别名。它扩展了 Filters and projections 中基于 电影数据集 的 Cypher® 查询示例。
MATCH(m:Movie)<-[r:ACTED_IN]-(a:Person)
WHERE (a.name="Keanu Reeves" AND (NOT(m.title CONTAINS "Matrix") OR m.released < 2000))
RETURN m.title, m.tagline, m.released, r.roles AS actingRoles
此查询匹配一个 Movie 以及它的演员。它仅应返回演员为 Keanu Reeves 的电影,且排除标题中包含 Matrix 且在 1999 年之后上映的任何电影。同时将关系中的属性 roles 添加到投影中,并将其别名设为 actingRoles。
在电影数据集上执行它会得到以下结果
| m.title | m.tagline | m.released | actingRoles |
|---|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
模式创建
定义更复杂的模式
-
定义模式的变量。关系和节点一样,都是可以在查询中创建并重复使用的变量。
const movieNode = new Cypher.Node(); const personNode = new Cypher.Node(); const actedIn = new Cypher.Relationship();到目前为止,关系尚未与节点相连。这是因为这里创建的是用于保存模式匹配到的关系的变量,而不是关系本身。
-
使用
new Cypher.Pattern在已经声明的变量基础上定义任意复杂的模式。const pattern = new Cypher.Pattern(movieNode, { labels: ["Movie"] }) .related(actedIn, { type: "ACTED_IN" }) .to(personNode, { labels: ["Person"] });请注意,在 Cypher Builder 中,模式的工作方式与 Cypher 类似,链中的每个元素要么是关系,要么是节点。
-
在
MATCH子句中使用pattern变量。const clause = new Cypher.Match(pattern) -
运行脚本。得到的 Cypher 如下:
MATCH (this0:Movie)-[this1:ACTED_IN]->(this2:Person) WHERE (this0.title = $param0 AND this0.released < $param1) RETURN this0.title, this0.tagline, this0.released{ param0: 'The Matrix', param1: 2000 }
欲了解更多关于创建模式的信息,请参阅 Patterns。
基数、方向和属性
虽然在前面的步骤中更改了 MATCH 的目标,但查询中所有的过滤器和关系仍然指向正确的变量:this0。然而,模式本身不正确——它返回的是一个 Movie 对 Person 的行为,而不是 Person 在 Movie 中的行为。
为了解决此问题,请在 related 选项中添加 direction: "left"。
const pattern = new Cypher.Pattern(movieNode, { labels: ["Movie"] })
.related(actedIn, { type: "ACTED_IN", direction: "left" })
.to(people, { labels: ["Person"] });
模式本身的任意方面(基数、方向和属性)都可以通过“链”中每个元素的方法在模式声明时进行修改。完成这些更改后,脚本将产生如下结果。
MATCH (this0:Movie)<-[this1:ACTED_IN]-(this2:Person)
WHERE (this0.title = $param0 AND this0.released < $param1)
RETURN this0.title, this0.tagline, this0.released
布尔运算
到目前为止,示例中使用的所有过滤器均为 AND 类型。对于此类情况,Cypher Builder 在 WHERE 子句中提供了快捷方法 .and。然而,过滤器也可以以更高级的方式使用,例如嵌套运算。
WHERE (
a.name="Keanu Reeves" AND
(
NOT(m.title CONTAINS "Matrix")
OR m.released < 2000)
)
此高级过滤由 3 条基本比较组成:
-
a.name = "Keanu Reeves" -
m.title CONTAINS "Matrix" -
m.released < 2000
这些比较分别使用了逻辑运算 AND、NOT 和 OR。
随着高级查询的增长,它们可能会影响可维护性。为避免此问题,建议将查询拆分为更基本的比较,然后再进行组合。
该过程与对节点和模式的操作方式类似。
const isKeanu = Cypher.eq(personNode.property("name"), new Cypher.Param("Keanu Reeves"));
const titleContainsMatrix = Cypher.contains(titleProp, new Cypher.Param("The Matrix"));
const releasedBefore2000 = Cypher.lt(yearProp, new Cypher.Param(2000));
您可以使用 titleProp 和 yearProp 来 复用变量。亦可为第一个操作创建一个 personName 变量,而不是直接传递属性。
要使用布尔运算符组合比较,需要采用不同于之前 where().and() 简写的策略。因为现在涉及嵌套运算(NOT 与 OR)。实现方式如下:
-
在
.where()方法内部使用Cypher.and、Cypher.or与Cypher.not方法。const clause = new Cypher.Match(pattern) .where(Cypher.and(isKeanu, Cypher.or(Cypher.not(releasedBefore2000), titleContainsMatrix))) .return(titleProp, taglineProp, yearProp);将查询拆分到独立变量中再组合,是为每个部分添加上下文的好机会。这有助于阅读高级过滤,因为基本操作拥有了更清晰的名称。
-
运行脚本。得到的 Cypher 如下:
MATCH (this0:Movie)<-[this1:ACTED_IN]-(this2:Person) WHERE (this2.name = $param0 AND (NOT (this0.title CONTAINS $param1) OR this0.released < $param2)) RETURN this0.title, this0.tagline, this0.released{ param0: 'Keanu Reeves', param1: 'The Matrix', param2: 2000 }请务必再次确认所有变量均指向正确的参数、节点或关系。
投影别名
最后,您可以添加投影别名。
-
若要返回别名为
actingRoles的r.roles,请将roles添加到属性列表中。const rolesProperty = actedIn.property("roles"); -
和之前一样,将属性加入
.return语句,但这次以包含别名值的元组形式传入。.return(titleProp, taglineProp, yearProp, [rolesProperty, "actingRoles"]); -
运行查询。结果如下所示。
RETURN this0.title, this0.tagline, this0.released, this1.roles AS actingRoles
结论
您的脚本应如下所示
import Cypher from "@neo4j/cypher-builder";
const movieNode = new Cypher.Node();
const actedIn = new Cypher.Relationship();
const personNode = new Cypher.Node();
const pattern = new Cypher.Pattern(movieNode, { labels: ["Movie"] })
.related(actedIn, { type: "ACTED_IN", direction: "left" })
.to(personNode, { labels: ["Person"] });
const titleProp = movieNode.property("title");
const yearProp = movieNode.property("released");
const taglineProp = movieNode.property("tagline");
const rolesProperty = actedIn.property("roles");
const isKeanu = Cypher.eq(personNode.property("name"), new Cypher.Param("Keanu Reeves"));
const titleContainsMatrix = Cypher.contains(titleProp, new Cypher.Param("The Matrix"));
const releasedBefore2000 = Cypher.lt(yearProp, new Cypher.Param(2000));
const clause = new Cypher.Match(pattern)
.where(Cypher.and(isKeanu, Cypher.or(Cypher.not(titleContainsMatrix), releasedBefore2000)))
.return(titleProp, taglineProp, yearProp, [rolesProperty, "actingRoles"]);
const { cypher, params } = clause.build();
console.log(cypher);
console.log(params);
执行脚本后得到的查询如下:
MATCH (this0:Movie)<-[this1:ACTED_IN]-(this2:Person)
WHERE (this2.name = $param0 AND (NOT (this0.title CONTAINS $param1) OR this0.released < $param2))
RETURN this0.title, this0.tagline, this0.released, this1.roles AS actingRoles
{ param0: 'Keanu Reeves', param1: 'The Matrix', param2: 2000 }
您已学习如何构建高级查询并使用 AS 为投影起别名。