I am trying to see if node :Customer exists based on a relationship in my optional match. I then want to create some relationships to my customer if they have an order. I am not sure what is the correct syntax for this.
MERGE (o:Order {account: 'j593jfsh', id: '35353'})
OPTIONAL MATCH (c:Customer)-[:HAS_ORDER]->(o)
MERGE (c)-[:HAS_SESSION]->(s)
MERGE (c)-[:HAS_ORDER]->(o)
WHERE c IS NOT NULL"
One way you could do this is to use pattern comprehension in place of the OPTIONAL MATCH. This would collect all customers having orders into a list, and then you could use FOREACH to MERGE the relationships. If there are no customers, the list will be empty, and FOREACH will have nothing to process.
...
MERGE (o:Order {account: 'j593jfsh', id: '35353'})
WITH o, s, [(c:Customer)-[:HAS_ORDER]->(o) | c] as customers
FOREACH (c in customers |
MERGE (c)-[:HAS_SESSION]->(s)
MERGE (c)-[:HAS_ORDER]->(o)
)
...
Related
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.
So this is a very basic question. I am trying to make a cypher query that creates a node and connects it to multiple nodes.
As an example, let's say I have a database with towns and cars. I want to create a query that:
creates people, and
connects them with the town they live in and any cars they may own.
So here goes:
Here's one way I tried this query (I have WHERE clauses that specify which town and which cars, but to simplify):
MATCH (t: Town)
OPTIONAL MATCH (c: Car)
MERGE a = ((c) <-[:OWNS_CAR]- (p:Person {name: "John"}) -[:LIVES_IN]-> (t))
RETURN a
But this returns multiple people named John - one for each car he owns!
In two queries:
MATCH (t:Town)
MERGE a = ((p:Person {name: "John"}) -[:LIVES_IN]-> (t))
MATCH (p:Person {name: "John"})
OPTIONAL MATCH (c:Car)
MERGE a = ((p) -[:OWNS_CAR]-> (c))
This gives me the result I want, but I was wondering if I could do this in 1 query. I don't like the idea that I have to find John again! Any suggestions?
It took me a bit to wrap my head around why MERGE sometimes creates duplicate nodes when I didn't intend that. This article helped me.
The basic insight is that it would be best to merge the Person node first before you match the towns and cars. That way you won't get a new Person node for each relationship pattern.
If Person nodes are uniquely identified by their name properties, a unique constraint would prevent you from creating duplicates even if you run a mistaken query.
If a person can have multiple cars and residences in multiple towns, you also want to avoid a cartesian product of cars and towns in your result set before you do the merge. Try using the table output in Neo4j Browser to see how many rows are getting returned before you do the MERGE to create relationships.
Here's how I would approach your query.
MERGE (p:Person {name:"John"})
WITH p
OPTIONAL MATCH (c:Car)
WHERE c.licensePlate in ["xyz123", "999aaa"]
WITH p, COLLECT(c) as cars
OPTIONAL MATCH (t:Town)
WHERE t.name in ["Lexington", "Concord"]
WITH p, cars, COLLECT(t) as towns
FOREACH(car in cars | MERGE (p)-[:OWNS]->(car))
FOREACH(town in towns | MERGE (p)-[:LIVES_IN]->(town))
RETURN p, towns, cars
I have nodes representing algorithms with the author property. I want to create nodes for people who are in the author of the algorithms and create WORKED_ON relationships between these people and the algorithms. So I tried:
FOREACH (p:author IN al:Algorithm | CREATE (p:PERSON).
(p)-[:WORKED_ON]->(al:Algorithm))
But it returns:
Invalid input ':': expected "IN" (line 2, column 11 (offset: 156)).
"FOREACH (p:author IN al:Algorithm | CREATE (p:PERSON)"
assuming that the author property contains a list of names , separated by a comma, you could do something like this
MATCH (a:Algorithm)
FOREACH( authorName IN SPLIT(a.author,',') |
MERGE (p:Person {name:authorName})
MERGE (p)-[:WORKED_ON]->(a)
)
NOTE : for the MERGE to work fast, you should set
CREATE CONSTRAINT ON (p:Person) ASSERT p.name IS UNIQUE
FOREACH works on a collection, so you would have to collect the Algorithm nodes first. Something like,
MATCH (n:Algorithm) with collect(n) as algos
FOREACH(a in algos | CREATE (p:Person {name: a.author})-[:WORKED_ON]->(a))
However, there may be a simpler way create those Person nodes,
MATCH (a:Algorithm)
CREATE (a)<-[:WORKED_ON]-(:Person {name: a.author})
What I'm trying to do is to write a query - I already made it a webservice(working on local machine, so I get the name and people as parameters) - which connects people who share the same hobbies and set the hobbies as the relationship property as an array.
My first attempt was;
MERGE (aa:Person{name:$name})
WITH aa, $people as people
FOREACH (person IN people |
MERGE (bb:Person{name:person.name})
MERGE (bb)-[r:SHARESSAMEHOBBY]->(aa)
ON MATCH SET r.hobbies = r.hobbies + person.hobby
ON CREATE SET r.hobbies = [person.hobby])
However this caused duplicated property elements like ["swimming","swimming"]
I'm trying to set only unique properties. Then I tried the following query;
MERGE (aa:Person{name:$name})
WITH aa, $people as people FOREACH (person IN people | MERGE (bb:Person{name:person.name}) MERGE (bb)-[r:SHARESSAMEHOBBY]->(aa)
WITH r, COALESCE(r.hobbies, []) + person.hobby AS hobbies
UNWIND hobbies as unwindedHobbies
WITH r, collect(distinct, unwindedHobbies) AS unique
set r.as = unique)
However now it gives me syntax error;
errorMessage = "[Neo.ClientError.Statement.SyntaxError] Invalid use of WITH inside FOREACH
Any help is appreciated.
This should work:
MERGE (aa:Person {name: $name})
WITH aa
UNWIND $people AS person
MERGE (bb:Person {name: person.name})
MERGE (bb)-[r:SHARESSAMEHOBBY]-(aa)
WITH r, person, CASE
WHEN NOT EXISTS(r.hobbies) THEN {new: true}
WHEN NOT (person.hobby IN r.hobbies) THEN {add: true}
END AS todo
FOREACH(ignored IN todo.new | SET r.hobbies = [person.hobby])
FOREACH(ignored IN todo.add | SET r.hobbies = r.hobbies + person.hobby);
You actually had 2 issues, and the above query addresses both:
If a SHARESSAMEHOBBY relationship already existed in the opposite direction (from aa to bb), the following MERGE clause would have caused the unnecessary creation of a second SHARESSAMEHOBBY relationship (from bb to aa):
MERGE (bb)-[r:SHARESSAMEHOBBY]->(aa)
To avoid this, you should have used a non-directional relationship pattern (which is is permitted by MERGE, but not CREATE) to match a relationship in either direction, like this:
MERGE (bb)-[r:SHARESSAMEHOBBY]-(aa)
You needed to determine whether it is necessary to initialize a new hobbies list or to add the person.hobby value to an existing r.hobbies list that did not already have that value. The above query uses a CASE clause to assign to todo either NULL, or a map with a key indicating what additional work to do. It then uses a FOREACH clause to execute each thing to do, as appropriate.
I am trying to implement https://neo4j.com/blog/moving-relationships-neo4j/ pointer functionality for using it as a team order machine.See http://imgur.com/a/MViF0 for a model. I am using this cypher query.
MERGE (list:LIST)
WITH list
MATCH (u) WHERE ID(u) IN [421, 419, 420]
MERGE (team:TEAM{name: u.name})
MERGE (team)-[:PARTOF]->(list)
WITH collect(team)as elems,list
FOREACH (n IN RANGE(0, LENGTH(elems)-2) |
FOREACH (prec IN [elems[n]] |
FOREACH (next IN [elems[n+1]] |
MERGE (prec)-[:NEXT]->(next))))
with list
MATCH (elem:TEAM) WHERE NOT (elem)<-[:NEXT]-()
MERGE (list)-[:POINTER]->(elem)
Now this works quite nicely, but I have only one problem. This line:
MATCH (u) WHERE ID(u) IN [421, 419, 420]
returns my original teams ordered by id, but I would like to define my order by the pattern in the [421,419,420] pattern, like a function that
return * order by my array input.
Keep in mind that it should work for any amount of teams,this is just an example. And that my original team node isn't labeled a team but something else, so we make a duplicate every time. Any input appreciated, thanks.
Try to use the statement "unwind":
MERGE (list:LIST)
WITH list
UNWIND [421, 419, 420] as uid
MATCH (u) WHERE id(u) = uid
MERGE (team:TEAM{name: u.name})
...
[Update] Of course, it is possible to know the order manually for each node:
MERGE (list:LIST)
WITH list, [3871013, 3871011, 3871012] as ids
MATCH (u) WHERE ID(u) IN ids
WITH list, u,
FILTER(x in RANGE(0,size(ids)-1) WHERE ids[x] = id(u)) as orderIndex
ORDER BY orderIndex[0] // Sort by node position in the array of identifiers
MERGE (team:TEAM{name: u.name})
...