1

Is there a possibility with cypher to not use certain relations based on property?

We have a new requirement that says that relations should never be hard deleted but should get a flag "deleted = true" so we kind of have an audit trail for relations of our nodes. (Something like X was a friend of Y but a little more complex and for all relation types)

This is doable in a cypher query by just using WHERE r.deleted = false but in more complex queries that traverse 4-5 relation types this might become harder.

Is there an easy way to just say that Neo4J shouldn't use any relation with the property "deleted" set to true?

isaias-b
  • 2,255
  • 2
  • 25
  • 38
ChrisR
  • 14,370
  • 16
  • 70
  • 107

3 Answers3

2

Not exactly what you're asking, but you could also change the type of the relationship from SOMETHING to SOMETHING_DELETED. Then when you specify the traversal you can skip those relationships.

albertoperdomo
  • 1,941
  • 12
  • 14
1

Indeed, keeping a deleted property quickly becomes unmanageable as your graph model evolves: the property creeps into each and every one of your queries. To my knowledge, Neo4j provides nothing to help you with this.

However, I've had success with albertoperdomo's approach. Some of the details are in a question I asked here a couple of months ago.

You want to replace two things of the nodes that you want to soft delete: their labels (so they can't be looked up directly) and their relationships (so they can't be reached from other nodes). In my case, I simply prepend labels and relationship types with an underscore (_).

The benefits are clear: your existing queries are (probably!) safe because the soft-deleted nodes are no longer taken into account (they can't be reached anymore, unless you omit the relationship type obviously, i.e. (a)--(b) or something).

I also like to think this is the most performant way of implementing a soft-delete because you're effectively cutting off pieces of your graph -- instead of matching on a property (even if it's indexed).

There is a catch, however. There is no general way to replace labels and relationships. You can't, for example say something along the lines of:

match (:Person {id: 123})-[r]-()
set type(r) = '_' + type(r);

You'll have to replace each label and relationship separately, for every type of node you have. Things also get tricky if relationships are optional, because it's hard to "carry" your initial node across the whole of your query (hence my initial question).

The way I solved this was by stringing together the different parts of the query with UNION:

MATCH (review:Review {Id: {id}})<-[wrote:WROTE_REVIEW]-(owner)
DELETE wrote CREATE (review)<-[:_WROTE_REVIEW]-(owner)
UNION
MATCH (review:Review {Id: {id}})-[evaluates:EVALUATES]->(product)
DELETE evaluates CREATE (review)-[:_EVALUATES]->(product)
UNION
...
UNION
MATCH (review:Review {Id: {id}})
REMOVE review:Review") SET review:_Review;

Yes, you are MATCHing the same node in each of the subqueries, but I think the performance implications are negligible if your properties are indexed but mainly because you create/read nodes much more often than you delete them (otherwise, your DB would be empty!)

Community
  • 1
  • 1
Jan Van den bosch
  • 3,542
  • 3
  • 26
  • 38
0

query like this

(user)-[:friend {deleted: 'false'}]->(f)-[:works_in {deleted: 'false'} ]-(company)

EMR
  • 123
  • 1
  • 9