Skip to main content

Vortex-RDF for Python

PyPI

Python bindings for Vortex-RDF, a columnar RDF store format built on Vortex. Stores are opened lazily from .vortex files and queried in place, without loading the dataset into memory. The bindings are read-only (mutations are in the roadmap): build .vortex files with serialize_rdf (file → file), then open and query them; in-memory builds are not yet supported.

A separate vortex-rdflib package builds an rdflib integration on these bindings; see its own documentation.

Install

pip install vortex-rdf

Quick start

from vortex_rdf import VortexRdfStore, serialize_rdf

serialize_rdf("data.nt", "data.vortex", layout="dictionary")   # RDF file -> .vortex file
store = VortexRdfStore("data.vortex")                            # lazy open; layout auto-detected
store.count_quads(p="<http://xmlns.com/foaf/0.1/name>")          # match count, no terms materialized
store.get_quads(p="<http://xmlns.com/foaf/0.1/name>")            # [(s, p, o, g), ...]

Reading quads

Every read takes a pattern as the keyword arguments s, p, o, g; an omitted position is a wildcard. Terms cross the boundary as N-Triples strings (<iri>, _:b0, "lit"@en, "3"^^<http://www.w3.org/2001/XMLSchema#integer>); the graph of a quad in the default graph is the empty string, which is also how a pattern selects it. A malformed term raises ValueError; a failing store operation raises VortexRdfError.

len(store)                                                   # number of quads
store.layout()                                               # "dictionary" | "default" | "typed-object"
store.indexes()                                              # e.g. ["secondary-by-reference"]
store.count_quads(p="<http://xmlns.com/foaf/0.1/name>")      # int
store.get_quads(p="<http://xmlns.com/foaf/0.1/name>")        # [(s, p, o, g), ...]
store.match_columns(p="<http://xmlns.com/foaf/0.1/name>")    # (subjects, predicates, objects, graphs)

get_quads returns whole quads; match_columns returns the same rows transposed into four parallel columns, for callers that work a position at a time. Both are served from the term-code columns whenever the store can (Dictionary layout, resident dictionary) and from the matched quads otherwise; results are identical. On the code path a term that repeats down a column is one shared Python string, so a caller converting terms into its own representation can rely on the cached string it is handed.

Term codes (low-level)

For Dictionary-layout stores, match_codes returns the matched rows as four zero-copy u32 term-code columns — memoryview(col).cast("I") views the Rust memory directly — decodable through a term_dict() handle:

cols = store.match_codes(p="<http://xmlns.com/foaf/0.1/name>")  # (s, p, o, g) or None
dictionary = store.term_dict()                                    # TermDict or None
subjects = memoryview(cols[0]).cast("I")
dictionary.decode(subjects[0])                       # N-Triples string for that code
dictionary.decode_many(cols[0])                      # bulk-decode a whole column
dictionary.encode("<http://xmlns.com/foaf/0.1/name>")  # code for a term, or None

decode_many decodes a batch in one GIL-released call. Buffer-protocol inputs — a column straight from match_codes, an array("I", ...), a uint32 NumPy array — are read in a single bulk copy with no per-element int conversion; any sequence of ints works too. encode is the inverse of decode and tolerant of spelling: an IRI with or without angle brackets, a literal with an explicit xsd:string type or an upper-case language tag, and the default graph as "", default or [] all resolve to the stored form's code (a malformed term raises ValueError); encode_many does a batch. Both term_dict() and match_codes return None when the code path does not apply (a non-Dictionary layout, or an append tail). A dictionary left in the file by the residency budget is served by reading it on demand — TermDict.file_backed says so — with the same calls.

Consumers can join, count, and de-duplicate entirely in code space and decode each distinct term once, never materializing a term string for a row they discard. The handle and the columns carry the pieces a query layer pushes below a pattern:

d = store.term_dict()
lo, hi = d.prefix_range("<http://xmlns.com/foaf/0.1/")   # codes of one namespace: one range
literals = d.prefix_range('"')                            # codes of every literal
true_codes, unknown = d.filter_codes("num_lt", "42")      # codes a term predicate holds for
true_codes, unknown = d.filter_codes("lang_matches", "en")

# Narrow inside the store, before a row is gathered: a code set or range per position.
cols = store.match_codes(p=NAME, keep={"o": range(lo, hi)})
cols = store.match_codes(keep={"s": true_codes, "g": [0]}, limit=100, offset=20)
n = store.count_quads(p=NAME, keep={"o": true_codes}, limit=1)   # an existence test

# Many probes in one GIL-released call, answered in input order.
views = store.match_codes_many([(None, NAME, None, None), {"s": "<http://ex.org/bob>", "limit": 5}])
counts = store.count_quads_many([{"p": NAME, "keep": {"o": literals}}])

# Column kernels, order-preserving: first-seen distinct values, nested-loop joins.
s, p, o, g = cols
s.distinct(); o.value_counts()
left_idx, right_idx = o.join_indices(s)     # rows where o == s, as index pairs
o.take(left_idx)                            # gather a joined column

filter_codes(kind, arg) answers (true_codes, unknown_codes): the codes for which the predicate definitely holds, and the codes inside its domain the native layer leaves to the caller's own evaluator (an ill-typed number, a datatype it does not order). Codes outside the domain — non-literals, for the literal predicates — appear in neither. Kinds: is_literal, is_iri, is_blank, datatype <iri>, lang <tag>, lang_matches <range>, str_prefix <p> (strstarts(str(?v), p): a string-like literal's lexical form, an IRI, a blank node's label), and num_lt/num_le/num_gt/num_ge/num_eq/num_ne <number> (value comparison for well-formed numeric literals; different XSD datatypes order by their IRIs; a non-literal is False under = and the orderings and True under !=). keep takes a dict from position ("s", "p", "o", "g" or 0–3) to a code set (U32Column, u32 buffer or int sequence) or a code range (range with step 1, or (lo, hi)); limit/offset window the rows in base order, and a filtered file scan stops at the first block that fills the window. A probe of the *_many calls is an (s, p, o, g) tuple or a dict with keys s, p, o, g, keep, limit, offset; every probe is parsed before any is evaluated.

Build options

serialize_rdf(input_path, output_path, *, format=None, layout="dictionary", indexes=[])

Every option after the two paths is keyword-only. format is an RDF format name ("ntriples", "nquads", "turtle", "trig", "n3", "rdfxml", "jsonld", or the short aliases nt, nq, ttl, rdf, xml), detected from the input file extension when omitted. Opening auto-detects the layout and indexes — VortexRdfStore takes no layout argument; store.layout() and store.indexes() report the same names.

layout — how terms are encoded into columns. "dictionary" is the default in every vortex-rdf frontend (Python, JS and the CLI):

Value Notes
"dictionary" (default) Terms replaced by codes into a sorted term dictionary. Most compact and fastest to query; backs match_codes/term_dict
"default" All four terms as N-Triples strings
"typed-object" Object split into kind/value/datatype/language columns

indexes — secondary access paths, each costing extra space:

Value Notes
"secondary-by-reference" Sorted predicate/object columns plus row-id back-references, so predicate-only and object-only patterns use a binary search instead of a full scan
"secondary-by-copy" Two complete extra copies of the quad columns — one sorted by (p, o, s, g), one by (o, s, p, g) — giving predicate- and object-bound patterns (including predicate+object prefix lookups) the same sorted access path subjects have

Bytes & files

The default open is lazy and file-backed. VortexRdfStore(path, in_memory=True) loads the store into memory once, so each subsequent match skips the per-call file-scan pipeline.

For Dictionary-layout files the term dictionary is lifted into memory when its compressed size in the file fits the residency budget — 512 MiB by default, overridable process-wide with VORTEX_RDF_DICT_MAX_RESIDENT_BYTES. VortexRdfStore(path, max_resident_bytes=n) sets the budget for that open (the environment variable is ignored for it). A dictionary left file-backed is point-read through its chunk leaves: the string reads resolve each chunk's distinct codes with one dictionary scan, and term_dict() hands out a handle that reads the file on demand (TermDict.file_backed), so match_codes and the code path keep working.

Stores also round-trip through bytes: store.to_bytes() serializes to the native container (the same exchange format as the .vortex file, the CLI and the JS bindings), and VortexRdfStore.from_bytes(data) opens such a buffer — bytes or bytearray — as a fully in-memory store.

Development

Managed with uv; maturin runs under the hood as the build backend:

cd python
uv sync                      # creates .venv, builds + installs the extension
uv run pytest tests          # run the test suite
uv run maturin develop --uv  # fast rebuild while iterating on Rust code

Rust source changes are picked up by uv sync automatically (see [tool.uv] cache-keys in pyproject.toml). Without uv: python -m venv .venv && pip install maturin pytest && maturin develop && pytest tests.

Building from source (the sdist or a development build) additionally requires libclang: a transitive build dependency of the Vortex file engine (custom-labels, via vortex-io) generates C bindings with bindgen at compile time. It is preinstalled on most dev setups (Xcode, LLVM on Windows); on Linux install e.g. clang-devel (dnf) or libclang-dev (apt). Installing a published wheel needs none of this.

Benchmarks

bench/run.py measures these bindings against pyoxigraph, pycottas, rdflib and lightrdf on a file → store → query workload and writes bench/results.json for the dashboard's Python tab; bench/test_codspeed.py is the instrumented suite CodSpeed runs.

python3 python/bench/run.py                 # full run
BENCH_DIM=32 python3 python/bench/run.py    # quick pilot
uv run pytest bench/test_codspeed.py --codspeed

Harness design (per-library virtualenvs, dataset parity with js/bench/datasets.ts, unsupported cells where a library lacks the operation, matched-row counts cross-checked and any disagreement recorded in config.countWarnings) is documented in bench/run.py, bench/worker.py and bench/adapters.py. Configuration variables:

Var Default Meaning
BENCH_SIZE 1,048,576 rows (value shared with the Rust and JS suites)
BENCH_DIM unset optional cube shorthand, D³ rows; ignored if BENCH_SIZE is set
BENCH_GRAPHS_QUADS 8 named graphs the comparative bench asks for
MUT_BATCH 10000 quads per add/delete batch
BENCH_PYTHON 3.13 Python version the per-library virtualenvs are provisioned with
BENCH_SUBJ_RATIO / BENCH_OBJ_RATIO 0.1 / 0.5 distinct subjects / objects per row
BENCH_PREDICATES 32 distinct predicates
BENCH_GRAPHS 1 distinct named graphs in the generator; 1 means default graph only
BENCH_LITERAL_FRAC 0.4 fraction of objects that are literals
BENCH_SLOW_PHASE_MS 30000 a phase slower than this runs once, without warmup
PY_BENCH_QUERY_ITERS / PY_BENCH_QUERY_WARMUP 10 / 5 measured / warmup iterations per query
PY_BENCH_HEAVY_ITERS / PY_BENCH_FULL_SCAN_ITERS 3 / 3 iterations for the heavy and full-scan phases
CODSPEED_BENCH_DIM / CODSPEED_BENCH_DIM_QUADS 32 / 13 CodSpeed suite: D³ triples / D⁴ quads

License

MIT

Metadata

Release files for vortex-rdf 0.11.0

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

Source distribution (sdist)

Source distribution for vortex-rdf 0.11.0
File Size Uploaded
vortex_rdf-0.11.0.tar.gz 482.9 kB Details

Built distributions (wheels)

Table of built distributions (wheels) for vortex-rdf 0.11.0
File
vortex_rdf-0.11.0-cp311-abi3-win_amd64.whl CPython 3.11 abi3 Windows x86-64 Details
vortex_rdf-0.11.0-cp311-abi3-manylinux_2_28_x86_64.whl CPython 3.11 abi3 Linux glibc 2.28+ x86-64 Details
vortex_rdf-0.11.0-cp311-abi3-manylinux_2_28_aarch64.whl CPython 3.11 abi3 Linux glibc 2.28+ ARM64 Details
vortex_rdf-0.11.0-cp311-abi3-macosx_11_0_arm64.whl CPython 3.11 abi3 macOS 11.0+ ARM64 Details
vortex_rdf-0.11.0-cp311-abi3-macosx_10_12_x86_64.whl CPython 3.11 abi3 macOS 10.12+ x86-64 Details

Total release size: 62.5 MB

Release files / vortex_rdf-0.11.0.tar.gz

Download URL vortex_rdf-0.11.0.tar.gz
Size 482.9 kB
Tags Source
SHA-256 checksum
How to use checksums
7736b8694974ae891a42d63409603f7145478c9471e0ed1dc08ca0dda5ef78df
BLAKE2b-256 checksum
How to use checksums
dd6dfe7c0afc20f4cb8cb5cacf1b634fc31eda94f784c49c4a5ef3e9167ddb92
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 Oct 2, 2026.

Transparency log

Release files / vortex_rdf-0.11.0-cp311-abi3-win_amd64.whl

Download URL vortex_rdf-0.11.0-cp311-abi3-win_amd64.whl
Size 13.5 MB
Tags CPython 3.11 Windows x86-64 abi3
SHA-256 checksum
How to use checksums
54060f59378e1cded8caeec856d849cf7acbf2a1eecd252c559f3a749427a99b
BLAKE2b-256 checksum
How to use checksums
63b3f8535e71e9c8c3bee124927f75f065076480b65c45e43111a0c2d379c484
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 Oct 2, 2026.

Transparency log

Release files / vortex_rdf-0.11.0-cp311-abi3-manylinux_2_28_x86_64.whl

Download URL vortex_rdf-0.11.0-cp311-abi3-manylinux_2_28_x86_64.whl
Size 13.0 MB
Tags CPython 3.11 Linux glibc 2.28+ x86-64 abi3
SHA-256 checksum
How to use checksums
903047b7457e15a88cec1af0c52a19d3b94b45f75f2a693a2e2b9234c19ca462
BLAKE2b-256 checksum
How to use checksums
dfc662f01ba44dda6d0d46b1e4b207aee2bb63ca27096506134a1642d09fe3bd
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 Oct 2, 2026.

Transparency log

Release files / vortex_rdf-0.11.0-cp311-abi3-manylinux_2_28_aarch64.whl

Download URL vortex_rdf-0.11.0-cp311-abi3-manylinux_2_28_aarch64.whl
Size 12.1 MB
Tags CPython 3.11 Linux glibc 2.28+ ARM64 abi3
SHA-256 checksum
How to use checksums
7a841a3cf04add7f5af2e0dcb066f0b5fc80c416a1d8a28f169247cfc9f1ffca
BLAKE2b-256 checksum
How to use checksums
1bc86dbfedabb760552f70427f474961c08f594fddcf4b59e8797d3c9bbd8056
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 Oct 2, 2026.

Transparency log

Release files / vortex_rdf-0.11.0-cp311-abi3-macosx_11_0_arm64.whl

Download URL vortex_rdf-0.11.0-cp311-abi3-macosx_11_0_arm64.whl
Size 11.3 MB
Tags CPython 3.11 abi3 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
759aad368a314b42b7b253d899757d985ba4bff990ab9960faa8ce46a958c7e3
BLAKE2b-256 checksum
How to use checksums
8f2e21552f234808e8082ff1d5971d77c4353d8fec49fffac3807ba337a0f382
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 Oct 2, 2026.

Transparency log

Release files / vortex_rdf-0.11.0-cp311-abi3-macosx_10_12_x86_64.whl

Download URL vortex_rdf-0.11.0-cp311-abi3-macosx_10_12_x86_64.whl
Size 12.1 MB
Tags CPython 3.11 abi3 macOS 10.12+ x86-64
SHA-256 checksum
How to use checksums
07c57f08d0e45d12c89cf89617952e3e36be02e88881949e1f2f52f09632d9b0
BLAKE2b-256 checksum
How to use checksums
e6dededd5f49e14efd64974fc82dd8909296799796bbf3887f5dfb108213d7bd
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 Oct 2, 2026.

Transparency log

Release history Release notifications | RSS feed

0.12.1

6 release files

0.12.0

6 release files

This release

0.11.0 This release

6 release files

0.10.0

6 release files

0.5.0

6 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