Saltearse al contenido

Embedded (Python)

El paquete de Python pone todo el motor de NamiDB dentro de tu proceso. Es el mismo motor que corre en el daemon de Rust y en Cloud — lo que cambia es dónde viven los bytes y dónde se ejecuta el motor.

Cuándo usar el modo embedded

  • Notebooks y exploración. Abres un namespace, consultas, lo descartas.
  • Scripts y fixtures de CI. Grafos determinísticos desde memory://.
  • Aplicaciones de un solo proceso. Tú eres dueño del bucket y no quieres un salto de red.

Si tu app necesita un límite de red (múltiples procesos, un lenguaje que no sea Python o Rust, RBAC, autenticación), usa el servidor HTTP en su lugar.

Abrir un cliente

import namidb
client = namidb.Client(uri)

La URI selecciona el backend. Todos los backends hablan el mismo Cypher.

URIBackend
memory://<ns>Dentro del proceso, efímero
file:///abs/dir?ns=<ns>Sistema de archivos local
s3://<bucket>[/<prefix>]?ns=<ns>&region=<region>AWS S3
s3://<bucket>?ns=<ns>&endpoint=<url>MinIO, R2, Tigris, LocalStack
gs://<bucket>?ns=<ns>Google Cloud Storage
az://<account>/<container>?ns=<ns>Azure Blob Storage

Las credenciales vienen de las variables de entorno estándar de cada nube (AWS_ACCESS_KEY_ID, GOOGLE_APPLICATION_CREDENTIALS, etc.). NamiDB no introduce su propia superficie de credenciales en uso self-hosted. Consulta Backends de almacenamiento para la referencia por backend.

Ejecutar una consulta

client.cypher(query, params=None) es el punto de entrada. Parsea, planifica, ejecuta y devuelve un result set.

result = client.cypher(
"MATCH (p:Person) WHERE p.age >= $min RETURN p.name AS name, p.age AS age",
params={"min": 18},
)

Parámetros

Los parámetros se pasan como dict en params=. Se bindean en tiempo de ejecución y nunca se concatenan al string de la consulta.

client.cypher(
"CREATE (p:Person {name: $name, age: $age})",
params={"name": "Alice", "age": 30},
)

Formatos de resultado

El objeto resultado expone las mismas filas en varias formas:

result = client.cypher("MATCH (p:Person) RETURN p.name AS name, p.age AS age")
result.rows() # list[dict] — lo más simple para resultados chicos
result.to_pandas() # pandas.DataFrame
result.to_polars() # polars.DataFrame
result.to_arrow() # pyarrow.Table — zero-copy

Para result sets grandes, prefiere to_arrow() o to_polars() por encima de rows() — evitan el overhead de crear un objeto Python por fila.

Async (acypher)

El mismo paquete expone una superficie async para usar dentro de un runtime async (FastAPI, aiohttp, Trio, asyncio):

import asyncio
import namidb
async def main():
client = namidb.Client("s3://my-bucket?ns=prod&region=us-east-1")
result = await client.acypher(
"MATCH (p:Person) RETURN count(*) AS n"
)
print(result.rows())
asyncio.run(main())

Regla práctica: usa acypher cuando ya estás dentro de un event loop, usa cypher en cualquier otro caso.

Ejemplo end-to-end

import namidb
# El mismo código funciona contra cualquier backend; solo cambia la URI.
client = namidb.Client("file:///tmp/namidb?ns=demo")
# Carga en bulk con UNWIND.
client.cypher(
"UNWIND $people AS p CREATE (:Person {name: p.name, age: p.age})",
params={"people": [
{"name": "Alice", "age": 30},
{"name": "Bob", "age": 25},
{"name": "Carol", "age": 42},
]},
)
# Consulta y proyecta a un DataFrame de pandas.
df = client.cypher(
"MATCH (p:Person) "
"WHERE p.age >= $min "
"RETURN p.name AS name, p.age AS age "
"ORDER BY p.age DESC",
params={"min": 18},
).to_pandas()
print(df)

Cierra el script y ejecútalo otra vez — como la URI es file://, los datos siguen ahí. Abre otro proceso apuntando a la misma URI y verás el mismo grafo.

Por qué esto funciona sin coordinador

NamiDB usa escrituras condicionales sobre el bucket (o flock más rename atómico para file://) para hacer compare-and-swap sobre el manifest. No hay un servicio externo de locks, ni Raft, ni etcd. Dos procesos pueden competir por una escritura sobre el mismo namespace y exactamente uno gana; el que pierde ve una época vencida y reintenta o falla. Esto es lo que hace seguro ejecutar el modo embedded desde múltiples réplicas de tu servicio sin coordinación fuera del bucket.

Siguientes pasos