Checking if a node exists


(Oleg ) #1

I'm trying to find a way to determine if a node exists . My logic is given an ID,

if a node (doc{id}) exists:
create (doc{id})-[:RELATED_ID]->(doc{id}) -to itself
else if a node (draft{id}) exists:
create (doc{id})-[:RELATED_ID]->(draft{id}) -to the draft node
else
create (doc{id})-[:RELATED_ID]->(other{id}) -to an other node

I've found that there's maybe a way with CASE and FOREACH (https://porterhau5.com/blog/creating-conditional-statements-with-cypher/)
or maybe apoc.when (https://stackoverflow.com/questions/43481472/if-else-with-cypher-neo4j)

Which would be the best way to continue?


(12kunal34) #2

2nd option using apoc would be best way.


(Andrew Bowman) #3

I'm a little confused by your requirements here. Each of the cases requires a :Doc node with the id exists (so a relationship can be created between it and some other node), but you state at the start that if that node exists then it should only create a relationship to itself, which would ensure none of the other conditions are triggered. Could you clarify your requirements a bit?


(Oleg ) #4

Ok, good catch, I didn't think my explanation all the way through... I have an current/existing doc{id1} node that I'm reading in a data file for. Each data file has a list of related IDs... so far I've seen them refer to doc or to draft types, and I want to make an other type to capture that as well for later... the doc node referred to could be itself or another doc.

So given a current doc{id1} and an id2(maybe equal to id1) I want to check if it's an existing doc or draft

if a node (doc{id2}) exists:
create (doc{id1})-[:RELATED_ID]->(doc{id2}) -to another doc or itself
else if a node (draft{id2}) exists:
create (doc{id1})-[:RELATED_ID]->(draft{id2}) -to the draft node
else
create (doc{id1})-[:RELATED_ID]->(other{id2}) -to an other node


(Andrew Bowman) #5

Got it.

One alternative to consider, if these labels are all subsets of some more general type, you might consider applying an additional label on all of them for that type, and creating the index on that type and id property (though there's not a uniqueness constraint you would still encounter issues of which one to use if you get multiple items back for the same id).

But for the current state of things, assuming that the only thing you'd be creating are relationships and not the nodes themselves, you can optional match to all of them and then use coalesce() to get the first non-null node to use for relationship creation:

MATCH (origin:Doc {id:$id1})
OPTIONAL MATCH (doc:Doc {id:$id2})
OPTIONAL MATCH (draft:Draft {id:$id2})
OPTIONAL MATCH (other:Other {id:$id2})
WITH origin, coalesce(doc, draft, other) as node
CREATE (origin)-[:RELATED_ID]->(node)

(Oleg ) #7

Ah, I didn't know about coalesce but I should've explained a little better still, thank you for your patience... I do want to create the other node if neither the doc nor draft nodes exist. It would be a placeholder that could be used later, I just want to capture it now. Could I combine your answer with apoc.do.when() something like this?

MATCH (origin:Doc {id:$id1})
OPTIONAL MATCH (doc:Doc {id:$id2})
OPTIONAL MATCH (draft:Draft {id:$id2})
OPTIONAL MATCH (other:Other {id:$id2})
WITH origin, coalesce(doc, draft, other) as node
with origin, count(node) as cnt
call apoc.do.when(cnt = 1, 'MERGE (origin)-[:RELATED_ID]->(node)', 'MERGE (other:Other {id:$id2})) MERGE (origin)-[:RELATED_ID]->(other)')

Is it possible to get a reference to node inside the second part of apoc.do.when?


(Andrew Bowman) #8

We'll need to modify this one. When using coalesce(), it will only return a single value (the first non-null value from those passed to it) so we'll need to do something a bit different here:

MATCH (origin:Doc {id:$id1}) 
OPTIONAL MATCH (doc:Doc {id:$id2}) 
OPTIONAL MATCH (draft:Draft {id:$id2}) 
OPTIONAL MATCH (other:Other {id:$id2}) 
WITH origin, coalesce(doc, draft, other) as node 
CALL apoc.do.when(node is null, 'MERGE (other:Other {id:id}) RETURN other as node', 'RETURN node', {id:$id2, node:node}) YIELD value
WITH origin, value.node as node
MERGE (origin)-[:RELATED_ID]->(node)

In the case before the CALL when node = null, none of the other optional matches succeeded so we need to MERGE in a new :Other node by id. Otherwise we just return the node from the coalesce().


(Oleg ) #9

Yes, that's it! :) There was only a small problem in the null check, it needed to be node is null, but it works like I need it to, thank you very much for your help!


(Andrew Bowman) #10

Good catch, I tripped on that one. Query fixed. Glad to help!