Saltearse al contenido

SDK de Python

namidb es el wrapper de Python sobre el storage + query engine de NamiDB. Respaldado por Rust vía pyo3 y construido con maturin.

Instalar

Ventana de terminal
pip install namidb # core
pip install 'namidb[pandas]' # + interop con DataFrame
pip install 'namidb[polars]' # + interop con Polars

Wheels abi3 pre-compilados para Python ≥ 3.9 en Linux (x86_64 + aarch64), macOS (arm64) y Windows (x86_64). En macOS Intel cae al sdist. pyarrow >= 14 es una dependencia transitiva obligatoria.

Abrir un namespace

import namidb as tg
client = tg.Client("s3://my-bucket?ns=prod&region=us-east-1")

Los seis esquemas de URI están soportados: memory://, file://, s3://, gs://, az://.

Cypher

Client.cypher(query, params=None) ejecuta una query y devuelve un QueryResult:

client.cypher("CREATE (a:Person {name: 'Alice', age: 30})")
client.cypher("CREATE (a:Person {name: 'Bob', age: 25})")
result = client.cypher(
"MATCH (p:Person) WHERE p.age > $min RETURN p.name AS name, p.age AS age",
params={"min": 26},
)
print(result.columns) # ['name', 'age']
print(len(result)) # 1
print(result.first()) # {'name': 'Alice', 'age': 30}
for row in result.rows():
print(row)

API async

La misma superficie está disponible como corrutina vía Client.acypher:

import asyncio
import namidb as tg
async def main() -> None:
client = tg.Client("memory://acme")
await client.acypher("CREATE (p:Person {name: 'Alice'})")
result = await client.acypher(
"MATCH (p:Person {name: $name}) RETURN p.name AS name",
params={"name": "Alice"},
)
print(result.rows())
asyncio.run(main())

Impulsado por el puente tokio de pyo3-async-runtimes — cada llamada corre sobre el mismo runtime multi-thread de tokio que respalda la API síncrona. Mezclar sync + async desde el mismo Client es seguro.

Mapeo de tipos (Cypher ↔ Python)

RuntimeValue de CypherTipo de Python
NullNone
Boolbool
Integerint
Floatfloat
Stringstr
Bytesbytes
Vector(Vec<f32>)list[float]
Listlist
Mapdict[str, ...]
Datedatetime.date
DateTime (UTC µs)datetime.datetime UTC
Node{"_kind": "node", "id", "label", "properties"}
Rel{"_kind": "rel", "edge_type", "src", "dst", "properties"}
Pathlist[Node|Rel] alternando

bool se chequea intencionalmente antes que int para que True / False no hagan round-trip como Integer(1) / Integer(0).

Inserts masivos

Para miles de filas, preferí merge_nodes / merge_edges:

import uuid
import namidb as tg
client = tg.Client("memory://acme")
client.merge_nodes(
"Person",
[{"id": str(uuid.uuid4()), "name": f"p{i}", "age": 20 + i} for i in range(10_000)],
)
client.merge_edges(
"KNOWS",
[
{"src": "uuid-a", "dst": "uuid-b", "since": 2020},
{"src": "uuid-b", "dst": "uuid-c", "since": 2021},
],
)
client.commit() # WAL + manifest CAS
client.flush() # memtable -> SSTs L0

Salida en Arrow / pandas / polars

result = client.cypher(
"MATCH (p:Person) RETURN p.name AS name, p.age AS age ORDER BY p.age DESC"
)
table = result.to_arrow() # pyarrow.Table
df = result.to_pandas() # pandas.DataFrame (requiere extra pandas)
pl_df = result.to_polars() # polars.DataFrame (requiere extra polars)

El orden de columnas sigue la proyección del RETURN del plan parseado, así que RETURN p.name AS name, p.age AS age siempre produce columnas ["name", "age"] incluso cuando matchean cero filas.

Scans por label sin el round-trip de Cypher:

table = client.scan_label_arrow("Person")

Cache stats

print(client.cache_stats())
# {"adjacency": {"hits": ..., "misses": ..., "bytes": ...}, ...}

Backends de almacenamiento

Ver Operación / Backends de almacenamiento para la matriz completa de credenciales.

Compilar desde el código fuente

Ventana de terminal
pip install maturin
git clone https://github.com/namidb/namidb.git
cd namidb/crates/namidb-py
maturin develop --release --extras test