Cypher Linked LIst: how to unshift and replace by index - neo4j

I am trying to create a Linked List structure with Neo/Cypher as per the recomendation here: CYPHER store order of node relationships of the same label when I create
(p:Parent)-[r1:PARENTID_RELTYPE]->(c1:Child)-[r2:PARENTID_RELTYPE]->(c2:Child)-[r3:PARENTID_RELTYPE]->(c3:Child)
But I am having trouble understanding the syntax for the required sequence of events in order to unshift new nodes onto the structure or replace a particular index with a different node.
Where I am confused is the part where I need to add the new node and repair the structure that keeps the old node still inside the linked list sturcture. There should be only one relationship of each type (PARENTID_RELTYPE) that stems from a Parent node; But there can be multiple relationships of different type from each parent. Child nodes can be featured multiple times within a LinkedList and a child node could be featured in a LinkedList of multiple Parents or in a LinkedList of the same parent but a different relationship type.
So one of three things could happen when I try to unshift:
There is no existing child that links to the parent by the PARENTID_RELTYPE
There already exists a child node that links to the parent by PARENTID_RELTYPE
There already exists a child node that links to the parent by PARENTID_RELTYPE and that child node is simply a duplicate of the child node I am attempting to unshift onto the linked list structure (in which case the intended result is to have the same child node in the zero and first indices of the linked list).
The answer url mentioned above helps me greatly in understanding how to read a linked list structure in Neo/Cypher but because of the alternative way for which conditionals are handled in cypher, I am having trouble understanding how to write to the structure (and also delete from he structure).
Below is my initial attempt to do so but I am somewhat baffled by what I need the syntax to do.
MATCH (a:%s {id: {_parentnodeid}}), (b:%s {id: {_id}})
MERGE a-[relold:%s]->b
ON CREATE
SET relold.metadata = {_metaData}
ON MATCH
...
I am very grateful for help you can provide.

[UPDATED]
In the following queries, for simplicity I pretend that:
We find the Parent node of interest by name.
The relationship type of current interest is Foo.
General Notes:
The OPTIONAL MATCH clauses find the sibling, if any, that should follow the child being inserted.
The FOREACH clauses take care of linking the that sibling, if any, to the child being inserted, and then deletes the obsolete relationship to that sibling.
To unshift the Child having an id of 123 right after the Parent node:
MATCH (p:Parent {name:"Fred"})
OPTIONAL MATCH (p)-[r:Foo]->(c:Child)
WITH p, r, COLLECT(c) AS cs
MERGE (cNew:Child {id:123})
CREATE (p)-[rNew:Foo]->(cNew)
FOREACH (x IN cs |
CREATE (cNew)-[:Foo]->(x)
DELETE r)
RETURN p, rNew, cNew;
To insert the Child node having an id of 123 at index 4 (i.e, make it the 5th child):
MATCH (p:Parent {name:"Fred"})
MATCH (p)-[:Foo*3]->()-[r:Foo]->(c:Child)
OPTIONAL MATCH (c)-[r1:Foo]->(c1:Child)
WITH c, r1, COLLECT(c1) AS c1s
MERGE (cNew:Child {id:123})
CREATE (c)-[rNew:Foo]->(cNew)
FOREACH (x IN c1s |
CREATE (cNew)-[:Foo]->(x)
DELETE r1)
RETURN c, rNew, cNew;
To replace the Child at index 4 (i.e, the 5th child) with the Child having an id of 123:
MATCH (p:Parent { name:"Fred" })
MATCH (p)-[:Foo*4]->(c0)-[r:Foo]->(c:Child)
OPTIONAL MATCH (c)-[r1:Foo]->(c1:Child)
WITH c0, r, c, r1, COLLECT(c1) AS c1s
MERGE (cNew:Child { id:123 })
CREATE (c0)-[rNew:Foo]->(cNew)
DELETE r, c
FOREACH (x IN c1s |
CREATE (cNew)-[:Foo]->(x)
DELETE r1)
RETURN c0, rNew, cNew;
Note: The DELETE r, c clause always deletes the node being replaced (c). That is only suitable if that is what you actually want to happen, and will only succeed if c does not have relationships other than r. To explore how to address more specific needs, please ask a new question.

If I'm following, your nodes may belong to multiple linked lists. A simple 'next' relation is insufficient because when lists cross--share a child node--the 'next' relations will drag in all the downstream nodes of both lists. So you're making the 'next' relations unique to each list by adding the id of the parent node. (Note using the metadata id may lead to issues down the road.)
So you might have a parent p1 whose id=1, and a unique relationship 'n_p1' to link its children, and a child 'c' whose id=21 you want to add.
For the parent with no child you could add your new child by:
MATCH (c {id:21}), (p {id:1}) WHERE NOT p-[:n_p1]->() MERGE p-[:n_p1]->c
And if the parent has one or more children, find the last one that's not the same as the one being added:
MATCH (c {id:21}), (p {id:1})-[:n_p1*1..5]->(cn) WHERE NOT cn-[:n_p1]->() AND NOT cn.id=c.id MERGE cn-[:n_p1]->c
Somebody else might have a better way, but you could UNION these together. Remember the parts of a UNION must return the same columns, so just return the new child c. The whole thing might look like this:
MATCH (c {id:21}), (p {id:1}) WHERE NOT p-[:n_p1]->() MERGE p-[:n_p1]->c return c UNION MATCH (c {id:21}), (p {id:1})-[:n_p1*1..5]->(cn) WHERE NOT cn-[:n_p1]->() AND NOT cn.id=c.id MERGE cn-[:n_p1]->c return c;

Related

Delete the previous node and relationship and point it to the current node with same relationship

I'm trying to form a relationship b/w parent and child.
MERGE (c:Child{name:'CA'})
MERGE (p:Parent{name:'Parent'})
For creating a relationship,
MERGE (p)-[:parent_of]->(c)
This queries are written in a separate java function named createParentChildRelationship.
Whenever I try to call this function with a same Parent name with a different child name passing a parameter to the function, it forms a new relationship . But I need only the current child node to point the parent node, by deleting the previous child nodes and it's relationship with the parent.
So I tried this way of approaching it,
MERGE (c:Child{name:'CA'})
MERGE (p:Parent{name:'Parent'})
MATCH (c)-[r:parent_of]-(p) DELETE r, c, p
CREATE (p)-[:parent_of]->(c)
But it shows me 1 property is set but relationship isn't created
Two problems with your query:
You're deleting the parent, the child, and the relationship. You can't create a new relationship to a new child if you just deleted the parent. Also you're deleting the child you just created, not the current child of the parent, so that needs fixing too.
If a MATCH fails, then there are no rows to execute upon, and nothing else will happen in the query. You may want an OPTIONAL MATCH instead, or maybe a pattern comprehension.
You could give this a try instead:
MERGE (p:Parent {name:'Parent'})
WITH p, [(p)-[:parent_of]->(existing) | existing] as existingChildren
FOREACH (child IN existingChildren | DETACH DELETE child)
MERGE (c:Child {name:'CA'})
CREATE (p)-[:parent_of]->(c)
Try this, it works on my neo4j desktop;
Find that relationship between CA and parent (OPTIONAL because it will continue the query if there is no match found)
Delete the relationship
Find that parent and create if not found (MERGE)
Using the child and parent nodes, create a new relationship
OPTIONAL MATCH (c:Child)-[r:parent_of]-(:Parent{name:'Parent'})
DELETE r
MERGE (p2:Parent{name:'Parent'})
MERGE (p2)-[r2:parent_of]->(c2:Child{name:'CA'})
RETURN c2, r2, p2
You cannot delete c and p since it is still part of the query during create.

Deleting duplicate relationships in neo4j - is this correct?

I have developed a query which, by trial and error, appears to find all of the duplicated relationships in a Neo4j DB. I want delete all but one of these relationships but I'm concerned that I have not thought of problematic cases that could result in data deletion.
So, does this query delete all but one of a duplicated relationship?
MATCH (a)-->(b)<--(a) # identify where the duplication is present
WITH DISTINCT a, b
MATCH (a)-[r]->(b) # get all duplicated paths themselves
WITH a, b, collect(r)[1..] as rs # remove the first instance from the list
UNWIND rs as r
DELETE r
If I replace the UNWIND rs as r; DELETE r with WITH a, b, count(rs) as cnt RETURN cnt it seems to return the unnecessary relationships.
I'm still relucant to put this somewhere to be used by others, though....
Thanks
First of all, let me (strictly) define the term: "duplicate relationships". Two relationships are duplicates if they:
Connect the same pair of nodes (call them a and b)
Have the same relationship type
Have exactly the same set of properties (both names and values)
Have the same directionality between a and b (iff directionality is significant for use case)
Your query only considers #1 and #4, so it generally could delete non-duplicate relationships as well.
Here is a query that will take all of the above into consideration (assuming #4 should be included):
MATCH (a)-[r1]->(b)<-[r2]-(a)
WHERE TYPE(r1) = TYPE(r2) AND PROPERTIES(r1) = PROPERTIES(r2)
WITH a, b, apoc.coll.union(COLLECT(r1), COLLECT(r2))[1..] AS rs
UNWIND rs as r
DELETE r
Aggregating functions (like COLLECT) use non-aggregated terms as grouping keys, so there is no need for the query to perform a separate redundant DISTINCT a,b test.
The APOC function apoc.coll.union returns the distinct union of its 2 input lists.

neo4j create child node and link it to another childern

Intention:
Using neo4j, I would like to add a Child node to a Parent Node. If there are other existing children, I must link the child to all siblings
What I Tried:
Suppose we know for sure that Parent exists. We want to add generic nodes under it. I got this and modified it from:
here
match(tbl: Parent {name: "existing_node"})
optional match(c: Child {name: "generic_node"})
create(n: Child{name: "generic_node"})
FOREACH (o IN CASE WHEN c IS NOT NULL THEN [c] ELSE [] END |
CREATE (o)-[:SIBLING]->(n)
CREATE (n)-[:SIBLING]->(o)
)
Problem Description:
If I run the query multiple times, the following happens:
Creates a new node
Creates a new node and links it with previous node
Creates 2 new nodes and link one of them to the existing ones and to the newly created one. I was expecting it to create only 1 node and link it to all other nodes.
Creates 4 new nodes, and connects one of them to the existing nodes, and connects them as a chain. I was expecting only 1 node to be created and linked to all other nodes.
...
What I am not understanding is why many new nodes are being created? In the FOREACH, I was thinking that (n) would refer to the same newly created node and not create new nodes.
What would be the correct way to add a new sibling and connect it to all other ones if they exist?
Can someone explain why this one creates multiple nodes? There is something happening behind the scenes which I am not understanding.
This should do what you want (it will not create duplicates, no matter how many times the query is executed):
// Find the parent, p
MATCH (p:Parent {name: "existing_node"})
// Find or create Child, c, and ensure it is associated with p
MERGE (c:Person {name: "generic_node"})
MERGE (p)-[:HAS_CHILD]->(c)
// Find each child of p (other than c), and ensure it has a `SIBLING` relationship with c
WITH p, c
MATCH (p)-[:HAS_CHILD]->(n)
WHERE n <> c
MERGE (c)-[:SIBLING]-(n)
Note that there is no need to make two SIBLING relationships (one in each direction) between every pair of sibling nodes, since later on you can use non-directional MATCH queries that do not care about directionality. For example:
// Relationship pattern has no arrow, so matches relationship going in either direction
MATCH (c1:Child)-[:SIBLING]-(c2:Child)
...
Similarly, the MERGE (c)-[:SIBLING]-(n) clause above does not specify an arrow either, to ensure that 1 and only one relationship is created between c and n.
I think this might achieve what you trying to accomplish
// Find the parent
MATCH (tbl:Parent {name: "existing_node"})
// Optionally fin existing children and put them in a collection
OPTIONAL MATCH (tbl)<-[:CHILD]-(c:Child {name: "generic_node"})
WITH tbl, collect(c) AS children
// create the new child and the relationship to the found parent
CREATE (n:Child {name: "generic_node"})
CREATE (tbl)<-[:CHILD]-(n)
// create the sibling relationships to any pre-existing children
FOREACH (c IN children |
CREATE (c)-[:SIBLING]->(n)
)
Why your query created duplicates...
The first time you ran the optional match there were no children matched so you created a new child node and that was that.
The second time you run it the optional match finds the child you just created and then creates another one
The third time it finds both previous children in two rows and since you don't collect them to a single row your create statemetn is called two times. Now there are four children so the next time the create is called four times.
THe foreach only ends up operating on that one child node at a time because there is no collection of child nodes.

How to duplicate node tree in neo4j?

How can I create full node tree from existing node tree, Actually i have 1 node and i need to find all relation and nodes that has my existing top node. I need to create full node tree to another node.
Copy All Node Tree from A with Relation and create Duplicate same node and relation for B Node
Okay, this is a tricky one.
As others have mentioned apoc.refactor.cloneNodesWithRelationships() can help, but this will also result in relationships between cloned nodes and the originals, not just the clones, as this proc wasn't written with this kind of use case in mind.
So this requires us to do some Cypher acrobatics to find the relationships from the cloned nodes that don't go to the cloned nodes so we can delete them.
However at the same time we also have to identify the old root node so we can refactor relationships to the new root (this requires separate processing for incoming and outgoing relationships).
Here's a query that should do the trick, making assumptions about the nodes since you didn't provide any details about your graph:
MATCH (a:Root{name:'A'})
WITH a, id(a) as aId
CALL apoc.path.subgraphNodes(a, {}) YIELD node
WITH aId, collect(node) as nodes
CALL apoc.refactor.cloneNodesWithRelationships(nodes) YIELD input, output
WITH aId, collect({input:input, output:output}) as createdData, collect(output) as createdNodes
WITH createdNodes, [item in createdData WHERE item.input = aId | item.output][0] as aClone // clone of root A node
UNWIND createdNodes as created
OPTIONAL MATCH (created)-[r]-(other)
WHERE NOT other in createdNodes
DELETE r // get rid of relationships that aren't between cloned nodes
// now to refactor relationships from aClone to the new B root node
WITH DISTINCT aClone
MATCH (b:Root{name:'B'})
WITH aClone, b
MATCH (aClone)-[r]->()
CALL apoc.refactor.from(r, b) YIELD output
WITH DISTINCT b, aClone
MATCH (aClone)<-[r]-()
CALL apoc.refactor.to(r, b) YIELD output
WITH DISTINCT aClone
DETACH DELETE aClone

Cypher - Delete node with given property and reconnect graph

I have a graph consisting of paths.
I need to delete all nodes, that have a property:
linksTo:'javascript'
After deleting i have to reconnect the paths. This means i need to create a new relationship for each gap. This relationship has a property named deltaTime that holds some integer value. This value (deltaTime) should be the sum of all deltaTime-properties of the deleted relationships of this path. Please look at the following picture for better understanding.
I don't know how to detect multiple "bad" nodes in a row, with a variable row-length.
It would help if you could provide the labels involved in your graph, as well as the relationship types you're using.
Assuming all these paths are chains (only single relationships connecting each node), something like this should work for you:
// first, add a label on all the nodes we plan on deleting
// it helps if they already have the same label especially if linksTo property is indexed.
MATCH (n{linksTo:'javascript'})
SET n:ToDelete
With the appropriate nodes labeled, we'll find the segments of nodes to delete, the surrounding nodes that need to be connected, and create the new relationship. We ensure a and b are start and end nodes of the chain by ensuring there are no :ToDelete nodes linking to a, or from b.
MATCH (a:ToDelete)
WHERE NOT (:ToDelete)-->(a)
AND ()-->(a)
MATCH p = (a)-[rel*0..]->(b:ToDelete)
WHERE ALL(node in nodes(p) WHERE node:ToDelete)
AND NOT (b)-->(:ToDelete)
AND (b)-->()
WITH a, b, REDUCE(s = 0, r IN rel | s + r.value) as sum
// now get the adjacent nodes we need to connect
MATCH (x)-[r1]->(a), (b)-[r2]->(y)
WITH x, y, sum + r1.value + r2.value AS sumValue
// making up relationship type and property name as I don't know what you're using
MERGE (x)-[:Rel{value: sumValue}]->(y)
Finally, when you're sure the new relationships look correct, delete the nodes you no longer need.
MATCH (n:ToDelete)
DETACH DELETE n

Resources