概述
OPTIONAL MATCH的作用与MATCH类似,都用于实现图模式匹配,它们的不同之处在于:
MATCH:如果一个图模式没有匹配结果,不返回任何记录。OPTIONAL MATCH:如果一个图模式没有匹配结果,返回一个null值。
OPTIONAL关键词既可以用于单条MATCH语句,也可以用于一组MATCH语句。
示例图

CREATE GRAPH myGraph {
NODE User ({name string}),
NODE Club ({since uint32}),
EDGE Follows ()-[{createdOn date}]->(),
EDGE Joins ()-[{memberNo uint32}]->()
}
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,两个语句的结果集将进行左连接(Left join),即第一个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子句的评估
使用OPTIONAL MATCH时,WHERE子句是在图模式匹配阶段就被评估的,也就是说,它在OPTIONAL逻辑生效之前就已经起作用了。
本查询返回没有关注者的用户:
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 |