IF...ELSE with Cypher Neo4J
Solution 1
APOC Procedures just updated with support for conditional cypher execution. You'll need version 3.1.3.7 or greater (if using Neo4j 3.1.x), or version 3.2.0.3 or greater (if using Neo4j 3.2.x).
Here's an example of some of the cases you mentioned, using the new procedures:
CALL apoc.when($refs.client IS NOT NULL,
"MATCH (cl:client {uuid: refs.client}) RETURN cl", '', {refs:$refs}) YIELD value
WITH value.cl as cl // which might be null...
...
...
CALL apoc.do.when(cl IS NOT NULL,
"DELETE tcr
MERGE (t)-[:references]->(cl)", '', {tcr:tcr, t:t, cl:cl}) YIELD value
...
...
RETURN {
client: cl {.uuid}, ...
}
In your return, map projection is enough to meet your needs, you'll get an object with the uuid if cl
exists, or a null for client
if not.
Solution 2
Older question, but there are some new tricks to achieve conditional Cypher execution, especially with subqueries introduced in Neo4j 4.1.x.
This knowledge base article covers the options available, and we'll keep the article updated as additional approaches become available.
https://neo4j.com/developer/kb/conditional-cypher-execution/
Note that the APOC approach in my older answer is still valid, and included in the article.
Senica Gonzalez
Passionate about solving problems. Whether that is wood working or programming, finding a creative solution to a difficult problem is satisfying. I grew up in rural North Carolina where programmers were scarce. Programming for me was an escape...a way to get in touch with technology and the outside world, a way to dream of doing something meaningful and impacting others.
Updated on June 09, 2022Comments
-
Senica Gonzalez almost 2 years
Has there been any update to the syntax of an
IF/ELSE
statement in Cypher?I know about
CASE
and theFOREACH
"hacks" but they are so unsightly to read :)I was wanting to do something with optional parameters such as:
CASE WHEN exists($refs.client) THEN MATCH (cl:client {uuid: $refs.client}) END ... // and later use it like CASE WHEN exists(cl) THEN DELETE tcr MERGE (t)-[:references]->(cl) END // and again in my return RETURN { client: CASE WHEN exists(cl) THEN {uuid: cl.uuid} ELSE NULL END, }
I know that doesn't make a lot of sense given the context, but I'm basically passing in a refs object which may or may not contain parameters (or the parameters exist and are NULL)
Somewhere I read there might be an update to how an "if/else" may be handled in neo4j so I really just wanted to check in and see if anyone was aware of a "nicer" way to handle cases like this.
Currently, I just handle all my queries in code and run a bunch of smaller queries, but it requires duplicate lookups for creating and deleting references. I'd like to move it all into one larger query so I can use variable references.
Again, I know I could use
FOREACH...CASE
, but when there is a lot of smaller cases like this, it gets hairy.Currently the error is
{ Error: Invalid input 'S': expected 'l/L' (line 7, column 9 (offset: 246)) " CASE true WHEN exists($refs.client) THEN MATCH (cl:client {uuid: $refs.client}) END" ^
I also know that I can use
WITH...CASE
if I'm passing back a known value, but cannot do aMATCH
inside it.One of the reasons for wanting to do
MATCH
inside theCASE
at the top of the query, is because I want the query to fail if the property on refs exists but theMATCH
does not succeed. UsingOPTIONAL MATCH
does not accomplish this.EDIT Oh, also... I'm reviewing using
MATCH (cl:client {uuid: $refs.client}) WHERE exists($refs.client)
but I recall that not working correctly.EDIT I can do
MATCH...WHERE exists()
but later it's futile if I can't doMERGE WHERE exists()
EDIT For reference to show why I'm asking about an IF/ELSE, here is the query I'm looking at. I've modified it from the above example so it doesn't error out.
MATCH (u:user {uuid: $uid})-[:allowed_to {read: true}]->(c:company {uuid: $cid}) MATCH (t:timesheet {uuid: $tid})<-[:owns]-(:timesheets)<-[:owns]-(u) // Make sure the incoming references are valid or fail query // Here, I'd like only do a match IF $refs.client exists and IS NOT NULL. If it is null or does not exist, I don't want the query to fail. OPTIONAL MATCH will not fail if the value is passed in is invalid but will simply return NULL. Which is why IF/ELSE (or CASE) would be helpful here. MATCH (cl:client {uuid: $refs.client}) MATCH (ca:case {uuid: $refs.case}) MATCH (s:step {uuid: $refs.step}) MATCH (n:note {uuid: $refs.note}) // clone timesheet entry to a revision CREATE (t)-[:assembled_with]->(r:revision) SET r = t, r.created_at = $data.updated_at WITH * // Get the old references MATCH (t)-[tcr:references]->(rc:client) MATCH (t)-[tcar:references]->(rca:case) MATCH (t)-[tsr:references]->(rs:step) MATCH (t)-[tnr:references]->(rn:note) // Copy old references to revision (won't create new relationships with NULL) MERGE (r)-[:references]->(rc) MERGE (r)-[:references]->(rca) MERGE (r)-[:references]->(rs) MERGE (r)-[:references]->(rn) // Update the current timesheet with new data SET t += $data // If new references are incoming, delete the old ones and update for new ones DELETE tcr DELETE tcar DELETE tsr DELETE tnr MERGE (t)-[:references]->(cl) MERGE (t)-[:references]->(ca) MERGE (t)-[:references]->(s) MERGE (t)-[:references]->(n) WITH * // Get the new count of revisions MATCH (t)-[:assembled_with]->(_r:revision) RETURN { uuid: t.uuid, start: t.start, end: t.end, description: t.description, client: CASE WHEN exists(cl.uuid) THEN {uuid: cl.uuid} ELSE NULL END, case: CASE WHEN exists(ca.uuid) THEN {uuid: ca.uuid} ELSE NULL END, step: CASE WHEN exists(s.uuid) THEN {uuid: s.uuid} ELSE NULL END, note: CASE WHEN exists(n.uuid) THEN {uuid: n.uuid} ELSE NULL END, revisions: count(_r) }
-
InverseFalcon over 3 yearsAs it's been a number of years and new options for this are available, please refer to my newer answer (referencing our knowledge base article on conditional cypher execution) instead of this answer.
-
Pygirl over 3 yearsThanks for posting out the new tricks +1. can you just tell me what's the advantage of using apoc module? Actually I am a noob. I am done with neo4j (cypher) So wanted to know whether I should use apoc for all the problems.
-
InverseFalcon about 3 yearsAPOC is a supplemental library. It offers some functionality that is currently impossible with Cypher alone. For other cases, it offers an alternate choice. You can include it and decide when and where to use its functions and procedures.