## 17.2. Triggers

This section describes procedures that can be used to register triggers whenever data in Neo4j is created, updated, or deleted.

In a trigger you register Cypher statements that are called when data in Neo4j is changed (created, updated, deleted). You can run them before or after commit.

Enable `apoc.trigger.enabled=true` in `$NEO4J_HOME/conf/apoc.conf` first.

`CALL apoc.trigger.add(name, statement, selector) yield name, statement, installed` | add a trigger statement under a name, in the statement you can use {createdNodes}, {deletedNodes} etc., the selector is {phase:'before/after/rollback'} returns previous and new trigger information
`CALL apoc.trigger.remove(name) yield name, statement, installed` | remove previously added trigger, returns trigger information
`CALL apoc.trigger.removeAll() yield name, statement, installed` | removes all previously added triggers , returns trigger information
`CALL apoc.trigger.list() yield name, statement, installed` | update and list all installed triggers
`CALL apoc.trigger.pause(name)` | it pauses the trigger
`CALL apoc.trigger.resume(name)` | it resumes the paused trigger

The transaction data from Neo4j is turned into appropriate data structures to be consumed as parameters to your statement.

The parameters available are:

Statement | Description
--- | ---
transactionId | returns the id of the transaction
commitTime | return the date of the transaction in milliseconds
createdNodes | when a node is created our trigger fires (list of nodes)
createdRelationships | when a relationship is created our trigger fires (list of relationships)
deletedNodes | when a node is delated our trigger fires (list of nodes)
deletedRelationships | when a relationship is delated our trigger fires (list of relationships)
removedLabels | when a label is removed our trigger fires (map of label to list of nodes)
removedNodeProperties | when a properties of node is removed our trigger fires (map of key to list of map of key,old,node)
removedRelationshipProperties | when a properties of relationship is removed our trigger fires (map of key to list of map of key,old,relationship)
assignedLabels | when a labes is assigned our trigger fires (map of label to list of nodes)
assignedNodeProperties | when node property is assigned our trigger fires (map of key to list of map of key,old,new,node)
assignedRelationshipProperties | when relationship property is assigned our trigger fires (map of key to list of map of key,old,new,relationship)

You can use these helper functions to extract nodes or relationships by label/relationship-type or updated property key.

Table 17.1. Helper Functions
`apoc.trigger.nodesByLabel({assignedLabels/assignedNodeProperties},'Label')` | function to filter labelEntries by label, to be used within a trigger statement with {assignedLabels} and {removedLabels} {phase:'before/after/rollback'} returns previous and new trigger information
`apoc.trigger.propertiesByKey({assignedNodeProperties},'key')` | function to filter propertyEntries by property-key, to be used within a trigger statement with {assignedNode/RelationshipProperties} and {removedNode/RelationshipProperties}. Returns [{old,[new],key,node,relationship}]

### 17.2.1. Triggers Examples

Set properties connected to a node. We could add a trigger that when is added a specific property on a node, that property is added to all the nodes connected to this node

Dataset

    CREATE (d:Person {name:'Daniel'})
    CREATE (l:Person {name:'Mary'})
    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)

Now we add the trigger using `apoc.trigger.propertiesByKey` on the `surname` property

    CALL apoc.trigger.add('setAllConnectedNodes','UNWIND apoc.trigger.propertiesByKey({assignedNodeProperties},"surname") as prop
    WITH prop.node as n
    MATCH(n)-[]-(a)
    SET a.surname = n.surname', {phase:'after'});

So when we add the `surname` property on a node, it’s added to all the nodes connected (in this case one level deep)

    MATCH (d:Person {name:'Daniel'})
    SET d.surname = 'William'

The `surname` property is add/change on all related nodes

Update labels. Dataset

    CREATE (k:Actor {name:'Keanu Reeves'})
    CREATE (l:Actor {name:'Laurence Fishburne'})
    CREATE (c:Actor {name:'Carrie-Anne Moss'})
    CREATE (m:Movie {title:'Matrix'})
    CREATE (k)-[:ACT_IN]->(m)
    CREATE (l)-[:ACT_IN]->(m)
    CREATE (c)-[:ACT_IN]->(m)

We add a trigger using `apoc.trigger.nodesByLabel` that when the label `Actor` of a node is removed, update all labels `Actor` with `Person`

    CALL apoc.trigger.add('updateLabels',"UNWIND apoc.trigger.nodesByLabel({removedLabels},'Actor') AS node
    MATCH (n:Actor)
    REMOVE n:Actor SET n:Person SET node:Person", {phase:'before'})

    MATCH(k:Actor {name:'Keanu Reeves'})
    REMOVE k:Actor

Create relationship on a new node. We can add a trigger that connect every new node with label `Actor` and as `name` property a specific value

    CALL apoc.trigger.add('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'})

Pause trigger. We have the possibility to pause a trigger without remove it, if we will need it in the future

Resume paused trigger. When you need again of a trigger paused

Enforcing property type. For this example, we would like that all the `reference` node properties are of type `STRING`

    CALL apoc.trigger.add("forceStringType",
    "UNWIND apoc.trigger.propertiesByKey({assignedNodeProperties}, 'reference') AS prop
    CALL apoc.util.validate(apoc.meta.type(prop) <> 'STRING', 'expected string property type, got %s', [apoc.meta.type(prop)]) RETURN null", {phase:'before'})

    CREATE (a:Node) SET a.reference = 1

    Neo.ClientError.Transaction.TransactionHookFailed

Other examples.

    CALL apoc.trigger.add('timestamp','UNWIND {createdNodes} AS n SET n.ts = timestamp()');
    CALL apoc.trigger.add('lowercase','UNWIND {createdNodes} AS n SET n.id = toLower(n.name)');
    CALL apoc.trigger.add('txInfo',   'UNWIND {createdNodes} AS n SET n.txId = {transactionId}, n.txTime = {commitTime}', {phase:'after'});
    CALL apoc.trigger.add('count-removed-rels','MATCH (c:Counter) SET c.count = c.count + size([r IN {deletedRelationships} WHERE type(r) = "X"])')
    CALL apoc.trigger.add('lowercase-by-label','UNWIND apoc.trigger.nodesByLabel({assignedLabels},'Person') AS n SET n.id = toLower(n.name)')