Match if node exists and set new property

I'm trying to write a query where if a node exists sets a new property with a specific value, if not, it creates a new node
I wrote this, but it doesn't work:

MATCH (n:User_friend)
RETURN 
CASE WHEN n.name='name surname' then set n.phone_number=123456789
ELSE CREATE node (n:User friend { n.phone_number: 123456789})
END

I have read the documents about CASE here, but obviously I wasn't able to understand it.

CASE can only be used for expressions, you cannot use it to execute conditional Cypher, so SET and CREATE can't be used within.

I think MERGE might be what you're looking for, as it has ON CREATE and ON MATCH clauses that execute depending on if the MERGE resulted in matching to an existing node or creating a new one.

That said, this looks like a type of case that MERGE may not cover, since the property that you're attempting to check/lookup by (name) isn't the one that you want to set if creating the new node (phone_number).

We might be able to do this with a bit of clever aggregation:

MATCH (n:User_friend)
WHERE n.name='name surname'
SET n.phone_number=123456789
WITH count(n) as nodesAltered
WHERE nodesAltered = 0
CREATE (n:User_friend {phone_number: 123456789})

Usually, when rows go to 0 (due to filtering or not finding something that meets a MATCH) everything else becomes no-op, since operations execute per row. But when we aggregate, we can turn 0 rows back to 1. In this case, we're doing a count() of n, which will be 0 if the MATCH didn't find anything, and a non-zero otherwise. We can use a WHERE clause after this to filter, so only in the case that no node was matched to and altered will we create a new node.

2 Likes

It's so simple and yet so ingenious. I hadn't thought about it, because I had been reluctant to use CASE. Thank you very much

CASE can only be used for expressions, you cannot use it to execute conditional Cypher, so SET and CREATE can't be used within.

There might have been a change or I am confusing something. But you can use CASE WHEN in a FOREACH specifically for write statements (Clauses)?

You can, because CASE is not a clause, it is an expression (or at least results in one), similar to how you can use a function for an expression within a FOREACH.

I believe you can use APOC's apoc.do.when

to do writes upon conditions.

See: Conditional Cypher Execution - APOC Documentation

And:

Ah, I think I did not get the i in CASE WHEN node.needsUpdate THEN [1] ELSE [] END | ...do stuff like CREATE and SET... part of the referenced example right.

The CASE WHEN expression gives a boolean and is finished when the conditional cypher starts, correct?

The CASE here evaluates a boolean, and depending on its value either uses a list with 1 element, or an empty list, as the thing that is iterated over for the FOREACH, meaning that the contents of the FOREACH happen once, or it doesn't happen at all (no elements in the list).

1 Like