cancel
Showing results for 
Search instead for 
Did you mean: 

Head's Up! These forums are read-only. All users and content have migrated. Please join us at community.neo4j.com.

Deleting nodes related to specific node and then adding nodes again

Hi, I can't tell what is wrong with my query.
I have a node (KnowledgeEntry) that has an outgoing 'CONTAINS' relationship to multiple nodes of type KnowledgeRecord. The query is used to update a KnowledgeEntry and its Records.

Firstly, I want to set the new KnowledgeEntry values which works perfectly fine. Then I want to delete all KnowledgeRecord nodes that are connected to the KnowledgeEntry.

Then I loop over the new Records and create them and add them to the KnowledgeEntry.

If I run the delete query solely it works perfectly fine. But when I re-add the new KnowledgeRecords in the same query the old KnowledgeRecords won't be deleted. I cannot figure out how to accomplish what I'm trying to do. Do I need to run two separate queries?

Here's my query:

MATCH (knowledgeEntry:KnowledgeEntry) WHERE knowledgeEntry.id = $id

SET knowledgeEntry += {
  title: $knowledgeEntry.title
}

/* Here I delete all knowledgeRecords connected to the knowledgeEntry */
WITH knowledgeEntry
OPTIONAL MATCH (knowledgeEntry)-[rel:CONTAINS]->(rec:KnowledgeRecord)
DETACH DELETE rel

/* Here I re-add the new knowledgeRecords */
WITH knowledgeEntry
UNWIND $knowledgeRecords AS record
CREATE (knowledgeRecord:KnowledgeRecord)
SET knowledgeRecord = record
CREATE (knowledgeEntry)-[:CONTAINS]->(knowledgeRecord)

RETURN knowledgeEntry, collect(knowledgeRecord) AS knowledgeRecords;
1 ACCEPTED SOLUTION

The query itself almost looks fine (though per the discussion, if you want to delete the connected :KnowledgeRecord nodes rather than the relationship between the nodes, you'll want to change which variable you're using for the DETACH DELETE), but you will need to reset the cardinality before you create the new records.

That is, for a specific knowledgeEntry, if there were 5 connected recs to delete, you would still have 5 rows for that knowledgeEntry going into the create part of the query, multiplying the number of nodes created and relationships made. WITH knowledgeEntry doesn't reset cardinality or deduplicate rows.

You would either need to reset the cardinality with an aggregation or DISTINCT:

WITH knowledgeEntry, count(knowledgeEntry) as ignore

or

WITH DISTINCT knowledgeEntry

You may want to PROFILE a run of this query to see what it's doing. Also, check the id() of the connected nodes before and after this query. If the id()s of the rec nodes before the execution of the query are no longer present in the graph after, then they are getting deleted properly, and something else is going on.

View solution in original post

6 REPLIES 6

If you want to delete the Records: DETACH DELETE rec
If you want to keep the Records, but delete the rel between Entry and Record: DELETE rel

If I assume you want to delete the Records:

WITH knowledgeEntry
OPTIONAL MATCH (knowledgeEntry)-[rel:CONTAINS]->(rec:KnowledgeRecord)
DETACH DELETE rec

WITH knowledgeEntry
UNWIND $knowledgeRecords AS record
CREATE (knowledgeRecord:KnowledgeRecord)
SET knowledgeRecord = record
CREATE (knowledgeEntry)-[:CONTAINS]->(knowledgeRecord)

If you're trying to delete the relationships, the second WITH may be preventing that. So, temporarily mutate the graph.

WITH knowledgeEntry
OPTIONAL MATCH (knowledgeEntry)-[rel:CONTAINS]->(rec:KnowledgeRecord)
SET rel.deleteme = 1

WITH knowledgeEntry
UNWIND $knowledgeRecords AS record
CREATE (knowledgeRecord:KnowledgeRecord)
SET knowledgeRecord = record
CREATE (knowledgeEntry)-[:CONTAINS]->(knowledgeRecord)

WITH knowledgeEntry
MATCH (knowledgeEntry)-[rel:CONTAINS {deleteme: 1}]->()
DELETE rel

I want to delete the Records and add the new ones ($knowledgeRecords param represents the new ones)

With the first query you provided the result doesn't change unfortunately.
Actually, every time I run this query the amount of knowledgeRecords that are already connected is multiplied by the length of $knowledgeRecords param.

So if the Entry had two records and I want to replace them with three new records the result is a Entry node with six record nodes.
If the Entry has six record nodes and I want to replace them with five new records the result is a Entry node with 30 record nodes.

Could this be a bug???

More likely something odd going on with your id and this MATCH clause.

How are you populating the $id, $knowledgeEntry, and $knowledgeRecords parameters?

I'm using the java bolt driver api

The params object is a Map:
Map<String, Object>
and it looks like this:

Map.of("id", id, "knowledgeEntry", knowledgeEntry, "knowledgeRecords", records),

When debugging those are my values:

  • id is a String
  • knowledgeEntry is a Map<String, Object> with only one entry so far: String title
  • knowledgeRecords is a List<Map<String, Object>> and each of the entries has the following properties:
    String content
    String type
    Integer order
try (Session session = driver.session()) {
    return session.writeTransaction(transaction -> transaction.run(cypher, params).stream()
        .map(function)
        .collect(Collectors.toList()));
 }

KnowledgeEntry and KnowledgeRecords are basically my domain classes converted to a map / list of maps.
Also, i dont know if it is important but in both types of map objects there are some null values.

There's enough going on here that the nulls might have some side-effects, but I doubt it. Nothing is deleted until the transaction is complete, so there might be some conflict with that, and your CREATE (knowledgeRecord:KnowledgeRecord) of a new blank node of the same label. You might want to delete in one command, and create in another.

That said, a little deformation of the graph, and doing the delete at the end, might work. Give it a try.

Avoid duplicate nodes with constraints:

CREATE CONSTRAINT ON (e::KnowledgeEntry) ASSERT e.id IS UNIQUE

Duplicates out of the way - deform, create, delete:
Avoid OPTIONAL MATCH here, you don't need to delete something that isn't there.

MATCH (e:KnowledgeEntry {id: $id})
SET e.title = $knowledgeEntry.title

WITH e
MATCH (e)-[rel:CONTAINS]->(rec:KnowledgeRecord)
SET rec.deleteMe = 1

WITH e
UNWIND $knowledgeRecords AS record
CREATE (r:KnowledgeRecord)<-[:CONTAINS]-(e)
SET r = record

MATCH (deleteRecord:KnowledgeRecord {deleteMe: 1})
DETACH DELETE deleteRecord

The query itself almost looks fine (though per the discussion, if you want to delete the connected :KnowledgeRecord nodes rather than the relationship between the nodes, you'll want to change which variable you're using for the DETACH DELETE), but you will need to reset the cardinality before you create the new records.

That is, for a specific knowledgeEntry, if there were 5 connected recs to delete, you would still have 5 rows for that knowledgeEntry going into the create part of the query, multiplying the number of nodes created and relationships made. WITH knowledgeEntry doesn't reset cardinality or deduplicate rows.

You would either need to reset the cardinality with an aggregation or DISTINCT:

WITH knowledgeEntry, count(knowledgeEntry) as ignore

or

WITH DISTINCT knowledgeEntry

You may want to PROFILE a run of this query to see what it's doing. Also, check the id() of the connected nodes before and after this query. If the id()s of the rec nodes before the execution of the query are no longer present in the graph after, then they are getting deleted properly, and something else is going on.