更新数据

之前您学习了如何在 Cypher® 中表示节点、关系、标签、属性和模式。本节通过介绍如何使用 Cypher 更新和删除数据来进一步扩展您的知识。

尽管这些是标准的 CRUD(创建、读取、更新和删除)操作,但图数据库中的某些功能与其他类型的数据库略有不同。在后续内容中,您可能会识别出一些相似点和不同点。

使用 Cypher 更新数据

数据中可能已经存在节点或关系,但您想修改其属性。您可以通过匹配要查找的模式并使用 SET 关键字来添加、删除或更新属性来实现此目的。

我们将继续使用以下数据集

要创建上述图,请运行 Cypher 查询

CREATE (diana:Person {name: "Diana"})
CREATE (melissa:Person {name: "Melissa", twitter: "@melissa"})
CREATE (dan:Person {name: "Dan", twitter: "@dan", yearsExperience: 6})
CREATE (sally:Person {name: "Sally", yearsExperience: 4})
CREATE (john:Person {name: "John", yearsExperience: 5})
CREATE (jennifer:Person {name: "Jennifer", twitter: "@jennifer", yearsExperience: 5})
CREATE (joe:Person {name: "Joe"})
CREATE (mark:Person {name: "Mark", twitter: "@mark"})
CREATE (ann:Person {name: "Ann"})
CREATE (xyz:Company {name: "XYZ"})
CREATE (x:Company {name: "Company X"})
CREATE (a:Company {name: "Company A"})
CREATE (Neo4j:Company {name: "Neo4j"})
CREATE (abc:Company {name: "ABC"})
CREATE (query:Technology {type: "Query Languages"})
CREATE (etl:Technology {type: "Data ETL"})
CREATE (integrations:Technology {type: "Integrations"})
CREATE (graphs:Technology {type: "Graphs"})
CREATE (dev:Technology {type: "Application Development"})
CREATE (java:Technology {type: "Java"})
CREATE (diana)-[:LIKES]->(query)
CREATE (melissa)-[:LIKES]->(query)
CREATE (dan)-[:LIKES]->(etl)<-[:LIKES]-(melissa)
CREATE (xyz)<-[:WORKS_FOR]-(sally)-[:LIKES]->(integrations)<-[:LIKES]-(dan)
CREATE (sally)<-[:IS_FRIENDS_WITH]-(john)-[:LIKES]->(java)
CREATE (john)<-[:IS_FRIENDS_WITH]-(jennifer)-[:LIKES]->(java)
CREATE (john)-[:WORKS_FOR]->(xyz)
CREATE (sally)<-[:IS_FRIENDS_WITH]-(jennifer)-[:IS_FRIENDS_WITH]->(melissa)
CREATE (joe)-[:LIKES]->(query)
CREATE (x)<-[:WORKS_FOR]-(diana)<-[:IS_FRIENDS_WITH]-(joe)-[:IS_FRIENDS_WITH]->(mark)-[:LIKES]->(graphs)<-[:LIKES]-(jennifer)-[:WORKS_FOR]->(Neo4j)
CREATE (ann)<-[:IS_FRIENDS_WITH]-(jennifer)-[:IS_FRIENDS_WITH]->(mark)
CREATE (john)-[:LIKES]->(dev)<-[:LIKES]-(ann)-[:IS_FRIENDS_WITH]->(dan)-[:WORKS_FOR]->(abc)
CREATE (ann)-[:WORKS_FOR]->(abc)
CREATE (a)<-[:WORKS_FOR]-(melissa)-[:LIKES]->(graphs)<-[:LIKES]-(diana)

利用到目前为止的上述示例数据集,您可以更新 Jennifer 的节点以添加她的出生日期。下一个 Cypher 语句展示了如何实现这一点。

  1. 首先,您需要找到 Jennifer 的现有节点。

  2. 接下来,使用 SET 创建新属性(语法为 variable.property)并设置其值。

  3. 最后,您可以返回 Jennifer 的节点,以确保信息已正确更新。

MATCH (p:Person {name: 'Jennifer'})
SET p.birthdate = date('1980-01-01')
RETURN p

查询结果

Set Properties: 1
Rows: 1

+------------------------------------------------------+
| p                                                    |
+------------------------------------------------------+
|(Person: {birthdate: '1980-01-01', name: 'Jennifer'}) |
+------------------------------------------------------+

有关使用 date() 和其他时间函数的更多信息,请访问 Cypher 手册 → 时间函数

如果您想更改 Jennifer 的出生日期,可以使用上面相同的查询再次找到 Jennifer 的节点,并在 SET 子句中放入不同的日期。

您还可以更新 Jennifer 与 Company 节点之间的 WORKS_FOR 关系,以包含她开始在那里工作的年份。为此,您可以使用与上述更新节点类似的语法。

MATCH (:Person {name: 'Jennifer'})-[rel:WORKS_FOR]-(:Company {name: 'Neo4j'})
SET rel.startYear = date({year: 2018})
RETURN rel

查询结果

Set Properties: 1
Rows: 1

+-----------------------------------------+
| rel                                     |
+-----------------------------------------+
| [:WORKS_FOR {startYear: '2018-01-01'}]  |
+-----------------------------------------+

如果您想在上述查询上返回图视图,可以将变量添加到 p:Personc:Company 的节点,并将返回行写为 RETURN p, rel, c

使用 Cypher 删除数据

另一个要介绍的操作是如何在 Cypher 中删除数据。对于此操作,Cypher 使用 DELETE 关键字删除节点和关系。它与 SQL 等其他语言中删除数据非常相似,但有一个例外。

因为 Neo4j 符合 ACID 标准,所以如果一个节点仍然有关系,您就不能删除它。如果您可以这样做,那么您可能会得到一个指向空的关系和一个不完整的图。

删除关系

要删除关系,您需要找到要删除关系上的起始节点和结束节点,然后使用 DELETE 关键字,如下面的代码块所示。现在,我们先删除 Jennifer 和 Mark 之间的 IS_FRIENDS_WITH 关系。我们将在后续练习中重新添加此关系。

MATCH (j:Person {name: 'Jennifer'})-[r:IS_FRIENDS_WITH]->(m:Person {name: 'Mark'})
DELETE r

查询结果

+-----------------------------------------+
| Deleted Relationships: 1                |
| Rows: 0                                 |
+-----------------------------------------+

删除节点

要删除没有任何关系的节点,您需要找到要删除的节点,然后使用 DELETE 关键字,就像您上面删除关系时所做的那样。您现在可以删除 Mark 的节点,稍后将其恢复。

MATCH (m:Person {name: 'Mark'})
DELETE m

查询结果

+-----------------------------------------+
| Deleted Nodes: 1                        |
| Rows: 0                                 |
+-----------------------------------------+

如果您不小心创建了一个空节点并需要删除它,您可以使用以下 Cypher 语句来完成

MATCH (n)
WHERE id(n) = 5
DETACH DELETE n

此语句不仅删除节点,还删除其所有关系。要运行此语句,您应该知道节点的内部 ID。

删除节点及其关系

您可以运行一个单独的语句来同时删除节点及其关系,而不是运行最后两个查询来删除 IS_FRIENDS_WITH 关系和 Mark 的 Person 节点。如上所述,Neo4j 符合 ACID 标准,因此不允许删除仍然有关系的节点。使用 DETACH DELETE 语法会告诉 Cypher 删除节点拥有的任何关系,并删除节点本身。

该语句如下面的代码所示。首先,您在数据库中找到 Mark 的节点。然后,DETACH DELETE 行会删除 Mark 节点拥有的任何现有关系,然后再删除节点本身。

MATCH (m:Person {name: 'Mark'})
DETACH DELETE m

删除属性

您也可以删除属性,但除了使用 DELETE 关键字之外,您还可以使用其他几种方法。

第一个选项是在属性上使用 REMOVE。这会告诉 Neo4j 您想将该属性从节点中完全删除,并且不再存储它。

第二个选项是使用前面提到的 SET 关键字将属性值设置为 null。与其他数据库模型不同,Neo4j 不存储空值。相反,它只存储对您的数据有意义的属性和值。这意味着您可以在图中的各种节点和关系上拥有不同类型和数量的属性。

为了向您展示这两种选项,让我们来看看各自的代码。

//delete property using REMOVE keyword
MATCH (n:Person {name: 'Jennifer'})
REMOVE n.birthdate

//delete property with SET to null value
MATCH (n:Person {name: 'Jennifer'})
SET n.birthdate = null

查询结果

+-----------------------------------------+
| Set Properties: 1                       |
| Rows: 0                                 |
+-----------------------------------------+

使用 MERGE 避免重复数据

之前曾简要提及,Cypher 中有一些方法可以避免创建重复数据。其中一种方法是使用 MERGE 关键字。MERGE 执行“选择或插入”操作,首先检查数据是否存在于数据库中。如果存在,则 Cypher 会按原样返回数据,或者对现有节点或关系进行您指定的任何更新。如果数据不存在,则 Cypher 将使用您指定的信息创建数据。

在节点上使用 MERGE

首先,让我们看一个示例,通过使用下面的查询将 Mark 添加回我们的数据库。您可以使用 MERGE 来确保 Cypher 检查数据库中是否存在 Mark 的节点。由于您在前面的示例中删除了 Mark 的节点,Cypher 将不会找到现有匹配项,并将创建一个新节点,其 name 属性设置为 'Mark'。

MERGE (mark:Person {name: 'Mark'})
RETURN mark

查询结果

如果您再次运行相同的语句,Cypher 这次将找到一个已存在的节点,其 name 属性设置为 Mark,因此它将返回匹配的节点而不进行任何更改。

在关系上使用 MERGE

就像您使用 MERGE 在 Cypher 中查找或创建节点一样,您也可以这样做来查找或创建关系。让我们重新创建之前示例中 Mark 和 Jennifer 之间的 IS_FRIENDS_WITH 关系。

MATCH (j:Person {name: 'Jennifer'})
MATCH (m:Person {name: 'Mark'})
MERGE (j)-[r:IS_FRIENDS_WITH]->(m)
RETURN j, r, m

请注意,这里使用 MATCH 来查找 Mark 的节点和 Jennifer 的节点,然后我们才使用 MERGE 来查找或创建它们之间的关系。

为什么我们不使用单个语句?

MERGE 会查找您指定的整个模式,以确定是返回现有模式还是创建新模式。如果整个模式(节点、关系以及任何指定的属性)不存在,Cypher 就会创建它。

Cypher 绝不会在模式中产生匹配和创建的混合部分。为了避免匹配和创建的混合,您需要先匹配模式中任何现有元素,然后再对您可能想要创建的任何元素进行合并,就像我们在上面的语句中所做的那样。

仅供参考,下面是导致重复的 Cypher 语句。由于此模式(Jennifer IS_FRIENDS_WITH Mark)在数据库中不存在,Cypher 将全新创建整个模式——包括两个节点以及它们之间的关系。

//this statement will create duplicate nodes for Mark and Jennifer
MERGE (j:Person {name: 'Jennifer'})-[r:IS_FRIENDS_WITH]->(m:Person {name: 'Mark'})
RETURN j, r, m

处理 MERGE 标准

也许您想使用 MERGE 来确保不创建重复项,但您希望在模式创建时初始化某些属性,并在仅匹配时更新其他属性。在这种情况下,您可以将 ON CREATEON MATCHSET 关键字一起使用来处理这些情况。

让我们看一个例子。

MERGE (m:Person {name: 'Mark'})-[r:IS_FRIENDS_WITH]-(j:Person {name:'Jennifer'})
  ON CREATE SET r.since = date('2018-03-01')
  ON MATCH SET r.updated = date()
RETURN m, r, j
© . All rights reserved.