知识库

理解不存在的属性和处理空值

在 Neo4j 中,由于没有表模式或类似的机制来限制可能的属性,因此节点和关系属性的“不存在”和“空”是等价的。也就是说,实际上不存在具有空值的属性;空表示该属性根本不存在。

这与具有表模式的关系型数据库形成对比,在关系型数据库中,即使某个列的值为空,该列也存在于表的每一行中。

例如,如果我们有一个 :Person 节点的图,则以下两个查询被认为是相同的

MATCH (p:Person)
WHERE p.id = $personId
RETURN NOT EXISTS(p.email)
MATCH (p:Person)
WHERE p.id = $personId
RETURN p.email IS NULL

无论该属性是否在其他节点上使用(只是在当前节点上缺失),或者该属性在图中是否没有意义或从未使用过(例如 p.pinkElephant),只要属性不存在,上述 NULL 检查都会返回 true。

同样,以下两个查询会导致属性被移除

MATCH (p:Person)
WHERE p.id = $personId
SET p.email = null
MATCH (p:Person)
WHERE p.id = $personId
REMOVE p.email

如果需要一种方法来动态地从节点中清除多个属性,可以使用映射来更新节点属性,这将非常有用。

WITH {email:null, dob:null} as clearProps
MATCH (p:Person)
WHERE p.id = $personId
SET p += clearProps

NULL 映射值

这也表明,尽管节点和关系属性的“不存在”和“空”是等价的,但这对于映射或集合并不适用。如上所示,具有空值的映射条目与缺少该条目的映射是不同的。

这种区别在使用映射投影时尤其有用。映射投影是节点属性的映射视图,可用于强制显示返回数据中的属性,即使该属性在该节点上不存在。

例如,在此查询中,返回的节点数据中的 email 属性可能会也可能不会返回,具体取决于该属性是否存在于每个返回的节点上

MATCH (p:Person)
RETURN p

但是,如果希望 email 属性始终显示,如果属性不存在则显示 NULL,则可以使用如下所示的映射投影

MATCH (p:Person)
RETURN p {.*, .email}

NULL 列表值

列表中也允许使用 NULL。

RETURN [1,2,3,null,5]

但是,节点或关系属性上的聚合会跳过或忽略空值。

MATCH (p:Person)
RETURN count(p.email) as emailCount

计数不包括空值。

MATCH (p:Person)
RETURN collect(p.email) as emails

这会跳过空值;返回的集合不包含任何空值。

在逻辑表达式和其他上下文中处理 NULL

Neo4j 开发人员手册中有一节关于在逻辑表达式中处理 NULL的详细内容,并详细说明了表达式何时会返回 NULL。

使用 COALESCE() 为空值使用默认值

在某些情况下,我们可能希望在节点或关系上不存在属性时显示回退默认值。或者我们可能希望对可能的空值执行相等或不相等比较,并在该值恰好为空时使用默认值。

COALESCE() 允许处理这些情况。它接受任意数量的参数,并返回遇到的第一个非空值。

MATCH (p:Person)
RETURN collect(COALESCE(p.email, 'NOT SET')) as emails

与之前忽略空值不同,我们看到列表中包含了“未设置”。

MATCH (p:Person)
WHERE COALESCE(p.optedIn, false) <> true
RETURN p

使用 COALESCE(),我们可以将空值视为比较目的下的不同值。请注意,对空值的相等或不相等比较,例如 null <> true,会返回空值,而不是布尔值,因此 COALESCE() 在这里特别有用。

MATCH (p:Person)
RETURN COALESCE(p.email, p.backupEmail, p.backupBackupEmail, 'NOT SET') as email

允许使用多个回退,将使用第一个非空值。

使用 NULL 进行匹配和合并

空节点变量

虽然可以在 MATCH 中执行节点变量为空的操作(例如来自失败的 OPTIONAL MATCH),但不能使用空节点变量创建或合并模式。

当没有 :PinkElephant 节点时,这可以工作,但不会返回记录

OPTIONAL MATCH (node1:PinkElephant)
WITH node1
MATCH (node1)-[:ACTED_IN]->(m:Movie)
RETURN node1

但是,这两个都会抛出错误

OPTIONAL MATCH (node1:PinkElephant)
WITH node1
MERGE (node1)-[:ACTED_IN]->(m:Movie)
RETURN node1

OPTIONAL MATCH (node1:PinkElephant)
WITH node1
CREATE (node1)-[:ACTED_IN]->(m:Movie)
RETURN node1
空属性值

当我们改为使用空属性值时,可以使用空属性值进行 MATCH 或 CREATE,但不能使用 MERGE。

这些可以工作

OPTIONAL MATCH (ele:PinkElephant)
WITH ele
MATCH (node1:Person {name:ele.name})-[:ACTED_IN]->(m:Movie)
RETURN node1

OPTIONAL MATCH (ele:PinkElephant)
WITH ele
CREATE (node1:Person {name:ele.name})-[:ACTED_IN]->(m:Movie)
RETURN node1

但这个会抛出错误

OPTIONAL MATCH (ele:PinkElephant)
WITH ele
MERGE (node1:Person {name:ele.name})-[:ACTED_IN]->(m:Movie)
RETURN node1