Reputation: 442
I'm looking for a way to create (or if exist, find a matching pattern) for MERGE
with 3 and more relationships coming out from single node.
I know how to find/create 2 nodes related to single node, for example:
MERGE (f1:Friend)
MERGE (f2:Friend)
MERGE (w2)<-[:HAS_FRIEND]-(p:Person)-[:HAS_FRIEND]->(w1)
ON CREATE {do something}
ON MATCH SET {do something else}
I understand that you can continue creating/finding more nodes if you add them to right-most or left-most nodes, e.g.
MERGE (friend1)<-[:HAS_FRIEND]-(p:Person)-[:HAS_FRIEND]->(friend2)-[:HAS_PET]->(pet:Pet)-[:HAS_TOY]->(toy:Toy)
But how to MERGE
(find if WHOLE pattern exists or create it if not found) something like 3 :Friend
nodes coming out of single :Person
node
Upvotes: 3
Views: 497
Reputation: 66999
You cannot use ON CREATE
and ON MATCH
in the way you envision, as that would require you to somehow craft a single MERGE clause that could safely and completely handle the rest of your complex use case. That is not possible.
The sample query below shows how to handle your use case (as far as I understand it).
Assumptions:
myName
parameter.names
parameter.Person
label.HAS_FRIEND
relationship should exist between a pair of Person
nodes. (Notice that the (p)-[:HAS_FRIEND]-(f)
pattern does not specify a relationship direction, so it will match either direction.)The following query ensures that all the people whose names are in the $names
list are connected (in either direction) to the person whose name is given by $myName
. It will also conditionally perform the equivalent of "ON CREATE" or "ON MATCH" processing (except on steroids, since you are not limited to SET
clauses), depending on whether the query had to create new nodes.
MATCH (me:Person {name: $myName})
WITH me, [(me)-[:HAS_FRIEND]-(f) WHERE f.name IN $names | f] AS foundFriends
CALL {
WITH me, foundFriends
UNWIND [x IN $names WHERE NOT x IN [ff IN foundFriends | ff.name] | x] AS neededName
CREATE (me)-[:HAS_FRIEND]->(newFriend:Person {name: neededName})
RETURN COLLECT(newFriend) AS newFriends
}
CALL apoc.do.when(
SIZE(newFriends) > 0,
'RETURN "CREATED" AS result',
'RETURN "MATCHED" AS result',
{me: me, foundFriends: foundFriends, newFriends: newFriends}) YIELD value
RETURN value.result
This query uses the CALL subquery syntax introduced in neo4j 4.0.
And it also uses the APOC procedure apoc.do.when to conditionally process Cypher code (to do something like "ON CREATE" or "ON MATCH" processing). In the sample above, the Cypher code simply returns "CREATED" or "MATCHED". But your actual code could be much more complicated (and also make use of the parameters passed via the {me: me, foundFriends: foundFriends, newFriends: newFriends}
argument).
Upvotes: 0
Reputation: 11216
Not completely sure I follow, but is something like this what you are looking for? Given a list of friends and me merge each Person/Friend
mode and then merge the relationship between them as `[:FRIEND].
with ['dave','mary','derek'] as friends
merge (me:Person {name: 'me'})
with me, friends
unwind friends as friend
merge (f:Friend {name: friend})
merge (me)-[:FRIEND]->(f)
return *
If you had a friendship going the other way though and you did not want to create a new relationship in the other direction a second time through you could guard against that with something like this. Say we introduce me's good friend rich into the equation but rich is actually friends in the other direction.
merge (me:Person {name: 'me'})
merge (rich:Friend {name: 'rich'})
merge (me)<-[:FRIEND]-(rich)
return *
Then if we add rich to the list we are trying to merge but add a step after each :Friend
is merged to guard against creating one if it already exists in the other direction.
with ['dave','mary','derek','rich'] as friends
merge (me:Person {name: 'me'})
with me, friends
unwind friends as friend
merge (f:Friend {name: friend})
with me, f
match (f)
where not (me)<-[:FRIEND]-(f)
merge (me)-[:FRIEND]->(f)
return *
The match (f) where not (me)<-[:FRIEND]-(f)
guards against merging
the relationship if it already exists albeit in the other direction.
It prevents this from happening
Upvotes: 4