I'm trying to implement a soft delete in Neo4j. The graph described in Cypher from Alice's viewpoint is as such:
(clyde:User)<-[:FOLLOWS]-(alice:User)-[:LIKES]->(bob:User)
Instead of actually deleting a node and its relationships, I'm
- changing its label so it can no longer be looked up directly, i.e. dropping its
User
label and adding a_User
label (notice the underscore) - replacing its relationships so it can't be reached anymore by my normal queries, e.g. deleting its
:FOLLOWS
relationships and replacing it with:_FOLLOWS
relationships.
So this is basically the equivalent of moving a row to an archiving table in a relational database. I figured this is a pretty efficient approach because you're effectively never visiting the parts of the graph that have been soft-deleted. Also, you don't have to modify any of your existing queries.
The result of soft-deleting Alice should be this:
(clyde:User)<-[:_FOLLOWS]-(alice:_User)-[:_LIKES]->(bob:User)
My first attempt at the query was this:
match (user:User {Id: 1})
optional match (user)-[follows:FOLLOWS]->(subject)
remove user:User set user:_User
delete follows
create (user)-[:_FOLLOWS]->(subject);
The problem is that when this user is not following anyone, the query tries to create a relationship between user
and null
because the second match is optional, so it gives me this error: Other node is null.
My second attempt was this:
match (user:User {Id: 1})
remove user:User set user:_User
optional match (user)-[follows:FOLLOWS]->(subject)
foreach (f in filter(f in collect({r: follows, n: subject}) where f.r is not null) | delete f.r create (user)-[:_FOLLOWS]->(f.n));
So I'm putting the relationship and the subject into a map, collecting these maps in a collection, throwing every "empty" map away and looping over the collection. But this query gives me this error:
SyntaxException: Invalid input '.': expected an identifier character, node labels, a property map, whitespace or ')' (line 1, column 238)
Does anyone know how I can fix this?
Thanks, Jan
Could you change the label first and then match for relationships? Then you should be able to use 'non-optional' match, and not have to deal with the cases where there are no follows relationships, something like
Or you could carry the user, follows and subject and filter on where subject is not null. Something like
Edit:
If the problem is that you want to do this for more than one kind of relationship, then you could try
You can keep extending it with other relationship types, just be sure to limit
user
when you carry, since multiple matches(user)-[r]->(other)
means there are multiple results for user, or you'll run the next query part multiple times.I don't think there is a generic way to do it in cypher since you can't dynamically build the relationship type (i.e.
CREATE (a)-[newRel:"_"+type(oldRel)]->(b)
doesn't work)Is something like that what you are looking for or am I misunderstanding your question?