Skip to main content

GestaltDB

Coverage Documentation

GestaltDB is a pure Python graph database toolkit for attributed graphs. It stores nodes, edges, labels, typed adjacency records, and property indexes on embedded key-value backends.

Documentation: https://mylonasc.github.io/gestaltdb/

Install From PyPI

With pip:

python -m pip install gestaltdb

With uv:

uv add gestaltdb

Install columnar ingestion dependencies:

python -m pip install "gestaltdb[arrow,polars]"

Install all optional backends and serializers:

python -m pip install "gestaltdb[all]"

Optional extras include lmdb, leveldb, rocksdb, arrow, polars, fast-ingest, msgpack, protobuf, bloom, docs, dev, and all.

Basic Example

from tempfile import TemporaryDirectory

from gestaltdb.graphdb import Edge, GraphDB, Node
from gestaltdb.kvstores import LevelDBStore
from gestaltdb.serializers import PickleSerializer

with TemporaryDirectory() as tmpdir:
    graph = GraphDB(LevelDBStore(path=f"{tmpdir}/graph"), PickleSerializer())

    graph.put_node(Node(node_id="alice", labels=["Person"], properties={"name": "Alice"}))
    graph.put_node(Node(node_id="bob", labels=["Person"], properties={"name": "Bob"}))
    graph.put_edge(Edge(
        edge_id="alice-knows-bob",
        source="alice",
        target="bob",
        properties={"type": "knows", "since": 2024},
    ))

    result = graph.query('MATCH (a:Person {name: "Alice"}) MATCH (a)-[:knows]->(b) RETURN a.id, b.name')
    print(result.records)

    graph.close()

Arrow Ingestion Example

This example ingests entity columns from PyArrow arrays. JSONSerializer lets GestaltDB build node and edge payloads from structured columns.

from tempfile import TemporaryDirectory

import pyarrow as pa

from gestaltdb.graphdb import GraphDB
from gestaltdb.kvstores import LevelDBStore
from gestaltdb.serializers import JSONSerializer
from gestaltdb import IndexMaintenanceMode

with TemporaryDirectory() as tmpdir:
    graph = GraphDB(LevelDBStore(path=f"{tmpdir}/graph"), JSONSerializer())

    graph.create_node_property_index("name")

    result = graph.ingest_arrow(
        pa.array(["alice", "bob", "carol"]),
        pa.array(["alice-knows-bob", "bob-knows-carol"]),
        pa.array(["alice", "bob"]),
        pa.array(["bob", "carol"]),
        pa.array(["knows", "knows"]),
        labels=pa.array([["Person"], ["Person"], ["Person"]]),
        node_properties={"name": pa.array(["Alice", "Bob", "Carol"]), "age": pa.array([34, 36, 29])},
        edge_properties={"since": pa.array([2024, 2025])},
        index_mode=IndexMaintenanceMode.DEFER_REBUILD,
    )
    print(result)  # {'nodes': 3, 'edges': 2, 'rebuilt_indexes': ..., 'stale_indexes': ()}

    result = graph.query('MATCH (a:Person {name: "Alice"}) MATCH (a)-[:knows]->(b) RETURN a.id, b.name')
    print(result.records)

    graph.close()

Polars Ingestion Example

This example ingests the same graph from Polars DataFrames. Property columns are converted into node and edge payloads during ingestion.

from tempfile import TemporaryDirectory

import polars as pl

from gestaltdb.graphdb import GraphDB
from gestaltdb.kvstores import LevelDBStore
from gestaltdb.serializers import JSONSerializer
from gestaltdb import IndexMaintenanceMode

nodes = pl.DataFrame({
    "node_id": ["alice", "bob", "carol"],
    "labels": [["Person"], ["Person"], ["Person"]],
    "name": ["Alice", "Bob", "Carol"],
    "age": [34, 36, 29],
})

edges = pl.DataFrame({
    "edge_id": ["alice-knows-bob", "bob-knows-carol"],
    "source": ["alice", "bob"],
    "target": ["bob", "carol"],
    "edge_type": ["knows", "knows"],
    "since": [2024, 2025],
})

with TemporaryDirectory() as tmpdir:
    graph = GraphDB(LevelDBStore(path=f"{tmpdir}/graph"), JSONSerializer())
    graph.create_node_property_index("name")

    graph.ingest_polars(
        nodes,
        edges,
        node_property_columns=["name", "age"],
        edge_property_columns=["since"],
        index_mode=IndexMaintenanceMode.DEFER_REBUILD,
    )

    result = graph.query('MATCH (a:Person) MATCH (a)-[:knows]->(b) RETURN a.name, b.name ORDER BY a.name')
    print(result.records)

    graph.close()

Install From A Checkout

From a local checkout:

uv sync

Install into another project:

uv add /path/to/gestaltdb

With pip:

python -m pip install /path/to/gestaltdb

Backend and Ingestion Recommendations

For the current library:

  • Use LevelDBStore for small local graphs, examples, and straightforward embedded use.
  • Use PyRexStore/RocksDB for large append-only loads and Arrow/Polars columnar ingestion.
  • Use LMDBStore when LMDB's storage model is desirable and you can size map_size ahead of loading.
  • Use JSONSerializer with GraphDB.ingest_polars or GraphDB.ingest_arrow when input data is already tabular and JSON-compatible.
  • Use pre-serialized node_value and edge_value columns when upstream data already has serializer-compatible payload bytes.
  • Keep IndexMaintenanceMode.MAINTAIN for incremental writes that need indexes ready immediately.
  • Use IndexMaintenanceMode.DEFER or DEFER_REBUILD for bulk loads when you want to move secondary-index work out of the write path.

Measured locally on 100k nodes and 500k edges, RocksDB native columnar ingestion was 1.16x faster end-to-end than LevelDB on the same Python JSON payload path, and Polars JSON payload construction was 1.86x faster than Python JSON serialization. Deferred indexing made the write phase 8.72x faster, but immediate full rebuild made total ingest-plus-rebuild 17.2% slower for that subset. Treat these as workload-specific guidance and benchmark your graph shape, serializer, and indexes.

Features

  • Attributed Node and Edge objects with stable IDs.
  • Native node labels and typed edge traversal through edge.properties["type"].
  • LMDB, LevelDB, and RocksDB/PyRex storage backends.
  • Pickle, JSON, MessagePack, and Protobuf serializers.
  • Label, relationship type, property, composite, and range indexes.
  • Read-only Cypher subset for indexed scans, typed traversal, filtering, ordering, limits, and chained MATCH clauses.
  • Bulk and columnar ingestion helpers for Arrow and Polars.
  • Typed path and subgraph sampling.

See the full documentation for backend selection, indexing, Cypher syntax, ingestion, sampling, and benchmarks.

Name origin

The name GestaltDB is inspired by Gestalt psychology and the idea that the whole is something more than its parts.

Metadata

Release files for gestaltdb 0.6.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for gestaltdb 0.6.1
File Size Uploaded
gestaltdb-0.6.1.tar.gz 99.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for gestaltdb 0.6.1
File Interpreter ABI Platform
gestaltdb-0.6.1-py3-none-any.whl Python 3 none any Details

Total release size: 185.2 kB

Release files / gestaltdb-0.6.1.tar.gz

Download URL gestaltdb-0.6.1.tar.gz
Size 99.8 kB
Tags Source
SHA-256 checksum
How to use checksums
87c1ca5fa7c871fc9c450b6a6657166e0fbb2b850028452f39d61e7396d962cb
BLAKE2b-256 checksum
How to use checksums
f8a8431c2947236644e6e7a787e9e6cb1078253a20bcfca1cae66388593a1d9a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 13, 2026.

Transparency log

Release files / gestaltdb-0.6.1-py3-none-any.whl

Download URL gestaltdb-0.6.1-py3-none-any.whl
Size 85.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
8868a30a92274aa86bc66d5f181768a073d9dd7ff0af3d65eaf1628d30ead8b1
BLAKE2b-256 checksum
How to use checksums
0469bf904f2aaba5af48307053ed260816db52deed450f9e9d1ff77f622ce5e7
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 13, 2026.

Transparency log

Release history Release notifications | RSS feed

0.11.0

2 release files

0.10.2

2 release files

0.9.0

2 release files

0.7.0

2 release files

0.6.2

2 release files

This release

0.6.1 This release

2 release files

0.6.0

2 release files

0.5.2

2 release files

0.5.1

2 release files

0.3.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page