Boris
Boris

Reputation: 787

How to duplicate a node and set values on the original in Cypher

I'm creating a very basic versioning system for my nodes in my database. What I want to do is when there is an edit:

  1. Replicate node
  2. Update the node
  3. Link (oldreplica)-[:IsPreviousVersionOf]->(replica)
  4. Delete (oldreplica)-[:IsPreviousVersionOf]->(node)
  5. Link (replica)-[:IsPreviousVersionOf]->(node)
  6. Increment the version number on the node

This way I don't have to touch any of the relationships the node has. Every query I write ends up with the node and replica having the same content.

Is there a way to do this, or do I need to write two queries?

Upvotes: 3

Views: 1346

Answers (2)

Boris
Boris

Reputation: 787

Okay, I've figured it out!

// Find the editable Node
MATCH (mainNode)-[:HasInternal]->(:EditableUuid{uuid:'2828b80b-e3d8-478e-ad31-b791b4e8d318'})
// Create a copy and copy the contents
CREATE (copy:Content)
SET copy = mainNode

// Create a new subquery so that we don't set the contents of both copy and mainNode
WITH mainNode, copy
// Increment the version number on the mainNode
SET mainNode.version = mainNode.version + 1
// Connect replica to the mainNode
MERGE (copy)-[:IsPreviousVersionOf]->(mainNode)

// Create a new subquery where we change the links of any old versions
with mainNode, copy, copy.version - 1 as oldversion
// Find older versions and connect them to the copy
MATCH (oldreplica:Content{version:oldversion})-[oldRel:IsPreviousVersionOf]->(mainNode)
MERGE (oldreplica)-[:IsPreviousVersionOf]->(copy)
// Delete the old relationship of the nodes
DELETE oldRel

This way the 'newest' version is always the one that already has the links and the old ones are just copies of that one in time.

Upvotes: 1

FrobberOfBits
FrobberOfBits

Reputation: 18002

You can do this all in one query; I think the two pieces you're missing are how to copy the node in the first place, and how to "join" subqueries using the WITH keyword. Here's an example. You should triple-check this query, because your terminology and relationship shifting is tricky, so please verify carefully this is what you want.

The WITH blocks here bind certain matched nodes to variables which you can use in subsequent queries. In this way we can carry over the results of one query into another. The other bit is that nodes are just maps of their properties, so you can "copy" a node by setting one map to another.

MATCH (node:Content{version:1})
WITH node as oldNode
CREATE (copy:Cotent)
SET copy=oldNode
WITH oldNode, copy
MATCH (oldreplica:Content{version:0})
MERGE (oldNode)-[:IsPreviousVersionOf]->(copy)
MERGE (oldreplica)-[oldRel:IsPreviousVersionOf]->(copy)
DELETE oldRel
WITH copy, oldNode
MERGE (copy)-[:IsPreviousVersionOf]->(oldNode)
MERGE (oldreplica)-[:IsPreviousVersionOf]->(copy);

Upvotes: 1

Related Questions