6

I am adding iteratively data to my neo4j database but I am stuck with how to overwrite or update existing data and to check whether the data does not already exist in there.

Basically I have a set of movies with their corresponding id's, e.g.:

[
  {id: 'gameofthrones', genre: 'fantasy', release: '2017'},
  {id: 'inception', genre: 'scifi', release: '2010'},
  ...
]

I can add the movies as follows:

CREATE 
(m1:Movie {id: 'gameofthrones', genre: 'fantasy', release: '2017'}), 
(m2:Movie {id: 'inception', genre: 'scifi', release: '2010'}) 

However, when I run the script two times, then it creates 4 nodes instead of keeping it at two nodes.

So my question is, how can I make sure that it checks whether the node id is already present, and if so overwrite it instead of creating a new node?

I tried (but only the properties get added)

// data
attributes['id']         = 'gameofthrones';
attributes['genre']     = 'fantasy';
...

// query
MERGE ( s:Movie {attributes}.id)
ON CREATE SET ( s:Movie {attributes} )

which I call in NodeJS as follows:

executeQuery(queryStr, {"attributes": attributes})

// cypher (nodejs)
function executeQuery(queryStr, params) {
    var qq = Q.defer();
    db.cypher({
        query: queryStr,
        params: params,
    }, function (error, results) {
        if (error) {
            qq.reject(error);
        } else {
            if (results.length != 0) {
                qq.resolve(true);
            } else {
                qq.resolve(false);
            }
        };
    });
    return qq.promise;
};
WJA
  • 6,676
  • 16
  • 85
  • 152

2 Answers2

4

you must change your query to this

MERGE ( s:Movie {attributes}.id)
ON CREATE SET s += {attributes}
ON MATCH SET s += {attributes} // optional

this should work, but you should use apoc.map.clean() so you do not set the id twice, which can cause some problems.

MERGE ( s:Movie {attributes}.id)
ON CREATE SET s += apoc.map.clean({attributes},['id'],[])
Tomaž Bratanič
  • 6,319
  • 2
  • 18
  • 31
  • it's better to separate the attributes from the id. so that it doesn't get overrriden, which (might) trigger another index-update – Michael Hunger Mar 24 '17 at 06:13
3

You can achieve this with MERGE clause as follows

MERGE (m1:Movie {id: 'gameofthrones'})
ON CREATE SET m1.genre = 'fantasy', m1.release = '2017'

MERGE (m2:Movie {id: 'inception'})
ON CREATE SET m2.genre: 'scifi', m2.release = '2010' 

Ideally you want to create queries with parameters instead of literal strings. You can achieve this if you user apoc.load.json

with "file:///home/sample.json" as url // can be also http://url/sample.json
CALL apoc.load.json(url) yield value
UNWIND value as item
MERGE (m1:Movie {id: item.id})
ON CREATE SET m1.genre = item.genre, m1.release = item.release

example for dynamic properties with apoc functions:

with "file:///home/sample.json" as url // can be also http://url/sample.json
CALL apoc.load.json(url) yield value
UNWIND value as item
MERGE (m1:Movie {id: item.id})
ON CREATE SET m1 += apoc.map.clean(item,['id'],[])

or if you do not have apoc plugin:

with "file:///home/sample.json" as url // can be also http://url/sample.json
CALL apoc.load.json(url) yield value
UNWIND value as item
MERGE (m1:Movie {id: item.id})
ON CREATE SET m1 += item

note that id will first be merged and later updated with ON CREATE SET and you want to avoid writing a single property twice, using apoc and above query we can achieve that

Tomaž Bratanič
  • 6,319
  • 2
  • 18
  • 31
  • Thanks. So I tried that with parameters, but I am not getting it to work. Updated my question. – WJA Mar 13 '17 at 12:20
  • The thing is, the number of attributes change dynamically. I would not want to have to setup the properties every time in the string. – WJA Mar 13 '17 at 12:27
  • added an example with dynamic properties – Tomaž Bratanič Mar 13 '17 at 12:32
  • I am not getting the purpose of retrieving the data as you are doing. I already have the data in an Object form (pre-loaded). Why do I need to get it again as a JSON? – WJA Mar 13 '17 at 12:44
  • no need just an example... you can skip the apoc.load.json if you already have an object and just do this `with my_object as value UNWIND value as item MERGE (m1:Movie {id: item.id}) ON CREATE SET m1 += item` – Tomaž Bratanič Mar 13 '17 at 12:46