Search code examples
neo4jcypherneo4j-apoc

Neo4j APOC assignedRelationshipProperties, removedRelationshipProperties triggers and apoc.index.in


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.


Solution

  • 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