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. For example, in a graph database, when a new property is added to the Employee node, a new Tax, Vacation, and Salary node should be created, along with the relationships between them. Triggers can also be used to log historical data, for example, to keep track of employees' previous salaries.

Introduction

Memgraph supports running openCypher clauses after a certain event happens during database transaction execution, i.e. triggers.

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

Creating a trigger

To create a new trigger, a query of the following format should be used:

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

As you can see from the format, you can choose on what object event needs to happen , on () node or --> relationship. After that you can define on what type of event you what to execute the trigger CREATE, UPDATE or DELETE. After the EXECUTE is series of Cypher clauses you want to execute.

An example of a trigger would be:

CREATE TRIGGER exampleTrigger
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 may seem complex, so let's break it down:

  • CREATE TRIGGER exampleTrigger: This statement creates the trigger. Here the part exampleTrigger is the name of the trigger and it must be unique.
  • ON UPDATE AFTER COMMIT EXECUTE: This statement specifies what kind of event should activate the execution of trigger. This one will be triggered for every update operation and the query below will be executed after the update event has been committed.
  • UNWIND updatedObjects AS updatedObject: If multiple objects were updated, unwind the list 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.

Trigger name

Each created trigger must have a globally unique name. This implies that you can't have a pair of triggers with the same name, even if they apply to different events.

Event type

Optionally, users can define on which event a trigger should execute its statements. The event type is defined using the following part:

ON ( () | --> ) CREATE | UPDATE | DELETE

There are three main event types:

  • CREATE
  • UPDATE
  • DELETE

For each event type, users can specify whether to execute the trigger statements only on the events that happened on a vertex, or on an edge. Vertices are denoted with (), and edges with -->.

Few examples would be:

  • ON CREATE - trigger the statements only if an object (vertex and/or edge) was created during the transaction execution.
  • ON () UPDATE - trigger the statements only if a vertex was updated (e.g. property was set on it) during the transaction execution.
  • ON --> DELETE - trigger the statements only if an edge was deleted during the transaction execution.

Each event comes with certain information that can be used in the openCypher statements the trigger executes. The information is contained in the form of predefined variables.

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

Statement execution phase

A trigger can execute its statements at a specified phase, before or after committing 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.

Predefined variables

Statements that a trigger executes can contain certain predefined variables which contain information about the event that triggered it. Values of predefined variables are determined by database transactions, that is, by all the creations, updates or deletes that are part of a single transaction.

Based on the event type, the following predefined variables are available:

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

createdVertices

List of all created vertices.

createdEdges

List of all created edges

createdObjects

List of all created objects where each element is a map. If the element contains a created vertex, it will be in the following format

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

If the element contains a created edge, it will be in the following format

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

deletedVertices

List of all deleted vertices.

deletedEdges

List of all deleted edges

deletedObjects

List of all deleted objects where each element is a map. If the element contains a deleted vertex, it will be in the following format

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

If the element contains a deleted edge, it will be in the following format

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

General notes about the predefined variables for updates

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 under a property on the same object was changed multiple times, only one update will be generated. The same applies for the labels on the vertex.

setVertexProperties

List of all set 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 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 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 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 vertex labels. Each element is in the following format:

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

removedVertexLabels

List of all removed 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 vertices.

updatedEdges

List of updates consisting of set and removed properties on edges.

updatedObjects

List of updates consisting of set and removed properties on edges and vertices, and set and removed labels on vertices.

Elements of the predefined variables for update

Each element has a similar format as the previously defined elements.

If the element contains information about a set 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 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 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 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 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 vertex label, it's in the following format:

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

Owner

The user who executes the create query is going to be the owner of the trigger. Authentication and authorization are not supported in Memgraph Community, thus the owner will always be Null, and the privileges are not checked in Memgraph Community. In Memgraph Enterprise the privileges of the owner are used when executing openCypherStatements, in other words, the execution of the statements will fail if the owner doesn't have the required privileges. More information about how the owner affects the trigger can be found in the reference guide.

Dropping a trigger

A trigger can be removed by running the following query:

DROP TRIGGER trigger_name;

Trigger info

Users can get info about all the triggers by using the following query:

SHOW TRIGGERS;

which returns results in the following format:

trigger namestatementevent typephaseowner
name of the triggerstatement which the trigger executesevent which triggers the statementphase at which the trigger executes its statementowner of the trigger or Null

How to guide

Memgraph supports database triggers that can be executed if a particular type of event occurs. Events are related to changes in data, such as created, updated, and deleted nodes or relationships.

How to create a trigger?

You can create a trigger by executing Cypher clauses. Creating a trigger will ensure that some procedural code is executed on a certain type of event. All created triggers are persisted on the disk, which means they will be active on database reruns and no information is ever lost.

Trigger execution upon node creation

Node creation is the most common event your Memgraph database can react to. For example, you need to update some values on a created node. If you need a trigger after the node (vertex) has been created, you can set up the following trigger:

CREATE TRIGGER createVertex
ON () CREATE AFTER COMMIT EXECUTE
UNWIND createdVertices AS createdVertex
SET createdVertex.created = timestamp()

Here the trigger's name is createdVertex and should be unique. Cypher clause ON CREATE defines on what event will trigger be executed. AFTER COMMIT EXECUTE means the trigger will be executed after changes have been committed to the database. For the sake of ease of use, triggers have a set of predefined variables. One of them is createdVertices, a list of all created nodes (vertices). In this example, the list is unwound by a Cypher clause UNWIND. To find a complete list of predefined variables, supported operations, and configuration details, look at triggers reference guide.

In this trigger, a node is getting a timestamp upon creation via the SET createdVertex.created = timestamp() Cypher clause.

Trigger execution upon node update

Node property updates are common in graphs, in order to react to them, you can create a trigger for that type of event:

CREATE TRIGGER updateVertex
ON () UPDATE AFTER COMMIT EXECUTE
UNWIND updatedVertices AS updatedVertex
SET updatedVertex.updated_at = timestamp()
 

The trigger for node updates is almost the same as node creation. Notice different predefined variable updatedVertices and ON UPDATE Cypher clause.

In this trigger, a node is getting a new updated timestamp via SET updatedVertex.updated_at = timestamp() Cypher clause.

Trigger execution upon node or relationship creation

You can also set up a trigger for multiple events, such as node or relationship creation. It doesn't matter what will be created, a node or relationship, but the event will execute the trigger. A sample query for that kind of trigger:

CREATE TRIGGER exampleTrigger
ON CREATE AFTER COMMIT EXECUTE
UNWIND createdObjects AS createdObject
WITH CASE
        WHEN createdObject.vertex IS NOT null THEN createdObject.vertex
        WHEN createdObject.edge IS NOT null THEN createdObject.edge
    END AS object
SET object.created_at = timestamp();

A predefined variable createdObjects is a list of dictionaries. Each dictionary contains information about the created object, which can be either a node or a relationship. The object's key event_type is set based on the dictionary and information within it, and the value of the key vertex or edge (depending on the type of object) is set to that created object.

In this trigger, the node or relationships property created_at is set to the current timestamp value via SET object.created_at = timestamp(); Cypher clause.

How to 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.

How to 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. For more details on dynamic algorithms, visit MAGE docs. 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.

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