触发器
触发器允许注册 Cypher 查询,当 Neo4j 中的数据发生变更(创建、更新、删除)时触发。触发器可以在事务提交前或提交后运行。
|
有几种实现方法:
此外, |
|
安装、更新或删除触发器是最终一致性操作。因此,它们不会立即被添加/更新/删除,而是具有由 APOC 配置 |
默认情况下,触发器是禁用的。我们可以通过在 apoc.conf 中设置以下属性来启用它们:
apoc.trigger.enabled=true
apoc.trigger.refresh=60000
| 选项键 | 值 | 描述 |
|---|---|---|
apoc.trigger.enabled |
true/false,默认 false |
启用/禁用该功能 |
apoc.trigger.refresh |
数字,默认 60000 |
触发集群节点间复制检查的间隔(毫秒) |
| 限定名称 | 类型 |
|---|---|
apoc.trigger.drop |
|
apoc.trigger.dropAll |
|
apoc.trigger.install |
|
apoc.trigger.list |
|
apoc.trigger.show |
|
apoc.trigger.start |
|
apoc.trigger.stop |
|
来自 Neo4j 的事务数据被转换为适当的数据结构,作为语句的参数(例如 $createdNodes)进行消费。
可用的参数有:
| Statement | 描述 |
|---|---|
|
返回事务的 ID。 注意,此值仅适用于“after”和“afterAsync”阶段(参见 触发器阶段表)。否则,其值为 |
|
返回事务提交时间的毫秒数 |
|
当创建节点时触发(节点列表) |
|
当创建关系时触发(关系列表) |
|
当删除节点时触发(节点列表) |
|
当删除关系时触发(关系列表) |
|
当删除标签时触发(标签到节点列表的映射) |
|
当删除节点属性时触发(键到 [键, 旧值, 节点] 映射列表的映射) |
|
当删除关系属性时触发(键到 [键, 旧值, 关系] 映射列表的映射) |
|
当分配标签时触发(标签到节点列表的映射) |
|
当分配节点属性时触发(键到 [键, 旧值, 新值, 节点] 映射列表的映射) |
|
当分配关系属性时触发(键到 [键, 旧值, 新值, 关系] 映射列表的映射) |
|
包含该事务元数据的映射。事务元数据可以在客户端设置,例如通过 /docs/api/java-driver/current/org.neo4j.driver/org/neo4j/driver/TransactionConfig.html#metadata() |
apoc.trigger.install() 的第三个参数是一个映射 {phase: PHASE},其中 PHASE 是一个字符串,可以是以下值之一:
|
描述 |
|
触发器将在提交 |
|
触发器将在 |
|
触发器将在提交 |
|
触发器将在提交 |
|
与旧版本 Neo4j 不同,在 Neo4j 5 中无法修改在 “after” 阶段创建的实体。例如,以下查询将返回异常,消息为
相反,必须使用其他阶段或仅执行读取操作。 |
触发器示例
可以添加一个触发器,当在节点上设置特定属性时,该触发器会将相同的属性添加到连接到该节点的所有节点上。
数据集(在默认数据库 'neo4j' 中)
CREATE (d:Person {name:'Daniel', surname: 'Craig'})
CREATE (l:Person {name:'Mary', age: 47})
CREATE (t:Person {name:'Tom'})
CREATE (j:Person {name:'John'})
CREATE (m:Person {name:'Michael'})
CREATE (a:Person {name:'Anne'})
CREATE (l)-[:DAUGHTER_OF]->(d)
CREATE (t)-[:SON_OF]->(d)
CREATE (t)-[:BROTHER]->(j)
CREATE (a)-[:WIFE_OF]->(d)
CREATE (d)-[:SON_OF]->(m)
CREATE (j)-[:SON_OF]->(d)
使用上述数据集,如果添加了触发器并执行以下查询:MATCH (n:Person) WHERE n.name IN ['Daniel', 'Mary'] SET n.age=55, n.surname='Quinn',触发器语句中使用的 $assignedNodeProperties 将如下所示(其中 NODE(1) 是 (:Person {name: 'Daniel'}),而 NODE(2) 是 (:Person {name: 'Mary'}))
{
age: [{
node : NODE(1),
new: 55,
old: null,
key: "age"
},
{
node: NODE(2),
new: 55,
old: 47,
key: "age"
}],
surname: [{
node: NODE(1),
new: "Quinn",
old: "Craig",
key: "surname"
},
{
node: NODE(2),
new: "Quinn",
old: null,
key: "surname"
}]
}
结果是一个映射,其中键是已分配的属性,值是涉及的实体列表。列表中的每个元素包含节点本身、已更改属性的新值、旧值(如果属性不存在则为 null)以及属性名的键。
$removedNodeProperties 参数具有相同的结构和逻辑(在这种情况下,new 值将始终为 null)。
assignedRelationshipProperties 和 removedRelationshipProperties 的逻辑相同,唯一的区别在于 node: NODE(n) 键被替换为 relationship: RELATIONSHIP(n) 键。
例如,以下语句创建了一个触发器,对于每个 SET,都会使用当前日期更新 time 和 lasts 两个属性:
CALL apoc.trigger.install('neo4j', 'setLastUpdate',
"
UNWIND keys($assignedNodeProperties) AS k
UNWIND $assignedNodeProperties[k] AS map
WITH map.node AS node, collect(map.key) AS propList
MATCH (n)
WHERE id(n) = id(node) AND NOT 'lasts' in propList // to prevent loops
SET n.time = date(), n.lasts = propList
",
{phase: 'afterAsync'});
请注意,apoc.trigger.install 以及 apoc.trigger.drop、apoc.trigger.dropAll、apoc.trigger.stop 和 apoc.trigger.start 必须在系统数据库中执行。
在上面的例子中,使用 MATCH (n) WHERE id(n) = id(node) 是为了演示节点先通过 ID 找到,然后再设置参数。然而,更高效的做法是删除此命令,改为将倒数第二行修改为:SET node.time = date(), node.lasts = propList。注意,必须添加 AND NOT 'lasts' IN propList 条件以防止无限循环,因为 SET 命令将再次触发此查询。
在经过由 apoc.trigger.refresh 配置定义的时间后,可以执行以下查询:
MATCH (n:Person {name: 'Daniel'}) set n.age = 123, n.country = 'Italy'
执行
MATCH (n:Person {name: 'Daniel'}) return n
可以设置属性 time 为今天的日期,并将 lasts=['country','age']。
当 surname 属性添加到节点时,它也会被添加到所有连接到它的节点上(此例为一级深度)。
MATCH (d:Person {name:'Daniel'})
SET d.surname = 'William'
要添加一个触发器,将每个标签为 Actor 的新节点连接起来并为 name 属性分配特定值,请运行以下查询:
CALL apoc.trigger.install('neo4j','create-rel-new-node',
"
UNWIND $createdNodes AS n
MATCH (m:Movie {title:'Matrix'})
WHERE n:Actor AND n.name IN ['Keanu Reeves','Laurence Fishburne','Carrie-Anne Moss']
CREATE (n)-[:ACT_IN]->(m)
",
{phase:'before'}
)
CREATE (k:Actor {name:'Keanu Reeves'})
CREATE (l:Actor {name:'Laurence Fishburne'})
CREATE (c:Actor {name:'Carrie-Anne Moss'})
CREATE (a:Actor {name:'Tom Hanks'})
CREATE (m:Movie {title:'Matrix'})
在带有 $deletedRelationships 或 $deletedNodes 的触发器查询中,无法使用 Cypher 函数 properties() 获取已删除实体的属性,因为已删除的实体表示为虚拟节点和虚拟关系。相反,触发器查询必须使用 apoc.any.properties 函数来检索已删除节点和关系的属性。
CALL apoc.trigger.install('neo4j', 'createLogNodeOnDelete',
"
UNWIND $deletedNodes as deletedNode
CREATE (log:Log) (1)
SET log:$(labels(deletedNode)) (2)
SET log += apoc.any.properties(deletedNode) (3)
",
{phase: 'afterAsync'});
| 1 | 为每个已删除的节点创建一个标签为 Log 的节点。 |
| 2 | 使用 动态标签 将已删除节点的标签添加到 Log 节点。注意:labels 是分配给已删除节点的。 |
| 3 | 使用 apoc.any.properties 函数将已删除节点的属性添加到 Log 节点。 |
有了这个触发器,已删除节点的标签和属性将被分配给 Log 节点,如下所示:
Person 节点CREATE (d:Person:Actor {name:'Daniel', surname: 'Craig'})
Person 节点MATCH (n:Person {name: "Daniel"})
DETACH DELETE n
Log 节点返回关于已删除节点的信息MATCH (n:Log) RETURN n
| n |
|---|
(:Log:Person:Actor {surname: "Craig",name: "Daniel"}) |
为防止某些事务锁,通常建议使用 afterAsync 阶段。这将阻止查询无限期挂起。
请注意,apoc.trigger.stop 和 apoc.trigger.start 过程是最终一致的。因此,需要等待一段设置好的时间,以便更改能够传播。等待时间由 apoc.trigger.refresh 配置定义。
要暂停触发器而不将其删除(以便后续使用),请使用以下过程:
要恢复已暂停的触发器,请使用以下过程:
添加 \{params: {parameterMaps}} 以插入附加参数。
CALL apoc.trigger.install('neo4j', 'timeParams',
"UNWIND $createdNodes AS n SET n.time = $time",
{}, {params: {time: timestamp()}}
);
CALL apoc.trigger.install('neo4j', 'timestamp',
"UNWIND $createdNodes AS n SET n.ts = timestamp()",
{});
CALL apoc.trigger.install('neo4j', 'lowercase',
"UNWIND $createdNodes AS n SET n.id = toLower(n.name)",
{});
CALL apoc.trigger.install('neo4j', 'txInfo',
"UNWIND $createdNodes AS n SET n.txId = $transactionId, n.txTime = $commitTime",
{phase:'after'});
CALL apoc.trigger.install('neo4j', 'count-removed-rels',
"MATCH (c:Counter) SET c.count = c.count + size([r IN $deletedRelationships WHERE type(r) = "X"])",
{})
删除触发器
要在 'neo4j' 数据库中删除名为 'test' 的触发器,请运行以下查询:
CALL apoc.trigger.drop('neo4j', 'test')
要删除 'neo4j' 数据库中的所有触发器,请运行以下查询:
CALL apoc.trigger.dropAll('neo4j')
触发器列表
可以返回数据库中触发器的完整列表。例如,如果创建了以下查询中的触发器:
CALL apoc.trigger.install('neo4j', 'count-removals',
"MATCH (c:Counter) SET c.count = c.count + size([f IN $deletedNodes WHERE id(f) > 0])",
{})
然后可以运行(同样,在经过 apoc.trigger.refresh 配置定义的时间之后):
CALL apoc.trigger.show('neo4j')
| 名称 (name) | query | selector | params | installed | paused |
|---|---|---|---|---|---|
"count-removals" |
{} |
{} |
TRUE |
FALSE |
|
请注意,由于触发器操作是最终一致的(基于 |