条件查询 (WHEN)

WHENTHENELSE 配合使用,可以根据特定条件执行查询的不同分支。通过这种方式,它实现了类似于其他编程语言中 IF 语句的控制流机制。

有关在表达式中使用 WHEN 分支的信息,请参阅 条件表达式 (CASE)

示例图

以下图表用于下方的示例

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

CREATE (alice:Person {name:'Alice', age: 65}),
       (bob:Person {name: 'Bob', age: 25}),
       (charlie:Person {name: 'Charlie', age: 61}),
       (daniel:Person {name: 'Daniel', age: 39}),
       (eskil:Person {name: 'Eskil', age: 39}),
       (bob)-[:WORKS_FOR]->(alice),
       (alice)-[:WORKS_FOR]->(daniel),
       (charlie)-[:WORKS_FOR]->(daniel),
       (bob)-[:LOVES]->(eskil),
       (charlie)-[:LOVES]->(alice)

独立 WHEN 分支

独立 WHEN 分支的语法
WHEN predicate THEN [{]
  <conditionalQuery>
  [}]
[WHEN ...]*
[ELSE [{]
  <conditionalQuery>
[}]]

第一个谓词计算结果为 true 的分支将被执行。如果没有任何 WHEN 分支被执行且存在 ELSE 分支,则执行 ELSE 分支。如果没有任何 WHEN 分支计算结果为 true 且不存在 ELSE 分支,则不会执行任何分支,也不会产生任何行。以下示例展示了此逻辑。

条件逻辑
WHEN false THEN RETURN 1 AS x
WHEN true THEN RETURN 2 AS x
WHEN true THEN RETURN 3 AS x
ELSE RETURN 3 AS x

由于第二个 WHEN 分支为 true,它将执行,而前面的分支(为 false)和后续的 WHEN 分支(即使为 true)以及 ELSE 分支都将被跳过。

结果
x

2

行:1

在独立 WHEN 分支中条件执行查询
WHEN true THEN {
  MATCH (n:Person) WHERE n.name STARTS WITH "A"
  RETURN n.name AS name
}
ELSE {
  MATCH (n:Person)
  RETURN n.name AS name
}
上述示例和下述示例中包含的 {} 并非必须(除非明确说明),但它们明确了查询中不同的条件分支。
结果
名称 (name)

"Alice"

行:1

规则

UNION 类似,WHEN/ELSE 结构的所有部分中,列的数量和名称必须完全相同。这意味着返回的表达式必须使用 AS 进行别名命名。

不允许:不对返回的表达式使用别名
WHEN true THEN RETURN 2
ELSE RETURN 3
不允许:在不同分支中使用不同的列名
WHEN true THEN RETURN 2 AS x
ELSE RETURN 3 AS y
不允许:在不同分支中返回不同数量的列
WHEN true THEN RETURN 2 AS x, 3 AS y
ELSE RETURN 3 AS x

WHEN 也不能作为查询中的常规子句定位。例如,它不能紧接在 MATCH 子句之后。

不允许:将 WHEN 结构用作常规子句
MATCH (n)-[:WORKS_FOR]->(manager:Person)
WHEN manager IS NULL THEN {
  MERGE (n)-[:WORKS_FOR]->(newManager: Person {name: 'Peter', age: 36})
}
RETURN n.name AS employees,
       m.name AS manager,
       newManager.name AS newManagerNode

相反,如果 WHEN 结构是大型查询的一部分,则必须将其置于子查询内和/或组合 UNION 查询的不同侧。

条件子查询

WHEN 可以用在一个或多个 CALL 子查询 中,仅当指定的条件计算结果为 true 时才执行一组操作。

条件 CALL 子查询的语法
[<outerQuery>]
<callSubquery> {
  WHEN predicate THEN [{]
    <conditionalQuery>
 [}]
 [WHEN ...]*
 [ELSE [{]
  <conditionalQuery>
 [}]]
}
[<callSubquery> ...]*
[<outerQuery>]
示例 1. 单个条件 CALL 子查询

在此示例中,WHEN 用于为条件 (manager IS NULL) 计算结果为 true 的每一行执行一个 CALL 子查询

条件 CALL 子查询
MATCH (n:Person)
OPTIONAL MATCH (n)-[:WORKS_FOR]->(manager:Person)
CALL (*) {
  WHEN manager IS NULL THEN {
    MERGE (newManager: Person {name: 'Peter', age: 36})
    MERGE (n)-[:WORKS_FOR]->(newManager)
    RETURN newManager, n.name AS employee
  }
}
RETURN newManager.name AS newManager,
       collect(employee) AS employees

由于只有 DanielEskil 没有向外的 WORKS_FOR 关系,他们现在已被连接为新的 Peter 节点的员工。

结果
newManager employees

"Peter"

["Daniel", "Eskil"]

行:1

示例 2. 链式条件 CALL 子查询

不相关的条件 CALL 子查询可以依次链式执行,每个子查询的执行都取决于一组定义的条件。

此示例使用条件逻辑为个人分配年龄组。第二个 CALL 随后会收集其经理(他们 WORK_FOR 的对象)的姓名和年龄组,但仅针对那些经理比自己年长的个人。

链式执行多个条件 CALL 子查询
MATCH (n:Person)
OPTIONAL MATCH (n)-[r:WORKS_FOR]->(m:Person)
CALL (*) {
  WHEN n.age > 60 THEN {
    SET n.ageGroup = 'Veteran'
    RETURN n.ageGroup AS ageGroup
  }
  WHEN n.age >= 35 AND n.age <= 59 THEN {
    SET n.ageGroup = 'Senior'
    RETURN n.ageGroup AS ageGroup
  }
  ELSE {
    SET n.ageGroup = 'Junior'
    RETURN n.ageGroup AS ageGroup
  }
}
CALL (*) {
  WHEN m.age > n.age THEN {
    RETURN collect([m.name, m.ageGroup]) AS manager
  }
}
RETURN n.name AS name, ageGroup, manager

返回 Bob 是因为他是图中唯一拥有更年长经理的人。

结果
名称 (name) ageGroup manager

"Bob"

"Junior"

[["Alice", "Veteran"]]

行:1

EXISTSCOLLECTCOUNT 子查询表达式也可以包含 WHEN 分支。

条件 EXISTSCOLLECTCOUNT 子查询的语法
[<outerExpression>]
EXISTS|COUNT|COLLECT {
  WHEN predicate THEN [{]
   <conditionalQuery>
 [}]
 [WHEN ...]*
 [ELSE [{]
   <conditionalQuery>
 [}]]
}
[<outerExpression>]
示例 3. 条件 EXISTS 子查询

在此示例中,WHEN 用于 EXISTS 子查询内部,以根据谓词 (n.age > 40) 的评估结果有条件地执行不同分支。

CALL 子查询不同,EXISTS 子查询中返回的变量在外部作用域中不可用(COUNTCOLLECT 子查询也是如此)。
EXISTS 子查询中的 WHEN
MATCH (n:Person)
WHERE EXISTS {
  WHEN n.age > 40 THEN {
    RETURN n.name AS x
  }
  ELSE {
    MATCH (n)-[:LOVES]->(x:Person)
    RETURN x
  }
}
RETURN n.name AS name,
       n.age AS age

AliceCharlie 都大于 40 岁,因此他们由 WHEN 分支返回,而 Bob 则由 ELSE 分支返回。请注意,图中的一些 Person 节点在条件子查询的任何分支中都没有被匹配到,因此不会被返回。

结果
名称 (name) age

"Alice"

65

"Charlie"

61

"Bob"

25

行:3

将条件查询与 UNION 结合使用

多个条件查询的结果也可以使用 UNION [DISTINCT]UNION ALL 进行组合;前者会从结果集中删除重复项,后者则不会(有关更多信息,请参阅 组合查询 (UNION))。

如果条件查询以 WHEN 开头并涉及 UNION,则 WHEN 分支必须括在花括号 {} 中(相同的 {} 也可用于组合 UNIONUNION ALL)。

使用 {} 组合独立 WHEN 分支与 UNION 的语法
{
  WHEN predicate THEN [{]
    <conditionalQuery>
  [}]
  [WHEN ...]*
  [ELSE [{]
    <conditionalQuery>
[}]]
}
UNION [DISTINCT|ALL]
{
  WHEN predicate THEN [{]
   <conditionalQuery>
  [}]
  [WHEN ...]*
  [ELSE [{]
    <conditionalQuery>
[}]]
}
[UNION [DISTINCT|ALL] ...]*
使用 {} 组合带有 UNION 的条件分支
{
  WHEN true THEN RETURN 1 AS x
  WHEN false THEN RETURN 2 AS x
  ELSE RETURN 3 AS x
}
UNION
{
  WHEN false THEN RETURN 4 AS x
  WHEN false THEN RETURN 5 AS x
  ELSE RETURN 6 AS x
}
结果
x

1

6

行:2

如果通过 UNION 组合的查询不是以 WHEN 分支开头的,则不需要花括号。

示例 4. 将条件逻辑与 UNION 结合使用

在下面的示例中,UNION 用于组合两个条件子查询的结果(包含整个查询的第一个 CALL 是必需的,以便为每个 person 从每个条件子查询部分收集最终结果)。

该查询根据两个条件对人员进行分类:他们是否拥有 LOVES 关系以及他们是否小于或大于 40 岁。最终结果为每个 person 提供了一个 status 消息列表,显示了两个条件组合后的结果。

将条件子查询与 UNION 结合使用
MATCH (n:Person)
CALL (n) {
  OPTIONAL MATCH (n)-[r:LOVES]->(m:Person)
  CALL (*) {
    WHEN r IS NULL THEN {
      RETURN n.name AS person, "Loves no one" AS message
    }
    ELSE {
      RETURN n.name AS person, "Loves somebody" AS message
    }
  }
  RETURN person, message
  UNION
  CALL (*) {
    WHEN n.age < 40 THEN {
      RETURN n.name AS person, "Under 40" AS message
    }
    ELSE {
      RETURN n.name AS person, "40 or older" AS message
    }
  }
  RETURN person, message
}
RETURN person, collect(message) AS status
结果
person 状态 (status)

"Alice"

["Loves no one", "40 or older"]

"Charlie"

["Loves somebody", "40 or older"]

"Daniel"

["Loves no one", "Under 40"]

"Eskil"

["Loves no one", "Under 40"]

"Peter"

["Loves no one", "Under 40"]

"Bob"

["Loves somebody", "Under 40"]

行数: 6