Martin Bamford
Martin Bamford

Reputation: 404

Create Compositional vs Aggregational relationship in Cypher/Neo4J

Just learning about Graph Databases and NEO4J. I assume that the same distinction between compositional and aggregational relationships applies in Graph databases as in other databases.

Creating an aggregational relationship in NEO4J/Cypher, assuming we already have a Country node called 'Italy' in the database, the below query will create a Currency node called 'Euro' if one doesn't already exist and then create a relationship from Italy to Euro if that relationship doesn't already exist...

MATCH (co:Country {name:'Italy'}) 
MERGE (cu:Currency {name:'Euro'}) 
MERGE (co)-[cc:COUNTRYCURRENCY]->(cu) 
RETURN cc

If there is not already a relationship Italy->Euro but there is already a Currency node 'Euro' (for example because we had already created the Euro for it to be used by another country, eg 'France'), then the above query would not create a second duplicate node called 'Euro'. it would reuse the existing 'Euro' node and create a new relationship to it from 'Italy'. This is correct behaviour for an aggregational relationship.

So say I want to merge a compositional relationship instead. IE If a parent already has a child with specified name/properties then I don't want to create a duplicate so I need to use MERGE. But if the specified parent doesn't have this child yet and there is already a child node for a different parent with the same name/properties as this child node then I want to create a new one in the context of this parent rather than reuse the existing one. So I need to make the whole operation conditional on whether a relationship already exists between the parent and a child having given properties. Is this syntactically possible in a one-liner?

Upvotes: 1

Views: 103

Answers (2)

Jasper Blues
Jasper Blues

Reputation: 28756

In Neo4j any node is allowed to exist independently, there is no enforcement of a constraint that says the child can only exist while the parent does.

However you can:

  • Create a whole path as an atomic operation.
  • Create a child, only if the parent exists.
  • Delete a parent and any/all of its child relationships.

Meanwhile a single relationship/edge can of course only exist between two nodes/vertices.

Question:

If I understood your question correctly:

  • The parent definitely exists.
  • The child maybe exists, for that parent.
  • Another child that looks the same, but does not belong to this parent, isn't the parent's child.

Solution:

MERGE (p:Person {name: 'Jasper'})
MERGE (p)-[r:HAS_CHILD]-(c:Child {name: 'Kris'})

What happened:

  • Regardless of whether there was an existing child named 'Kris', with or without a parent, a new child will be created.

Explanation:

Merge matches a whole pattern, so if that exact pattern does not exist, it will be treated as a create.

Upvotes: 1

InverseFalcon
InverseFalcon

Reputation: 30397

We have a knowledge base article on Understanding how MERGE works that covers several cases, including the one for this question.

The section in the article starting with "MERGE using combinations of bound and unbound variables for different use cases" covers what you're after. You want to MATCH or MERGE on the parent node, and then MERGE the relationship to the child node:

MERGE (p:Person {name: 'Jasper'})
MERGE (p)-[r:HAS_CHILD]->(c:Person {name: 'Kris'})
...

MERGE is like a MATCH, and if the MATCH fails, then a CREATE. When we already have bound variables (p is bound to a node because of the MERGE on the first line) then that existing bound node will be used, the node for p won't be recreated.

Upvotes: 1

Related Questions