Neo4j Cypher: Create a relation only if the end node exists - neo4j

For the following Cypher statement:
start n=node:types(id={typeId}), g=node:groups(id={groupId})
create unique (n)<-[:has_type]-(unit {props})-[:has_group]->(g)
return unit
There are cases when g may be null (i.e. a group with id groupId does not exist).
In such a case, what should I do to make this statement still create the unit, but skip the has_group relation to g?
Right now, unit does not get created, presumably because g is null.
I'm using Neo4j Advanced 1.8
Thanks!

I would suggest to move the definition of g to the where clause, since starting at a non-existing node gives error and thus one can't continue the query to the create phase. Note the '?' which handles the null values in Cypher:
start n=node:types(id={typeId})
create unique (n)<-[:has_type]-(unit {props})-[:has_group]->(g)
where g.id?={groupId}
return unit
the query might need some tweaking, this is just my first untested shot.
edit: After some trying I came to a conclusion, that you might want to do 2 different queries, first for creating the first part of relationships with the unique node which is always and the second to create the relationship to the group which may not happen:
start n=node:types(id={typeId})
create unique (n)<-[:has_type]-(unit {props})
return unit
start unit=node:unitProps({unitPropsValue}) ,g=node:groups(id={groupId})
create unique unit-[:has_group]->g
return g
the second query will fail with an error in case the group does not exist, but that does not matter since you will still reach the target. For some strange reason I couldn't manage to implement some restrictions in the where clause like I tried in the first shot. following query seems to simply jump over the where conditions (maybe a bug?) although in my comprehension of Cypher it shall match the already existing group, but it does create a new g node instead:
start n=node(1)
create unique n-[:TYPE1]-(uniq {uid:333})
with uniq
create unique uniq-[:TYPE2]->g
where has(g.gid) and g.gid=999
return g

You can use WITH clause to achieve this in one query,
start n=node:types(id={typeId})
create unique (n)<-[:has_type]-(unit {props})
WITH unit
START g=node:groups(id={groupId})
create unique (unit)-[:has_group]->(g)
WHERE g <> null
return unit
if g is null, second won't get executed at all. even WHERE g <> null might not be required here. Kindly try and confirm

You can try this
MATCH (g:groups)
WHERE id(g)={groupId}
create unique (unit {props})-[:has_group]->(g)
WITH unit, create unique (unit)-[:has_type]->(n)
return unit

Since this is the only thing I can find related to this, I will add how I am dealing with this since none of the other answers are good enough for my purposes.
MATCH (a:TEST{id:1})
OPTIONAL MATCH (b:TEST)
WHERE b.id IN [2,3,5]
// collect b so that there are no nulls, and rows aren't lost when no match
WITH a, collect(b) AS c
FOREACH(n IN c | CREATE UNIQUE (a)-[:HAS]->(n))
RETURN a
Will work for Cypher 2.3+ (I can't test any earlier than that)
For those with APOC, you can also use CALL apoc.cypher.doIt() to break out the write.

Related

Sub queries in Cypher Query Language

I want to write a CREATE relationship statement for a project i'm working on. The statement has to be something like this
CREATE (match (p:Halt) where p.name="Ananda College" return p)-[:next_halt {route:['103'],dist:1.45}]->(MATCH (p:Halt) where p.name="Borella" return p)
As you can see i want the start node and end node to have values coming from another CQL statement.
But when i run this query there seems to be a syntax error. I have gone through some tutorials to see where my query is wrong but being a beginner i can't really tell.
Invalid input '(': expected whitespace, comment, node labels, MapLiteral, a parameter, ')' or a relationship pattern (line 1, column 15 (offset: 14))
"CREATE (match (p:Halt) where p.name="Ananda College" return p)-[:next_halt {route:['103'],dist:1.45}]->(MATCH (p:Halt) where p.name="Borella" return p)"
Your syntax is rather mixed up here. Please reread the dev documentation and maybe look at the Cypher cheat sheet.
As for proper syntax, you don't even need nesting to get what you want. First, you match on your start and end nodes, then you can use the bound variables in other parts of your query, such as to create a relationship:
MATCH (start:Halt), (stop:Halt)
WHERE start.name = "Ananda College" AND stop.name="Borella"
CREATE (start)-[:next_halt {route:['103'], dist:1.45}]->(stop)
If you aren't sure if nodes (or the relationship) exist or not, you can use MERGE instead, which will MATCH on existing nodes (or relationships) or create them if they do not exist.

Relationship not being applied correctly between nodes in Neo4j

I am trying to run the code on this GraphGist to play with the Buendia Family Tree in Neo4j.
When I create two nodes and try to connect them with a relationship, as such:
CREATE (JoseArcadioBuendia:Male {name:'Jose Arcadio Buendia',Gender:'Male'})
then
CREATE (UrsulaIguaran:Female {name:'Ursula Iguaran',Gender:'Female'})
then
CREATE (JoseArcadioBuendia)-[:HUSBAND]->(UrsulaIguaran)
I get this:
Why is this code incorrect? The syntax looks fine and it appears to run for the author of the Gist. Am I doing something incorrectly?
I presume that you did not include all 3 CREATE clauses in a single query.
Cypher identifiers (like JoseArcadioBuendia and UrsulaIguaran) only exist for the life of a single query. The DB does not persist these identifiers. So, if you have a query that contains just CREATE (JoseArcadioBuendia)-[:HUSBAND]->(UrsulaIguaran)), neo4j would not know that the nodes already exist and would therefore create 2 new nodes for you (as well as the relationship).
If you had put all 3 clauses in a single query, you would have seen the results you expected. Otherwise, your query would first have to use MATCH to associate those identifiers with the proper nodes (similar to #Bond's answer).
The third line is incorrect. You are not specifying any nodes. Try the following:
MATCH (a:Male), (b:Female)
WHERE a.name = 'Jose Arcadio Buendia' AND b.name = 'Ursula Iguaran'
CREATE (a)-[:HUSBAND]->(b);

Can I create and relate two nodes with the same name but different ids in neo4j

I have created two nodes in neo4j with the same name and label but with different ids:
CREATE (P:names {id:"1"})
CREATE (P:names{id:"2"})
My question is if I can create a relationship between these two nodes like this:
MATCH (P:names),(P:names)
WHERE P.id = "1" AND P.id = "2"
CREATE (P)-[r:is_connected_with]->(P) RETURN r"
I try it but it doesn't work.
Is it that I shouldn't create nodes with the same name or there is a workaround?
How about the following?
First run the create statements:
CREATE (p1:Node {id:"1"}) // note, named p1 here
CREATE (p2:Node {id:"2"})
Then, do the matching:
MATCH (pFirst:Node {id:"1"}), (pSecond:Node {id:"2"}) // and here we can call it something else
CREATE pFirst-[r:is_connected_with]->(pSecond)
RETURN r
Basically, you are matching two nodes (with the label Node). In your match you call them p1 and p2 but you can change these identifiers if you wish. Then, simply create the relationship between them.
You should not create identifiers with the same name. Also note that p1 and p2 are not the name of the node, it is the name of the identifier in this particular query.
EDIT: After input from the OP I have created a small Gist that illustrates some basics regarding Cypher.
#wassgren has the right answer about how to fix your query but I might be able to fill in some details about why and it's too long to leave in a comment.
The character before the colon when describing a node or relationship is referred to as an identifier, it's just a variable representing a node/rel within a Cypher query. Neo4j has some naming conventions that you are not following and as a result, it makes your query harder to read and will be harder for you to get help in the future. Best practices are:
Identifiers start lowercase: person instead of Person1, p instead of P
Labels are singular and have their first character capitalized: (p1:Name), not (p1:Names) or (p1:names) or (p1:name)
Relationships are all caps, [r:IS_CONNECTED_WITH], not [r:is_connected_with], though this one gets broken all the time ;-)
Back to your query, it both won't work and it doesn't follow conventions.
Won't work:
MATCH (P:names),(P:names)
WHERE P.id = "1" AND P.id = "2"
CREATE (P)-[r:is_connected_with]->(P) RETURN r
Will work, looks so much better(!):
MATCH (p1:Name),(p2:Name)
WHERE p1.id = "1" AND p2.id = "2"
CREATE (p1)-[r:IS_CONNECTED_WITH]->(p2) RETURN r
The reason your query doesn't work, though, is that by writing MATCH (P:names),(P:names) WHERE P.id = "1" AND P.id = "2", you are essentially saying "find a node, call it 'P', with an ID of both 1 and 2." That's not what you want and it obviously won't work!
If you're trying to create many nodes, you would rerun this query for each pair of nodes you want to create, changing the ID you assign each time. You can create the nodes and their relationship in one query, too:
CREATE (p1:Name {id:"1"})-[r:IS_CONNECTED_WITH]->(p2:Name {id:"2"}) RETURN r
In the app, just change the ID you want to assign to the nodes before you run the query. The identifiers are instance variables, they disappear when the query is complete.
EDIT #1!
One more thing, setting the id property within your app and assigning it to the database instead of relying on the Neo4j-created internal ID is a best practice. I suggest avoiding sequential IDs and instead using something to create a unique ID. In Ruby, many people use SecureRandom::uuid for this, I'm sure there's a parallel in whatever language(s) you are using.
EDIT #2!
Neo4j supports integer properties. {id:"1"} != {id: 1}. If your field is supposed to be an integer, use an integer.

Construct cypher to add many relationships in Neo4j

I wanted to add relationships to existing nodes, so I made cypher like this:
start n1=node:node_auto_index(id='0'),n2=node:node_auto_index(id='1') create n1-[:{quantity:1}]->n2;
start n1=node:node_auto_index(id='1'),n2=node:node_auto_index(id='2') create n1-[:USES_COMPONENT{quantity:7}]->n2;
start n1=node:node_auto_index(id='1'),n2=node:node_auto_index(id='3') create n1-[:USES_COMPONENT{quantity:11}]->n2;
start n1=node:node_auto_index(id='1'),n2=node:node_auto_index(id='4') create n1-[:USES_COMPONENT{quantity:14}]->n2;
but got many error messages (error around> {quantity). When I add only one of those, it says [[ Index node_auto_index does not exist ]].
You can't create a relationship without a type. If you add a type to the first statement, e.g. USES_COMPONENT like the others, that should resolve the error. You can match a relationship without using its type, but you can't create it without a type. If you want to match it without type, then you also need to drop the :, i.e.
n1-[{quantity:1}]->n2
is a valid pattern.
With regards to index, how have you configured your node_auto_index?
The error message means you don't have an index by that name. Usually you could fix this by enabling node auto-indexing -- Which is OK, but there's a better way to write your query:
MATCH (n0), (n1), (n2), (n3), (n4)
WHERE id(n0)=0 AND id(n1)=1 AND id(n2)=2 AND id(n3)=3 AND id(n4)=4
CREATE (n0)-[:USES_COMPONENT { quantity: 1 }]->(n1),
(n1)-[:USES_COMPONENT { quantity: 7 }]->(n2),
(n1)-[:USES_COMPONENT { quantity: 11}]->(n3),
(n1)-[:USES_COMPONENT { quantity: 14}]->(n4)
RETURN n0, n1, n2, n3, n4;
It's easier to use MATCH along with a WHERE clause to find a particular node (in general). Also notice that you can create as many new relationships as you want with a single query.
Double check this query before you use it -- you were re-using the n2 variable in confusing ways, so you need to make sure I got the right IDs bound to the right nodes for you.

Neo4j / Cypher create node if missing

I'd like to bundle a whole bunch of operations in as few requests as possible. The logic behind is should be the following:
check if head with key=value exists, create if it doesn't
check if tail with key=value exists, create if it doesn't
create a unique relationship (all kinds of properties)
set a bunch of properties for head & tail
I already stumbled upon create unique, but this seems to work for relationships only. How do I make sure that at least the head node exist so I can run a create unique statement on it?
What I'm doing now is firing multiple requests.The first one to find out if the node exists:
start x=node:index({key}={value}) return ID(x) as id
If that doesn't return an id, I fire another request to create the node and finally the final request to create the second node and relationships:
start n=node({id})
create unique n-[:POINTS_TO {label:{label}}]->(x {{key}:{value}})
return n,x
I'm wondering if there's a nicer way to bundle of all this...
if you use the auto-index, you can do:
start n=node:node_auto_index(key={value})
with count(*) as exists
where exists=0
create (n {key: {value}}
return n;
statement 2:
start n=node:node_auto_index(key={value})
create unique n-[:REL {foo:"bar"}]->(m {a:"b"})
set r.answer = 42
set m.c="d"
return r,m;
in Neo4j 2.0 there is MERGE for what you want.

Resources