关系与高级过滤

本页展示如何添加关系和更高级的过滤器,以及如何为投影结果起别名。它扩展了 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

在电影数据集上执行它会得到以下结果

表 1. 结果
m.title m.tagline m.released actingRoles

"The Matrix"

"Welcome to the Real World"

1999

["Neo"]

"Johnny Mnemonic"

"The hottest data on earth. In the coolest head in town"

1995

["Johnny Mnemonic"]

"Something’s Gotta Give"

null

2003

["Julian Mercer"]

"The Devil’s Advocate"

"Evil has its winning ways"

1997

["Kevin Lomax"]

"The Replacements"

"Pain heals, Chicks dig scars…​ Glory lasts forever"

2000

["Shane Falco"]

模式创建

定义更复杂的模式

  1. 定义模式的变量。关系和节点一样,都是可以在查询中创建并重复使用的变量。

    const movieNode = new Cypher.Node();
    const personNode = new Cypher.Node();
    
    const actedIn = new Cypher.Relationship();

    到目前为止,关系尚未与节点相连。这是因为这里创建的是用于保存模式匹配到的关系的变量,而不是关系本身。

  2. 使用 new Cypher.Pattern 在已经声明的变量基础上定义任意复杂的模式。

    const pattern = new Cypher.Pattern(movieNode, { labels: ["Movie"] })
        .related(actedIn, { type: "ACTED_IN" })
        .to(personNode, { labels: ["Person"] });

    请注意,在 Cypher Builder 中,模式的工作方式与 Cypher 类似,链中的每个元素要么是关系,要么是节点。

  3. MATCH 子句中使用 pattern 变量。

    const clause = new Cypher.Match(pattern)
  4. 运行脚本。得到的 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。然而,模式本身不正确——它返回的是一个 MoviePerson 的行为,而不是 PersonMovie 中的行为。

为了解决此问题,请在 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

这些比较分别使用了逻辑运算 ANDNOTOR

随着高级查询的增长,它们可能会影响可维护性。为避免此问题,建议将查询拆分为更基本的比较,然后再进行组合。

该过程与对节点和模式的操作方式类似。

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));

您可以使用 titlePropyearProp复用变量。亦可为第一个操作创建一个 personName 变量,而不是直接传递属性。

要使用布尔运算符组合比较,需要采用不同于之前 where().and() 简写的策略。因为现在涉及嵌套运算(NOTOR)。实现方式如下:

  1. .where() 方法内部使用 Cypher.andCypher.orCypher.not 方法。

    const clause = new Cypher.Match(pattern)
        .where(Cypher.and(isKeanu, Cypher.or(Cypher.not(releasedBefore2000), titleContainsMatrix)))
        .return(titleProp, taglineProp, yearProp);

    将查询拆分到独立变量中再组合,是为每个部分添加上下文的好机会。这有助于阅读高级过滤,因为基本操作拥有了更清晰的名称。

  2. 运行脚本。得到的 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 }

    请务必再次确认所有变量均指向正确的参数、节点或关系。

投影别名

最后,您可以添加投影别名。

  1. 若要返回别名为 actingRolesr.roles,请将 roles 添加到属性列表中。

    const rolesProperty = actedIn.property("roles");
  2. 和之前一样,将属性加入 .return 语句,但这次以包含别名值的元组形式传入。

        .return(titleProp, taglineProp, yearProp, [rolesProperty, "actingRoles"]);
  3. 运行查询。结果如下所示。

    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 为投影起别名。

© . This site is unofficial and not affiliated with Neo4j, Inc.