= Running Cypher fragments

We can use Cypher as safe, graph-aware, partially compiled scripting language within APOC.

// include::overview.adoc[tags=cypher,labeloffset=-1]

[separator=¦,opts=header,cols="1,1m,5"]
|===
include::../build/generated-documentation/apoc.cypher.csv[]
|===


== Example: Fast Node-Counts by Label

Counts by label are quickly provided by the counts-store, but only if they are the the single thing in the query, like

[source,cypher]
----
MATCH (:Person) RETURN count(*);
----

It also works to combine several with `UNION ALL`, but not via `WITH`

.Doesn't work
[source,cypher]
----
MATCH (:Person) WITH count(*) as people
MATCH (:Movie) RETURN people, count(*) as movies;
----

.Works
[source,cypher]
----
MATCH (:Person) RETURN count(*)
UNION ALL
MATCH (:Movie) RETURN count(*);
----


But with `apoc.cypher.run` we can construct the statement and run each of them individually, so it completes in a few ms.

[source,cypher]
----
call db.labels() yield label
call apoc.cypher.run("match (:`"+label+"`) return count(*) as count", null) yield value
return label, value.count as count
----

You can use a similar approach to get the property-keys per label:

[source,cypher]
----
CALL db.labels() yield label
call apoc.cypher.run("MATCH (n:`"+label+"`) RETURN keys(n) as keys LIMIT 1",null) yield value
RETURN label, value.keys as keys
----


== Running a cypher statement timeboxed

There's a way to terminate a cypher statement if it takes longer than a given threshold. Consider an expensive statement calculating cross product of shortestpaths for each pair of nodes:

[source,cypher]
----
call apoc.cypher.runTimeboxed("match (n),(m) match p=shortestPath((n)-[*]-(m)) return p", null, 10000) yield value
return value.p
----



== Conditional cypher execution

Sometimes queries require conditional execution logic that can't be adequately expressed in Cypher, even with CASE.

APOC's conditional execution procedures simulate an if / else structure, where a supplied boolean condition determines which cypher query is executed.

==== WHEN Procedures

[cols="1m,5"]
|===
| CALL apoc.when(condition, ifQuery, elseQuery:'', params:{}) yield value | based on the condition, executes read-only ifQuery or elseQuery with the given parameters
| CALL apoc.do.when(condition, ifQuery, elseQuery:'', params:{}) yield value | based on the condition, executes writing ifQuery or elseQuery with the given parameters
|===

For example, if we wanted to match to neighbor nodes one and two traversals away from a start node, and return the smaller set (either those one hop away, or those that are two hops away), we might use:

[source,cypher]
----
 MATCH (start:Node)-[:REL]->(a)-[:REL]->(b)
 WITH collect(distinct a) as aNodes, collect(distinct b) as bNodes
 CALL apoc.when(size(aNodes) <= size(bNodes), 'RETURN aNodes as resultNodes', 'RETURN bNodes as resultNodes', {aNodes:aNodes, bNodes:bNodes}) YIELD value
 RETURN value.resultNodes as resultNodes
----

Or, if we wanted to conditionally set or create graph elements if we deem some account to be suspicious, but still want to continue other query operations in either case, we could use `apoc.do.when`:

[source,cypher]
----
MATCH (acc:Account)
OPTIONAL MATCH (acc)-[r:ACCESSED_BY]->(suspect:User)
WHERE suspect.id in {suspiciousUsersIdList}
CALL apoc.do.when(r IS NOT NULL, 'SET acc:Suspicious', '', {acc:acc}) YIELD value
// ignore value and continue
WITH acc
...
----

==== CASE Procedures

For more complex conditional logic, case procedures allow for a variable-length list of condition / query pairs, where the query following the first conditional evaluating to true is executed. An elseQuery block is executed if none of the conditionals are true.

[cols="1m,5"]
|===
| CALL apoc.case([condition, query, condition, query, ...], elseQuery:'', params:{}) yield value | given a list of conditional / read-only query pairs, executes the query associated with the first conditional evaluating to true (or the else query if none are true) with the given parameters
| CALL apoc.do.case([condition, query, condition, query, ...], elseQuery:'', params:{}) yield value | given a list of conditional / writing query pairs, executes the query associated with the first conditional evaluating to true (or the else query if none are true) with the given parameters
|===

If we wanted to MATCH to selection nodes in a column, we could use entirely different MATCHES depending on query parameters, or based on data already in the graph:

[source,cypher]
----
 MATCH (me:User{id:{myId}})
 CALL apoc.case(
  [{selection} = 'friends', "RETURN [(me)-[:FRIENDS]-(friend) | friend] as selection",
   {selection} = 'coworkers', "RETURN [(me)-[:WORKS_AT*2]-(coworker) | coworker] as selection",
   {selection} = 'all', "RETURN apoc.coll.union([(me)-[:FRIENDS]-(friend) | friend], [(me)-[:WORKS_AT*2]-(coworker) | coworker]) as selection"],
   'RETURN [] as selection', {me:me}) YIELD value
 RETURN value.selection as selection
----