Rabosh

A store, its model and its indexes as one object with one lifecycle.

Rabosh.open(Path.of("data")).use { db ->
db.put(Key.of("user:1"), """{"name":"ada","team":"analytics"}""")
db.flush()

db.createIndex(IndexDefinition.inverted("$.team"))
db.query(Query.where(path("$.team") eq "analytics")).use { rows ->
while (rows.next()) println(rows.key)
}

println(db.schema().render())
db.indexCandidates().forEach(::println)
}

What this is for. Assembling the engine by hand means knowing four things in the right order, three of which no signature discloses: the observer has to be installed in the store's options before the store opens, because a flush can begin the moment it does; each layer then has to be attached, and two attachments scan every uncovered segment twice; and the index catalog has to be closed or its mappings stay live, which on Windows means files that can never be deleted. open and close are those four things, done once, in an order that is written down.

It is faster than the manual wiring in exactly one place, and that place is attach: the layers are attached without backfilling and then fed by a single DocumentStore.backfill through one CompositeSegmentObserver, so a segment neither layer covers is read once rather than once per layer. Everything else here is ergonomics.

What it is not. Not a server, not a connection pool, not a transaction manager beyond what WriteBatch already is, and not a second copy of the layers' API. The surface is deliberately narrow — writes, reads, queries, index management, the model — and everything else is reached through store, indexCatalog and catalog, which stay public and unwrapped. A facade that re-exported five modules would drift from them, and a query answered here must be the same query answered there: this class holds no planner, no matcher and no definition of what a predicate means. It delegates to QueryEngine, which is the only one.

Concurrency. The same contract the store makes: one writing thread, any number of reading threads. The cached planner statistics behind query are guarded, so concurrent queries are safe; the state of a query lives in its QueryCursor, as it does through the engine directly.

Types

Link copied to clipboard
object Companion

Properties

Link copied to clipboard

The schema catalog, or null when RaboshOptions.schema is false.

Link copied to clipboard

The directory this database owns.

Link copied to clipboard

The index catalog, or null when RaboshOptions.indexes is false.

Link copied to clipboard

The options it was opened with.

Link copied to clipboard

A snapshot of the store's current sizes.

Link copied to clipboard

The store underneath, unwrapped.

Functions

Link copied to clipboard
fun attach()

Builds derived data for segments that have none, in one pass over them.

Link copied to clipboard

Covers whatever the sidecars do not, on that same thread, returning at once.

Link copied to clipboard
open override fun close()

Closes the store, then the index catalog. Idempotent.

Link copied to clipboard
fun compact()

Flushes, then compacts until no level is over its budget. Returns when the tree is in shape.

Link copied to clipboard

Defines an index and builds it over everything already written.

Link copied to clipboard

Defines an index and builds it on a thread of the database's own, returning at once.

Link copied to clipboard
fun delete(key: Key)

Commits a deletion of key. Deleting an absent key is legal and writes a tombstone.

Link copied to clipboard
fun dropIndex(handle: IndexHandle)

Removes an index and deletes its posting files.

Link copied to clipboard
fun explain(query: Query, snapshot: Snapshot? = null): Explain

How query would be answered, and why.

Link copied to clipboard
fun flush()

Seals the active memtable and writes every sealed one out as a segment.

Link copied to clipboard
fun get(key: Key): Variant?

The current version of key, or null if it is absent or deleted.

fun get(key: Key, snapshot: Snapshot): Variant?

The version of key that snapshot sees, or null if it was absent or deleted then.

Link copied to clipboard
fun indexCandidates(options: IndexCandidateOptions = IndexCandidateOptions.DEFAULT): List<IndexCandidate>

The paths worth an index, best first.

Link copied to clipboard

The defined indexes, ascending by id.

Link copied to clipboard
fun keys(query: Query, snapshot: Snapshot? = null): List<Key>

The keys query matches, materialised. Convenient where the result is known to be small.

Link copied to clipboard
fun put(key: Key, document: Variant)

Commits document under key, replacing any current version.

fun put(key: Key, json: String)

Parses json and commits it under key.

Link copied to clipboard
fun query(query: Query, snapshot: Snapshot? = null): QueryCursor

Runs query and returns its rows, in key order.

Link copied to clipboard
fun rotate()

Seals the active memtable and starts a new log.

Link copied to clipboard
fun scan(from: Key? = null, to: Key? = null, snapshot: Snapshot? = null): DocumentCursor

An ordered walk over the documents in [from, to], both bounds inclusive and both optional.

Link copied to clipboard

The model of what is in the database: which paths exist, how often, with what types.

Link copied to clipboard

A fixed view of the database as it is now. Close it when done.

Link copied to clipboard
fun sync()

Forces every commit so far to stable storage.

Link copied to clipboard
open override fun toString(): String
Link copied to clipboard
fun write(batch: WriteBatch)

Commits batch as one record, atomically and as one view.