Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

IF...ELSE with Cypher Neo4J

Tags:

neo4j

cypher

Has there been any update to the syntax of an IF/ELSE statement in Cypher?

I know about CASE and the FOREACH "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 a MATCH inside it.

One of the reasons for wanting to do MATCH inside the CASE at the top of the query, is because I want the query to fail if the property on refs exists but the MATCH does not succeed. Using OPTIONAL 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 do MERGE 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)
}
like image 371
Senica Gonzalez Avatar asked Apr 18 '17 20:04

Senica Gonzalez


1 Answers

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.

like image 65
InverseFalcon Avatar answered Oct 21 '22 20:10

InverseFalcon