sum 2 graphs in neo4j with multiple MERGE - neo4j

in brief: how can we MERGE multiple nodes and relations just like the way we do with MATCH and CREATE: we can do multiple CREATE or MATCH for nodes or relations, separated with comma, but this action is not allowed with MERGE
in detail: suppose I have two graphs:
G1: (a)-[r1]->(b)<-[r2]-(c)
G2: (a)-[r1]->(b)<-[r3]-(d)
I have G1 inserted in neo4j, and G2 ready to push to db. The normal way to do it is to merge each node pair and then merge the relation; in this example for r1 relation there would be no change in db, since G1 already has the relation, however for the second one, my CQL first create node d then add relation r3
Is there a way to push G2 to db in one step? something like:
MERGE (a), (b), (c), (a)-[r1]->(b)<-[r3]-(d)
to create such result:
(a)-[r1]->(b)<-[r2]-(c)
^
|
[r3]
|
(d)

Not with a single MERGE statement. You would need to follow the pattern of doing a MERGE for each node, then a MERGE for each relationship.
That said, Neo4j does use transactions, so while this is broken into multiple clauses in your Cypher query, the transaction is applied atomically when committed.

Related

Create Relationship between Account Nodes based on Listings Nodes sharing same Phone Number

I have 2 Account Nodes, and several listing nodes, as seen below. The Match statement results in 2 Accounts being show with a relationship to each Listing thats associated to that account.
What im wanting to do is create a relationship between the two Accounts based on at least 1 of there listings each sharing the same phone number.
If possible im wanting to see the relationship between the two account nodes drawn and a relationship between the two listings so long as the listings are from different Account.
MERGE (:Account {account_id:11})
MERGE (:Listing {account_id:11, listing_id:1001, phone:99468320, author:'Paul'})
MERGE (:Account {account_id:12})
MERGE (:Listing {account_id:12, listing_id:1002, phone:97412521, author:'Sam'})
MERGE (:Listing {account_id:12, listing_id:1003, phone:97412521, author:'Sam'})
MERGE (:Listing {account_id:12, listing_id:1004, phone:99468320, author:'Sam'})
MERGE (:Listing {account_id:12, listing_id:1004, phone:0, author:'Same'})
MATCH (a:Account),(l:Listing)
WHERE a.account_id = l.account_id
CREATE (a)-[:LISTING]->(l)
RETURN a,l;
For the latter i did try the following but it went a bit crazy as it linked every listing to each other that had the same number appose to only doing so if the account_id was different.
match (p1:Listing)
with p1
match (p2:Listing)
where p2.phone = p1.phone and p1 <> p2
merge(p1)-[r:SHARED_PHONE]-(p2)
RETURN p1, p2
First of all, you should carefully consider if you really need the SHARED_PHONE relationships, as you will have to update the relationships every time a phone number is added, removed, or changed. That could complicate a lot of your queries and make your DB unnecessarily slower. Also, you could end up with a lot of SHARED_PHONE relationships (that you may not really need). Instead of creating the relationships, you could consider incorporating into the relevant queries the discovery of the nodes with the same phone numbers.
However, if you decide that you really need that relationship, here is one way to do what you want:
[UPDATED]
MATCH (n: Listing)
WITH n.phone AS phone, COLLECT(n) AS ns
FOREACH(i IN RANGE(0, SIZE(ns)-2) |
FOREACH(x IN [ns[i]] |
FOREACH(y IN [z IN ns[i+1..] WHERE x.account_id <> z.account_id] |
MERGE (x)-[:SHARED_PHONE]-(y)
)))
The WITH clause collects all the (unique) Listing nodes that share the same phone, and the nested FOREACH clauses execute the minimum number of MERGEs needed to ensure that all the appropriate nodes are connected by a SHARED_PHONE relationship (in either direction). The innermost FOREACH also ensures that the nodes to be connected do not have the same account_id.

Carrying variables from one query to another to create chained nodes with cypher

This may be a stupid way to do this. I want to create a chain of nodes, possibly thousands of them in the following form:
(n0)-[r0]->(n1)-[r1]->(n2)...
I have programatically generated cypher which looks something like this:
MERGE (n0:Person)-[r0:RelType]->(n1:Person)
WITH n1 MERGE (n1:Person)-[r1:RelType]->(n2:Person)
WITH n2 MERGE (n2:Person)-[r2:RelType]->(n3:Person)
WITH n3 MERGE (n3:Person)-[r3:RelType]->(n4:Person)
WITH n4 MERGE (n4:Person)-[r4:RelType]->(n5:Person)
...
I then copy pasted above queries in neo4j web console and ran, but it gave following error:
Can't create node `n1` with labels or properties here. The variable is already declared in this context
I understand (or I dont?) we cannot use MERGE inside WITH . Also I know we can bulk import nodes, relationships from CSV using Neo4jImport. But I was just curious if we can generate a bunch of cyphers to copy paste them in neo4j web console and create the desired graph.
If the only thing you want is to create a long chain of nodes, you can just unwind a range :
CREATE INDEX ON :Person(id)
UNWIND range(1,100) AS i
MERGE (p:Person {id: i-1})
MERGE (p2:Person {id: i})
MERGE (p)-[:RelType]->(p2)
#Luanne is on the right track, but I think this is what you want:
CREATE (n1:Person)
WITH n1 AS n CREATE (n)-[:RelType]->(n1:Person)
WITH n1 AS n CREATE (n)-[:RelType]->(n1:Person)
WITH n1 AS n CREATE (n)-[:RelType]->(n1:Person)
WITH n1 AS n CREATE (n)-[:RelType]->(n1:Person)
(and so on...)
Except for the first line, all other lines are identical. I used CREATE because I don't think you want to use MERGE at all, since I believe you are trying to create totally new data. You can use MERGE instead if I am wrong about that.
I think you should remove the label when merging the relationship on the node n1 supplied via the WITH
MERGE (n0:Person)-[r0:RelType]->(n1:Person)
WITH n1 MERGE (n1)-[r1:RelType]->(n2:Person)
WITH n2 MERGE (n2)-[r2:RelType]->(n3:Person)
WITH n3 MERGE (n3)-[r3:RelType]->(n4:Person)
WITH n4 MERGE (n4)-[r4:RelType]->(n5:Person)
(untested)

Give a specific pattern from a node during clause

I have the nodes: (a:charlie), (b:economy), and (c:bicycle) . I want to create this pattern:
create (a:charlie)-[x:wants_make]->(b:economy)->[y:by_using]->(c:bicycle)
But it gives me cartesian product. I already thought to skip the creation of the node (b) giving to relation [x:want_make]a property. But node (b) has many other relations in the same context(economic context). What I want to get the pattern above.
Any suggestion?
If your query looks like this:
MATCH (a:charlie), (b:economy), (c:bicycle)
MERGE (a)-[:wants_make]->(b)-[:by_using]->(c);
then it is saying both of these things:
Create a wants_make relationship between every charlie node and every economy node.
Create a by_using relationship between every economy node and every bicycle node.
So, if the number of charlie, economy, and bicycle nodes are C, E, and B -- this results in (C * E * B) merges, which is a Cartesian product of a Cartesian product.
Also, your data model seems to be wrong. For example, it seems much more reasonable to have a Person label instead of a charlie label.
A more reasonable query might look something like this:
MERGE (a:Person {name: 'Charlie Brown'})
MERGE (c:Bicycle {id: 123})
MERGE (a)-[:wants_make]->(b:Economy)
MERGE (b)-[:by_using]->(c);
This query avoids Cartesian products by being more specific about the first and last nodes in the path, and it also avoids creating nodes and relationships that already exist.
And, going even further, you might want to combine wants_make, Economy, and by_using into a single economizes_by_using relationship:
MERGE (a:Person {name: 'Charlie Brown'})
MERGE (c:Bicycle {id: 123})
MERGE (a)-[:economizes_by_using]->(c);
You might need to break up your query a bit:
MATCH (a:charlie), (b:economy), (c:bicycle)
MERGE (a)-[:wants_make]->(b), (b)->[:by_using]->(c)

Neo4j: Multiple Relationships Merge Issue

I've a neo4j schema in which I've 3 nodes. e.g. p,b,c
I want to write a Merge query such that
MERGE (p)-[:has_b]->(b),
MERGE (p)-[:has_c]->(c1),
MERGE (p)-[:has_c]->(c2)
where c1 and c2 are instance of c node having different property values.
i.e. Merge on all three relationships.
If any of 3 merge queries creates a new node, all relationships should use newly created p node.
I can achieve this if I had only two relationships using
(c)<-[:has_c]-MERGE (p)-[:has_b]->(b)
Any suggestions how to do it for 3 relationships as in my case?
FYI, I'm using py2neo which isn't helping at all.
Nodes don't have instances. A node is a node and it has a label.
You can MERGE your nodes first to make sure they exist and that all relationships use the same p:
MERGE (p:LabelA {k: "v"})
MERGE (b:LabelB {k: "v"})
MERGE (c1:LabelC {k: "v"})
MERGE (c2:LabelC {k: "v"})
MERGE (p)-[:has_b]->(b)
MERGE (p)-[:has_c]->(c1)
MERGE (p)-[:has_c]->(c2)
This will create the nodes and relationships only once.

Cant use Merge inside foreach for existed nodes

I am expecting he following query to create nodes (only if exits) and relations by a given source node (1) and a list(2) this way:
MERGE (p1:C9{userId: '1'}) WITH p1, [{userId:"2"}] AS users
FOREACH (user IN users | MERGE
((p1)-[r1:follow]->(:C9 {userId: user.userId})))
Thats the outcome:
Now if I am executing this query again by switching the node id's this way:
MERGE (p1:C9{userId: '2'}) WITH p1, [{userId:"1"}] AS users
FOREACH (user IN users | MERGE
((p1)-[r1:follow]->(:C9 {userId: user.userId})))
We got this:
neo4j duplicated for me the node with id=1. I want it to merge in case of existed nodes.
I expected to see only two nodes connected to each other by merging existed nodes.
any idea what I should fix?
Thanks,
ray.
I normally avoid FOREACH when I can use an UNWIND, so I would start with something like this:
MERGE (p1:C9 {userId: '1'})
WITH p1, [{userId:"2"}] AS users
UNWIND users AS user
MERGE (p1)-[r1:follow]->(:C9 {userId: user.userId})
Sometimes you also want to separate your node creation from your relationship creation. If you do both at the same time, I think that Neo4j can think that you want a unique combination of node (with properties) and relationship.
MERGE (p1:C9 {userId: '1'})
WITH p1, [{userId:"2"}] AS users
UNWIND users AS user
MERGE (p2:C9 {userId: user.userId})
MERGE (p1)-[r1:follow]->(p2)
You can use MERGE within FOREACH.
But you have to understand the semantics of MERGE. It tries to MATCH a full pattern and if it does not find it it will fully CREATE that pattern.
You in your case you try to find a pattern within the context of p1 and not globally and if not found it will create it within the context of p1.
So if you change your query to:
MERGE (p1:C9{userId: '2'})
WITH p1, [{userId:"1"}] AS users
FOREACH (user IN users |
MERGE (p2:C9 {userId: user.userId})
MERGE (p1)-[r1:follow]->(p2)
)
I.e. create p2 first and then MERGE the relationship, it will work.

Resources