feat(8.0): full query surface at historical generations via ephemeral index materialization
Historical Db values (now()/asOf() pins that history has moved past) now serve the COMPLETE query surface - vector/hybrid search, graph traversal, cursor pagination, and aggregation - by materializing ephemeral in-memory indexes over the exact at-generation record set. The historical-query throw is gone; NotYetSupportedAtHistoricalGenerationError is deleted. Materializer (Brainy.materializeAtGeneration): - Copies the at-G record set (live bytes for ids untouched since the pin, immutable before-images otherwise) into a fresh MemoryStorage; a final reconciliation pass under the commit mutex makes the copy exact even when transactions commit mid-build. - Opens a read-only Brainy over the copy: init rebuilds the metadata and graph-adjacency indexes from the records; the vector index is built by inserting every at-G vector (the at-G HNSW graph never existed on disk, so there is nothing to restore). Host embedder and aggregate definitions are shared - no second model load, aggregates backfill at-G values. - Cost is the documented contract: O(n at G) time and memory, ONCE per Db (handle cached; freed by release(), with a FinalizationRegistry backstop that also closes leaked readers). A native VersionedIndexProvider serves the same reads from retained segments with no rebuild. Db routing (src/db/db.ts): metadata-level find()/related() keep the free record path; index-only dimensions (query/vector/near/connected/cursor/ aggregate/includeRelations/non-metadata modes) route to the cached materialization; unsupported where-operators on the record path re-route there too instead of erroring. Speculative with() overlays keep the one honest boundary - SpeculativeOverlayError (overlay entities carry no embeddings, so index reads over them would be silently incomplete); metadata find()/get()/filter related() work on overlays. UpdateParams.vector contract now honored: an explicit pre-computed vector applies directly (with dimension validation) in update() and transact update ops, re-indexing HNSW - previously it was silently ignored unless data also changed. GraphAdjacencyIndex: adjacency now derives from the two verb-id LSM trees filtered through the live-verb tombstone set (entity->entity edge trees deleted - they carried no verb ids, so removeVerb could never tombstone them and traversal served stale neighbors forever). Neighbor reads batch- load live verbs via the unified cache; addVerb seeds the cache. Proofs (tests/integration/db-mvcc.test.ts, 24 green): historical vector search finds old vector placement including since-deleted entities; historical graph traversal walks the old wiring after a rewire; historical aggregation computes at-G group values; asOf() pins get the same surface; the materialization builds once per Db and release() closes the ephemeral reader (it refuses reads afterwards); overlays throw the documented error. ADR-001 updated to the no-throws historical model.
This commit is contained in:
parent
8f93add705
commit
e5feae4104
11 changed files with 869 additions and 340 deletions
|
|
@ -23,9 +23,11 @@ import type { Entity, FindParams } from '../types/brainy.types.js'
|
|||
|
||||
/**
|
||||
* @description Thrown when a `where` clause uses an operator this in-memory
|
||||
* evaluator does not implement. Callers (historical `find()` on a `Db`)
|
||||
* convert this into the documented historical-query error rather than
|
||||
* returning silently-wrong results.
|
||||
* evaluator does not implement. Callers (historical/speculative `find()` on
|
||||
* a `Db`) never let it surface as silently-wrong results: at a historical
|
||||
* generation the query is rerouted through the at-generation index
|
||||
* materialization (which evaluates the full live operator surface); on a
|
||||
* speculative overlay it becomes a `SpeculativeOverlayError`.
|
||||
*/
|
||||
export class UnsupportedWhereOperatorError extends Error {
|
||||
/** The unrecognized operator name. */
|
||||
|
|
@ -253,8 +255,9 @@ export function whereMatches(entity: Entity, where: Record<string, unknown>): bo
|
|||
* entity. Used by historical and speculative `find()` to decide whether a
|
||||
* changed/overlaid entity belongs in the result set. The caller guarantees
|
||||
* the query carries no index-only dimensions (semantic `query`/`vector`,
|
||||
* `connected` traversal, …) — those are rejected with the documented
|
||||
* historical-query error before evaluation starts.
|
||||
* `connected` traversal, …) — those are routed to the at-generation index
|
||||
* materialization (historical) or rejected with `SpeculativeOverlayError`
|
||||
* (overlays) before evaluation starts.
|
||||
*
|
||||
* @param entity - The resolved entity.
|
||||
* @param params - The metadata-level find parameters.
|
||||
|
|
|
|||
Reference in a new issue