I have a cypher that looks like the following
MERGE (col)-[:CONNECTS]->(o)
ON CREATE SET col.name = "SOME NAME"
Now I want to add the following node and relationship only if the merge creates (not matches):
CREATE (o)-[:NEEDS]->(p:anode)
How is this achieved?
There is no built in conditional creation right now in Cypher, I guess it will be added to upcoming versions.
For now you can do a little trick, when creating you set a property on the relationship that will tell it is a new creation, you then do a foreach/case on this property, create the other relationship and remove the property.
Code explains better than words :
MERGE (col)-[r:CONNECTS]->(o)
ON CREATE SET col.name = "SOME NAME", r.new = 1
FOREACH (x IN CASE WHEN r.new = 1 THEN [1] ELSE [] |
CREATE (o)-[:NEEDS]->(p:anode)
)
REMOVE r.new
Related
This seems like a simple use case but I'm having trouble locating an example in the docs.
I want to do the following:
MERGE(node:Graph{id:{id}})
ON CREATE SET node.firstseen = timestamp(),
SET node.id = {id},
node.list = [list]
Examples of this would be appreciated, thanks!
You have a comma before your SET clause:
ON CREATE SET node.firstseen = timestamp(),
^
The correct query is
MERGE (node:Graph {id:$id})
ON CREATE SET node.firstseen = timestamp()
SET
node.list = $list
You don't need to set the id field, the MERGE takes care of this. The list field will be set every time you call the query, the firstseen timestamp only when the node doesn't exist.
Assuming you pass id and list as parameters:
MERGE (node:Graph {id: $id})
ON CREATE SET
node.firstseen = timestamp(),
node.list = $list
You cannot have a SET clause within another SET clause. Besides, it is unnecessary to SET node.id = $id anyway, since your MERGE already guarantees that property has that value.
I tried to run below cypher query in Neo4j(3 Merge Statement in once)
MERGE (paymentinstrument{SENDER_CREDITCARD:'123444'})-[rPAYMENTINSTRUMENT:PROFILECHANGE_PI{}]->( profilechange )
ON CREATE SET rPAYMENTINSTRUMENT.CREATETIMESTAMP = toInt('1413911269726')
ON MATCH SET rPAYMENTINSTRUMENT.UPDATETIMESTAMP = toInt('1413911269726')
MERGE (profilechange{PROFILECHANGEIDENTIFIER:'ABCD'})-[rPROFILECHANGEDEVICE:HAS_DEVICE{}]->( device )
ON CREATE SET rPROFILECHANGEDEVICE.CREATETIMESTAMP = toInt('1413911269726')
ON MATCH SET rPROFILECHANGEDEVICE.UPDATETIMESTAMP = toInt('1413911269726')
MERGE (profilechange{PROFILECHANGEIDENTIFIER:'ABCD'})-[rPROFILECHANGEIPADDRESS:HAS_IP{}]->( ipaddress )
ON CREATE SET rPROFILECHANGEIPADDRESS.CREATETIMESTAMP = toInt('1413911269726')
ON MATCH SET rPROFILECHANGEIPADDRESS.UPDATETIMESTAMP = toInt('1413911269726')
which encountered below error
Can't create node profilechange with labels or properties here. The
variable is already declared in this context
Does anyone have idea or workaround for this issue ?Thanks
You have two merges starting with
MERGE (profilechange{PROFILECHANGEIDENTIFIER:'ABCD'})
so, its like you are defining two references with the same name
second use, should be
MERGE (profilechange)--
Also, you syntax is bad in the error prone sense.
In "Learning Neo4j", the author :) advises this syntax
(reference:Label{key:'value'})-[r2:RELATIONNAME]->(reference2:Label{you:'got it'})
My target is to create node + set new property to it in case not exists
if it's exist I just want to update it's property
Tried this:
MATCH (user:C9 {userId:'44'})
CREATE UNIQUE (user{timestamp:'1111'})
RETURN user
*in case the node with the property userId=44 already existed I just want to set it's property into 1111 else just create it and set it.
error I am getting:
user already declared (line 2, column 16 (offset: 46))
"CREATE UNIQUE (user{timestamp:'1111'})"
should I switch to Merge or?
thanks.
Yes, you should use the MERGE statement.
MERGE (user:C9 {userId:'44'})
// you can set some initial properties when the node is created if required
//ON CREATE SET user.propertykey = 'propertyvalue'
ON MATCH SET user.timestamp = '1111'
RETURN user
You mention unique constraints - I assume you have one set up. You definitely should do to prevent duplicate nodes being created. It will also create a schema index to improve the performance of your node lookup. If you do not yet have a unique constraint then it can be created like so
CREATE CONSTRAINT ON (u:C9) ASSERT u.userId IS UNIQUE
See the Neo4j MERGE documentation.
Finally, to understand what is going on in your query let's have a quick look line by line.
MATCH (user:C9 { userId:'44' })
This matches the node with label :C9 that has a userId property with value 44 and assigns it the identifier user.
CREATE UNIQUE (user{timestamp:'1111'})
This line is simply trying to create a new node with no label and a property timestamp with value '1111'. The exception you are seeing is a result of you using the same user identifier that has already been used in the first line. However, this is not a supported way to use CREATE UNIQUE as it requires a match first and will then create bits of the pattern that does not exist. The upside of this is that it is stopping this unwanted node (user{timestamp:'1111'}) being created in the graph.
RETURN user
This line is pretty self explanatory and is not being reached.
EDIT
There seems to be some confusion surrounding CREATE UNIQUE and when it should be used. This query
CREATE UNIQUE (user:C9 {timestamp:'1111'})
will fail with the message
This pattern is not supported for CREATE UNIQUE
To use CREATE UNIQUE you would first match an existing node and then use that to create a unique pattern in the graph. So to create a relationship from user to a second node you would use
MATCH (user:C9 { userId: '44' }
CREATE UNIQUE (user)-[r:FOO]-(bar)
RETURN r
If there is no relationships of type FOO from user then a new node will be created to represent bar and relationship of type :FOO will be created between them. Conversely, if the MATCH statement does not make a match then no nodes or relationships will be created.
For versioning purposes I wanna have VERSION node in my db. This node and its relationsship should only be created if the property size of a FILE node changes. The FILE node should always contain the current value for size. Since it isn't possible to use MATCH and WHERE in the foreach, how to make such a scenario work? Is there something like a "If-Clause" for using with foreach?
MERGE (root:FOLDER {fullpath: {newRoot}.fullpath})
ON CREATE SET root={newRoot}
FOR EACH (newFile IN {newFiles} |
MERGE (file:FILE {fullpath:newFile.fullpath}) ON CREATE SET file.guid = newFile.guid
SET file.visited = 1
MERGE (root)-[:CONTAINS]->(file)
And the following two lines should only execute if file.size != newFile.size (size changed)
SET file.size = newFile.size
CREATE (file)-[:HASVERSION{v:1}]->(v:VERSION {size:newFile.size})
I hope you understand what I want to achieve. Please give advice if there are better solutions for Node versioning purposes.
fadanner,
Here's what I found. This would work in the earlier answer I gave you as well (instead of the FOREACH).
MERGE (root:FOLDER {fullpath: {newRoot}.fullpath})
ON CREATE SET root={newRoot}
WITH root UNWIND {newFiles} AS newFile
MERGE (file:FILE {fullpath : newFile.fullpath})
ON CREATE SET file.guid = newFile.guid
SET file.visited = 1
MERGE (root)-[:CONTAINS]->(file)
WITH file, newFile
WHERE file.size <> newFile.size
CREATE (file)-[:HASVERSION {v : 1}]->(v:VERSION {size : newFile.size})
This is more complicated than the simple example that I tested, so there may be errors. But see if something like this won't solve your problem.
Grace and peace,
Jim
I need to create a relation between two users once, and update its properties since then. Is there a way to do something like "create if not exist otherwise update" in Neo4j using Cypher?
MERGE (u1:User)-[r:REL]->(u2:User)
ON CREATE SET
u1.prop1 = val1,
u2.prop2 = val2,
r.prop3 = val3
ON MATCH SET
u1.prop1 = newVal1,
u2.prop2 = newVal2,
r.prop3 = newVal3
Have a look at the Neo4j docs for "MERGE".