0
votes

I'm currently writing a mongoose plugin to maintain neo4j nodes automatically using the javascript neo4j driver and currently working on mapping updates (the saving part works atm) to a cypher query.

I'm currently stuck at the use case where I want to update multiple relationships between the document (a :Class, saved as node in neo4j) and the updated references (students of type :Person, saved as different nodes in neo4j) while deleting the old relationships tied to the old references (students in this case). This is a result of updating an array of subdocument references in neo4j.

The main problem, besides the cross (cartesian) product that is created in the query, is that the following query creates twice as much relationships as it deletes:

MATCH (doc:Class {m_id: '5bf5b9df11c344021de89395'}), 
      (I:Person {m_id: '5bf5b9df11c344021de89393'}), 
      (II:Person {m_id: '5bf5b9df11c344021de89394'}), 
      (:Class {m_id: '5bf5b9df11c344021de89395'})-[r]->() 
WHERE type(r) IN ["HAS_STUDENT"] 
CREATE (doc)-[:HAS_STUDENT]->(I), (doc)-[:HAS_STUDENT]->(II) 
DELETE r;

This removes 2 relationships (which is correct), but creates 4 new relationships (should be 2).

Also using a single CREATE in the form of CREATE (I)<-[:HAS_STUDENT]-(doc)-[:HAS_STUDENT]->(II) is not a valid option as this query is created in a for loop and can have an increasing amount of new relationships to be created. (Also the main reason why I want to optimize the query.)

I've had multiple versions of this query which all resulted in the same output. The problem is probably in the WHERE part, because when I use the query below it creates the right amount of relationships. The only missing thing is that it should only delete the old relationships:

MATCH (doc:Class {m_id: '5bf5b9df11c344021de89395'}), 
      (I:Person {m_id: '5bf5b9df11c344021de89393'}), 
      (II:Person {m_id: '5bf5b9df11c344021de89394'}) 
CREATE (doc)-[:HAS_STUDENT]->(I), (doc)-[:HAS_STUDENT]->(II) 
WITH doc 
MATCH (doc)-[r]->(n) 
WHERE type(r) IN ["HAS_STUDENT"] 
DELETE r;

So my main question, besides resolving the problem, is how to efficiently write this query for the described behavior. I use the neo4j driver, so I have availability of using parameters.

NOTE: I and II will later be used as variables to assign unique relationships values, that's why I haven't used a single MATCH for the :Person nodes.

Also, the IN of the relationshiptype in the WHERE clause is used because of the possibility of different types of relationships to be created (and thus the old ones of that type deleted).

At last, I already have looked into UNWIND and FOREACH but can't seem to fit these into the described usecase.

1
For this use case, it looks like you should probably use a Neo4j javascript Object Graph Mapper (OGM). Also, do you have the APOC plugin? And to clarify, are you simply trying to remap relationships to end at a different node?Tezra
Well that (The OGM) is actually what I'm trying to create sort of. For the APOC, I don't really want people that use the library to be dependent on APOC as goes for an external OGM. Last part, yes kind of, but I want to combine the create clause with the ones that will create relationships with properties. That's why I thought the best option would be do delete all old relationships and create new ones.Sven Westerlaken

1 Answers

2
votes

The problem with your cypher, is that each operation is performed per row

So here

MATCH (doc:Class {m_id: '5bf5b9df11c344021de89395'}), // 1
      (I:Person {m_id: '5bf5b9df11c344021de89393'}), // 1
      (II:Person {m_id: '5bf5b9df11c344021de89394'}), // 1
      (:Class {m_id: '5bf5b9df11c344021de89395'})-[r]->() // 2
      // 1 match * 1 match  * 1 match * 2 match  = 2 rows
WHERE type(r) IN ["HAS_STUDENT"] 
CREATE (doc)-[:HAS_STUDENT]->(I), (doc)-[:HAS_STUDENT]->(II) 
// 2 create * 2 rows = 4 new relations
DELETE r;

So you need to cut out the "duplicate" rows using DISTINCT before you do your create

MATCH (doc:Class {m_id: '5bf5b9df11c344021de89395'}), 
      (:Class {m_id: '5bf5b9df11c344021de89395'})-[r]->() 
WHERE type(r) IN ["HAS_STUDENT"] 
DELETE r;
WITH DISTINCT doc
MATCH (I:Person {m_id: '5bf5b9df11c344021de89393'}), 
      (II:Person {m_id: '5bf5b9df11c344021de89394'})
CREATE (doc)-[:HAS_STUDENT]->(I), (doc)-[:HAS_STUDENT]->(II)