概述
OPTIONAL MATCH
的作用与MATCH
类似,都用于实现图模式匹配,它们的不同之处在于:
MATCH
:如果一个图模式没有匹配结果,不返回任何记录。OPTIONAL MATCH
:如果一个图模式没有匹配结果,返回一个null
值。
<optional match statement> ::=
"OPTIONAL" <match statement>
| "OPTIONAL" "{" <match statement>... "}"
| "OPTIONAL" "(" <match statement>... ")"
如果OPTIONAL MATCH
中的图模式使用了之前语句中声明过的变量,那么它会隐式地进行子查询。此时,OPTIONAL MATCH
对该变量的记录逐条进行图模式匹配,返回匹配结果或null
。
注意:使用OPTIONAL MATCH
时,WHERE
子句是在图模式匹配阶段就被评估的,也就是说,它在OPTIONAL
逻辑生效之前就已经起作用了。
示例图

CREATE GRAPH myGraph {
NODE User ({name string}),
NODE Club ({since uint32}),
EDGE Follows ()-[{createdOn date}]->(),
EDGE Joins ()-[{memberNo uint32}]->()
} PARTITION BY HASH(Crc32) SHARDS [1]
INSERT (rowlock:User {_id: 'U01', name: 'rowlock'}),
(brainy:User {_id: 'U02', name: 'Brainy'}),
(purplechalk:User {_id: 'U03', name: 'purplechalk'}),
(mochaeach:User {_id: 'U04', name: 'mochaeach'}),
(lionbower:User {_id: 'U05', name: 'lionbower'}),
(c01:Club {_id: 'C01', since: 2005}),
(c02:Club {_id: 'C02', since: 2005}),
(rowlock)-[:Follows {createdOn: '2024-01-05'}]->(brainy),
(mochaeach)-[:Follows {createdOn: '2024-02-10'}]->(brainy),
(brainy)-[:Follows {createdOn: '2024-02-01'}]->(purplechalk),
(purplechalk)-[:Follows {createdOn: '2024-05-03'}]->(lionbower),
(brainy)-[:Joins {memberNo: 1}]->(c01),
(lionbower)-[:Joins {memberNo: 2}]->(c01),
(mochaeach)-[:Joins {memberNo: 9}]->(c02)
检查存在性
用户rowlock
没有加入俱乐部C01
,因此这个查询不返回任何结果:
MATCH (:User {name: "rowlock"})->(c:Club {_id: "C01"})
RETURN c
结果:无数据返回
然而,使用OPTIONAL MATCH
时,查询返回一行null
记录,代表没有匹配结果:
OPTIONAL MATCH (:User {name: "rowlock"})->(c:Club {_id: "C01"})
RETURN c
结果:
c |
---|
null |
保留所有流入查询的记录
此查询中,两个MATCH
语句基于共同变量u
进行等值连接(Equi-join):
MATCH (u:User)
MATCH (u)-[:Joins]->(c:Club)
RETURN u.name, c._id
结果:
u.name | c._id |
---|---|
mochaeach | C02 |
Brainy | C01 |
lionbower | C01 |
为保留所有用户,无论他们是否加入了某个俱乐部,你可以将第二个MATCH
替换成OPTIONAL MATCH
。第一个MATCH
获取的每个用户点依次流入OPTIONAL MATCH
,即使没有匹配结果,用户点也不会被舍弃:
MATCH (u:User)
OPTIONAL MATCH (u)-[:Joins]->(c:Club)
RETURN u.name, c._id
结果:
u.name | c._id |
---|---|
mochaeach | C02 |
Brainy | C01 |
rowlock | null |
lionbower | C01 |
purplechalk | null |
保持查询运行
当某条语句产生空结果时,查询会在此处停止执行,因为后续语句将没有数据可供操作。
例如,以下查询将不会返回任何结果,因为MATCH
找不到匹配的点,导致RETURN
语句根本不会被执行:
MATCH (u:User) WHERE u.name = "Masterpiece1989"
RETURN CASE WHEN u IS NULL THEN "User not found" ELSE u END
结果:无数据返回
为了确保即使在未找到匹配项时查询仍能继续执行,可以使用OPTIONAL MATCH
:
OPTIONAL MATCH (u:User) WHERE u.name = "Masterpiece1989"
RETURN CASE WHEN u IS NULL THEN "User not found" ELSE u END
结果:User not found
WHERE子句的评估
本查询返回没有关注者的用户:
MATCH (n:User)
OPTIONAL MATCH p = (n)<-[:Follows]-()
FILTER p IS NULL
RETURN COLLECT_LIST(n.name) AS Names
结果:
Names |
---|
["mochaeach","rowlock"] |
如果将FILTER
替换为WHERE
,将无法获得预期结果,因为WHERE
子句是在OPTIONAL
生效之前就被评估的:
MATCH (n:User)
OPTIONAL MATCH p = (n)<-[:Follows]-()
WHERE p IS NULL
RETURN COLLECT_LIST(n.name) AS Names
结果:
Names |
---|
["mochaeach","Brainy","rowlock","lionbower","purplechalk"] |
使用MATCH语句块
你可以使用花括号{}
或圆括号()
将多条MATCH
语句包裹起来,并将OPTIONAL
应用于整个MATCH
语句块。这样,整个语句块会被视为一个整体:如果其中任何一部分匹配失败,查询不会中断,而是为该块中引入的所有变量返回null
。
FOR name IN ["rowlock", "Masterpiece1989", "Brainy"]
OPTIONAL {
MATCH (u:User) WHERE u.name = name
MATCH (u)->(c:Club)
}
RETURN table(name, u.name, c._id)
结果:
name | u.name | c._id |
---|---|---|
rowlock | null |
null |
Masterpiece1989 | null |
null |
Brainy | Brainy | C01 |