Metadata-Version: 2.4
Name: nomosdb
Version: 0.24.0
Summary: Python driver for NomosDB, the graph database: Bolt, transactions, cluster routing, asyncio
Author: DingBo
License-Expression: Apache-2.0
Project-URL: Homepage, https://github.com/cndingbo2030/nomosdb
Project-URL: Documentation, https://github.com/cndingbo2030/nomosdb/blob/main/docs/sdk/python.md
Project-URL: Source, https://github.com/cndingbo2030/nomosdb/tree/main/nomosdb-python
Project-URL: Changelog, https://github.com/cndingbo2030/nomosdb/blob/main/CHANGELOG.md
Project-URL: Issues, https://github.com/cndingbo2030/nomosdb/issues
Keywords: graph database,nomosdb,bolt,cypher,neo4j
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Framework :: AsyncIO
Classifier: Topic :: Database
Classifier: Topic :: Database :: Front-Ends
Classifier: Typing :: Typed
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Provides-Extra: async
Requires-Dist: aiohttp>=3.8.0; extra == "async"
Provides-Extra: pandas
Requires-Dist: pandas>=1.3.0; extra == "pandas"
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"
Requires-Dist: pytest-asyncio>=0.21; extra == "dev"
Requires-Dist: aiohttp>=3.8.0; extra == "dev"
Requires-Dist: neo4j>=5; extra == "dev"
Requires-Dist: build; extra == "dev"
Requires-Dist: twine; extra == "dev"
Dynamic: license-file

# nomosdb — the Python driver for NomosDB

The official Python driver for [NomosDB](https://github.com/cndingbo2030/nomosdb), a graph database you query in Cypher (NomosDB calls its dialect DQL).

- Native **Bolt 5.0–5.4**, the protocol the Neo4j drivers use.
- **Transactions** that commit or roll back as a whole.
- **Managed transactions** that are retried on conflicts and leader changes.
- **Cluster routing** with causal consistency.
- **asyncio**.
- Pure Python, no dependencies, Python 3.9+.

```bash
pip install nomosdb
```

## Quick start

```python
import nomosdb

driver = nomosdb.driver("bolt://localhost:7687", auth=("nomosdb", "secret"))

with driver.session() as session:
    # one statement, committed on its own; records stream in batches
    for record in session.run("MATCH (p:Person) WHERE p.age > $min RETURN p.name AS name", min=30):
        print(record["name"])

    # a managed transaction: on a transient failure the whole function runs again
    def transfer(tx, a, b, amount):
        tx.run("MATCH (x:Account {id: $a}) SET x.balance = x.balance - $n", a=a, n=amount)
        tx.run("MATCH (x:Account {id: $b}) SET x.balance = x.balance + $n", b=b, n=amount)

    session.execute_write(transfer, 1, 2, 10)

    # an explicit transaction
    with session.begin_transaction() as tx:
        tx.run("CREATE (:Person {name: $name})", name="Ann")
        tx.commit()   # leaving the block without commit() rolls back

    # bulk loading: one retried transaction per batch, the batch as $rows
    session.write_batch("UNWIND $rows AS r CREATE (:Person {name: r.name})",
                        ({"name": f"p{i}"} for i in range(100_000)), batch_size=5000)

driver.close()
```

### asyncio

```python
import asyncio
import nomosdb

async def main():
    async with nomosdb.async_driver("bolt://localhost:7687", auth=("nomosdb", "secret")) as driver:
        async with driver.session() as session:
            result = await session.run("MATCH (p:Person) RETURN p.name AS name")
            async for record in result:
                print(record["name"])

asyncio.run(main())
```

Every method of the blocking API exists in the async one under the same name. The async version adds `await`, `async with` and `async for`.

## URIs

| URI | Meaning |
|-----|---------|
| `bolt://host:7687` | One server. |
| `bolt+s://host:7687` | One server over TLS, with the certificate verified (system CAs, or `trusted_certificates="ca.pem"`). |
| `bolt+ssc://host:7687` | One server over TLS, accepting a self-signed certificate. |
| `nomosdb://host:7687` | A cluster. `neo4j://` is also accepted. |
| `nomosdb+s://`, `nomosdb+ssc://` | A cluster over TLS. |

With a cluster URI, the driver asks any member for the routing table. Writes go to the leader and reads are spread over the members. Each session passes its bookmark to the next transaction, so a read sees the session's earlier writes on whichever member serves it.

## Results

`session.run()` and `tx.run()` return a `Result`. You can iterate it, or call one of these methods:

- `single()`: the one record, or `None`.
- `data()`: a list of dicts.
- `value()`: the first column.
- `to_list()`: all records.
- `peek()`: the next record without consuming it.
- `consume()`: skips the remaining records and returns the `ResultSummary` (`counters.nodes_created` and the other counters, `query_type`, `bookmark`).

A `Record` reads by name or by position: `record["name"]`, `record[0]`, `record.data()`.

Nodes, relationships and paths come back as `nomosdb.graph.Node`, `Relationship` and `Path`. You read their properties like a dict, and they also have `labels`, `type`, `start_node`, `end_node` and `element_id`.

## Errors

| Exception | When |
|-----------|------|
| `ClientError` | The statement is wrong: syntax, a missing parameter, a constraint. `code` holds the server's `Neo.ClientError.*` code. |
| `TransientError` | A write conflict or a lock timeout. Managed transactions retry it. |
| `AuthError` | Wrong credentials, or a role that is too low. |
| `ServiceUnavailable`, `SessionExpired` | No server is reachable, or a connection broke. Managed transactions retry these. |
| `TransactionCommitUnknown` | The connection broke after COMMIT was sent, so the transaction may have committed. It is never retried automatically. |

## Driver options

`nomosdb.driver(uri, auth, **options)` accepts these options:

| Option | Default | Meaning |
|--------|---------|---------|
| `max_connection_pool_size` | 100 | Most connections kept per server. |
| `connection_timeout` | 30 s | Limit on TCP connect, TLS and Bolt handshake. |
| `connection_acquisition_timeout` | 60 s | How long to wait for a free pooled connection. |
| `max_transaction_retry_time` | 30 s | How long managed transactions keep retrying. |
| `fetch_size` | 1000 | Records per batch. |
| `trusted_certificates` | system CAs | CA file for `+s` URIs. |
| `user_agent` | `nomosdb-python/<version>` | Sent to the server. |

## The earlier client

The 0.1 API still works:

- `NomosDB(uri).query()` returns rows as dicts.
- The ORM (`@node_class`, `Model.objects.filter(...)`) works as before.

With a `bolt://` URI these use the driver above, so `db.session().begin_transaction()` is a real transaction.

With `http://`, each statement commits on its own, and `begin_transaction()` raises `TransactionError`. It no longer returns an object that only looked like a transaction.

## Development

```bash
cd nomosdb-python
python tools/unasync.py              # after editing nomosdb/_async/: regenerates nomosdb/_sync/
NOMOSDB_SERVER=../build/tools/nomosdb-server NOMOSDB_CERTS=../build/tools/nomosdb-certs python -m pytest tests
```

The tests start real `nomosdb-server` processes: a single server, a TLS server and a three-member cluster.

License: Apache-2.0.
