feat(8.0): u64 BigInt graph provider contract — punch list a-d,g,h
GraphIndexProvider now speaks BigInt at the boundary (D.2 mirror): - getNeighbors/getVerbIdsBySource/getVerbIdsByTarget take entity ints and return entity/verb ints as bigint[] - new REQUIRED verbIntsToIds(bigint[]) batch reverse resolver (L.7 identity-fingerprint design — verb ids are UUIDs by contract, so the provider-side interning is losslessly reversible) - addVerb(verb, sourceInt, targetInt) returns the interned verb int; removeVerb(verbId) joins the contract Coordinator (brainy.ts) owns ALL UUID <-> int conversion: getOrAssign on writes, getInt on reads (unmapped UUID -> empty result without calling the provider), getUuid / verbIntsToIds on returns, plus a bounded ~100k-entry insertion-order warm cache for verb-int -> verb-id pairs fed by addVerb returns and resolver results. GraphVerb gains derived sourceInt/targetInt (populated at add time, never persisted). findConnectedSubtype gains a native fast path that routes single-type single-subtype outgoing BFS through the provider when available. JS GraphAdjacencyIndex satisfies the contract while staying string/u32-keyed internally: entity ints resolve through the shared entity-id mapper (threaded in by the coordinator on init/fork/checkout), verb ints come from an in-process append-only interning map re-derived from storage on rebuild/cold-start. ColumnStoreProvider widens the same way: addEntity/ removeEntity take bigint, sortTopK/filteredSortTopK return bigint[]. relate() now rejects a caller-supplied id with a teaching error — verb ids are brainy-generated UUIDs by contract in 8.0 (previously a passed id was silently ignored). No Roaring64 provider-boundary decode site exists yet; the JS-internal column store stays Roaring32 and the Treemap decoder lands with the first consumer of provider-returned filter buffers. Public brain API unchanged. 1413 tests green (+10 new BigInt contract tests).
This commit is contained in:
parent
62f6472fa0
commit
2427bb7960
17 changed files with 1164 additions and 328 deletions
|
|
@ -150,8 +150,20 @@ export interface MetadataIndexProvider {
|
|||
|
||||
getStats(): Promise<MetadataIndexStats>
|
||||
|
||||
/** The shared UUID ↔ int mapper. Brainy reads `.getUuid(intId)` off the result. */
|
||||
getIdMapper(): { getUuid(intId: number): string | undefined }
|
||||
/**
|
||||
* The shared UUID ↔ int mapper — the single source of truth for entity-int
|
||||
* resolution at the provider boundary. The coordinator (`brainy.ts`) resolves
|
||||
* UUID → int exactly once before every graph-index call (`getOrAssign` on
|
||||
* writes, `getInt` on reads — `undefined` means "never mapped", i.e. the
|
||||
* entity has no relations) and converts provider-returned ints back with
|
||||
* `getUuid`. Ints are u32 today (the `EntityIdSpaceExceeded` guard enforces
|
||||
* the ceiling on the JS path), so `Number(bigint)` narrowing is lossless.
|
||||
*/
|
||||
getIdMapper(): {
|
||||
getOrAssign(uuid: string): number
|
||||
getInt(uuid: string): number | undefined
|
||||
getUuid(intId: number): string | undefined
|
||||
}
|
||||
|
||||
/** The column store the coordinator delegates `where`/`orderBy` to. */
|
||||
readonly columnStore: import('./indexes/columnStore/types.js').ColumnStoreProvider
|
||||
|
|
@ -160,19 +172,75 @@ export interface MetadataIndexProvider {
|
|||
/**
|
||||
* The `'graphIndex'` provider — a drop-in for `GraphAdjacencyIndex`.
|
||||
* Brainy calls this surface via `this.graphIndex.*` (some optional-chained).
|
||||
*
|
||||
* **8.0 u64 contract — BigInt at the boundary.** Reads take entity ints
|
||||
* (from the metadata index's idMapper) and return entity/verb ints as
|
||||
* `bigint[]`. The coordinator owns ALL UUID ↔ int conversion: it resolves
|
||||
* UUIDs to ints once at the `brainy.ts` boundary (`getOrAssign` on writes,
|
||||
* `getInt` on reads, returning empty results for unmapped UUIDs without
|
||||
* calling the provider) and maps returned ints back (`getUuid` for entities,
|
||||
* {@link GraphIndexProvider.verbIntsToIds} for verbs). Implementations may
|
||||
* stay u32 internally — `Number(bigint)` narrowing is lossless under the
|
||||
* shipped `EntityIdSpaceExceeded` u32 guard — but must speak the bigint
|
||||
* contract at this surface.
|
||||
*/
|
||||
export interface GraphIndexProvider {
|
||||
/** `false` until the index has loaded; Brainy probes this before fast paths. */
|
||||
readonly isInitialized: boolean
|
||||
|
||||
/**
|
||||
* @description Entity ints reachable from `id` (1 hop), deduped.
|
||||
* @param id - The entity's interned int (from the shared idMapper).
|
||||
* @param options - Direction (`'both'` default) and limit/offset pagination.
|
||||
* @returns Neighbor entity ints. Empty when the entity has no edges.
|
||||
*/
|
||||
getNeighbors(
|
||||
id: string,
|
||||
optionsOrDirection?: { direction?: 'in' | 'out' | 'both'; limit?: number; offset?: number } | 'in' | 'out' | 'both'
|
||||
): Promise<string[]>
|
||||
getVerbIdsBySource(sourceId: string, options?: { limit?: number; offset?: number }): Promise<string[]>
|
||||
getVerbIdsByTarget(targetId: string, options?: { limit?: number; offset?: number }): Promise<string[]>
|
||||
id: bigint,
|
||||
options?: { direction?: 'in' | 'out' | 'both'; limit?: number; offset?: number }
|
||||
): Promise<bigint[]>
|
||||
/**
|
||||
* @description Verb ints for all edges originating at `sourceInt`.
|
||||
* @param sourceInt - The source entity's interned int.
|
||||
* @param options - Optional limit/offset pagination.
|
||||
* @returns Verb ints, resolvable via {@link GraphIndexProvider.verbIntsToIds}.
|
||||
*/
|
||||
getVerbIdsBySource(sourceInt: bigint, options?: { limit?: number; offset?: number }): Promise<bigint[]>
|
||||
/**
|
||||
* @description Verb ints for all edges pointing at `targetInt`.
|
||||
* @param targetInt - The target entity's interned int.
|
||||
* @param options - Optional limit/offset pagination.
|
||||
* @returns Verb ints, resolvable via {@link GraphIndexProvider.verbIntsToIds}.
|
||||
*/
|
||||
getVerbIdsByTarget(targetInt: bigint, options?: { limit?: number; offset?: number }): Promise<bigint[]>
|
||||
/**
|
||||
* @description Batch reverse resolver: verb ints → verb-id strings. REQUIRED —
|
||||
* the provider owns the durable verb-int interning (Brainy keeps only a
|
||||
* bounded in-memory warm cache fed by `addVerb` returns and this resolver;
|
||||
* pure optimization, no durability role).
|
||||
* @param verbInts - Verb ints as returned by the read methods.
|
||||
* @returns One entry per input, order-preserving; `null` for unknown ints.
|
||||
*/
|
||||
verbIntsToIds(verbInts: bigint[]): Promise<(string | null)[]>
|
||||
getVerbsBatchCached(verbIds: string[]): Promise<Map<string, GraphVerb>>
|
||||
|
||||
/**
|
||||
* @description Index one verb. The coordinator resolves both endpoint ints
|
||||
* via `idMapper.getOrAssign` and mirrors them onto `verb.sourceInt` /
|
||||
* `verb.targetInt` before the call.
|
||||
* @param verb - The verb to index (endpoint UUIDs + derived `sourceInt`/`targetInt`).
|
||||
* @param sourceInt - The source entity's interned int.
|
||||
* @param targetInt - The target entity's interned int.
|
||||
* @returns The interned verb int for `verb.id` (feeds Brainy's warm cache).
|
||||
*/
|
||||
addVerb(verb: GraphVerb, sourceInt: bigint, targetInt: bigint): Promise<bigint>
|
||||
/**
|
||||
* @description Remove one verb from the index by its id string. The verb's
|
||||
* interned int stays reserved (ints are never recycled within a generation).
|
||||
* @param verbId - The verb's UUID string.
|
||||
* @returns Resolves once the verb no longer appears in reads.
|
||||
*/
|
||||
removeVerb(verbId: string): Promise<void>
|
||||
|
||||
rebuild(): Promise<void>
|
||||
flush(): Promise<void>
|
||||
close(): Promise<void>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue