0

I use Neo4j 3.3.5 Community Edition with APOC apoc-3.3.0.2-all.jar

I have triggers that allow me to add/remove all properties from a particular relationships to/from manual index:

CALL apoc.trigger.add('HAS_VALUE_ON_CREATED_RELATIONSHIPS_TRIGGER',
'UNWIND {createdRelationships} AS r 
MATCH (d:Decision)-[r:HAS_VALUE_ON]->(ch:Characteristic) 
CALL apoc.index.addRelationship(r, keys(r)) RETURN count(*)', {phase:'after'})

CALL apoc.trigger.add('HAS_VALUE_ON_DELETED_RELATIONSHIPS_TRIGGER',
\"UNWIND {deletedRelationships} AS r 
MATCH (d:Decision)-[r:HAS_VALUE_ON]->(Characteristic) 
CALL apoc.index.removeRelationshipByName('HAS_VALUE_ON', r) RETURN count(*)\", {phase:'after'})

My business logic can also introduce new properties or remove existing ones from the existing relationship so I think in order to keep my index up to date I should also use another two statements, like:

assignedRelationshipProperties
removedRelationshipProperties

am I right? If so, could you please show how both of them can be used in order to add new triggers and update/remove properties in index from MATCH (d:Decision)-[r:HAS_VALUE_ON]->(ch:Characteristic) relationship ?

UPDATE #1

I have verified solution provided in the answer section but unfortunately it doesn't work. Please see details below:

I have created the following trigger:

CALL apoc.trigger.add('TEST_TRIGGER', "UNWIND keys({assignedRelationshipProperties}) AS key 
UNWIND {assignedRelationshipProperties}[key] AS map 
WITH map 
WHERE type(map.relationship) = 'LIVES_IN' 
CALL apoc.index.addRelationship(map.relationship, keys(map.relationship)) 
RETURN count(*)", {phase:'before'})

verified that trigger exists by CALL apoc.trigger.list()

create the following nodes and relationship:

CREATE (p:Person) return p
CREATE (c:City) return c

MATCH (p:Person), (c:City) CREATE (p)-[r:LIVES_IN]->(c) RETURN type(r)

tried to access it by index query:

MATCH (p:Person)-[r:LIVES_IN]->(c:City) 
CALL apoc.index.in(c, 'LIVES_IN', 'time:10') YIELD node AS person 
RETURN person

the quthe ery returns empty result which is fine for now.

assigned relationship new property time with value = 10:

MATCH (p:Person)-[r:LIVES_IN]->(c:City) SET r.time = 10 RETURN r

tried to access it by index query:

MATCH (p:Person)-[r:LIVES_IN]->(c:City) 
CALL apoc.index.in(c, 'LIVES_IN', 'time:10') YIELD node AS person 
RETURN person

the query successfully returns the expected Person node

Now, I reassigned the time property another value = 11

MATCH (p:Person)-[r:LIVES_IN]->(c:City) SET r.time = 11 RETURN r

tried to access it by index query one more time:

MATCH (p:Person)-[r:LIVES_IN]->(c:City)
CALL apoc.index.in(c, 'LIVES_IN', 'time:10') YIELD node AS person 
RETURN person

MATCH (p:Person)-[r:LIVES_IN]->(c:City) 
CALL apoc.index.in(c, 'LIVES_IN', 'time:11') YIELD node AS person 
RETURN person

the query returns empty result for both of them..

Why was the index not updated after the property change?

UPDATE #2

I have created the following trigger:

CALL apoc.trigger.add('HAS_VALUE_ON_ASSIGNED_RELATIONSHIP_PROPERTIES_TRIGGER',
"UNWIND apoc.trigger.propertiesByKey({assignedRelationshipProperties}, 'time') AS prop WITH prop.relationship as r 
CALL apoc.index.addRelationship(r, keys(r)) 
RETURN count(*)", {phase:'after'})

verified that trigger exists by CALL apoc.trigger.list()

create the following nodes and relationship:

CREATE (p:Person) return p
CREATE (c:City) return c

MATCH (p:Person), (c:City) CREATE (p)-[r:LIVES_IN]->(c) RETURN type(r)

tried to access it by index query:

MATCH (p:Person)-[r:LIVES_IN]->(c:City)
CALL apoc.index.in(c, 'LIVES_IN', 'time:10') YIELD node AS person 
RETURN person

the query returns empty result which is fine for now.

assigned relationship new property time with value = 10:

MATCH (p:Person)-[r:LIVES_IN]->(c:City) SET r.time = 10 RETURN r

tried to access it by index query:

MATCH (p:Person)-[r:LIVES_IN]->(c:City) 
CALL apoc.index.in(c, 'LIVES_IN', 'time:10') YIELD node AS person 
RETURN person

the query successfully returns the expected Person node

Now, I reassigned the time property another value = 11

MATCH (p:Person)-[r:LIVES_IN]->(c:City) SET r.time = 11 RETURN r

tried to access it by index query one more time:

MATCH (p:Person)-[r:LIVES_IN]->(c:City) 
CALL apoc.index.in(c, 'LIVES_IN', 'time:10') YIELD node AS person 
RETURN person

MATCH (p:Person)-[r:LIVES_IN]->(c:City) 
CALL apoc.index.in(c, 'LIVES_IN', 'time:11') YIELD node AS person 
RETURN person

the query returns empty result for both of them..

So, this approach ran into the same issue as I previously described above. And the other drawback is that I need to specify the exact key name - in this case time. But I'm not interested in some certain key but instead I'm interested in the add/update/delete of all keys of the HAS_VALUE_ON relationship.

alexanoid
  • 24,051
  • 54
  • 210
  • 410

2 Answers2

3

The assignedRelationshipProperties is quite tricky. The structure of this parameter is Map<String, List<Map<String, Object>>>.

Where, the first String is the key of the property, and the list elements are maps with the following keys :

  • key : the key of the property
  • old: the old value if any
  • new : the new value assigned to the property
  • relationship: the relationship in question

To be more visual, this is what the parameter look like in a debug format :

enter image description here

For your particular use case, updating the lucene index on relationship property updates for a particular relationship type, you can use the following query :

CALL apoc.trigger.add('test-rel-trigger', 
'UNWIND keys({assignedRelationshipProperties}) AS key
UNWIND {assignedRelationshipProperties}[key] AS map
WITH map WHERE type(map.relationship) = "HAS_VALUE_ON"
CALL apoc.index.addRelationship(map.relationship, keys(map.relationship)) RETURN count(*)'
, {phase:'before'})

As for removal, because you index the full map of properties, I believe you can just replace assignedRelationshipProperties with removedRelationshipProperties

Christophe Willemsen
  • 19,399
  • 2
  • 29
  • 36
  • Thanks for your answer. I verified this solution but unfortunately it doesn't work – alexanoid May 12 '18 at 05:52
  • @MichaelHunger sorry, I forgot to mention that I have provided the detailed explanation of this(what doesn't work with this approach) in my updated question - the section UPDATE #1 – alexanoid May 14 '18 at 06:16
  • Looks like this issue was fixed in Neo4j 3.4.0/apoc-3.4.0.1-all.jar – alexanoid May 19 '18 at 15:02
1

There are dedicated helper functions for using these, which are explained in the docs to access nodes/rels or properties of those data structures:

see: https://neo4j-contrib.github.io/neo4j-apoc-procedures/#_triggers

Table 4. 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}]

Michael Hunger
  • 41,339
  • 3
  • 57
  • 80
  • Thanks for your answer. Please see my updated question(UPDATE #2). With this approach I also ran into the same issue as I previously described above. And the other drawback is that I need to specify the exact key name - in this case `time`. But I'm not interested in some certain key but instead I'm interested in the add/update/delete of all keys of the `HAS_VALUE_ON` relationship. What am I doing wrong and how to fix it ? – alexanoid May 13 '18 at 07:05
  • btw. I think it would be nice to also allow the function to filter for labels/rel-types perhaps you want to create an GH issue for apoc on that – Michael Hunger May 14 '18 at 09:56
  • Thanks, I have created GH issue already https://github.com/neo4j-contrib/neo4j-apoc-procedures/issues/805 – alexanoid May 14 '18 at 10:03
  • One more GH issue for Neo4j: https://github.com/neo4j/neo4j/issues/11789 – alexanoid May 16 '18 at 11:26