What’s a Trigger?If you haven’t worked with triggers before, a trigger is a database method of running some action whenever an event happens. You can use them to make the database react to events, rather than passively accept data, which makes them a good fit for streaming data, which is a set of events coming in.Triggers need two pieces:
- A trigger condition (which event should the trigger fire on?)
- A trigger action (what to do when the trigger fires?)
Triggers are available in Neo4j through Awesome Procedures on Cypher (APOC), and you can find the documentation on them here.
Installing and preparing
So the first thing we need to do is to create new database in Neo4j Desktop. Once installed, we should install the APOC plugin, which should be directly available in Neo4j Desktop's "plugins" section. The trigger functionality that we will be using us actually part of the APOC library, and you can find documentation on them over here.
In order to generate the dataset, I will use the Faker plugin again. See the previous blogpost for more on that, but installing that is actually really easy. First we need to download the latest release from github, unzip that file into plugins directory of your freshly baked server, and then manually do a small piece of restructuring in the file structure:
- put neo4jFaker-0.9.1.jar in plugins directory
- and put ddgres directory in plugins directory.
- delete all other directories
dbms.security.procedures.unrestricted=fkr.*
to neo4j.conf. Easy. The file structure should look like this:
And the neo4j.conf should have a line like this.apoc.trigger.enabled=true
to neo4j.conf as well.
Generating the dataset
call dbms.functions() yield namewith namewhere name starts with "fkr"return *
If it returns with a list of functions, then we are good to go.
Next we just need to run two queries to create the dataset: 5000 persons with 15000 MEETS relationships.
foreach (i in range(1,5000) |create (p:Person { id : i })set p += fkr.person('1940-01-01','2020-05-15')set p.healthstatus = fkr.stringElement("Sick,Healthy")set p.confirmedtime = datetime()-duration("P"+toInteger(round(rand()*100))+"DT"+toInteger(round(rand()*10))+"H")set p.birthDate = datetime(p.birthDate)set p.addresslocation = point({x: toFloat(51.210197+rand()/100), y: toFloat(4.402771+rand()/100)})set p.name = p.fullNameremove p.fullName);
and then
match (p:Person)with collect(p) as personscall fkr.createRelations(persons, "MEETS" , persons, "1-n") yield relationships as meetsRelations1call fkr.createRelations(persons, "MEETS" , persons, "1-n") yield relationships as meetsRelations2call fkr.createRelations(persons, "MEETS" , persons, "1-n") yield relationships as meetsRelations3with meetsRelations1+meetsRelations2+meetsRelations3 as meetsRelationsunwind meetsRelations as meetsRelationset meetsRelation.starttime = datetime()-duration("P"+toInteger(round(rand()*100))+"DT"+toInteger(round(rand()*10))+"H")set meetsRelation.endtime = meetsRelation.starttime + duration("PT"+toInteger(round(rand()*10))+"H"+toInteger(round(rand()*60))+"M")set meetsRelation.meettime = duration.between(meetsRelation.starttime,meetsRelation.endtime)set meetsRelation.meettimeinseconds=meetsRelation.meettime.seconds;
Working with triggers
As mentioned above, you can find the documentation for the trigger procedures and functions over here. Once the configuration flag is set (apoc.trigger.enabled=true) in neo4j.conf, we can add the triggers to the database, and start testing them. so let's add them first.
Adding two triggers
You will find that there are different types of triggers that you can add. I will explore two types in this post:
- a trigger that will fire as soon as a LABEL is added to a node in the database.
- a trigger that will fire as soon as a property is set in the database.
CALL apoc.trigger.add('highriskpersonadded','UNWIND apoc.trigger.nodesByLabel($assignedLabels,"HighRiskPerson") AS n MATCH (n)-[:MEETS]-(p:Person) set p:ElevatedRiskPerson',{phase:'after'})
CALL apoc.trigger.add('healthstatuspropertychangedtosick','UNWIND apoc.trigger.propertiesByKey($assignedNodeProperties,"healthstatus") AS propwith prop.node as nMATCH (n)-[:MEETS]-(p:Person) where n.healthstatus = "Sick"set p:MuchElevatedRiskPerson',{phase:'after'})
Managing the triggers
- Listing the trigger:
call apoc.trigger.list()
- There's another procedure for removing the trigger:
call apoc.trigger.remove('<name of trigger>') - And one for pausing the trigger:
call apoc.trigger.pause('<name of trigger>') - Or resuming the trigger:
call apoc.trigger.resume('<name of trigger>')
Triggering the triggers
match (p:Person {healthstatus:"Healthy"})with plimit 1set p:HighRiskPerson;
And then we immediately see that our Neo4j browser reacts by highlighting the fact that some new labels have been added to the database.
And then we run a quick query to see that indeed, the HighRiskPerson node has been connected to other nodes that have the ElevatedRiskPerson label:
So our first trigger is clearly working. Let's try the other one.
match (p:Person {healthstatus:"Healthy"})with plimit 1set p:VeryHighRiskPersonset p.healthstatus = "Sick";
And next thing we know, we also see that the VeryHighRiskPerson and MuchElevatedRiskPerson labels have been added:
So that all seems to have worked. This really allows for much more automated actions on the database, which could be extremely useful in a sensitive use case like Contact Tracing - but I could equally see how this would be super useful for use cases like Fraud Detection or something similar.
No comments:
Post a Comment