With the following graph:
(Boxer)-[:STARTS]->(Round)-[:CONTINUES]->(Round)-[:CONTINUES]->(Round)-[:CONTINUES]->(Round)
I want to remove a (Round) in the linked list.
I got a successful result by doing this:
MATCH (round:Round {uuid: $round.uuid})
MATCH (prevRound)-[:CONTINUES]->(round)-[:CONTINUES]->(nextRound)
DETACH DELETE round
MERGE (prevRound)-[:CONTINUES]->(round)
But this will work for any Round except the first one, because it has a STARTS a relationship. So I tried this:
MATCH (round:Round {uuid: $round.uuid})
MATCH (prevRound)-[prevRel:CONTINUES|STARTS]->(round)-[nextRel:CONTINUES]->(nextRound)
DETACH DELETE round
MERGE (prevRound)-[prevRel]->(round)
But I get this error:
Neo4jError: Variable `prevRel` already declared
MERGE (prevRound)-[prevRel]->(nextRound)"
You cannot use a identifier to dynamically create a relationship in cypher.
In your statement the planner thinks that you are trying to use prevRel as an identifier in your MERGE but it is already used in the MATCH above.
Fortunately there is a solution for this using APOC. The apoc.merge.relationship procudeure can be used to create a new relationship type that is the same as the one you removed when you removed the round.
MATCH (round:Round {uuid: $round.uuid})
MATCH (prevRound)-[prevRel:CONTINUES|STARTS]->(round)-[nextRel:CONTINUES]->(nextRound)
DETACH DELETE round
WITH prevRound, prevRel, nextRound
CALL apoc.merge.relationship(prevRound, type(prevRel), {}, {}, nextRound) YIELD rel
RETURN prevRound, rel, nextRound
Related
I wrote a script to to batch create a bunch of relationship in neo4j. Here is the cypher:
:param batch => [{startId: 'abc123', endId: 'abc321'}, {startId: 'abc456', endId: 'abc654']
UNWIND $batch as row
MATCH (from {id: row.startId}
MATCH (to {id: row.endId}
CREATE (from)-[rel:HAS]->(to)
RETURN rel
The problem that there might be some startId/endId entries that don't match any nodes and are silently ignore. Is there a way to return the list of rows that don't match any nodes and create the relationship for the nodes that do match?
I tried OPTIONAL MATCH to fail-fast as soon an id doesn't find a startId/endId however, the query execution was really slow.
First of all, you should always try to specify a label for the node that is used to kick off a MATCH (unless the MATCH pattern uses any already-bound nodes). Otherwise, every single node in the DB must be scanned. In addition, you should consider using indexes to speed up your MATCHs (but, again, you'd need to specify the labels).
Here is a query that uses the APOC procedure apoc.do.when to create a new relationship when appropriate. It returns each row and the corresponding new relationship (or NULL if either node is not found):
UNWIND $batch as row
OPTIONAL MATCH (from:Foo {id: row.startId})
OPTIONAL MATCH (to:Foo {id: row.endId})
CALL apoc.do.when(
from IS NOT NULL AND to IS NOT NULL,
'CREATE (from)-[rel:HAS]->(to) RETURN rel',
'RETURN NULL AS rel',
{from: from, to: to}) YIELD value
RETURN row, value.rel AS rel
I would like to get a node, delete all outgoing relationships of a certain type and then add back relationships.
The problem I have is that once I grab the node, it still maintains it's previous relationships even after delete so instead of having 1 it keeps doubling whatever it has. 1->2->4->8 etc
Sample graph:
CREATE (a:Basic {name:'a'})
CREATE (b:Basic {name:'b'})
CREATE (c:Basic {name:'c'})
CREATE (a)-[:TO]->(b)
CREATE (a)-[:SO]->(c)
The query to delete the previous relationships and then add in the new relationships. (this is just a brief sample where in reality it wouldn't add back the same relationships, but more then likely point it to a different node).
MATCH (a:Basic {name:'a'})
WITH a
OPTIONAL MATCH (a)-[r:TO|SO]->()
DELETE r
WITH a
MATCH (b:Basic {name:'b'})
CREATE (a)-[:TO]->(b)
WITH a
MATCH (c:Basic {name:'c'})
CREATE (a)-[:SO]->(c)
If I change the CREATE to MERGE then it solves the problem, but it feels odd to have to merge when I know that I just deleted all the relationships. Is there a way to update "a" midway through the query so it reflects the changes? I would like to keep it in one query
The behavior you observed is due the subtle fact that the OPTIONAL MATCH clause generated 2 rows of data, which caused all subsequent operations to be done twice.
To force there to be only a single row of data after the DELETE clause, you can use WITH DISTINCT a (instead of WITH a) right after the DELETE clause, like this:
MATCH (a:Basic {name:'a'})
OPTIONAL MATCH (a)-[r:TO|SO]->()
DELETE r
WITH DISTINCT a
MATCH (b:Basic {name:'b'})
CREATE (a)-[:TO]->(b)
WITH a
MATCH (c:Basic {name:'c'})
CREATE (a)-[:SO]->(c)
I am using cypher. I am trying to delete all out going relationships before creating new ones on the same query.
i have weird situation if the relations/nodes already existed it's working as expected. if They never been created before I get:
(no changes, no rows)
This is my query:
match (user{userId:'a'})-[r:nearby_wifi]->() delete r
MERGE (p1:BT{userId:'a'}) WITH p1, [{bssid:"0a:18:d6:c1:3d:fd",level:"-51",timestamp:"1973-08-27 02:26:35.423",venueName:""},{bssid:"04:18:d6:c2:3e:2a",level:"-55",timestamp:"1973-08-27 02:26:35.425",venueName:""},{bssid:"0e:18:d6:c1:3d:fd",level:"-53",timestamp:"1973-08-25 11:06:07.392",venueName:""}] AS wifis
UNWIND wifis AS wifi
MERGE (p2:WIFI{bssid: wifi.bssid})
MERGE (p1)-[r1:nearby_wifi]->(p2)
SET r1.dist=wifi.dist
SET p1.lastTimeActive=1460378030215
SET p2.level=wifi.level
SET p2.timestamp=wifi.timestamp
SET p2.venueName=wifi.venueName
Any idea why when combining delete and the merge executions I got no changes(when graph empty)?
Thanks.
Replace first match with optional match
For example if you have no client nodes in your database, but have some person nodes query
Match (p:Client) with p Match (r:Person) return *
will get nothing, but query
Optional Match (p:Client) with p Match (r:Person) return *
will give you Persons. I think neo4j optimizer stops executing query after it gets no results and with optional match it gets null, and continues executing.
I want to add a "created by" relationship on nodes in my database. Any node should be able of having this relationship but there can never be more than one.
Right now my query looks something like this:
MATCH (u:User {email: 'my#mail.com'})
MERGE (n:Node {name: 'Node name'})
ON CREATE SET n.name='Node name', n.attribute='value'
CREATE UNIQUE (n)-[:CREATED_BY {date: '2015-02-23'}]->(u)
RETURN n
As I have understood Cypher there is no way to achieve what I want, the current query will only make sure there are no unique relationships based on TWO nodes, not ONE. So, this will create more CREATED_BY relationships when run for another User and I want to limit the outgoing CREATED_BY relationship to just one for all nodes.
Is there a way to achieve this without running multiple queries involving program logic?
Thanks.
Update
I tried to simplyfy the query by removing implementation details, if it helps here's the updated query based on cybersams response.
MERGE (c:Company {name: 'Test Company'})
ON CREATE SET c.uuid='db764628-5695-40ee-92a7-6b750854ebfa', c.created_at='2015-02-23 23:08:15', c.updated_at='2015-02-23 23:08:15'
WITH c
OPTIONAL MATCH (c)
WHERE NOT (c)-[:CREATED_BY]-()
CREATE (c)-[:CREATED_BY {date: '2015-02-23 23:08:15'}]->(u:User {token: '32ba9d2a2367131cecc53c310cfcdd62413bf18e8048c496ea69257822c0ee53'})
RETURN c
Still not working as expected.
Update #2
I ended up splitting this into two queries.
The problem I found was that there was two possible outcomes as I noticed.
The CREATED_BY relationship was created and (n) was returned using OPTIONAL MATCH, this relationship would always be created if it didn't already exist between (n) and (u), so when changing the email attribute it would re-create the relationship.
The Node (n) was not found (because of not using OPTIONAL MATCH and the WHERE NOT (c)-[:CREATED_BY]-() clause), resulting in no relationship created (yay!) but without getting the (n) back the MERGE query looses all it's meaning I think.
My Solution was the following two queries:
MERGE (n:Node {name: 'Name'})
ON CREATE SET
SET n.attribute='value'
WITH n
OPTIONAL MATCH (n)-[r:CREATED_BY]-()
RETURN c, r
Then I had program logic check the value of r, if there was no relationship I would run the second query.
MATCH (n:Node {name: 'Name'})
MATCH (u:User {email: 'my#email.com'})
CREATE UNIQUE (n)-[:CREATED_BY {date: '2015-02-23'}]->(u)
RETURN n
Unfortunately I couldn't find any real solution to combining this in one single query with Cypher. Sam, thanks! I've selected your answer even though it didn't quite solve my problem, but it was very close.
This should work for you:
MERGE (n:Node {name: 'Node name'})
ON CREATE SET n.attribute='value'
WITH n
OPTIONAL MATCH (n)
WHERE NOT (n)-[:CREATED_BY]->()
CREATE UNIQUE (n)-[:CREATED_BY {date: '2015-02-23'}]->(:User {email: 'my#mail.com'})
RETURN n;
I've removed the starting MATCH clause (because I presume you want to create a CREATED_BY relationship even when that User does not yet exist in the DB), and simplified the ON CREATE to remove the redundant setting of the name property.
I have also added an OPTIONAL MATCH that will only match an n node that does not already have an outgoing CREATED_BY relationship, followed by a CREATE UNIQUE clause that fully specifies the User node.
In SQL:
Delete From Person Where ID = 1;
In Cypher, what's the script to delete a node by ID?
(Edited: ID = Neo4j's internal Node ID)
Assuming you're referring to Neo4j's internal node id:
MATCH (p:Person) where ID(p)=1
OPTIONAL MATCH (p)-[r]-() //drops p's relations
DELETE r,p
If you're referring to your own property 'id' on the node:
MATCH (p:Person {id:1})
OPTIONAL MATCH (p)-[r]-() //drops p's relations
DELETE r,p
The cleanest sweep for a node with id "x" is
MATCH (n) where id(n) = x
DETACH DELETE n
https://neo4j.com/docs/cypher-manual/current/clauses/delete/#delete-delete-a-node-with-all-its-relationships
https://neo4j.com/docs/cypher-manual/current/functions/scalar/#functions-id
Old question and answered, but to delete node when it has relationships, use DETACH
MATCH (n) where ID(n)=<your_id>
DETACH DELETE n
or otherwise you get this:
Neo.ClientError.Schema.ConstraintValidationFailed: Cannot delete node<21>, because it still has relationships. To delete this node, you must first delete its relationships.
It's like SQL's CASCADE
When the node is a orphan.
Start n=node(1)
Delete n;
Following the link provided by #saad-khan, here's an example for getting the nodes and relationships ids.
The code below shows the ids, so you can make sure that you're deleting everything related to the given ID.
MATCH (node)-[relation:HAS]->(value)
where ID(node)=1234
RETURN ID(instance), ID(value), ID(r)
Ps.: ":HAS" is an example of an relationship.