TuringDB v3 (turingdb 3.0 on PyPI) runs every query on a new MLIR-based engine and supports most of openCypher, extended with versioning, vector search and fast lookup by native node ID.
If you know Cypher from Neo4j, you can write the queries you are used to. This page covers the basics and the handful of places where TuringDB differs. The rest of the language is documented in the pages that follow.
What is supported
| Area | Supported |
|---|---|
| Reading | MATCH, OPTIONAL MATCH, WHERE, WITH, RETURN [DISTINCT], ORDER BY, SKIP, LIMIT, UNWIND, UNION [ALL], LOAD CSV |
| Patterns | Multiple labels, edge type alternatives ([:A|B]), inline WHERE, variable-length paths (*1..3, ->+, {2,4}), quantified path patterns, named paths, pattern predicates |
| Expressions | Arithmetic, string and list concatenation, CASE, list and pattern comprehensions, reduce, all / any / none / single, list slicing, maps |
| Aggregation | count, sum, avg, min, max, collect, DISTINCT, implicit grouping, aggregates inside expressions |
| Subqueries | EXISTS { }, COUNT { }, CALL { } and OPTIONAL CALL { } |
| Writing | CREATE, MERGE with ON CREATE / ON MATCH, SET, REMOVE, DELETE, DETACH DELETE |
| Types | String, Integer, Double, Boolean, List, Map, DateTime, Duration, Embedding |
| TuringDB extras | Changes, commits and time travel, shortestPath, VECTOR SEARCH, gnn.graphSAGE, lookup by native node ID |
Basics
Queries are built from nodes and edges:
- Nodes are written in parentheses:
(n)is a node namedn. - Edges are written in square brackets:
[e]is an edge namede. - Labels are added with a colon:
(n:Person). A node can have several:(n:Person:Employee). - Edge types use the same syntax:
[e:FRIENDS_WITH]. An edge has exactly one edge type. - Properties are constrained with curly braces:
(n:Person {name: 'John', age: 20}).
MATCH (a:Person {name: 'Alice'})-[e:KNOWS]->(b:Person)
RETURN a.name, e.since, b.nameBy convention, node labels are written in PascalCase (Person, BankAccount), edge types in upper case with underscores (WORKS_AT, FRIENDS_WITH), and strings use single quotes.
Keywords are case-insensitive (MATCH and match are the same). Labels, edge types and property names are case-sensitive. Only // starts a comment:
// a line comment
MATCH (n:Person) RETURN n.name // also fine at the end of a lineUse // for comments. SQL-style -- and /* */ comments are not yet supported.
Running a query
Queries run through the Python SDK, the interactive shell, or the HTTP endpoint. Read queries run directly against the current graph; write queries must run inside a change.
from turingdb import TuringDB
client = TuringDB(host="http://localhost:6666")
df = client.query("MATCH (n:Person) RETURN n.name, n.age") # a pandas DataFrameEXPLAIN <query> returns the compiled query plan without running the query. It is the easiest way to check that a lookup uses a constant node scan.
Coming from Neo4j
TuringDB follows openCypher closely, so the queries you know carry over. These are the TuringDB specifics worth knowing:
RETURN nreturns the node’s internal ID, an integer. Return the properties you need withRETURN n.name, n.age, or usedb.getNodesto see every property of a few nodes.- Values go in the query text. Queries are plain strings, so put literal values directly in the text and escape quotes in your application. Query parameters such as
$nameare not yet supported. - One statement per request. Send each statement as its own request (a single trailing
;is fine). - Labels are set at creation. Every node has at least one label and every edge exactly one edge type, chosen when it is created.
- Property types are global. A property name has a single value type across the whole graph: if
ageis an Integer, it is an Integer on every node. - Typed numeric equality. Compare Integers with Integers, and match Doubles with a range (
n.score > 1.99 AND n.score < 2.01) or convert withtoInteger.<,>and arithmetic work across numeric types. - Forgiving names. An unknown label matches nothing and a misspelled property reads as
null. Check names withCALL db.labels()andCALL db.propertyTypes(). - Label alternatives. Write
WHERE n:A OR n:BorWHERE NOT n:A. Edge type alternatives ([:A|B]) work directly in patterns. - String matching. Use
STARTS WITH,ENDS WITHandCONTAINS. Regular expressions (=~) are not yet supported. - Loops and uniqueness. Use
UNWINDfor loops andMERGEto avoid duplicates. - Shortest paths use a TuringDB statement, see Shortest path.
- Function names are case-sensitive (
toInteger, notTOINTEGER), exceptcountandcollect.
Coming from TuringDB 1.x? The v3 engine replaces the previous one, and many earlier limits are gone: OPTIONAL MATCH, WITH, DISTINCT, UNION, MERGE, DELETE, aggregates in any position, UNWIND over any list, LOAD CSV followed by MATCH, and CREATE ... SET all work now. One function changed name: edgeType(e) is now type(e).
Data types
TuringDB offers these types for node and edge properties and for values in queries:
| Type | Literal | Notes |
|---|---|---|
| String | 'Alice' or "Alice" | Backslash escapes (\'). Backticks quote identifiers, not strings |
| Integer | 30, 0x1F | Signed 64-bit. Integer division truncates |
| Double | 3.14, 1e3 | ^ always returns a Double |
| Boolean | true, false | |
| List | [1, 'a', [2]] | May mix types and nest |
| Map | {a: 1, b: [1, 2]} | See Maps |
| DateTime | datetime('2024-03-15T10:30:00Z') | Microsecond precision, always UTC |
| Duration | duration({days: 2}) | Microsecond precision, no month component |
| Embedding | (1.2, 2.0, 0.0) | Parentheses, at least 2 numbers. See Vector search |
Embedding literals use parentheses. [1.2, 2.0, 0.0] is a List, which is a different type.
More detail on types, conversion and results is in Functions and data types.
Roadmap
We keep extending Cypher coverage and growing the library of procedures available through CALL. If there is a query form you would like to see, please tell us.

