Create a node within a foreach only on a given condition - neo4j

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

Related

Skip existing node while loading csv

i m new at neo4j and i d like to upload a csv file and create a set of nodes. However i have already some existing nodes that may exist on that csv file. Is there an option to load the csv, create the nodes based on each row and in case the node already exists skip that row?
Thanks
You can use the MERGE clause to avoid creating duplicate nodes and relationships.
However, you need to carefully read the documentation to understand how to use MERGE, as incorrect usage can cause the unintentional creation of nodes and relationships.
Merge will give you what you want, however you must be careful how you identify the record uniquely to prevent creating duplicates
I'll put the desired final form first as attention spans seem to be on the decline...
// This one is safe assuming name is a true unique identifier of your Friends
// and that their favorite colors and foods may change over time
LOAD CSV FROM 'data/friends.csv' AS line
MERGE (f:Friend { name: line[0]})
set a.favorite_food = line[1]
set a.favorite_color = line[2]
The merge above will create or find the Friend node with that matching name and then, regardless of whether we are creating it or updating it, set the attributes on it.
If we were to instead provide all the attributes in the merge as such:
// This one is dangerous - all attributes must match in order
// to find the existing Friend node
LOAD CSV FROM 'data/friends.csv' AS line
MERGE (f:Friend { name: line[0], favorite_food: line[1], favorite_color: line[2]})
Then we would fail to find an existing friend everytime their favorite_food or favorite_color was updated in our data being (re)loaded.
Here's an example for anyone who's imagination hasn't fully filled in the blanks...
//Last month's file contained:
Bob Marley,Hemp Seeds,Green
//This month's file contained:
Bob Marley,Soylent Green,Rainbow

Create doesn't make all nodes and relationships appear

I just downloaded and installed Neo4J. Now I'm working with a simple csv that is looking like that:
So first I'm using this to merge the nodes for that file:
LOAD CSV WITH HEADERS FROM 'file:///Athletes.csv' AS line
MERGE(Rank:rank{rang: line.Rank})
MERGE(Name:name{nom: line.Name})
MERGE(Sport:sport{sport: line.Sport})
MERGE(Nation:nation{pays: line.Nation})
MERGE(Gender: gender{genre: line.Gender})
MERGE(BirthDate:birthDate{dateDeNaissance: line.BirthDate})
MERGE(BirthPlace: birthplace{lieuDeNaissance: line.BirthPlace})
MERGE(Height: height{taille: line.Height})
MERGE(Pay: pay{salaire: line.Pay})
and this to create some constraint for that file:
CREATE CONSTRAINT ON(name:Name) ASSERT name.nom IS UNIQUE
CREATE CONSTRAINT ON(rank:Rank) ASSERT rank.rang IS UNIQUE
Then I want to display to which country the athletes live to. For that I use:
Create(name)-[:WORK_AT]->(nation)
But I have have that appear:
I would like to know why I have that please.
I thank in advance anyone that takes time to help me.
Several issues come to mind:
If your CREATE clause is part of your first query: since the CREATE clause uses the variable names name and nation, and your MERGE clauses use Name and Nation (which have different casing) -- the CREATE clause would just create new nodes instead of using the Name and Nation nodes.
If your CREATE clause is NOT part of your first query: your CREATE clause would just create new nodes (since variable names, even assuming they had the same casing, are local to a query and are not stored in the DB).
Solution: You can add this clause to the end of the first query:
CREATE (Name)-[:WORK_AT]->(Nation)
Yes, Agree with #cybersam, it's the case sensitive issue of 'name' and 'nation' variables.
My suggesttion:
MERGE (Name)-[:WORK_AT]->(Nation)
I see that you're using MERGE for nodes, so just in case any values of Name or Nation duplicated, you should use MERGE instead of CREATE.

Can't create node with labels or properties here. The variable is already declared in this context in Neo4j

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'})

Neo4j add nodes only on merge create

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

Neo4j 2.0 Merge with unique constraints performance bug?

Here's the situation:
I have a node that has a property ContactId which is set as unique and indexed. The node label is :Contact
(node:Contact {ContactId:1})
I have another node similar to that pattern for Address:
(node2:Address {AddressId:1})
I now try to add a new node that (among other properties, includes ContactId (for referencing))
(node3:ContactAddress {AddressId:1,ContactId:1})
When I run a merge command for each, the time for adding a node that contains a property that is set as unique in another node type seems to make the process much slower.
The ContactAddress node only contains relational properties between the Contact and Address nodes. Contact and Address nodes contain up to 10 properties each. Is this a bug, where Neo4j is check the property key -> value -> then node label?
Code and screenshot below:
string strForEach = string.Format("(n in {{{0}}} |
MERGE (c:{1} {{{2} : n.{2}}}) SET c = n)", propKey, label, PK_Field);
var query = client
.Cypher
.ForEach(strForEach)
.WithParam(propKey, entities.ToList());
Constraint checks are more expensive than just inserts. They also take a global lock on the constraint to prevent multiple insertion.
I saw you don't use parameters, but string substitiution, I really recommend to change that and go with parameters.
Also setting the whole node c to n triggers constraint check again.
Your probably want to use the ON CREATE SET clause of MERGE
(n in {nodes} |
MERGE (c:Label {key : n.key}}) ON CREATE SET c.foo = n.foo, c.bar = n.bar )

Resources