Neo4J deep duplicated nodes - neo4j

For the data (below), my query looks like that:
MATCH p = (ob:Obiect)<--(w:Word { value:'game' })-[*]-(x) RETURN {id: id(x), value: x.value}
CREATE (w0:Obiect { value : 'Obiect' })
CREATE (w1:Word { value:'game' })
CREATE (w2:Word { value:'unreal' })
CREATE (w3:Word { value:'a' })
CREATE (w4:Word { value:'b'})
CREATE (w5:Word { value:'c' })
CREATE (w6:Word { value:'d'})
CREATE (w1)-[:IS]->(w0)
CREATE (w2)-[:IS]->(w1)
CREATE (w3)-[:HAS]->(w2)
CREATE (w4)-[:HAS]->(w3)
CREATE (w5)-[:HAS]->(w4)
CREATE (w6)-[:HAS]->(w5)
CREATE (w6)-[:HAS]->(w3)
If the relation (value:d)-->(value:a) doesn't exist then it’s okay and my results looks like this:
id 4070 value unreal
id 4071 value a
id 4072 value b
id 4073 value c
id 4074 value d
but if the relation exists then i have duplicates nodes (below). The question is how to avoid this problem?
id 4063 value unreal
id 4064 value a
id 4065 value b
id 4066 value c
id 4067 value d
id 4064 value a
id 4067 value d
id 4066 value c
id 4065 value b
id 4064 value a

You can use the MERGE clause : http://docs.neo4j.org/chunked/stable/query-merge.html
MERGE (w3:Word { value:'a' })
MERGE (w4:Word { value:'b'})
MERGE(w3)-[:HAS]->(w4)
MERGE acts as "MATCH or CREATE" .
Be aware that MERGE will attempt to match the entire pattern, which can be confusing the first times, so I recommend you this helpful article from Graphaware : http://graphaware.com/neo4j/2014/07/31/cypher-merge-explained.html

Related

neo4j - cypher query to find nodes which properties match object parameter

I'm looking for a query which can find all of the nodes which have properties that match a parameter which is an object. The properties of the node can be a superset (contain more properties than the filter)
e.g.
:param obj : { first : "first" }
CREATE (n:Test { first : "first", second : "second" });
CREATE (m:Test { first : "first" });
CREATE (f:Fail { first : "bad", second : "second" });
MATCH (c)
WHERE
PROPERTIES(c) = $obj
RETURN c;
n and m should be returned as they are matching on first : "first"
it is doable with apoc, basically by matching the obj with a submap of the properties, containing only the keys that are also present in obj
WITH { first : "first" } AS obj
MATCH (c)
WHERE apoc.map.submap(properties(c),keys(obj),[],false)= obj
RETURN c

Creating relationships between nodes with WHERE clause and using ID in Neo4j

I have two nodes named Room(4) and Houses(4). They have been created in the following way:
CREATE (n:Room { code: 1})
CREATE (n:Room { code: 1})
CREATE (n:Room { code: 1})
CREATE (n:Room { code: 1})
CREATE (n:House { code: 1})
CREATE (n:House { code: 2})
CREATE (n:House { code: 3})
CREATE (n:House { code: 4})
These are some of the relations that i am trying to create between the nodes
MATCH (room:Room), (house:House{code:1})
WHERE id(room) = 40
CREATE UNIQUE (room)-[:PLACED_IN]->(house) ;
MATCH (room:Room), (house:House{code:2})
WHERE id(room) = 41
CREATE UNIQUE (room)-[:PLACED_IN]->(house) ;
MATCH (room:Room), (house:House{code:3})
WHERE id(room) = 42
CREATE UNIQUE (room)-[:PLACED_IN]->(house) ;
The ID's have not been defined before so it should be creating new rooms based on ID's or should i add the ID's manually while creating as currently the relationships are not being created due to WHERE clause?
Change your query to:
// match room by internal id
MATCH (room:Room)
WHERE id(room) = 40
// merge will create a relationship between `room.id = 40`
// and `house.code = 1`. If `house.code = 1` does not exists, it will be created
MERGE (room)-[:PLACED_IN]->(:House {code:1}) ;
MATCH (room:Room)
WHERE id(room) = 41
MERGE (room)-[:PLACED_IN]->(:House {code:2}) ;
MATCH (room:Room)
WHERE id(room) = 42
MERGE (room)-[:PLACED_IN]->(:House {code:3}) ;
Some tips:
Avoid depending on Neo4j internal IDs because the are not safe. Neo4j
reuses these IDs when nodes and relationships are deleted.
CREATE UNIQUE is deprecated. Use MERGE instead.

Neo4j: Creating Multiple Relationships Not Working

I am creating multiple relationships in one query. If a match is not found for the first relationship the second one is not created. If both matches exist then both relationships work.
Example:
Assume that 'MATCH1ID' below does not exist but 'MATCH2ID' does. Why does this happen?
DOES NOT WORK
MERGE (NewRecord:OBJECTNAME { Id: 'ABZDEFG' })
SET NewRecord.name='NEWNAME'
WITH NewRecord
MATCH (a)
WHERE a.Id = 'MATCH1ID'
CREATE (a)-[ar:Relationship1]->(NewRecord)
WITH NewRecord MATCH (b)
WHERE b.Id = 'MATCH2ID'
CREATE (b)-[br:Relationship2]->(NewRecord)
(If I switch the order of the node that does exist to be first in the order it works)
WORKS
MERGE (NewRecord:OBJECTNAME { Id: 'ABZDEFG' })
SET NewRecord.name='NEWNAME'
WITH NewRecord
MATCH (b)
WHERE b.Id = 'MATCH2ID'
CREATE (b)-[br:Relationship2]->(NewRecord)
WITH NewRecord
MATCH (a)
WHERE a.Id = 'MATCH1ID'
CREATE (a)-[ar:Relationship1]->(NewRecord)
You can try using a OPTIONAL MATCH instead of a simple MATCH:
MERGE (NewRecord:OBJECTNAME { Id: 'ABZDEFG' })
SET NewRecord.name='NEWNAME'
WITH NewRecord
OPTIONAL MATCH (a)
WHERE a.Id = 'MATCH1ID'
FOREACH(x IN (CASE WHEN a IS NULL THEN [] ELSE [1] END) |
CREATE (a)-[ar:Relationship1]->(NewRecord)
)
WITH NewRecord
OPTIONAL MATCH (b)
WHERE b.Id = 'MATCH2ID'
FOREACH(x IN (CASE WHEN b IS NULL THEN [] ELSE [1] END) |
CREATE (b)-[br:Relationship2]->(NewRecord)
)
Also, this query uses a trick with FORACH and CASE WHEN to handle conditions. In this case, the CREATE statement is executed only when a or b variables are not null.
See Neo4j: LOAD CSV - Handling Conditionals.

How to add unique data to a neo4j graph database

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;
};
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'],[])
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

How to search a node filtering by an array field using multiple values

I store some values inside an array of a node and I would like to filter it using more than a single value. How could I do that?
Here it is my node:
CREATE (t:Test { value:["a","b","c"] } );
CREATE (t:Test { value:["a","b","d"] } );
CREATE (t:Test { value:["a","b","x"] } );
CREATE (t:Test { value:["a","y","z"] } );
CREATE (t:Test { value:["a","f","k"] } );
I would like to create a query that retrieves only items that "a" and "b" are inside values column.
The query below is not working but is close to what I would like to do.
MATCH (c:Test) WHERE ["a", "b"] IN c.value RETURN c
I know that I could split it using two "IN" commands (example below) but I would to like to avoid creating dynamic queries on my system.
MATCH (c:Test)
WHERE ALL(x IN ["a","b"] WHERE x IN c.value)
RETURN c.value

Resources