Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions claude-skill.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,12 @@ The skill is structured so Claude only loads the reference it needs:

| Module | Covers |
| --- | --- |
| `startup` | Installation, starting the server, connecting, loading a graph |
| `querying` | `MATCH`, `WHERE`, joins, built-in functions |
| `writing` | `CREATE`, `SET`, the change and commit workflow |
| `algorithms` | Shortest path, vector search |
| `introspection` | Schema exploration, Python SDK reference |
| `startup` | Installation, starting the server, connecting (HTTP, authentication, embedded), loading a graph |
| `querying` | `MATCH`, `OPTIONAL MATCH`, paths, `WITH`, aggregation, `UNWIND`, subqueries, built-in functions, `LOAD CSV` |
| `writing` | `CREATE`, `MERGE`, `SET`, `DELETE`, the change and commit workflow |
| `importing` | CSV, Parquet, JSONL, GML and Neo4j imports |
| `algorithms` | Shortest path, vector search, embeddings, GNN sampling |
| `introspection` | Schema exploration, versioning and time travel, Python SDK reference |

## Links

Expand Down
55 changes: 55 additions & 0 deletions commands.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ turingdb start [options]
| `-reset-default` | Delete the default graph before starting | off |
| `-ui` | Launch the built-in visualizer proxy | off |
| `-ui-port <port>` | Visualizer proxy port | `8080` |
| `-auth-on` | Require a bearer token on every request. The token is read from the `TURINGDB_AUTH_TOKEN` environment variable, and must be set when you use this flag | off |
| `-start-timeout <ms>` | Time to wait for daemon readiness | `500` |

### Examples
Expand Down Expand Up @@ -53,6 +54,34 @@ Load a graph at startup:
turingdb -load mygraph
```

Require authentication:

```bash
TURINGDB_AUTH_TOKEN=s3cret turingdb start -demon -auth-on
```

Serve the binary protocol instead of JSON (see [Environment variables](#environment-variables)):

```bash
USE_TURING_PROTO=1 turingdb start -demon
```

<Note>
Server logs are written to `<turing-dir>/logs/turingdb.log`. Additional details about an import, such as a `LOAD JSONL`, are written there.
</Note>

### Interactive shell

Without `-demon`, `turingdb start` serves HTTP **and** opens an interactive Cypher shell. Besides Cypher queries and [engine commands](/query/procedures#commands), the shell has a few local commands:

| Command | Description |
|---|---|
| `cd <graph>` | Switch the active graph |
| `checkout change-<id>` | Work on a change. `checkout <commit>` opens a past commit, and `checkout` returns to the latest commit of main |
| `read <file>` | Run the queries in a file |
| `help` | List the available commands |
| `quit` / `exit` | Leave the shell |

---

## UI
Expand Down Expand Up @@ -116,3 +145,29 @@ Stop an instance running in a custom directory:
```bash
turingdb stop -turing-dir /data/myturing
```

---

## Environment variables

| Variable | Read by | Effect |
|---|---|---|
| `USE_TURING_PROTO` | server | `1` makes the server speak TuringDB's **binary protocol** instead of JSON over HTTP. The server then serves the binary protocol only: connect with `TuringDB(type="native")`, and use a server in the default mode for `curl`, the JSON client and the visualizer. See [client backends](/pythonsdk/reference#client-backends) |
| `TURINGDB_AUTH_TOKEN` | server and Python SDK | With `-auth-on`, the token the server requires. The SDK sends it as a bearer token when set |
| `TURINGDB_TYPE` | Python SDK | Default client backend (`json`, `native` or `embedded`) when `type` is not passed |

---

## HTTP API

The Python SDK is a thin client over a single endpoint, so you can query a server from any language. With the default (JSON) server:

```bash
curl -s -X POST 'http://localhost:6666/query?graph=mygraph' -d "MATCH (n) RETURN count(n)"
```

- The request body is the query text. The optional query-string parameters `change=<hex change id>` and `commit=<hash>` target a change or a past commit.
- With `-auth-on`, send `Authorization: Bearer <token>`. A missing or incorrect token returns HTTP `401` with an empty body.
- A successful response is `{"header": {"column_names": [...], "column_types": [...]}, "data": [chunk, ...], "time": <ms>}`, where each chunk is a list of columns.
- An error response is `{"error": "PARSE_ERROR", "error_details": "...", "time": <ms>}`. The code is one of `PARSE_ERROR`, `ANALYZE_ERROR`, `PLAN_ERROR`, `EXEC_ERROR`, `CHANGE_NOT_FOUND` or `COMMIT_NOT_FOUND`.
- Queries on a non-HEAD commit need `LOAD COMMIT '<hash>'` first when you talk to the API directly. The CLI and the SDK do this for you.
7 changes: 7 additions & 0 deletions docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,13 @@
"group": "Query Language",
"pages": [
"query/cypher_subset",
"query/matching",
"query/paths",
"query/aggregation",
"query/subqueries",
"query/functions",
"query/writing",
"query/procedures",
"query/cheatsheet"
]
},
Expand Down
5 changes: 3 additions & 2 deletions graph_dev/create_change.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,11 @@ description: "Creating a change on a TuringDB graph is similar to creating a new
</Tab>
<Tab title="Python SDK">
```python
# Create a new change on the graph
# Create a new change on the graph.
# The client is switched onto the new change automatically.
change = client.new_change()

# Checkout into the change
# Optional: checkout into a change that already exists
client.checkout(change=change)
```

Expand Down
11 changes: 10 additions & 1 deletion graph_dev/create_nodes_edges.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -69,4 +69,13 @@ description: "Once you have a valid change, you can start adding new nodes and e
In the case you create the nodes then the edges, you need to commit your changes after node creation.
</Warning>
</Tab>
</Tabs>
</Tabs>

## Merge, delete and bulk loading

- Use `MERGE` instead of `CREATE` to avoid duplicates: `MERGE (n:Person {name: 'Jane'})`. Run `COMMIT` first when `MERGE` should find nodes created earlier in the same change.
- Remove data with `DELETE` (edges, or nodes without edges) and `DETACH DELETE` (a node and all its edges).
- Every node needs at least one label, and every edge exactly one edge type.
- For more than a few thousand nodes or edges, build the graph with [`LOAD PARQUET`](/import_data/parquet) instead of Cypher writes.

See [Writing data](/query/writing) for the full reference.
8 changes: 7 additions & 1 deletion graph_dev/submit_change.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,10 @@ description: "Submit changes done to the database (just like a commit being subm
client.checkout()
```
</Tab>
</Tabs>
</Tabs>

<Note>
`CHANGE SUBMIT` commits any pending writes itself, so a `COMMIT` right before it is optional. In the Python SDK, call `client.checkout()` afterwards to return to main: the client points at the submitted change until you do.
</Note>

If two changes edit the same node, the second submit reports a conflict, for example `This change attempted to update Node 0 (...) which has been modified on main.` The failed change stays open: fix it and submit again, or discard it with `CHANGE DELETE`. See [Writing data](/query/writing#failed-queries-and-conflicts).
8 changes: 8 additions & 0 deletions graph_dev/time_travelling.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,14 @@ description: "You can time travel in you graph databases and see the commit hist
</Tab>
</Tabs>

## About `db.history()`

- Commits are listed newest first, and the latest one is marked with a literal `(HEAD)` suffix on its hash: strip it before passing the hash to `checkout`.
- Every `COMMIT` and every submitted change adds an entry.
- `nodeCount` and `edgeCount` count what **that commit** added, not the graph totals.
- Use hashes exactly as printed (they are not zero-padded). An unknown hash returns `COMMIT_NOT_FOUND` from `client.checkout(commit=...)`, and zeros from `CALL db.describeCommit('<hash>')`.
- A past commit is read-only, so make writes on main or in a change. Call `client.checkout()` to return to main.

## Lazy Commit Loading

TuringDB lazily loads commits from disk. When a graph is loaded, only the **HEAD commit** (the latest commit on the current branch) is fully loaded into memory. All other commits are registered but their data remains on disk until explicitly requested.
Expand Down
40 changes: 36 additions & 4 deletions graph_dev/update_properties.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -84,12 +84,28 @@ The `SET` clause is used to update property values on existing nodes and edges.
SET n.isMinor = true
```

<Warning>
`CREATE ... SET` is not supported. When creating nodes or edges, use brace initialisation to set properties directly in the `CREATE` clause:
<Tip>
When creating nodes or edges, you can set properties directly in the `CREATE` clause with braces:
```jsx
CREATE (:Person {name: 'Alice', age: 30})
```
`CREATE ... SET` also works: `CREATE (n:Item {sku: 'A1'}) SET n.price = 9.99`.
</Tip>

## Clearing and removing properties

Set a property to `null` to clear its value, or use `REMOVE` to remove properties:

```jsx
MATCH (n:Person {name: 'Alice'}) SET n.age = null
MATCH (n:Person {name: 'Alice'}) REMOVE n.position, n.surname
```

<Warning>
Each property name has a single type across the whole graph, so `SET n.age = 'old'` is reported as a type incompatibility when `age` is an Integer. See [Writing data](/query/writing#set-and-remove) for the full rules.
</Warning>

Labels cannot be changed after creation.
</Tab>
<Tab title="Python SDK">
```python
Expand Down Expand Up @@ -172,11 +188,27 @@ The `SET` clause is used to update property values on existing nodes and edges.
""")
```

<Warning>
`CREATE ... SET` is not supported. When creating nodes or edges, use brace initialisation to set properties directly in the `CREATE` clause:
<Tip>
When creating nodes or edges, you can set properties directly in the `CREATE` clause with braces:
```python
client.query("CREATE (:Person {name: 'Alice', age: 30})")
```
`CREATE ... SET` also works: `client.query("CREATE (n:Item {sku: 'A1'}) SET n.price = 9.99")`.
</Tip>

## Clearing and removing properties

Set a property to `null` to clear its value, or use `REMOVE` to remove properties:

```python
client.query("MATCH (n:Person {name: 'Alice'}) SET n.age = null")
client.query("MATCH (n:Person {name: 'Alice'}) REMOVE n.position, n.surname")
```

<Warning>
Each property name has a single type across the whole graph, so `SET n.age = 'old'` is reported as a type incompatibility when `age` is an Integer. See [Writing data](/query/writing#set-and-remove) for the full rules.
</Warning>

Labels cannot be changed after creation.
</Tab>
</Tabs>
33 changes: 25 additions & 8 deletions import_data/csv.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ description: "Stream rows from a CSV file in a query with LOAD CSV"

`LOAD CSV` streams the rows of a CSV file into a query. Unlike `LOAD JSONL` / `LOAD GML` (which build a whole graph from a file), `LOAD CSV` is a **reading statement** that yields one row at a time, bound to a variable you can reference in `RETURN`, `MATCH`, or `CREATE`.

The file must live in the `data` subdirectory of the TuringDB working directory (set by `-turing-dir`, default `$HOME/.turing`).
The file must live in the `data` subdirectory of the TuringDB working directory (set by `-turing-dir`, default `$HOME/.turing`). Absolute paths are re-rooted inside that directory.

## Syntax

Expand All @@ -21,7 +21,7 @@ LOAD CSV "<file>" [WITH HEADERS] [ON ERROR SKIP | ON ERROR FAIL] AS <row>
| `ON ERROR FAIL` | Abort on the first malformed row (the default) |
| `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.

## With headers

Expand All @@ -38,19 +38,36 @@ RETURN row[0] AS name, row[1] AS age
LIMIT 5
```

Read a row's fields as `row.col` or `row[<literal index>]`. Without `WITH HEADERS`, the header line comes back as an ordinary data row.

## Combining with the rest of a query

`LOAD CSV` composes with `MATCH`, `WITH` / `WHERE`, aggregates, `ORDER BY` and `UNWIND`. For example, join the rows with nodes that are already in the graph:

```jsx
LOAD CSV "people.csv" WITH HEADERS AS row
MATCH (p:Person {name: row.name})
RETURN p.name, row.city
```

<Note>
To use `row.x` inside an `OPTIONAL MATCH` pattern that directly follows `LOAD CSV`, project the field first: `... AS row WITH row.name AS nm OPTIONAL MATCH (p:Person {name: nm}) ...`.
</Note>

## Building a graph from CSV

Because `LOAD CSV` yields rows, you can drive `CREATE` from it inside a change:

```python
change = client.new_change()
client.checkout(change=change)
client.new_change()
client.query('LOAD CSV "people.csv" WITH HEADERS AS row CREATE (:Person {name: row.name, age: toInteger(row.age)})')
client.query("COMMIT")
client.query("COMMIT") # make the new nodes visible to the next query
client.query("CHANGE SUBMIT")
client.checkout()
```

<Note>
`LOAD CSV` followed by `MATCH` in the same read statement is not yet supported.
</Note>
<Tip>
This approach suits small files and additions to an existing graph. Creating edges with `LOAD CSV` matches both endpoints for every row, so it is best kept to a few thousand edges. For bulk loads, convert the CSV to Parquet and use [`LOAD PARQUET`](/import_data/parquet), which loads 100,000 nodes and 200,000 edges in about 0.2 seconds.
</Tip>

To skip malformed rows, add `ON ERROR SKIP` (the default, `ON ERROR FAIL`, stops at the first one). See [Writing data](/query/writing#bulk-writes) for a single-pass `MERGE` pattern that needs no `COMMIT`.
10 changes: 9 additions & 1 deletion import_data/gml.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,20 @@ graph [
| `source` / `target` | Required. Must reference the `id` of a node defined in the same file. |
| other fields | Optional. Stored as edge properties. |

**Supported property value types:** String, Integer, Double, Boolean. All property values are stored as strings in the TuringDB graph regardless of their original type in the GML file.
**Property values:** all property values are stored as **strings** in the TuringDB graph, regardless of their original type in the GML file. Convert them with `toInteger` / `toFloat` in your queries.

<Warning>
All imported nodes are assigned the label `GMLNode` and all edges the type `GMLEdge`, regardless of any fields present in the file. Any field other than `id`, `source`, and `target` is treated as a plain property. To query your data after import, use `MATCH (n:GMLNode)` and `MATCH ()-[e:GMLEdge]->()`.
</Warning>

<Warning>
Property names also get a **type suffix**, for example `label (String)` and `weight (String)`. Quote them with backticks in queries, and run `CALL db.propertyTypes()` to see the exact names:

```jsx
MATCH (n:GMLNode) RETURN n.`label (String)`, toInteger(n.`age (String)`)
```
</Warning>

## Import steps

<Steps>
Expand Down
Loading
Loading