Triggers and parameters

Hi i'm very new to neo4j and would like to use it in a project and have come stuck with a trigger.
I'm using neo4j 5.1.0 and would like you to create a trigger for when a player node is created and use event properties of the node so i've come up with :
CALL apoc.trigger.add('node-create-trigger', '
WITH $event AS event
MATCH (n:Node)
CALL apoc.log.info($event.eventId)
', {phase: 'after', params: {event: 'NODE_CREATED_OR_UPDATED'}})

but this doesn’t work and gives the error in the logs. : org.neo4j.exceptions.ParameterNotFoundException: Expected parameter(s): event. I've also tried with

', {phase: 'after', params: {
player: $event.player,
event: {
"identity": $event.player.identity,
"labels": [
"Player"
],
"properties": {
"playerId": $event.player.playerId,
"username": $event.player.username
}
}
}})

I was wondering if anyone could point me to a working example that's
newbie friendly

I don't see a reference to a parameter option when adding a trigger. It seems to make sense to not support one, as how would you inject a new value each time it ran. In your case if you could set a parameter, the $event will always be equal to 'NODE_CREATED_OR_UPDATED' since there is no mechanism to change it after adding the trigger. As such, you can hard code the value.

I am not clear on what your trigger is intending to do. First, you are matching on all Node entities and not doing anything with the match result. Why are you matching on all Nodes?

I have not used triggers, but it looks like you want to use the provided parameters to understand what changes and react to that. If you want to react to nodes being created, you can get the list of nodes created by referencing $createdNodes. There is $assignedNodeProperties that describe what properties changed and $removedNodeProperties what properties where removed.

Can you describe what outcome you are looking for and we can try to help.

Welcome aboard...neo4j is a lot of fun.

Hi Gary thank you so much for the quick response.
I think I may have confused the issue by asking chatGPT for
code examples and not knowing much thinking that looks like
what I want to achieve. so let me start by giving the requirements
I have a student node that has a [ENROLLED-ON] relationship with a course node
anytime a student is created/updated and it has [ENROLLED-ON] on course "A" a trigger is fired
and creates a new [TEACHES] relationship with student node and an existing teacher node.
there maybe 10000's of students so I didn't want to use "UNWIND" (as I've heard it's not the
best way to go) and thought that I could get the student node that set off the trigger
though the event property and use it to create the relationship ?
p.s i'm using a remote database and not embedded

I think that will be a nightmare to keep consistent. I feel you have the information you need to relate a student to each of their teachers. Assuming your data model looks similar to the following diagram, you can get a student's teachers from the query below.

match(n:Student{name:'student_name'})
match(n)-[:ENROLLED_IN]->(c:Course)<-[r:TEACHES]-(t:Teacher)
return c.title, t.name

Hi Gary unfortunately that won’t work in this instance as the [TEACHES] relationship with student node and teacher is created when certain criteria is met and the criteria for different teachers are created on the fly (the triggers hold the criteria ). needless to say I need the information of the student who has triggered the trigger is that possible if so how ? i'm sorry that i can’t go deeper into the requirements as it's R & D for a works project but appreciate your help.
P.S online i can see functions such as "CALL apoc.trigger.getTriggeredNodes()" and "CALL apoc.trigger.watch()" but these don’t seem to exist in my apoc library :(

Not sure what you need, but this will add trigger that gets triggered when a Student node is created. The action is to set a property (this is just an example). You can replace the line 'set node.test = true' with your action you want to take on each student node created.

CALL apoc.trigger.install('neo4j', 'student_node_created',
  "unwind $createdNodes as node 
  with node
  where 'Student' in labels(node)
  set node.test = true",
  {phase: 'afterAsync'});

Hi Gary thanks so much it does work but i was wondering if the unwind expects a list of nodes as opposed to just the one node that i need to operate on ?

i believe it's something like this that's needed:
CALL apoc.trigger.add('student_node_created_or_updated',
"WITH head($createdNodes) AS node
WHERE 'Student' IN labels(node) AND (EXISTS(node.test) OR EXISTS(node.updated))
SET node.test = true, node.updated = timestamp()",
{phase: 'afterAsync'});

but that gives me : exists(variable.property)is no longer supported. Please usevariable.property IS NOT NULL` instead.

so i looking for examples or if you know the new syntax that would be great. Again apologies for taking so much of your time

Remember that that this trigger will be executed each time something changes, not just you adding a Student node. Also, you can create multiple Student nodes in one transaction. As such, you have to write the query to handle $createdNodes having more than on Student node. Try the following:

CALL apoc.trigger.add('student_node_created_or_updated',
"
WITH [i in $createdNodes where i:Student and (i.test is not null or i.updates is not null)] AS nodes
forEach(i in nodes | SET i.test = true, i.updated = timestamp())
",
{phase: 'afterAsync'});

Also, the parser complained that 'apoc.trigger.add' is deprecated and that you should use 'apoc.trigger'install'. The only differences seems to be that a new parameter was added to specify the database that the trigger should be installed in. Assuming you are installing this in your 'neo4j' database, the updated code would be:

CALL apoc.trigger.install('neo4j', 'student_node_created_or_updated',
"
WITH [i in $createdNodes where i:Student and (i.test is not null or i.updates is not null)] AS nodes
forEach(i in nodes | SET i.test = true, i.updated = timestamp())
",
{phase: 'afterAsync'});

This has to be run against the system database, so execute ':use system' in the browser first.

Thanks for that. i now have triggers in the db that are set off only if i create nodes in the browser they remain silent when creating the nodes with the java org.neo4j.driver.Session.

P.S neo4j won’t let me create triggers in the system db

No .. all's well just had to do the whole thing in one transaction :+1:t5:

1 Like

I don’t understand why the behavior is different between the browser and the driver.

in the browser i do MERGE (node1:Node1 {name: 'test', id: 4544})-[:BELONGS_TO]->(:Node2 {n_id: 0}). == all in 1 step( transaction ). Trigger fired

in the api i was doing step 1. MERGE (node1:Node1 {name: 'test', id: 4544}). commit
step 2. Find (:Node2 {n_id: 0})
step 3 merge -[:BELONGS_TO]-> commit. Trigger not fired.

in the api i do MERGE (node1:Node1 {name: 'test', id: 4544})-[:BELONGS_TO]->(:Node2 {n_id: 0}). == all in 1 step( transaction ) again . Trigger fired

:+1:

The difference is in one case you are merging/finding existing nodes then merging the relationship given the nodes, versus merging the entire pattern. In the first case the nodes are not created (so no trigger), while in the second the nose are created in addition to the relationship (so trigger fired). The best practice in creating or merging relationships is to match/merge the nodes first and use their references in the create/merge the relationship. This ensures you don’t get unwanted skincare nodes.