Minimal ObjectBox LMDB reader toolkit
Project description
ob-dump-reader
Minimal ObjectBox LMDB reader toolkit for Python. Its core job: walk a
data.mdb file and hand you each stored object's raw FlatBuffers table
bytes, plus its entity id and object id — no per-field FlatBuffers or
schema knowledge for that part, just lmdb
(the py-lmdb bindings). It also
covers the handful of things flatc --python output can't: ToMany
relations (a separate LMDB structure, not a table field) and Flex/
ExternalPropertyType fields (flatc only knows the base FlatBuffers
type, not ObjectBox's semantic annotation on top) — see "Beyond flatc"
below.
Decoding those raw bytes into typed objects is left to the official
flatc --python compiler — not this package, and not ob-dump's own
C++ core either. This is a deliberate toolkit split, not a missing feature:
see the parent project's docs/BACKLOG.md
for the reasoning.
Workflow
# 1. Generate a schema JSON (entityId -> table name/shape) and a .fbs from
# your ObjectBox model, using the ob_dump CLI (see ../README.md):
ob_dump --schema objectbox-model.json -o schema.json
ob_dump --fbs objectbox-model.json -o schema.fbs
# 2. Generate typed Python classes with the *official* FlatBuffers compiler.
flatc --python -o models/ schema.fbs
import ob_dump_reader as ob
from models.Ammo import Ammo # from flatc --python, see step 2 above
AMMO_ENTITY_ID = 1 # from schema.json
def on_record(record: ob.ObRecord) -> None:
if record.entity_id == AMMO_ENTITY_ID:
ammo = Ammo.GetRootAs(record.data) # flatc-generated class decodes the bytes
print(f"{ammo.Name()}: {ammo.BcG1()}")
# ... insert into whatever your new database is.
ob.read_objectbox_records("/path/to/objectbox/dir", on_record)
Async
ob_dump_reader.aio mirrors the same API as async/await coroutines
(built on py-lmdb's own lmdb.aio
executor-based wrapper — LMDB itself has no native async I/O, so this
dispatches the same blocking calls to a thread executor rather than
avoiding them):
import ob_dump_reader.aio as ob
async def on_record(record: ob.ObRecord) -> None:
...
await ob.read_objectbox_records("/path/to/objectbox/dir", on_record)
Reads are in place, no copy
read_objectbox_records/read_objectbox_to_many_targets read
data.mdb/lock.mdb directly, with no temporary copy step — they open
the LMDB environment itself readonly=True (matching ob-dump's own C++
LmdbReader), so they never need a write-capable handle. This is exactly
what LMDB's MVCC design is for: any number of readers can safely run
alongside one concurrent writer, in any process, with zero risk to the
original data — even while a live ObjectBox process has the same store
open.
Beyond flatc: ToMany relations and Flex/ExternalPropertyType fields
ob_dump --schema lists each property's externalType and each entity's
relations (id, name, target entity) when present — use that to know
which of your fields need one of these:
import ob_dump_reader as ob
from ob_dump_reader.decode_helpers import (
decode_flex,
bytes_to_hex,
bytes_to_uuid_string,
try_parse_json_string,
)
# ToMany: not part of the FlatBuffers table at all, so flatc has no
# accessor for it — relation_id/source_object_id come from `ob_dump --schema`
# and the record you're looking at (record.object_id).
author_ids = ob.read_objectbox_to_many_targets(db_dir, relation_id, record.object_id)
# Flex: flatc gives you the raw bytes (a bytes/bytearray field) —
# decode with decode_flex.
value = decode_flex(ammo.SomeFlexField())
# ExternalPropertyType: flatc gives you the base type's plain value
# (a byte blob for Uuid/Int128/Decimal128/Bson, a string for
# Json/JavaScript/JsonToNative) — decode with the matching helper.
uuid = bytes_to_uuid_string(ammo.SomeUuidField())
blob = bytes_to_hex(ammo.SomeBsonField())
parsed = try_parse_json_string(ammo.SomeJsonField())
Every one of these mirrors ob-dump's own C++ decode (src/fb_decode.cpp)
exactly — same hex/UUID formatting, same JSON-parse-with-string-fallback
for JavaScript, same forward-only relation direction (see
docs/BACKLOG.md "ToMany relations" for why: the backward direction is an
auto-maintained index for ObjectBox's own query engine, not needed for a
one-directional dump).
Why this shape
The alternative — an FFI wrapper around ob-dump's C++ core — would mean
building/vendoring native C++ from a PyPI package for every platform. Not
needed here: py-lmdb already gives
a mature, actively-maintained Python binding to LMDB, and flatc --python
already gives an officially generated, correct decoder. This package is
only the small piece connecting the two — LMDB traversal and the
ObjectBox key format (docs/BACKLOG.md in the parent project) — and stays
that small on purpose.
Integrity & Licensing
ob-dump was developed as an independent implementation for reading data stored in the ObjectBox format. It adheres to a "Clean Room Design" approach regarding binary software:
- Purpose-Limited: Built solely to support data recovery and migration to another database, for projects whose own license is incompatible with
objectbox-c's (a closed-source binary — see "Why this exists" in the parent project'sdocs/BACKLOG.md). Not intended as, and not pursued as, a competing product to ObjectBox itself — no write support, no query engine, no ongoing-database use case, strictly a one-time read-only export path out of an existing store. - No Reverse Engineering: We have performed no decompilation, disassembly, or any other analysis of the closed-source
objectbox-cbinary. - Open Specification: Data parsing is based exclusively on public formats (LMDB and FlatBuffers) and the open-source code of the official schema generator (
objectbox_generator, licensed under Apache 2.0). - Model-Driven: The decoding process is driven by the user-provided
objectbox-model.jsonfile, which is an open, user-accessible schema definition.
This approach ensures full licensing integrity: ob-dump is an independent software project that contains no proprietary or misappropriated code, making it suitable for integration into projects with any licensing requirements.
License
MIT — see LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file ob_dump_reader-0.1.0a2.tar.gz.
File metadata
- Download URL: ob_dump_reader-0.1.0a2.tar.gz
- Upload date:
- Size: 32.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.26 {"installer":{"name":"uv","version":"0.11.26","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1d018e74e0e72948d298127f7a01d5a88374c0fde722a4d1b7bdba8436fb6720
|
|
| MD5 |
fe4dde4ab7bb06551aa1e12671834ac3
|
|
| BLAKE2b-256 |
0b77d102b79c04574e010d89fe6095185359fbfe72e394f41e83450352ae8de6
|
File details
Details for the file ob_dump_reader-0.1.0a2-py3-none-any.whl.
File metadata
- Download URL: ob_dump_reader-0.1.0a2-py3-none-any.whl
- Upload date:
- Size: 14.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.26 {"installer":{"name":"uv","version":"0.11.26","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
acebb31b0c7a8e5f77c9044588a03fc04a5747dbd8eb0c33b99e18d59f4096cb
|
|
| MD5 |
5e9ecf3b97369022064470786fa7eed7
|
|
| BLAKE2b-256 |
75471b687b5be35caee320ae4aac4d6b948cbcf7ea3627b15f6d1621554ba085
|