neo4j create child node and link it to another childern - neo4j

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.

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.

Is there a way a relationship is automatically created when the nodes are created, given that the labels of the nodes already exist?

I created two labels A and B with a relationship CONNECT. I need to create multiple nodes of of each label A and B and a relationship between them. i.e, for each node A and B, I need to create a relationship.
e.g.: a1:A -[:CONNECT]-> b1:B, a2:A -[:CONNECT]-> b2:B,a3:A -[:CONNECT]-> b3:B, ... an:A -[:CONNECT]-> bn:B
Is there a way to create the relationship automatically? When I create nodes, can the db automatically create a relationship between the
nodes of different existing labels? Please help.
I used the APOC procedure to create multiple relationships between nodes manually on a property - for 100 nodes of labels LABEL1 and LABEL2, on a property value, I can create a relationships manually with a single Cypher query using below proc:
MATCH (ref1:LABEL1), (ref2:LABEL2)
WHERE ref1.property = ref2.property
CALL apoc.create.relationship(ref1, ‘RELATIONSHIP_NAME', {}, ref2) YIELD rel
RETURN rel
But I want to know if a way exists, where a relationship between two labels A and B already exists; and whenever a new node of label A and B are created, a relationship automatically creates without running a cypher query.
If you want to create a relationship between 2 nodes created at the same time, just specify that pattern in your CREATE clause.
Here is example using LOAD CSV to import data:
LOAD CSV WITH HEADERS FROM 'file:///data.csv' AS row
CREATE (a:A {x: row.x})-[:CONNECT]->(b:B {y: row.y})
Generally, it would be risky for a DB to automatically create relationships on your behalf since it might cause relationships to be created that you did not intend.
[UPDATE]
However, if Label2 nodes are created at a later time than Label1 nodes, and you are sure you want to automatically create the CONNECT relationship, you can use APOC to create a trigger like this:
CALL apoc.trigger.add(
"create-CONNECT-rel",
"UNWIND $createdNodes AS ref2
WITH ref2
WHERE ref2:Label2
MATCH (ref1:Label1 {property: ref2.property})
CREATE (ref1)-[:CONNECT]->(ref2)",
{phase:'before'})

How to use cypher to create a relationship between items in an array and another node

I would like to use cypher to create a relationship between items in an array and another node.
The result from this query was a list of empty nodes connected to each other.
MATCH (person:person),(preference:preference)
UNWIND person.preferences AS p
WITH p
WHERE NOT (person)-[:likes]->(preference) AND
p = preference.name CREATE (person)-[r:likes]->(preference)
Where person.preferences contains an array of preference names.
Obviously I am doing something wrong. I am new to neo4j and any help with above would be much appreciated.
Properties are attributes of a nodes while relationships involve one or two nodes. As such, it's not possible to create a relationship between properties of two nodes. You'd need to split the properties into their own collection of nodes, and then create a relationship between the respective nodes.
You can do all that in one statement - like so:
create (:Person {name: "John"})-[:LIKES]->(:Preference {food: "ice cream"})
For other people, you don't want to create duplicate Preferences, so you'd look up the preference, create the :Person node, and then create the relationship, like so:
match (preference:Preference {food: "ice cream"})
create (person:Person {name: "Jane"})
create (person)-[:LIKES]->(preference)
The bottom line for your use case is you'll need to split the preference arrays into a set of nodes and then create relationships between the people nodes and your new preference nodes.
One thing....
MATCH (person:person),(preference:preference)
Creates a Cartesian product (inefficient and causes weird things)
Try this...
// Get all persons
MATCH (person:person)
// unwind preference list, (table is now person | preference0, person | preference1)
UNWIND person.preferences AS p
// For each row, Match on prefrence
MATCH (preference:preference)
// Filter on preference column
WHERE preference.name=p
// MERGE instead of CREATE to "create if doesn't exist"
MERGE (person)-[:likes]->(preference)
RETURN person,preference
If this doesn't work, could you supply your sample data and noe4j version? (As far as I can tell, your query should technically work)

Cypher Linked LIst: how to unshift and replace by index

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;

Making a relation in neo4j

I am not sure what I am doing wrong here, so here is how I create nodes
CREATE (urlnode_1:UrlNode {url:'url1', nodenumber:1})
CREATE (urlnode_2:UrlNode {url:'url2', nodenumber:2})
I create relations as follows
CREATE
(urlnode_1)-[:OutLink {anchor_text:['MY']}]->(urlnode_2)
Two nodes are created successfully first, now on running the code to create the relation, I would have liked the relation to exist between the two created nodes but it creates two new nodes say 3 and 4 and shows a relation between them. What am i doing wrong here?
To guide you the best way I can, let's sum up some Neo4j basics concerning node and relationships creation :
A node can have one or more labels, labels are meaned to group the nodes by domain (User, Speaker, Company, etc..see a label as a table name for e.g. ). A node can also have properties.
A relationship can have only ONE type, relationships are organizing the graph. Relationships can also have properties.
To create a node, you can use the CREATE writing clause :
CREATE (n:Person {firstname: 'John'})
The CREATE statement will not check if other nodes with same label and properties already exists, it will just create a new node
Relationships can also be created with the same clause :
MATCH (n:Person {firstname: 'John'}), (p:Person {firstname: 'Pierre'})
CREATE (n)-[:KNOWS]->(p)
A complete pattern can also be created in one go :
CREATE (n:Person {name:'Chris'})-[:KNOWS]->(p:Person {name:'Oliver'})
REMINDER : CREATE will not check for existing nodes.
--- AND NOW MERGE ---
MERGE will lazily check for existing nodes, see him as a MATCH OR CREATE clause :
MERGE (n:Person {firstname:'Fred'})
If the node with label Person and firstname Fred does not exist, the node will be created, otherwise nothing will happen. This is where come the handy ON MATCH and ON CREATE mentionned by #joslinm .
If you run this query multiple times after the node creation, your graph will not change, if you know the http protocol, you can say that MERGE is an indempotent request.
Be aware that, MERGE will ensure that an entire pattern exist in the database, by creating it if it does not already exist, meaning that if you do MERGE with a complete pattern, the entire pattern will be looked up for existence, not a single node :
Say a node with label Person and name property with value 'John' already exist in the db :
MERGE (n:Person {name:'John'})
will not affect the graph
However :
MERGE (n:Person {name:'John'})-[:KNOWS]->(:Person {name:'Nathalia'})
A new John node will be created, because the entire pattern does not exist.
It is recommended to use MERGE incrementally :
MERGE (n:Person {name:'John'})
MERGE (p:Person {name:'Nathalia'})
MERGE (n)-[:KNOWS]->(p)
If you want to know more about the MERGE clause, I can highly recommend you this wonderful article from Luanne on GraphAware : http://graphaware.com/neo4j/2014/07/31/cypher-merge-explained.html
Chris
If you create a relationship, a new one will get created every single time. They are not inherently unique. It sounds like you'd rather be merging the relationship; i.e., if they relationship is there, match it, if not, create it.
The merge syntax for it is as follows:
MERGE (a:Node)-[:LIKES]->(b:Node)
ON
MATCH SET a.msg = 'I matched!'
ON
CREATE SET a.msg = 'I created!'
RETURN a
You can try it out here: http://console.neo4j.org/
You'll notice that first the msg will be "I created!" then after it matches, it will be "I matched!"

Resources