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
pip install namidb # corepip install 'namidb[pandas]' # + interop con DataFramepip install 'namidb[polars]' # + interop con PolarsWheels 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®ion=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)) # 1print(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 asyncioimport 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 Cypher | Tipo de Python |
|---|---|
Null | None |
Bool | bool |
Integer | int |
Float | float |
String | str |
Bytes | bytes |
Vector(Vec<f32>) | list[float] |
List | list |
Map | dict[str, ...] |
Date | datetime.date |
DateTime (UTC µs) | datetime.datetime UTC |
Node | {"_kind": "node", "id", "label", "properties"} |
Rel | {"_kind": "rel", "edge_type", "src", "dst", "properties"} |
Path | list[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 uuidimport 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 CASclient.flush() # memtable -> SSTs L0Salida 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.Tabledf = 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
pip install maturingit clone https://github.com/namidb/namidb.gitcd namidb/crates/namidb-pymaturin develop --release --extras test