foreach query in cypher to merge similar nodes - neo4j

I'm trying to figure out how to run through my database, collect nodes that are similar, and then merge them into a single node and re-direct the previous relationships to the newly created node.
I basically created a bunch of nodes in which some or all of the properties had info, like this:
MERGE (company:Company{Name: 'Ford'})
MERGE (person:Person{Name: 'me'})
MERGE (car:Car{Make:'Ford', Model:'Aerostar', Color:'Blue', Transmission:'Auto'})
but I accidentally duplicated nodes that should have been merged, and instead created new nodes:
MERGE (car:Car{Make:'Ford', Model:'Aerostar', Color:'', Transmission:''})
MERGE (car:Car{Make:'Ford', Model:'Aerostar', Color:'Blue', Transmission:''})
MERGE (car:Car{Make:'Ford', Model:'Aerostar', Color:'Blue', Transmission:'Auto'})
MERGE (person)-[:drives]->(car)-[:parent_company]->(company)
so, what I want do do is take the three (car) nodes I accidentally created, merge all of their properties, delete the extra relationships created by extra nodes and correct the path, so (me) would only have a single [:drives] relationship connected to a single (car) connected by a single [:parent_company] relationship.
here is what I tried, but can't quite figure out:
MATCH p=(car:Car{Make:'Ford', Model:'Aerostar'})<-[:drives]-(person:Person{Name:'me'})
FOREACH (car in nodes (p) | SET Car.Color: 'Blue', Car.Transmission:'Auto')
/////This is where I'm stuck
EDIT: Another Attempt(I get "r already declared error):
START n = node(3) //node id for complete 'aerostar' node
WITH n
MATCH (company)<-[:parent_company]-(car:Car{Make:'Ford', Model:'Aerostar')<-[r:drives]-(person)
WITH n, company, r, car, person
MERGE (person)-[r]->(n)-[:parent_company]->(company)
DELETE car

[EDITED]
Does this work for you?
MATCH (car:Car{Make:'Ford', Model:'Aerostar'})<-[d:drives]-(person:Person{Name:'me'})
DELETE d, car
WITH person
CREATE (car:Car{Make:'Ford', Model:'Aerostar', Color:'Blue', Transmission:'Auto'})<-[d:drives]-(person);

I finally figured out how to do this:
MATCH (car:Car{Make:'Ford', Model:'Aerostar'})<-[d:drives]-(person:Person{Name:'me'})
WITH car,d
SKIP 1
DELETE car,d

Related

How to return nodes that have only one given relationship

I have nodes that represent documents, and nodes that represent entities. Entities can be referenced in document, if so, they are linked together with a relationship like that :
(doc)<-[:IS_REFERENCED_IN]-(entity)
The same entity can be referenced in several documents, and a document can reference several entities.
I'd like to delete, for a given document, every entity that are referenced in this given document only.
I thought of two different ways to do this.
The first one uses java to make a foreach and would basically be something like that :
List<Entity> entities = MATCH (d:Document {id:0})<-[:IS_REFERENCED_IN]-(e:Entity) return e
for (Entity entity : entities){
MATCH (e:Entity)-[r:IS_REFERENCED_IN]->(d:Document) WITH *, count(r) as nb_document_linked WHERE nb_document_linked = 1 DELETE e
}
This method would work but i'd like not to use a foreach or java code to make it. I'd like to do it in one cypher query.
The second one uses only one cypher query but doesn't work. It's something like that :
MATCH (d:Document {id:0})<-[:IS_REFERENCED_IN]-(e:Entity)-[r:IS_REFERENCED_IN]->(d:Document) WITH *, count(r) as nb_document_linked WHERE nb_document_linked = 1 DELETE e
The problem here is that nb_document_linked is not unique for every entity, it is a unique variable for all the entities, which mean it'll count every relationship of every entity, which i don't want.
So how could I make a kind of a foreach in my cypher query to make it work?
Sorry for my english, I hope the question is clear, if you need any information please ask me.
You can do something like:
MATCH (d:Document{key:1})<-[:IS_REFERENCED_IN]-(e:Entity)
WITH e
MATCH (d:Document)<-[:IS_REFERENCED_IN]-(e)
WITH COUNT (d) AS countD, e
WHERE countD=1
DETACH DELETE e
Which you can see working on this sample data:
MERGE (a:Document {key: 1})
MERGE (b:Document {key: 2})
MERGE (c:Document {key: 3})
MERGE (d:Entity{key: 4})
MERGE (e:Entity{key: 5})
MERGE (f:Entity{key: 6})
MERGE (g:Entity{key: 7})
MERGE (h:Entity{key: 8})
MERGE (i:Entity{key: 9})
MERGE (j:Entity{key: 10})
MERGE (k:Entity{key: 11})
MERGE (l:Entity{key: 12})
MERGE (m:Entity{key: 13})
MERGE (d)-[:IS_REFERENCED_IN]-(a)
MERGE (e)-[:IS_REFERENCED_IN]-(a)
MERGE (f)-[:IS_REFERENCED_IN]-(a)
MERGE (g)-[:IS_REFERENCED_IN]-(a)
MERGE (d)-[:IS_REFERENCED_IN]-(b)
MERGE (e)-[:IS_REFERENCED_IN]-(b)
MERGE (f)-[:IS_REFERENCED_IN]-(c)
MERGE (g)-[:IS_REFERENCED_IN]-(c)
MERGE (j)-[:IS_REFERENCED_IN]-(a)
MERGE (h)-[:IS_REFERENCED_IN]-(a)
MERGE (i)-[:IS_REFERENCED_IN]-(a)
MERGE (g)-[:IS_REFERENCED_IN]-(c)
MERGE (k)-[:IS_REFERENCED_IN]-(c)
MERGE (l)-[:IS_REFERENCED_IN]-(c)
MERGE (m)-[:IS_REFERENCED_IN]-(c)
On which it removes 3 Entities.
The first MATCH finds the entities that are attached to your input doc, and the second MATCH finds the number of documents that each of these entities is connected to.

Neo4j create graph that some nodes have more than one relationships

Over my Neo4j I want to create this graph:
SO I tried to create some nodes and relationships with:
MERGE (D:POINT {NAME:'d'})<-[:LINKS]-(A:POINT {NAME:'a'})-[:LINKS]->(B:POINT {NAME:'b'})-[:LINKS]->(C:POINT {NAME:'c'})
But I cannot find out how I will create the relationships between D and B points also I cannot find out how I will link the A and C as well.
Do you have any Idea how to do that?
In order to avoid unintentionally creating duplicate nodes and/or relationships, you must invoke MERGE on individual nodes and relationships.
To quote the dev manual:
When using MERGE on full patterns, the behavior is that either the
whole pattern matches, or the whole pattern is created. MERGE will not
partially use existing patterns — it’s all or nothing. If partial
matches are needed, this can be accomplished by splitting a pattern up
into multiple MERGE clauses.
For example, to properly create your graph without any duplicate nodes or relationships:
MERGE (A:POINT {NAME:'a'})
MERGE (B:POINT {NAME:'b'})
MERGE (C:POINT {NAME:'c'})
MERGE (D:POINT {NAME:'d'})
MERGE (A)-[:LINKS]->(B)
MERGE (A)-[:LINKS]->(C)
MERGE (A)-[:LINKS]->(D)
MERGE (B)-[:LINKS]->(C)
MERGE (D)-[:LINKS]->(B)
CREATE seems to be the natural way to me for creating nodes and relationships.
CREATE (D:POINT {NAME:'d'})<-[:LINKS]-(A:POINT {NAME:'a'})
, (A)-[:LINKS]->(B:POINT {NAME:'b'})<-[:LINKS]-(D)
, (B)-[:LINKS]->(C:POINT {NAME:'c'})<-[:LINKS]-(A)
You can do it by doing a MATCH before a MERGE eg. FOR the relationship between A and D do:
MATCH (A:POINT {NAME:'a'}),(B:POINT {NAME:'d'}) MERGE (A)-[:LINKS]->(B)

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