How to update existing relationship in neo4j? - neo4j

I have two nodes A and B.
They have a relationship R, with some property P, on this relationship.
How I can update this relationship R, with a new value for P?
I tried merge, but this creates a new relationship, but I want to update the existing one.

Match on your nodes and the relationship, then use SET to update the relationship property. For example:
MATCH (a {name:"A"})-[r]-(b {name:"B"})
SET r.P = "bar"
It's generally best, when looking up specific nodes, to use labels in the query, and have an index or unique constraint (whichever makes the most sense) to speed up your lookups.

Match (a:A), (b:B) Merge (a)-[r:YourRelations]->(b) Set r.P="new Prop" Return r

Related

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

Neo4j query node property.

I have database with entities person (name,age) and project (name).
can I query the database in cypher that specifies me it is person or project?
for example consider I have these two instances for each :
Node (name = Alice, age= 20)
Node (name = Bob, age = 31)
Node (name = project1)
Node (name = project2)
-I want to know, is there any way that I just say project1 and it tells me that this is a project.
-or I query Alice and it says me this is a person?
Thanks
So your use case is to search things by name, and those things can be of several types instead of a single type.
Just to note, in general, this is not what Neo4j is built for. Typically in Neo4j queries you know the type of the thing you're searching for, and you're exploring relationships between that thing (or things) to figure out associations or data derived from that.
That said, there are ways to do this, though it's worth going through the rest of your use cases and seeing if Neo4j is really the best tool for what you're trying to do
Whenever you're querying by a property, you either want a unique constraint on the label/property, or an index on the label/property. Note that you need a combination of a label and a property for this; you cannot blindly ask for a node with a property without specifying a label and get good performance, as it will have to do a scan of all nodes in your database (there are some older manual indexes in Neo4j, but I'm not sure if these will continue to be supported; the schema indexes are recommended by the developers).
There is a workaround to this, as Neo4j allows multiple labels on the same node. If you only expect to query certain types by name (for example, only projects and people), you might create a :Named label, and set that label on all :Project and :Person nodes (and any other labels where it should apply). You can then create an index on :Named.name. That way your query would be something like:
MATCH (n:Named)
WHERE n.name = 'blah'
WITH LABELS(n) as types
WITH FILTER(type in types WHERE type <> 'Named') as labels
RETURN labels
Keep in mind that you haven't specified if a name should be unique among node types, so it could be possible for a :Person or a :Project or multiple :Persons to have the same name, unsure how that affects what should happen on your end. If every named thing ought to have a unique name, you should create a unique constraint on :Named.name (though again, it's on you to ensure that every node you create that ought to be :Named has the :Named label applied on creation).
You should use node labels (like Person and Project) to represent node "types".
For example, to create a person and a project:
CREATE (:Person {name: 'Alice', age: 20})
CREATE (:Project {name: 'project1'})
To find the project(s) named 'Fred':
MATCH (p:Project {name: 'Fred'})
RETURN p;
To get a collection of the labels of node n, you can invoke the LABELS(n) function. You can then look in that collection to see if the label you are looking for is in there. For example, if your Cypher query somehow obtains a node n, then this snippet would return n if and only if it has the Person label:
.
.
.
WHERE 'Person' IN LABELS(n)
RETURN n;
[UPDATED]
If you want to find all nodes with the name property value of "Fred":
MATCH (n {name: 'Fred'})
...
If you want to find all relationships with the name property value of "Fred":
MATCH ()-[r {name: 'Fred'})-()
...
If you want to match both in a single query, you have many ways to do that, depending on your exact use case. For example, if you want a cartesian product of the matching nodes and relationships:
OPTIONAL MATCH (n {name: 'Fred'})
OPTIONAL MATCH ()-[r {name: 'Fred'})-()
...

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)

Iterate through collection with index in Cypher

Say I have matched a collection of relationships:
MATCH a-[r:BELONGS_TO]->b
How can I iterate through each relationship and assign it an index? In pseudocode:
for i in range(0, # of r's)
r.order = i
This should work:
MATCH (a)-[r:BELONGS_TO]->(b)
WITH collect(r) as rels
WITH rels, range(0, size(rels)) AS is
UNWIND is AS i
WITH rels[i] as rel, i
SET rel.order = i
You can hack it a bit :
MATCH (a)-[r:BELONGS_TO]->(b)
WITH collect(r) as relationships
UNWIND range(0, size(relationships)-1) as x
RETURN relationships[x]
Depending on your requirements, you may be able to use the "internal" ID that the neo4j DB automatically assigns to every relationship (and node). Although every internal ID is unique, after a relationship (or node) is deleted, its internal ID can be reused by a new relationship (or node). Over time, the set of active internal IDs for relationships (and nodes) may no longer have a 0 origin and may not have contiguous integer values.
If the internal ID is adequate for your needs, then there is no need to add your own id or index property.
You can use the ID() function to get the internal ID whenever you need it. For example, this is how you'd get the internal ID of every BELONGS_TO relationship:
MATCH ()-[r:BELONGS_TO]->()
RETURN ID(r);

How to add collection of values for a bidirectional relationship properties in neo4j?

I am creating 2 nodes in neo4j with a directional properties like below:
both employees are calling each other and they are connected with each other by CALLED relationship..
MATCH (e1:EMP),(e2:EMP) WHERE e1.NUMBER='200' AND e2.NUMBER='100' MERGE (e1)-[r:CALLED]->(e2) SET r.DURATION = ['233']
and my result is like below.
when I am creating a relation in revrese direction :
MATCH (e1:EMP),(e2:EMP) WHERE e1.NUMBER='100' AND e2.NUMBER='200' MERGE (e1)-[r:CALLED]->(e2) SET r.DURATION = "235" +r.DURATION[0..]
and my result is showing something strange
How can I add two properties in a collection so that it should look like
The concept of bidirectional exists only when querying. When creating relationships, every relationship must have a direction. What you have two different relations, one in each direction.
The relation from emp 200 to 100 has a property called duration with value ['233'].
Next, when you create a relation in the opposite direction from emp 100 to 200, this relation is a new one, it has nothing to do with the earlier relation except that the participating nodes are the same.
In this query
MATCH (e1:EMP),(e2:EMP)
WHERE e1.NUMBER='100' AND e2.NUMBER='200'
MERGE (e1)-[r:CALLED]->(e2) SET r.DURATION = "235" +r.DURATION[0..]
r.DURATION is null because the DURATION property does not yet exist on the relationship r from e1(100) to e2(200).
If you want to add durations to the relationship in a specific direction, you could use something like this
MATCH (e1:EMP),(e2:EMP)
WHERE e1.number='100' AND e2.number='200' MERGE (e1)-[r:CALLED]->(e2)
SET r.DURATION =["335"]+coalesce(r.DURATION,[])
Note that this inserts new duration values into the array on the relationship from emp 100 to 200. The values from emp 200 to 100 are unread and unmodified. If you wish to append values from the relationship in the opposite direction, you'll have to match it first to get the DURATION property. Doing this implies the same property value on the relation in both directions, and then I'd question why you need two relations instead of one.
If the direction is of no importance, use a single relation between e1 and e2.

Resources