Cypher

Cypher is the main direct graph-query surface. Use it when you want precise graph patterns, explicit procedures, or a reproducible query to paste into the frontend Direct Query tab. See Cypher And openCypher Compatibility for the supported scope and Cypher Writes And Schema for governed mutations.

Direct Query Basics

In the frontend:

  1. Select a database.

  2. Open Direct Query.

  3. Choose Cypher.

  4. Paste a read-only query.

  5. Run it and inspect the table result.

If a Cypher query returns an error like expected CONSTRUCT, the language selector is set to SPARQL. Switch it to Cypher and run the query again.

Without a web console, submit the query text through ExecuteCypher as shown in Connect To ShrikeDB. The examples below are templates over the named labels and properties. For Person examples, load people with the referenced properties; healthcare examples require ClaimCell and Procedure data; path procedures require the indicated node IDs and relationship properties.

Core Syntax

Implemented practical syntax includes:

  • MATCH, OPTIONAL MATCH, WHERE, and RETURN;

  • WITH, aggregation, DISTINCT, and UNWIND;

  • UNION and UNION ALL;

  • null-aware expressions, lists, maps, comprehensions and bounded read-only subqueries;

  • ORDER BY, SKIP, and LIMIT;

  • CALL …​ YIELD procedures;

  • CALL vector.knn(…​);

  • CALL kv.get(…​);

  • SEARCH collection AS alias;

  • RANK rerank_v3(…​);

  • governed CREATE, MERGE, property updates and node deletion;

  • EXPLAIN, PROFILE, and scoped schema catalog commands;

  • fixed and bounded named path materialization for supported shapes.

Use backticks around relationship or property names that contain ::

MATCH (cell:ClaimCell)-[:`tmsis:hasProcedure`]->(procedure:Procedure)
RETURN cell, procedure
LIMIT 10

Basic Graph Patterns

MATCH (n)
RETURN n
LIMIT 10
MATCH (cell:ClaimCell)-[:`tmsis:hasProcedure`]->(procedure:Procedure)
RETURN cell, procedure
LIMIT 25

Filtering And Aggregation

MATCH (cell:ClaimCell)-[:`tmsis:hasProcedure`]->(procedure:Procedure)
WHERE procedure = "urn:cms:tmsis:provider-spending:hcpcs:T1019"
RETURN procedure, count(*) AS cells
LIMIT 10

For semantic rollups that depend on profile-specific measures, the natural-language semantic surface may be more reliable than hand-written Cypher because it can use profile-defined aggregate fields.

Pipelines And Aggregation

WITH projects the bindings available to the next clause. It can group rows, compute aggregates and filter their aliases before the final result:

MATCH (p:Person)
WITH p.country AS country, count(*) AS people
WHERE people > 1
RETURN country, people
ORDER BY people DESC
LIMIT 20

Use a second projection for arithmetic on an aggregate; nested aggregate expressions such as RETURN count(*) + 1 are outside the current lowering scope.

MATCH (p:Person)
WITH count(*) AS people
RETURN people + 1 AS next_count

Lists, UNWIND And DISTINCT

This example needs no graph data:

UNWIND [3, 1, 3, NULL] AS x
RETURN count(*) AS rows, count(x) AS present,
       count(DISTINCT x) AS distinct_present,
       collect(DISTINCT x) AS values

The counts are 4 rows, 3 non-null values and 2 distinct non-null values. The collection contains 1 and 3; do not rely on their order without an ordered pipeline. An empty global aggregate still returns one row, while an empty grouped result has no groups to return.

Optional Matches And Nulls

MATCH (p:Person)
OPTIONAL MATCH (p)-[:KNOWS]->(friend:Person)
RETURN p, count(friend) AS friends
LIMIT 20

The optional clause preserves a person with no matching friend and supplies a null friend binding. count(friend) is zero for that row; count(*) would count the preserved row. Keep an optional-side filter attached to its OPTIONAL MATCH when it should constrain the optional match rather than remove the preserved outer row. A leading OPTIONAL MATCH without an existing row stream is outside the general planner’s scope.

RETURN NULL = NULL AS unknown_equality,
       NULL IS NULL AS is_null,
       coalesce(NULL, 'unknown') AS display_name,
       CASE WHEN 2 IN [1, 2, 3] THEN 'present' ELSE 'absent' END AS membership

This returns null, true, unknown, and present respectively. See Cypher And openCypher Compatibility for the full null and empty-aggregate rules.

Unions, Comprehensions And Subqueries

UNION removes duplicate rows; UNION ALL preserves them. Use matching output column names in every branch and one union mode throughout the query:

RETURN 1 AS value
UNION ALL
RETURN 2 AS value

List comprehensions and quantified predicates also work without graph data:

RETURN [x IN [1, 2, 3] WHERE x > 1 | x + 10] AS shifted,
       any(x IN [1, 2, 3] WHERE x > 2) AS has_large

The result is [12, 13] and true. Fixed-pattern comprehensions and COUNT { …​ } / COLLECT { …​ } read-only expressions are also supported within bounded output limits. COLLECT { …​ } must return one visible column; variable-length pattern comprehensions are rejected.

Use explicit imports for a correlated read-only subquery:

MATCH (p:Person)
CALL (p) {
  MATCH (p)-[:KNOWS]->(friend)
  RETURN friend
}
RETURN p, friend
LIMIT 20

A CALL branch with no output rows does not provide optional null extension. To filter by existence without projecting each matching friend:

MATCH (p:Person)
WHERE EXISTS { MATCH (p)-[:KNOWS]->(friend) }
RETURN p
LIMIT 20

WHERE NOT EXISTS { …​ } is also supported. These existence forms must be top-level WHERE predicates in the current implementation. Subqueries are read-only; mutation, schema commands and UNION inside them are rejected.

Path Aliases And Path Functions

For supported fixed or bounded path shapes, ShrikeDB materializes path values.

MATCH p = (a)-[:KNOWS]->(b)
RETURN p, length(p), nodes(p), relationships(p)
LIMIT 10
MATCH p = (a)-[:KNOWS*1..3]->(b)
RETURN nodes(p), length(p)
ORDER BY length(p)
LIMIT 20

Ordinary bounded patterns also work without a path alias:

MATCH (a:Person)-[:KNOWS*0..3]->(b:Person)
RETURN count(DISTINCT b) AS reachable_people

0..3 includes zero hops; ..3 starts at one hop. Bounded clauses enforce relationship uniqueness across their segments, allow node revisits through different edges, and check endpoint filters. Separate MATCH clauses can reuse an edge. Unbounded paths and unsupported relationship-variable bindings in bounded clauses are rejected rather than approximated. Exceeding path/candidate limits is an error, not a partial count; see Cypher controls.

Graph Analytics Procedures

Graph analytics use GDS-like stream procedures. They are read-only in normal query mode.

CALL gds.pageRank.stream({
  relationshipTypes: ["tmsis:hasProcedure"],
  maxIterations: 10,
  dampingFactor: 0.85,
  topK: 10
})
YIELD nodeId, score
RETURN nodeId, score
LIMIT 10
CALL gds.wcc.stream({
  relationshipTypes: ["tmsis:hasProcedure"]
})
YIELD nodeId, componentId
RETURN nodeId, componentId
LIMIT 10

Centrality examples:

CALL gds.degree.stream({
  relationshipTypes: ["tmsis:hasServicingProvider"],
  orientation: "REVERSE",
  topK: 10
})
YIELD nodeId, degree, score
RETURN nodeId, degree, score
ORDER BY score DESC
LIMIT 10
CALL gds.betweenness.stream({
  relationshipTypes: ["KNOWS"],
  topK: 10,
  sourceSampleSize: 256
})
YIELD nodeId, score
RETURN nodeId, score
ORDER BY score DESC
LIMIT 10

Weighted Path Procedures

Weighted path procedures run over finite numeric relationship weights. They lower through the same path-query operator as the SPARQL SERVICE extensions.

CALL gds.shortestPath.dijkstra.stream({
  sourceNode: "uid:1",
  targetNode: "uid:42",
  relationshipTypes: ["ROAD"],
  relationshipWeightProperty: "cost"
})
YIELD targetNodeId, totalCost, nodeIds, costs, path
RETURN targetNodeId, totalCost, nodeIds, costs
CALL gds.shortestPath.astar.stream({
  sourceNode: "uid:1",
  targetNode: "uid:42",
  relationshipTypes: ["ROAD"],
  relationshipWeightProperty: "cost",
  latitudeProperty: "lat",
  longitudeProperty: "lon",
  heuristicScale: 1.0,
  fallbackToDijkstra: true
})
YIELD targetNodeId, totalCost, nodeIds, costs
RETURN targetNodeId, totalCost, nodeIds, costs
CALL gds.shortestPath.yens.stream({
  sourceNode: "uid:1",
  targetNode: "uid:42",
  relationshipTypes: ["ROAD"],
  relationshipWeightProperty: "cost",
  k: 5
})
YIELD index, totalCost, nodeIds, costs
RETURN index, totalCost, nodeIds, costs
ORDER BY index
CALL gds.bellmanFord.stream({
  sourceNode: "uid:1",
  targetNodes: ["uid:42"],
  relationshipTypes: ["ROAD"],
  relationshipWeightProperty: "cost"
})
YIELD sourceNodeId, targetNodeId, distance, isNegativeCycle
RETURN sourceNodeId, targetNodeId, distance, isNegativeCycle

Multimodal Composition

CALL vector.knn("products_mm_vec", [0.031, -0.118, 0.442], 50, {ef: 256})
YIELD node AS product, score
MATCH (product:Product)-[:IN_CATEGORY]->(category:Category)
CALL kv.get("default", product) YIELD value AS product_json
RETURN product, category, score, product_json
LIMIT 12

Store product_json with PutKeyValue using the product UID as the key. The string argument selects the public default KV namespace. See Multimodal queries for the complete data setup and query flow.

Boundaries

The compatibility reference documents clause, expression, path and storage-semantic limits. When a query shape is not supported, prefer one of these paths:

  • simplify the graph pattern;

  • use a GDS-style procedure for a graph algorithm;

  • ask the semantic surface to compile a profile-backed query;

  • use SPARQL for RDF-shaped triple patterns.