Repository navigation
Update query language and SDK docs for TuringDB v3 - #19
Conversation
Split the single Cypher page into a Query Language group (overview, matching, paths, results/aggregation, subqueries, functions/types, writing, procedures/commands) and refresh the cheatsheet. Examples were run against turingdb 3.0.1. Also update pages that contradicted v3: LOAD CSV + MATCH now works, CREATE ... SET works, edgeType() is now type(), UNWIND/aggregates are unrestricted, JSONL ids may have gaps and arrays load as lists, GML property naming, shortestPath output format, vector search (score, HNSW, use ids directly), LOAD PARQUET types, and the SDK's native binary protocol (USE_TURING_PROTO), auth, embedded data_dir (no ~ expansion) and error handling.
| ``` | ||
|
|
||
| <Note> | ||
| Expressions in a `WITH` must be aliased: `WITH n.name AS name`. `RETURN` does not need aliases. |
| ``` | ||
|
|
||
| ```cypher | ||
| MATCH (n:Person) RETURN count(*), count(n.age), sum(n.age), avg(n.age), min(n.age), max(n.age) |
There was a problem hiding this comment.
Add spaces in long code blocks and breathing space by adding blank lines. + indent clauses properly
|
|
||
| **Rules:** | ||
|
|
||
| - Aggregates are **not allowed in `WHERE`**. Filter after a `WITH` instead (see above). |
|
|
||
| - Aggregates are **not allowed in `WHERE`**. Filter after a `WITH` instead (see above). | ||
| - Aggregates cannot be nested: `count(count(*))` is an error. | ||
| - A non-aggregated expression next to an aggregate may only use the grouping keys. |
| `range(start, end[, step])` includes both bounds. Unwound nodes stay nodes, so `p.name` works after `UNWIND ns AS p`. You can also `UNWIND` a list of maps and read their keys: `UNWIND [{a: 1}, {a: 2}] AS m RETURN m.a`. | ||
|
|
||
| <Warning> | ||
| Query parameters (`UNWIND $list`) are not supported. Write the list into the query text instead, for example `f"UNWIND {ids} AS x ..."` in Python. |
| RETURN [1, 2] + [3], 'a' || 'b' | ||
| ``` | ||
|
|
||
| - List literals can contain expressions, mixed types, nested lists and maps. |
| **`edgeType()` was removed in v3.** Use `type(e)` to get an edge's type as a string. | ||
| </Warning> | ||
|
|
||
| Calling a function that does not exist fails with `Function 'X' does not exist`. Functions that are **not** available include `substring`, `split`, `replace`, `left`, `right`, `rand`, `date()`, `timestamp()`, `properties`, `keys` and `elementId`. Do that kind of post-processing in your application, and filter strings with `STARTS WITH`, `ENDS WITH` and `CONTAINS`. |
There was a problem hiding this comment.
Not yet available. Please use a more positive framing for turingdb
|
|
||
| - `datetime` values are stored as microseconds since the Unix epoch, **normalised to UTC**. A date-only string means midnight, and an offset (`+02:00`) is converted to the equivalent UTC instant. | ||
| - `duration` is a signed count of microseconds. It has no month component, because a month has no fixed length. `duration(<integer>)` reads the number as microseconds. | ||
| - Arithmetic works: `datetime ± duration`, `datetime − datetime`, `duration × number`. |
There was a problem hiding this comment.
Do not say arithmetic works, that should be a given. Show instead an example of arithmetic on durations
|
|
||
| The second query returns `0` for people who know nobody. A `WHERE` attached to an `OPTIONAL MATCH` belongs to the optional part, so rows are kept, as in Neo4j. | ||
|
|
||
| <Warning> |
There was a problem hiding this comment.
Remove this and file a linear ticket
|
|
||
| Property constraints in a pattern, `MATCH (n {name: 'Matt', age: 20})`, are exactly equivalent to the same equality tests in `WHERE`. | ||
|
|
||
| <Warning> |
|
|
||
| Keep each query to a few thousand rows at most. | ||
|
|
||
| ## Property indexes |
There was a problem hiding this comment.
Remove any talk of property indexes for now
| ## Key Points | ||
|
|
||
| 1. **Single CREATE Query**: Due to TuringDB's current Cypher limitations, the entire graph must be created in a single CREATE statement. | ||
| 1. **Single CREATE Query**: This tutorial builds the whole graph in one `CREATE` statement, which lets the edges refer to the nodes by variable. You can also create the nodes first, `COMMIT`, and then `MATCH` them to create the edges, see [Writing data](/query/writing). To import a GML file as-is, use [`LOAD GML`](/import_data/gml). |
There was a problem hiding this comment.
Do not advertise GML now. Use one single create statement or LOAD PARQUET or LOAD JSONL
- Reformat all query examples: one clause per line, continuation lines indented, blank lines between statements. - Aggregation: drop the WITH alias note and the list-literals point, explain filtering on an aggregate and mixing properties with aggregates (with examples), reword the parameters note. - Functions: positive "not yet available" wording, replace the arithmetic sentence with duration arithmetic examples. - Matching: remove the optional-node warning, reword the regex note. - Remove property index documentation for now. - Tutorial: point to LOAD PARQUET / LOAD JSONL instead of LOAD GML.
| | `-auth-on` | Require a bearer token on every request. The token is read from the `TURINGDB_AUTH_TOKEN` environment variable, and startup fails if it is not set | off | | ||
| | `-start-timeout <ms>` | Time to wait for daemon readiness | `500` | | ||
|
|
||
| Flags use a **single dash** (`-demon`, not `--demon`). |
| ``` | ||
|
|
||
| <Warning> | ||
| Each property name has a single type across the whole graph, so `SET n.age = 'old'` fails if `age` is an Integer. Labels cannot be changed after creation: `SET n:Label` is not supported. `SET n += {...}` and `SET n = {...}` are not supported either. See [Writing data](/query/writing#set-and-remove) for the full rules. |
There was a problem hiding this comment.
Just say that labels can not be changed in another sentence below the warning block. Do not detail what is not supported
| | `AS <row>` | Variable bound to each row | | ||
|
|
||
| All values come back as **strings** — wrap them in `toInteger(...)` / `toFloat(...)` if you need numbers. | ||
| All values come back as **strings**. Wrap them in `toInteger(...)` / `toFloat(...)` / `datetime(...)` if you need other types. Files must be comma-separated: `FIELDTERMINATOR` and the Neo4j form `LOAD CSV WITH HEADERS FROM 'file:///...'` are not supported. |
There was a problem hiding this comment.
Remove the second sentence starting by Fields must
| LIMIT 5 | ||
| ``` | ||
|
|
||
| Without `WITH HEADERS`, the header line comes back as an ordinary data row. A row cannot be returned whole: read its fields as `row.col` or `row[<literal index>]`. |
There was a problem hiding this comment.
Be nicer to TuringDB, remove the proposition saying that rows can not be returned and just start with row indexing
| Each spec is `{"<property name>", <dimension>}`. The dimension must be **greater than 1**, and every array under that property must have exactly that many numeric elements (otherwise the load fails). This works for both node and relationship properties. Array properties not listed in `WITH EMBEDDINGS` are stored as Lists. | ||
|
|
||
| <Note> | ||
| When an import fails, the details are written to `<turing-dir>/logs/turingdb.log`. |
There was a problem hiding this comment.
The details are written -> additional details are written
| # `LOAD PARQUET` | ||
|
|
||
| `LOAD PARQUET` queries may be used to load a graph into TuringDB. The command is of the following format: | ||
| `LOAD PARQUET` is the fastest way to get data into TuringDB, and the recommended path for anything beyond a few thousand entities. Above roughly 5,000 nodes or 1,000 edges, write your data to Parquet and load it with this command: 100,000 nodes and 200,000 edges load in about 0.2 seconds, whereas Cypher-driven loads (`LOAD CSV` or `UNWIND` with `MATCH` and `CREATE` for the edges) take minutes at a few thousand edges. |
There was a problem hiding this comment.
End this block of text at a few thousand entities
| - `INT64` | ||
| - `DOUBLE` | ||
| - `BYTE_ARRAY` with exactly 0 levels of nesting | ||
| `INT32` and `FLOAT` columns are rejected (for example `Node column was not of type INT64` or `Unsupported column type`), so cast them to `int64` / `float64` first. To load an `INT64` column of microseconds as a `Duration`, name it in `WITH DURATIONS [<column>]`. |
What
Brings the docs in line with TuringDB v3 (
turingdb3.0.x), mainly the query language, which now supports most of openCypher.Query language (
query/): the singlecypher_subsetpage is now a group of pages.cypher_subsetstays at its URL as the overview, so existing links keep working.MATCH,OPTIONAL MATCH,WHERE, joins, lookup by native node ID)WITH,UNION, aggregates,UNWIND, comprehensions,CASE)EXISTS,COUNT,CALL)CREATE,MERGE,SET,REMOVE,DELETE, changes, indexes)Other pages that contradicted v3
import_data/csv:LOAD CSVfollowed byMATCHworks nowimport_data/jsonl: ids may have gaps, arrays load as lists,WITH DATETIMES/WITH DURATIONSimport_data/gml: type-suffixed property namesimport_data/parquet:LOAD PARQUETtypes and recipegraph_dev/update_properties:CREATE ... SETworksgraph_dev/*: change workflow notes,db.history()detailsshortest-path:pathis a flat list of IDs, and returning other variables failsvector-search:score,TYPE HNSW, useidsdirectly,LOAD VECTORfile formatpythonsdk/reference,commands,troubles: native binary protocol, auth, embedded, errors, env vars, HTTP APIclaude-skill,index,tutorials/create_graphHow it was checked
cypherexample inquery/(248 statements) was run against a realturingdb 3.0.1server. The remaining failures are examples that need data or files the test graph lacks (stations,my_index, a CSV).LOAD CSV/JSONL/GML/Parquet, vector search,shortestPath, native protocol, auth, embedded) were probed against 3.0.1.toUpper,abs,round,sqrt,reduceand reading a key from a map bound byWITH/UNWINDall work).npm run buildpasses (42 pages), all internal links and anchors resolve, andscripts/test-slug.mjspasses.Please check
mainin turing-db/turingdb is ahead of 3.0.1 (e.g.substring,split,keys,propertiesare inquery/AST/FunctionDecls.cppbut are not available in 3.0.1). The docs say these are not available; they will need updating with the next release.cypher_subsetpage had sections (Procedures, Commands, Indexes, Extensions, UNWIND, ...) that moved to other pages, so external links to those#anchorswill land at the top of the overview.LOAD PARQUET, about 2.5 ms per node for aSETembedding loop) come from the skills repo and were not re-measured.data_dir="~/.turing"in the SDK docs was wrong: the embedded backend does not expand~and created a literal~directory. Examples now use an absolute path.