EXISTS 子查询

EXISTS 子查询可用于判断指定的模式在图中是否至少存在一次。它的作用与 路径模式 相同,但功能更强大,因为它允许你在内部使用 MATCHWHERE 子句。

示例图

以下图表用于下方的示例

要重新创建该图,请在空的 Neo4j 数据库中运行以下查询

CREATE
(andy:Swedish:Person {name: 'Andy', age: 36}),
(timothy:Person {name: 'Timothy', nickname: 'Tim', age: 25}),
(peter:Person {name: 'Peter', nickname: 'Pete', age: 35}),
(andy)-[:HAS_DOG {since: 2016}]->(:Dog {name:'Andy'}),
(timothy)-[:HAS_CAT {since: 2019}]->(:Cat {name:'Mittens'}),
(fido:Dog {name:'Fido'})<-[:HAS_DOG {since: 2010}]-(peter)-[:HAS_DOG {since: 2018}]->(:Dog {name:'Ozzy'}),
(fido)-[:HAS_TOY]->(:Toy{name:'Banana'})

简单的 EXISTS 子查询

外部作用域引入的变量可以在 EXISTS 子查询中直接使用,无需导入。在这方面,EXISTS 子查询与 确实需要导入变量的 CALL 子查询不同。以下示例展示了这一点

MATCH (person:Person)
WHERE EXISTS {
    (person)-[:HAS_DOG]->(:Dog)
}
RETURN person.name AS name
名称 (name)

"Andy"

"Peter"

行:2

带有 WHERE 子句的 EXISTS 子查询

WHERE 子句可以与 MATCH 结合使用。在此作用域内,可以使用 MATCH 子句引入的变量以及外部作用域中的变量。

MATCH (person:Person)
WHERE EXISTS {
  MATCH (person)-[:HAS_DOG]->(dog:Dog)
  WHERE person.name = dog.name
}
RETURN person.name AS name
名称 (name)

"Andy"

行:1

条件 EXISTS 子查询

可以在 EXISTS 子查询中使用 WHEN,以便在谓词评估为 true 时有条件地执行分支。请注意,不同 WHEN 分支返回的列名和列数必须完全一致。有关更多信息,请参阅 条件查询 → 条件子查询

下面的示例根据年龄和宠物拥有情况筛选人员。如果某人超过 35 岁,则检查其是否养狗;如果 35 岁或以下,则检查其是否养猫。请注意,Peter 年满 35 岁且养狗,因此被排除在结果之外。

条件 EXISTS 子查询
MATCH (n:Person)
WHERE EXISTS {
  WHEN n.age > 35 THEN {
    MATCH (n)-[:HAS_DOG]->(:Dog)
    RETURN n AS petOwner
  }
  ELSE {
    MATCH (n)-[:HAS_CAT]->(:Cat)
    RETURN n AS petOwner
  }
}
RETURN n.name AS name,
       n.age AS age
名称 (name) age

"Andy"

36

"Timothy"

25

行:2

嵌套 EXISTS 子查询

EXISTS 子查询可以嵌套,如下例所示。嵌套也会影响作用域。这意味着可以从子查询内部访问所有来自外部作用域的变量,或者访问在同一个子查询中定义的变量。

MATCH (person:Person)
WHERE EXISTS {
  MATCH (person)-[:HAS_DOG]->(dog:Dog)
  WHERE EXISTS {
    MATCH (dog)-[:HAS_TOY]->(toy:Toy)
    WHERE toy.name = 'Banana'
  }
}
RETURN person.name AS name
名称 (name)

"Peter"

行:1

WHERE 子句之外的 EXISTS 子查询

EXISTS 子查询表达式可以出现在任何表达式有效的地方。此处的结果是一个布尔值,表示子查询是否能找到给定的模式。

MATCH (person:Person)
RETURN person.name AS name, EXISTS {
  MATCH (person)-[:HAS_DOG]->(:Dog)
} AS hasDog
名称 (name) hasDog

"Andy"

true

"Timothy"

false

"Peter"

true

行:3

带有 UNIONEXISTS 子查询

EXISTS 可以与 UNION 子句一起使用,此时不需要 RETURN 子句。值得注意的是,如果其中一个分支有 RETURN 子句,那么所有分支都需要有。下例展示了如果 UNION 的其中一个分支返回了至少一行数据,整个 EXISTS 表达式就会评估为 true。

MATCH (person:Person)
RETURN
    person.name AS name,
    EXISTS {
        MATCH (person)-[:HAS_DOG]->(:Dog)
        UNION
        MATCH (person)-[:HAS_CAT]->(:Cat)
    } AS hasPet
名称 (name) hasPet

"Andy"

true

"Timothy"

true

"Peter"

true

行:3

带有 WITHEXISTS 子查询

外部作用域的变量在整个子查询中均可见,即使在使用 WITH 子句时也是如此。为避免混淆,不允许对这些变量进行遮蔽(shadowing)。当内部作用域中定义的变量与外部作用域变量同名时,就会发生变量遮蔽。在下面的示例中,外部变量 name 被遮蔽,因此会抛出错误。

WITH 'Peter' as name
MATCH (person:Person {name: name})
WHERE EXISTS {
    WITH "Ozzy" AS name
    MATCH (person)-[:HAS_DOG]->(d:Dog)
    WHERE d.name = name
}
RETURN person.name AS name
GQLSTATUS 错误链

42N07: 错误: 语法错误或访问规则冲突 - 变量遮蔽。变量 name 遮蔽了来自外部作用域的同名变量,需要重命名。

42001:错误:语法错误或访问规则冲突 - 无效语法

只要使用不同的标识符,就可以在子查询中引入新变量。在下面的示例中,WITH 子句引入了一个新变量。请注意,主查询中引用的外部作用域变量 personWITH 子句之后依然可用。

MATCH (person:Person)
WHERE EXISTS {
    WITH "Ozzy" AS dogName
    MATCH (person)-[:HAS_DOG]->(d:Dog)
    WHERE d.name = dogName
}
RETURN person.name AS name
名称 (name)

"Peter"

行:1

带有 RETURNEXISTS 子查询

EXISTS 子查询在其末尾不需要 RETURN 子句。如果存在 RETURN 子句,它不需要被别名化,这与 CALL 子查询 不同。在 EXISTS 子查询中返回的任何变量在子查询之后都将不可用。

MATCH (person:Person)
WHERE EXISTS {
    MATCH (person)-[:HAS_DOG]->(:Dog)
    RETURN person.name
}
RETURN person.name AS name
名称 (name)

"Andy"

"Peter"

行:2

规则

以下规则适用于 EXISTS 子查询:

  • 允许使用任何非写入型查询。

  • 如果 EXISTS 子查询评估出至少一行数据,整个表达式将变为 true。这也意味着系统只需要评估是否存在至少一行数据,即可跳过其余的工作。

  • EXISTS 子查询与常规查询的不同之处在于可以省略最后的 RETURN 子句,因为在子查询中定义的任何变量在表达式外部都不可用,即使使用了最后的 RETURN 子句也是如此。

  • EXISTS 仅由一个模式和一个可选的 WHERE 子句组成时,可以省略子查询中的 MATCH 关键字。

  • EXISTS 子查询可以出现在查询中任何表达式有效的位置。

  • 任何在外部作用域中定义的变量都可以在子查询自身的作用域内引用。

  • 在子查询内部引入的变量不属于外部作用域,因此无法在外部访问。