COLLECT 子查询

COLLECT 子查询表达式可用于使用给定子查询返回的行创建列表。

COLLECT 子查询与 COUNTEXISTS 子查询的不同之处在于,最终的 RETURN 子句是必需的。RETURN 子句必须返回正好一列。

示例图

以下图用于下面的示例

subqueries graph

要重新创建图,请针对空 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'})

简单的 COLLECT 子查询

外部作用域引入的变量可以在 COLLECT 子查询中使用,无需导入。在这方面,COLLECT 子查询不同于 CALL 子查询,后者需要导入。以下查询对此进行了说明,并输出了名为 Ozzy 的狗的所有者

MATCH (person:Person)
WHERE 'Ozzy' IN COLLECT { MATCH (person)-[:HAS_DOG]->(dog:Dog) RETURN dog.name }
RETURN person.name AS name
name

"Peter"

行数:1

带有 WHERE 子句的 COLLECT 子查询

可以在 COLLECT 子查询中使用 WHERE 子句。MATCH 子句和外部作用域引入的变量可以在内部作用域中使用。

MATCH (person:Person)
RETURN person.name as name, COLLECT {
  MATCH (person)-[r:HAS_DOG]->(dog:Dog)
  WHERE r.since > 2017
  RETURN dog.name
} as youngDogs
name youngDogs

"Andy"

[]

"Timothy"

[]

"Peter"

["Ozzy"]

行数:3

带有 UNIONCOLLECT 子查询

COLLECT 可与 UNION 子句一起使用。以下示例使用 UNION 子句显示每个人拥有的宠物名称的集合

MATCH (person:Person)
RETURN
    person.name AS name,
    COLLECT {
        MATCH (person)-[:HAS_DOG]->(dog:Dog)
        RETURN dog.name AS petName
        UNION
        MATCH (person)-[:HAS_CAT]->(cat:Cat)
        RETURN cat.name AS petName
    } AS petNames
name petNames

"Andy"

["Andy"]

"Timothy"

["Mittens"]

"Peter"

["Ozzy", "Fido"]

行数:3

带有 WITHCOLLECT 子查询

外部作用域的变量对整个子查询可见,即使使用 WITH 子句也是如此。为避免混淆,不允许对这些变量进行遮蔽。当在内部作用域中定义了一个新引入的变量,其标识符与外部作用域变量相同,则会遮蔽外部作用域变量。在以下示例中,外部变量 name 被遮蔽,因此会抛出错误。

WITH 'Peter' as name
MATCH (person:Person {name: name})
RETURN COLLECT {
    WITH 'Ozzy' AS name
    MATCH (person)-[r:HAS_DOG]->(d:Dog {name: name})
    RETURN d.name
} as dogsOfTheYear
错误消息
The variable `name` is shadowing a variable with the same name from the outer scope and needs to be renamed (line 4, column 20 (offset: 92))

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

MATCH (person:Person)
RETURN person.name AS name, COLLECT {
    WITH 2018 AS yearOfTheDog
    MATCH (person)-[r:HAS_DOG]->(d:Dog)
    WHERE r.since = yearOfTheDog
    RETURN d.name
} as dogsOfTheYear
name dogsOfTheYear

"Andy"

[]

"Timothy"

[]

"Peter"

["Ozzy"]

行数:3

在其他子句中使用 COLLECT 子查询

COLLECT 可在查询中的任何位置使用,除了管理命令,在管理命令中,COLLECT 表达式受到限制。以下是一些示例,说明 COLLECT 如何在查询的不同位置使用

RETURN 中使用 COLLECT

MATCH (person:Person)
RETURN person.name,
       COLLECT {
            MATCH (person)-[:HAS_DOG]->(d:Dog)
            MATCH (d)-[:HAS_TOY]->(t:Toy)
            RETURN t.name
       } as toyNames
person.name toyNames

"Andy"

[]

"Timothy"

[]

"Peter"

["Banana"]

行数:3

SET 中使用 COLLECT

MATCH (person:Person) WHERE person.name = "Peter"
SET person.dogNames = COLLECT { MATCH (person)-[:HAS_DOG]->(d:Dog) RETURN d.name }
RETURN person.dogNames as dogNames
dogNames

["Ozzy", "Fido"]

行数:1
已设置的属性:1

CASE 中使用 COLLECT

MATCH (person:Person)
RETURN
   CASE
     WHEN COLLECT { MATCH (person)-[:HAS_DOG]->(d:Dog) RETURN d.name } = []  THEN "No Dogs " + person.name
     ELSE person.name
   END AS result
result

"Andy"

"No Dogs Timothy"

"Peter"

行数:3

使用 COLLECT 作为分组键

以下查询按狗的名称收集所有人员,然后计算每个组的平均年龄。

MATCH (person:Person)
RETURN COLLECT { MATCH (person)-[:HAS_DOG]->(d:Dog) RETURN d.name } AS dogNames,
       avg(person.age) AS averageAge
 ORDER BY dogNames
dogNames averageAge

[]

25.0

["Andy"]

36.0

["Ozzy", "Fido"]

35.0

行数:3

使用 COLLECTcollect()

COLLECT 处理 null 值的方式与聚合函数 collect() 不同。collect() 函数会自动删除 null 值。COLLECT 不会自动删除 null 值。但是,可以通过在子查询中添加过滤步骤来删除它们。

以下查询说明了这些差异

MATCH (p:Person)
RETURN collect(p.nickname) AS names
names

["Pete", "Tim"]

行数:1

RETURN COLLECT {
        MATCH (p:Person)
        RETURN p.nickname ORDER BY p.nickname
      } AS names
names

["Pete", "Tim", null]

行数:1

RETURN COLLECT {
        MATCH (p:Person)
        WHERE p.nickname IS NOT NULL
        RETURN p.nickname ORDER BY p.nickname
      } AS names
name

["Pete", "Tim"]

行数:1

规则

以下内容适用于 COLLECT 子查询

  • 允许任何非写入查询。

  • 使用 COLLECT 子查询时,最终的 RETURN 子句是必需的。RETURN 子句必须返回正好一列。

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

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

  • COLLECT 子查询中引入的变量不是外部作用域的一部分,因此无法从外部访问。