Triggers

Triggers

Database triggers are an integral part of most database systems. A trigger is a procedural code that is automatically executed in response to specific events. Events are related to some change in data, such as created, updated and deleted data records. The trigger is often used for maintaining the integrity of the information in the database.

Memgraph supports running openCypher clauses after a certain event happens during database transaction execution, i.e. triggers. For example, when a new Employee node is added, all relevant relationships should be created. Triggers can also be used to log historical data, for example, to keep track of employees' previous salaries.

You can create, delete and print triggers. All the triggers are persisted on the disk, so no information is lost on database reruns.

Create a trigger

To create a new trigger, use a query of the following format:

CREATE TRIGGER trigger_name [ ON [ () | --> ] CREATE | UPDATE | DELETE ]
[ BEFORE | AFTER ] COMMIT
EXECUTE openCypherStatements

The first line creates a trigger with a certain name and defines event type. Event type determines what event will trigger the execution of Cypher statements. The event type can be the creation, updated or deletion of nodes ( () ) or relationships ( --> ) before or after the commitment of that transaction.

Let's break down each element of the query.

Trigger name

Each created trigger must have a globally unique name. Two triggers can't have the same name even if they apply to different events.

Event type

The event type defines what will trigger the execution of certain Cypher statements.

There are three main event types:

  • CREATE
  • UPDATE
  • DELETE

Each event type can be fined tuned to trigger the execution only when nodes were modifed by adding (), when relationships were modified by adding --> or when any object was modified by not defining () or --> in the trigger creation.

For example:

  • ON CREATE - Execute certain Cypher statements if an object (node or a relationship) was created by a transaction.
  • ON () UPDATE - Execute certain Cypher statements if a node was updated by a transaction, perhaps a new property was added, or an existing property changed.
  • ON --> DELETE - Execute certain Cypher statements if a relationship was deleted by a transaction.

Event type produces information about created, updated or deleted objects within predefined variables, which can be used to write the Cypher queries trigger was set to execute.

If no event type is specified, the trigger executes its statements every time, and all the predefined variables can be used.

Predefined variables

When an event that triggers execution of certain Cypher queries is identified, information about the event, that is the information about the created, updated, or delted objects (nodes, relationships or both) in a single transaction are saved within a predifined variables.

Each event type creates different predefined variables:

Event typePredefined variables
ON CREATEcreatedVertices, createdEdges, createdObjects
ON () CREATEcreatedVertices
ON --> CREATEcreatedEdges
ON UPDATEsetVertexProperties, setEdgeProperties, removedVertexProperties, removedEdgeProperties, setVertexLabels, removedVertexLabels, updatedVertices, updatedEdges, updatedObjects
ON () UPDATEsetVertexProperties, removedVertexProperties, setVertexLabels, removedVertexLabels, updatedVertices
ON --> UPDATEsetEdgeProperties, removedEdgeProperties, updatedEdges
ON DELETEdeletedVertices, deletedEdges, deletedObjects
ON () DELETEdeletedVertices
ON --> DELETEdeletedEdges
no event type specifiedAll predefined variables can be used.
ON CREATE
createdVertices

List of all created nodes (vertices).

createdEdges

List of all created relationships (edges).

createdObjects

List of all created objects where each element is a map.

If the element containsa created node (vertex), it will be in the following format:

{
  "event_type": "created_vertex",
  "vertex": created_vertex_object
}

If the element contains a created relationship (edge), it will be in the following format:

{
  "event_type": "created_edge",
  "edge": created_edge_object
}
ON UPDATE

Setting an element to NULL is counted as a removal.

The changes are looked at on the transaction level only. That means if the value of a property on the same object was changed multiple times, only one update will be generated. The same applies for changes to node labels.

setVertexProperties

List of all set node (vertex) properties.

Each element is in the following format:

{
  "vertex": updated_vertex_object,
  "key": property_that_was_updated,
  "old": old_value_of_that_property,
  "new": new_value_of_that_property
}
setEdgeProperties

List of all set relationship (edge) properties.

Each element is in the following format:

{
  "edge": updated_vertex_object,
  "key": property_that_was_updated,
  "old": old_value_of_that_property,
  "new": new_value_of_that_property
}
removedVertexProperties

List of all removed node (vertex) properties.

Each element is in the following format:

{
  "vertex": updated_vertex_object,
  "key": property_that_was_updated,
  "old": old_value_of_that_property
}
removedEdgeProperties

List of all removed relationship (edge) properties.

Each element is in the following format:

{
  "vertex": updated_vertex_object,
  "key": property_that_was_updated,
  "old": old_value_of_that_property
}
setVertexLabels

List of all set node (vertex) labels.

Each element is in the following format:

{
  "label": label,
  "vertices": list_of_updated_vertices
}
removedVertexLabels

List of all removed node (vertex) labels.

Each element is in the following format:

{
  "label": label,
  "vertices": list_of_updated_vertices
}
updatedVertices

List of updates consisting of set and removed properties, and set and removed labels on nodes (vertices).

updatedEdges

List of updates consisting of set and removed properties on relationships (edges).

updatedObjects

List of updates consisting of set and removed properties on relationships (edges) and nodes (vertices), and set and removed labels on nodes (vertices).

Elements of update predefined variables

If the element contains information about a set node (vertex) property, it's in the following format:

{
  "event_type": "set_vertex_property",
  "vertex": updated_vertex_object,
  "key": property_that_was_updated,
  "old": old_value_of_that_property,
  "new": new_value_of_that_property
}

If the element contains information about a removed node (vertex) property, it's in the following format:

{
  "event_type": "removed_vertex_property",
  "vertex": updated_vertex_object,
  "key": property_that_was_updated,
  "old": old_value_of_that_property
}

If the element contains information about a set relationship (edge) property, it's in the following format:

{
  "event_type": "set_edge_property",
  "edge": updated_edge_object,
  "key": property_that_was_updated,
  "old": old_value_of_that_property,
  "new": new_value_of_that_property
}

If the element contains information about a removed relationship (edge) property, it's in the following format:

{
  "event_type": "removed_edge_property",
  "edge": updated_edge_object,
  "key": property_that_was_updated,
  "old": old_value_of_that_property
}

If the element contains information about a set node (vertex) label, it's in the following format:

{
  "event_type": "set_vertex_label",
  "vertex": updated_vertex_object,
  "label": label
}

If the element contains information about a removed node (vertex) label, it's in the following format:

{
  "event_type": "removed_vertex_label",
  "vertex": updated_vertex_object,
  "label": label
}
ON DELETE
deletedVertices

List of all deleted (nodes) vertices.

deletedEdges

List of all deleted (relationships) edges.

deletedObjects

List of all deleted objects where each element is a map.

If the element containsa created node (vertex), it will be in the following format:

{
  "event_type": "deleted_vertex",
  "vertex": deleted_vertex_object
}

If the element contains a created relationship (edge), it will be in the following format:

{
  "event_type": "deleted_edge",
  "edge": deleted_edge_object
}

Statement execution phase

A trigger can execute its statements at a specified phase, before or after Memgraph commits the transaction that triggered it. If the BEFORE COMMIT option is used, the trigger will execute its statements as part of that transaction before it's committed. If the AFTER COMMIT option is used, the trigger will execute its statements asynchronously after that transaction is committed.

Execute statements

A trigger can execute any valid openCypher query. No specific constraints are imposed on the queries. The only way trigger queries (i.e. statements) differ from standard queries is that a trigger query may use predefined variables, which are based on the event type specified for the trigger.

Example triggers

An example of a trigger would be:

CREATE TRIGGER exampleTrigger1 
ON () CREATE AFTER COMMIT EXECUTE
UNWIND createdVertices AS newNodes
SET newNodes.created = timestamp();

When a new node is created and the transaction is commited, the exampleTrigger1 will take the list of newly created nodes and for each new node set the property created to a timestamp value indicating the time of its creation.

A more complex example would be:

CREATE TRIGGER exampleTrigger2
ON UPDATE AFTER COMMIT EXECUTE
UNWIND updatedObjects AS updatedObject
WITH CASE
        WHEN updatedObject.vertex IS NOT null THEN updatedObject.vertex
        WHEN updatedObject.edge IS NOT null THEN updatedObject.edge
    END AS object
SET object.updated_at = timestamp();

The query is more complex, so let's break it down:

  • CREATE TRIGGER exampleTrigger: Creates the trigger and gives it a unique name exampleTrigger.
  • ON UPDATE AFTER COMMIT EXECUTE: Specifies what event will trigger the execution of Cypher statements. In exampleTrigger2 any update of any graph object will trigger the execution once the update event has been committed.
  • UNWIND updatedObjects AS updatedObject: If multiple objects were updated, unwind the list inside the predefined variable and go over each one.
  • WITH CASE...: The CASE expression checks what type of object was updated, a node (vertex) or a relationship (edge).
  • SET object.updated_at = timestamp();: Add an updated_at property to the object indicating when the action happened.

Create a trigger for Python query module

If you want a trigger to be activated by executing code from a Python query module, you can call the query module from the trigger. In the example below, the trigger will call query_module.new_edge(edge) each time a new relationship (edge) is created:

CREATE TRIGGER newEdge
ON CREATE BEFORE COMMIT EXECUTE
UNWIND createdEdges AS edge
CALL query_module.new_edge(edge) YIELD *;

Make sure your function accepts the proper Memgraph type, mgp.Edge in this case.

def new_edge(
    context: mgp.ProcCtx,
    edge: mgp.Edge
)

Memgraph Python API is defined by mgp.py script, and in it, you can find all supported types such as mgp.Edge, mgp.Vertex etc. If you want to explore the API further, feel free to check the reference guide on Python API.

Create a trigger for dynamic algorithms

Dynamic algorithms are often designed for dataset updates. With a trigger, you can ensure that any dataset is up to date and consistent. In the sample code below, a trigger is set to use MAGE pagerank_online algorithm.

CREATE TRIGGER pagerank_trigger 
BEFORE COMMIT 
EXECUTE CALL pagerank_online.update(createdVertices, createdEdges, deletedVertices, deletedEdges) 
YIELD node, rank
SET node.rank = rank;

In this case, all created or deleted objects are passed from the database transaction to the trigger. After each transaction that has created or deleted objects, the trigger will automatically execute the PageRank algorithm and update the rank property. This will ensure data consistency and lead to performance benefits.

Dropping a trigger

A trigger can be deleted by running the following query:

DROP TRIGGER trigger_name;

Trigger owner

The user who executes the CREATE TRIGGER query is the owner of the trigger. Authentication and authorization are not supported in Memgraph Community, thus the owner is always Null, and the privileges are not checked.

In Memgraph Enterprise, the privileges of the owner are used when executing Cypher statements, in other words, the execution of the statements will fail if the owner doesn't have the required privileges.

Trigger info

The following query will return information about all triggers:

SHOW TRIGGERS;

Information:

trigger namestatementevent typephaseowner
Trigger nameStatement the trigger executes.Event type that triggers the statement execution.When is the trigger executed.The trigger owner or Null.