@soulcraft/cor 3.0.0-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +16 -0
- package/README.md +328 -0
- package/dist/aggregation/NativeAggregationEngine.d.ts +118 -0
- package/dist/aggregation/NativeAggregationEngine.js +186 -0
- package/dist/cli.d.ts +12 -0
- package/dist/cli.js +236 -0
- package/dist/graph/GraphAccelerationAdapter.d.ts +153 -0
- package/dist/graph/GraphAccelerationAdapter.js +326 -0
- package/dist/graph/NativeGraphAdjacencyIndex.d.ts +286 -0
- package/dist/graph/NativeGraphAdjacencyIndex.js +944 -0
- package/dist/hnsw/AdaptiveDiskAnnModeSelector.d.ts +187 -0
- package/dist/hnsw/AdaptiveDiskAnnModeSelector.js +313 -0
- package/dist/hnsw/NativeDiskAnnWrapper.d.ts +489 -0
- package/dist/hnsw/NativeDiskAnnWrapper.js +1456 -0
- package/dist/index.d.ts +28 -0
- package/dist/index.js +29 -0
- package/dist/legacyLayoutGuard.d.ts +93 -0
- package/dist/legacyLayoutGuard.js +237 -0
- package/dist/license/constants.d.ts +30 -0
- package/dist/license/constants.js +30 -0
- package/dist/license/onlineValidator.d.ts +43 -0
- package/dist/license/onlineValidator.js +225 -0
- package/dist/license/types.d.ts +39 -0
- package/dist/license/types.js +5 -0
- package/dist/license.d.ts +64 -0
- package/dist/license.js +165 -0
- package/dist/migration/MigrationCoordinator.d.ts +171 -0
- package/dist/migration/MigrationCoordinator.js +249 -0
- package/dist/migration/indexEpochGuard.d.ts +163 -0
- package/dist/migration/indexEpochGuard.js +202 -0
- package/dist/native/NativeEmbeddingEngine.d.ts +79 -0
- package/dist/native/NativeEmbeddingEngine.js +318 -0
- package/dist/native/NativeRoaringBitmap32.d.ts +114 -0
- package/dist/native/NativeRoaringBitmap32.js +221 -0
- package/dist/native/ffi.d.ts +20 -0
- package/dist/native/ffi.js +48 -0
- package/dist/native/idMapperTestSupport.d.ts +67 -0
- package/dist/native/idMapperTestSupport.js +112 -0
- package/dist/native/index.d.ts +49 -0
- package/dist/native/index.js +87 -0
- package/dist/native/napi.d.ts +21 -0
- package/dist/native/napi.js +88 -0
- package/dist/native/types.d.ts +1298 -0
- package/dist/native/types.js +16 -0
- package/dist/plugin.d.ts +50 -0
- package/dist/plugin.js +388 -0
- package/dist/providerContracts.d.ts +277 -0
- package/dist/providerContracts.js +38 -0
- package/dist/resource/OsMemoryProbe.d.ts +175 -0
- package/dist/resource/OsMemoryProbe.js +206 -0
- package/dist/resource/ResourceManager.d.ts +491 -0
- package/dist/resource/ResourceManager.js +960 -0
- package/dist/utils/ColumnManifest.d.ts +97 -0
- package/dist/utils/ColumnManifest.js +129 -0
- package/dist/utils/NativeColumnStore.d.ts +284 -0
- package/dist/utils/NativeColumnStore.js +685 -0
- package/dist/utils/NativeMetadataIndex.d.ts +882 -0
- package/dist/utils/NativeMetadataIndex.js +2631 -0
- package/dist/utils/NativeUnifiedCache.d.ts +87 -0
- package/dist/utils/NativeUnifiedCache.js +273 -0
- package/dist/utils/binaryIdMapperFactory.d.ts +59 -0
- package/dist/utils/binaryIdMapperFactory.js +94 -0
- package/dist/utils/collation.d.ts +30 -0
- package/dist/utils/collation.js +40 -0
- package/dist/utils/columnStoreTypes.d.ts +161 -0
- package/dist/utils/columnStoreTypes.js +29 -0
- package/dist/utils/nativeBinaryEntityIdMapper.d.ts +211 -0
- package/dist/utils/nativeBinaryEntityIdMapper.js +381 -0
- package/dist/utils/nativeEntityIdMapper.d.ts +111 -0
- package/dist/utils/nativeEntityIdMapper.js +170 -0
- package/docs/ADR-002-diskann-100-percent-rust.md +294 -0
- package/docs/ADR-003-semantic-time-travel.md +100 -0
- package/docs/aggregation.md +251 -0
- package/docs/comparison.md +162 -0
- package/docs/deployment-limits.md +87 -0
- package/docs/diskann.md +184 -0
- package/docs/migration-3.0.md +396 -0
- package/docs/performance-budget.md +274 -0
- package/docs/performance.md +117 -0
- package/docs/scaling.md +439 -0
- package/docs/snapshot-safety.md +246 -0
- package/docs/u64-id-space.md +214 -0
- package/docs/verification-report.md +670 -0
- package/native/brainy-native.node +0 -0
- package/native/index.d.ts +3916 -0
- package/package.json +92 -0
- package/scripts/migrate-cortex-2x-to-3x.mjs +970 -0
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module utils/ColumnManifest
|
|
3
|
+
* @description Cor-local per-field manifest for the native column store.
|
|
4
|
+
*
|
|
5
|
+
* Replicates `@soulcraft/brainy`'s internal `ColumnManifest` (not a published
|
|
6
|
+
* export of brainy 7.24.0) so cor produces interchangeable manifest files.
|
|
7
|
+
* Stored as JSON at `_column_index/{field}/MANIFEST.json`; the manifest is the
|
|
8
|
+
* source of truth for which segments exist for a field. On startup the column
|
|
9
|
+
* store loads each field's manifest and discovers segments from it.
|
|
10
|
+
*
|
|
11
|
+
* Manifest JSON is written/read via the storage adapter's object-path
|
|
12
|
+
* primitives (`writeObjectToPath`/`readObjectFromPath`) — the same channel
|
|
13
|
+
* brainy uses for workspace-global metadata such as `_cow/`. Segment payloads
|
|
14
|
+
* themselves are NOT stored here; they are raw mmap-able binary blobs persisted
|
|
15
|
+
* separately by `NativeColumnStore`.
|
|
16
|
+
*/
|
|
17
|
+
import type { SegmentMeta } from './columnStoreTypes.js';
|
|
18
|
+
import { ValueType } from './columnStoreTypes.js';
|
|
19
|
+
/**
|
|
20
|
+
* @description The object-path JSON surface a manifest needs from storage.
|
|
21
|
+
* Present on every brainy filesystem/cloud adapter; the published
|
|
22
|
+
* `StorageAdapter` type omits it, so it is declared locally.
|
|
23
|
+
*/
|
|
24
|
+
interface ManifestStorage {
|
|
25
|
+
writeObjectToPath(path: string, value: unknown): Promise<void>;
|
|
26
|
+
readObjectFromPath(path: string): Promise<any>;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Manages a single field's segment manifest.
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* const manifest = new ColumnManifest('createdAt', '_column_index')
|
|
33
|
+
* await manifest.load(storage)
|
|
34
|
+
* const id = manifest.nextSegmentId()
|
|
35
|
+
* manifest.addSegment({ id, level: 0, count: 0, minValue: 0, maxValue: 0, file: 'L0-000001.cidx' })
|
|
36
|
+
* await manifest.save(storage)
|
|
37
|
+
*/
|
|
38
|
+
export declare class ColumnManifest {
|
|
39
|
+
/** Field this manifest covers. */
|
|
40
|
+
readonly fieldName: string;
|
|
41
|
+
/** Base storage path for the column index (e.g. `_column_index`). */
|
|
42
|
+
readonly basePath: string;
|
|
43
|
+
/** Manifest data — loaded from storage or initialized fresh. */
|
|
44
|
+
private data;
|
|
45
|
+
/**
|
|
46
|
+
* @param fieldName - The field name (e.g. `createdAt`, `name`, `score`).
|
|
47
|
+
* @param basePath - Base path for segment files (default `_column_index`).
|
|
48
|
+
*/
|
|
49
|
+
constructor(fieldName: string, basePath?: string);
|
|
50
|
+
/**
|
|
51
|
+
* @description Load the manifest from storage. If not found, stays fresh.
|
|
52
|
+
* @param storage - Storage adapter with object-path support.
|
|
53
|
+
*/
|
|
54
|
+
load(storage: ManifestStorage): Promise<void>;
|
|
55
|
+
/**
|
|
56
|
+
* @description Save the manifest to storage atomically, bumping the version.
|
|
57
|
+
* @param storage - Storage adapter with object-path support.
|
|
58
|
+
*/
|
|
59
|
+
save(storage: ManifestStorage): Promise<void>;
|
|
60
|
+
/**
|
|
61
|
+
* @description Register a new segment, keeping segments ordered by level then
|
|
62
|
+
* id for consistent iteration.
|
|
63
|
+
* @param meta - Segment metadata to record.
|
|
64
|
+
*/
|
|
65
|
+
addSegment(meta: SegmentMeta): void;
|
|
66
|
+
/**
|
|
67
|
+
* @description Allocate the next segment id (monotonically increasing).
|
|
68
|
+
* @returns The next available segment id.
|
|
69
|
+
*/
|
|
70
|
+
nextSegmentId(): number;
|
|
71
|
+
/**
|
|
72
|
+
* @description Roll back the most recent {@link nextSegmentId} allocation when
|
|
73
|
+
* the corresponding segment was not actually written, keeping the counter
|
|
74
|
+
* gap-free. Never goes below 1.
|
|
75
|
+
*/
|
|
76
|
+
rollbackSegmentId(): void;
|
|
77
|
+
/**
|
|
78
|
+
* @description All segments across all levels.
|
|
79
|
+
* @returns The segment list (ordered by level then id).
|
|
80
|
+
*/
|
|
81
|
+
getAllSegments(): SegmentMeta[];
|
|
82
|
+
/**
|
|
83
|
+
* @description Whether the manifest has no segments.
|
|
84
|
+
* @returns True if empty.
|
|
85
|
+
*/
|
|
86
|
+
isEmpty(): boolean;
|
|
87
|
+
/** Value type for this field (persisted in the manifest). */
|
|
88
|
+
get valueType(): ValueType;
|
|
89
|
+
set valueType(type: ValueType);
|
|
90
|
+
/**
|
|
91
|
+
* @description Storage path for this field's manifest JSON.
|
|
92
|
+
* @returns Path like `_column_index/createdAt/MANIFEST.json`.
|
|
93
|
+
*/
|
|
94
|
+
manifestPath(): string;
|
|
95
|
+
}
|
|
96
|
+
export {};
|
|
97
|
+
//# sourceMappingURL=ColumnManifest.d.ts.map
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module utils/ColumnManifest
|
|
3
|
+
* @description Cor-local per-field manifest for the native column store.
|
|
4
|
+
*
|
|
5
|
+
* Replicates `@soulcraft/brainy`'s internal `ColumnManifest` (not a published
|
|
6
|
+
* export of brainy 7.24.0) so cor produces interchangeable manifest files.
|
|
7
|
+
* Stored as JSON at `_column_index/{field}/MANIFEST.json`; the manifest is the
|
|
8
|
+
* source of truth for which segments exist for a field. On startup the column
|
|
9
|
+
* store loads each field's manifest and discovers segments from it.
|
|
10
|
+
*
|
|
11
|
+
* Manifest JSON is written/read via the storage adapter's object-path
|
|
12
|
+
* primitives (`writeObjectToPath`/`readObjectFromPath`) — the same channel
|
|
13
|
+
* brainy uses for workspace-global metadata such as `_cow/`. Segment payloads
|
|
14
|
+
* themselves are NOT stored here; they are raw mmap-able binary blobs persisted
|
|
15
|
+
* separately by `NativeColumnStore`.
|
|
16
|
+
*/
|
|
17
|
+
import { ValueType } from './columnStoreTypes.js';
|
|
18
|
+
/**
|
|
19
|
+
* Manages a single field's segment manifest.
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* const manifest = new ColumnManifest('createdAt', '_column_index')
|
|
23
|
+
* await manifest.load(storage)
|
|
24
|
+
* const id = manifest.nextSegmentId()
|
|
25
|
+
* manifest.addSegment({ id, level: 0, count: 0, minValue: 0, maxValue: 0, file: 'L0-000001.cidx' })
|
|
26
|
+
* await manifest.save(storage)
|
|
27
|
+
*/
|
|
28
|
+
export class ColumnManifest {
|
|
29
|
+
/** Field this manifest covers. */
|
|
30
|
+
fieldName;
|
|
31
|
+
/** Base storage path for the column index (e.g. `_column_index`). */
|
|
32
|
+
basePath;
|
|
33
|
+
/** Manifest data — loaded from storage or initialized fresh. */
|
|
34
|
+
data;
|
|
35
|
+
/**
|
|
36
|
+
* @param fieldName - The field name (e.g. `createdAt`, `name`, `score`).
|
|
37
|
+
* @param basePath - Base path for segment files (default `_column_index`).
|
|
38
|
+
*/
|
|
39
|
+
constructor(fieldName, basePath = '_column_index') {
|
|
40
|
+
this.fieldName = fieldName;
|
|
41
|
+
this.basePath = basePath;
|
|
42
|
+
this.data = {
|
|
43
|
+
version: 0,
|
|
44
|
+
field: fieldName,
|
|
45
|
+
valueType: ValueType.Number,
|
|
46
|
+
multiValue: false,
|
|
47
|
+
segments: [],
|
|
48
|
+
nextSegmentId: 1,
|
|
49
|
+
buildComplete: false
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* @description Load the manifest from storage. If not found, stays fresh.
|
|
54
|
+
* @param storage - Storage adapter with object-path support.
|
|
55
|
+
*/
|
|
56
|
+
async load(storage) {
|
|
57
|
+
try {
|
|
58
|
+
const data = await storage.readObjectFromPath(this.manifestPath());
|
|
59
|
+
if (data && data.field === this.fieldName) {
|
|
60
|
+
this.data = data;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
// Not found — start fresh (data is already initialized in the constructor).
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* @description Save the manifest to storage atomically, bumping the version.
|
|
69
|
+
* @param storage - Storage adapter with object-path support.
|
|
70
|
+
*/
|
|
71
|
+
async save(storage) {
|
|
72
|
+
this.data.version++;
|
|
73
|
+
await storage.writeObjectToPath(this.manifestPath(), this.data);
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* @description Register a new segment, keeping segments ordered by level then
|
|
77
|
+
* id for consistent iteration.
|
|
78
|
+
* @param meta - Segment metadata to record.
|
|
79
|
+
*/
|
|
80
|
+
addSegment(meta) {
|
|
81
|
+
this.data.segments.push(meta);
|
|
82
|
+
this.data.segments.sort((a, b) => (a.level !== b.level ? a.level - b.level : a.id - b.id));
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* @description Allocate the next segment id (monotonically increasing).
|
|
86
|
+
* @returns The next available segment id.
|
|
87
|
+
*/
|
|
88
|
+
nextSegmentId() {
|
|
89
|
+
return this.data.nextSegmentId++;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* @description Roll back the most recent {@link nextSegmentId} allocation when
|
|
93
|
+
* the corresponding segment was not actually written, keeping the counter
|
|
94
|
+
* gap-free. Never goes below 1.
|
|
95
|
+
*/
|
|
96
|
+
rollbackSegmentId() {
|
|
97
|
+
if (this.data.nextSegmentId > 1)
|
|
98
|
+
this.data.nextSegmentId--;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* @description All segments across all levels.
|
|
102
|
+
* @returns The segment list (ordered by level then id).
|
|
103
|
+
*/
|
|
104
|
+
getAllSegments() {
|
|
105
|
+
return this.data.segments;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* @description Whether the manifest has no segments.
|
|
109
|
+
* @returns True if empty.
|
|
110
|
+
*/
|
|
111
|
+
isEmpty() {
|
|
112
|
+
return this.data.segments.length === 0;
|
|
113
|
+
}
|
|
114
|
+
/** Value type for this field (persisted in the manifest). */
|
|
115
|
+
get valueType() {
|
|
116
|
+
return this.data.valueType;
|
|
117
|
+
}
|
|
118
|
+
set valueType(type) {
|
|
119
|
+
this.data.valueType = type;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* @description Storage path for this field's manifest JSON.
|
|
123
|
+
* @returns Path like `_column_index/createdAt/MANIFEST.json`.
|
|
124
|
+
*/
|
|
125
|
+
manifestPath() {
|
|
126
|
+
return `${this.basePath}/${this.fieldName}/MANIFEST.json`;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
//# sourceMappingURL=ColumnManifest.js.map
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module utils/NativeColumnStore
|
|
3
|
+
* @description Rust-accelerated implementation of brainy's `ColumnStoreProvider`.
|
|
4
|
+
*
|
|
5
|
+
* Brainy's unified column store keeps per-field sorted columns for filtering,
|
|
6
|
+
* range queries, and `orderBy` sorting at billion-entity scale. The TypeScript
|
|
7
|
+
* baseline ships in `@soulcraft/brainy`; cor registers this native version,
|
|
8
|
+
* which delegates every value-level operation to the Rust `NativeColumnStore`
|
|
9
|
+
* (`native/src/column_store`). The Rust engine owns the sorted-segment k-way
|
|
10
|
+
* merge, roaring-bitmap filters, and zero-copy mmap reads; this module owns the
|
|
11
|
+
* async storage bridge (segment persistence, manifests, deleted bitmaps) and
|
|
12
|
+
* the value typing/coercion that mirrors brainy exactly.
|
|
13
|
+
*
|
|
14
|
+
* Why cor needs this: cor's `MetadataIndexManager` does its own native
|
|
15
|
+
* metadata filtering, but brainy's *unfiltered* `find({ orderBy })` path calls
|
|
16
|
+
* `metadataIndex.columnStore.sortTopK(...)` directly. Without a `.columnStore`
|
|
17
|
+
* on cor's metadata index, that path throws. So the column store's real jobs
|
|
18
|
+
* here are POPULATE (as entities are indexed), PERSIST (segments + manifests),
|
|
19
|
+
* and SORT (`sortTopK`). `filter`/`rangeQuery`/`getFilterValues` are implemented
|
|
20
|
+
* for interface completeness and parity with the TS baseline.
|
|
21
|
+
*
|
|
22
|
+
* Storage is billion-scale and mmap-friendly: each flushed segment is written as
|
|
23
|
+
* a raw binary blob (NOT base64) via the storage adapter's `saveBinaryBlob`, and
|
|
24
|
+
* loaded zero-copy via `getBinaryBlobPath` + `loadSegmentFromPath` when a local
|
|
25
|
+
* path exists, falling back to `loadBinaryBlob` + `loadSegmentFromBuffer` for
|
|
26
|
+
* remote adapters with no local path. Manifests are small JSON files.
|
|
27
|
+
*
|
|
28
|
+
* Design decision: value typing/coercion is replicated from brainy's
|
|
29
|
+
* `ColumnStore.inferValueType`/`normalizeValue` so cor produces identical
|
|
30
|
+
* `.cidx` segment value types and ordering. See ADR-001 for the native string
|
|
31
|
+
* collation contract (code-point order, matching `compareCodePoints`).
|
|
32
|
+
*/
|
|
33
|
+
import type { StorageAdapter } from '@soulcraft/brainy';
|
|
34
|
+
import type { ColumnStoreProvider, EntityIdMapperLike } from './columnStoreTypes.js';
|
|
35
|
+
import { ValueType } from './columnStoreTypes.js';
|
|
36
|
+
import { RoaringBitmap32 } from '../native/NativeRoaringBitmap32.js';
|
|
37
|
+
/**
|
|
38
|
+
* @description The subset of cor's `MmapFileSystemStorage` that the column
|
|
39
|
+
* store needs for billion-scale raw segment persistence. cor builds against
|
|
40
|
+
* published `@soulcraft/brainy`, whose `StorageAdapter` type does not yet
|
|
41
|
+
* declare these binary-blob methods, so we narrow the adapter to this local
|
|
42
|
+
* interface at runtime. A storage that lacks `saveBinaryBlob` cannot persist
|
|
43
|
+
* mmap-able segments — that is a hard error, never a silent base64 fallback.
|
|
44
|
+
*/
|
|
45
|
+
export interface BinaryBlobStorage {
|
|
46
|
+
/** Persist a raw binary blob under a logical key. The adapter appends its own extension. */
|
|
47
|
+
saveBinaryBlob(key: string, data: Buffer): Promise<void>;
|
|
48
|
+
/** Load a previously persisted blob, or null if absent. */
|
|
49
|
+
loadBinaryBlob(key: string): Promise<Buffer | null>;
|
|
50
|
+
/** Delete a persisted blob. No-op if absent. */
|
|
51
|
+
deleteBinaryBlob(key: string): Promise<void>;
|
|
52
|
+
/** Filesystem path to a blob for zero-copy mmap, or null for non-local adapters. */
|
|
53
|
+
getBinaryBlobPath(key: string): string | null;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* NativeColumnStore — Rust-accelerated `ColumnStoreProvider`.
|
|
57
|
+
*
|
|
58
|
+
* Holds one native `NativeColumnStore` instance plus the TS-side state the Rust
|
|
59
|
+
* engine does not persist: per-field manifests, value types, deleted bitmaps,
|
|
60
|
+
* and the storage + id mapper handed in at `init`.
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* const store = new NativeColumnStore()
|
|
64
|
+
* await store.init(storage, idMapper)
|
|
65
|
+
* store.addEntity(42, { createdAt: Date.now(), name: 'alpha', score: 1.5 })
|
|
66
|
+
* await store.flush()
|
|
67
|
+
* const newest = await store.sortTopK('createdAt', 'desc', 10) // int ids
|
|
68
|
+
*/
|
|
69
|
+
export declare class NativeColumnStore implements ColumnStoreProvider {
|
|
70
|
+
/**
|
|
71
|
+
* The Rust engine. Owns tail buffers, segments, sort/filter/range. Mutable
|
|
72
|
+
* because {@link reset} replaces it with a fresh instance to discard all
|
|
73
|
+
* native state (the napi class exposes no in-place clear).
|
|
74
|
+
*/
|
|
75
|
+
private native;
|
|
76
|
+
/** Storage adapter narrowed to the binary-blob + object-path surface. */
|
|
77
|
+
private storage;
|
|
78
|
+
/** Per-field manifests tracking persisted segments. */
|
|
79
|
+
private readonly manifests;
|
|
80
|
+
/** Per-field value type, inferred from the first value seen (matches brainy). */
|
|
81
|
+
private readonly fieldTypes;
|
|
82
|
+
/**
|
|
83
|
+
* Per-field deleted-entity bitmaps. Entity int ids here are excluded from all
|
|
84
|
+
* native queries (the Rust engine also tracks them via `deleteEntity`); the
|
|
85
|
+
* TS copy is what gets persisted to `DELETED.bin` and re-applied on init.
|
|
86
|
+
*/
|
|
87
|
+
private readonly deletedEntities;
|
|
88
|
+
/**
|
|
89
|
+
* Create a native column store. Call {@link init} before queries that need
|
|
90
|
+
* storage (loading persisted segments). Writes work immediately — the Rust
|
|
91
|
+
* tail buffer holds them in memory until {@link flush}.
|
|
92
|
+
*/
|
|
93
|
+
constructor();
|
|
94
|
+
/**
|
|
95
|
+
* @description Initialize the store: narrow storage, discover persisted field
|
|
96
|
+
* manifests under `_column_index/`, load each field's segments into the Rust
|
|
97
|
+
* engine (zero-copy mmap when a local path exists), and re-apply persisted
|
|
98
|
+
* deleted bitmaps.
|
|
99
|
+
* @param storage - The brainy storage adapter (must support binary blobs).
|
|
100
|
+
* @param _idMapper - The shared entity id mapper (part of the provider
|
|
101
|
+
* contract). This native implementation returns int ids directly from
|
|
102
|
+
* sort/filter/range and lets cor's metadata index own the canonical
|
|
103
|
+
* UUID ↔ int mapping, so the mapper is accepted but not retained here.
|
|
104
|
+
* @throws If the storage adapter lacks the binary-blob primitives.
|
|
105
|
+
*/
|
|
106
|
+
init(storage: StorageAdapter, _idMapper: EntityIdMapperLike): Promise<void>;
|
|
107
|
+
/**
|
|
108
|
+
* @description Index an entity's field values into the Rust tail buffers.
|
|
109
|
+
* Synchronous, matching the provider contract. Value typing mirrors brainy:
|
|
110
|
+
* integer number → numeric (i64, rounded); non-integer number → float (f64);
|
|
111
|
+
* boolean → numeric 0/1; string → string. Arrays add one entry per element.
|
|
112
|
+
* `null`/`undefined` values are skipped.
|
|
113
|
+
* @param entityIntId - The entity's u32 id (from the shared id mapper).
|
|
114
|
+
* @param fields - Map of field name → value (or array of values).
|
|
115
|
+
*/
|
|
116
|
+
addEntity(entityIntId: number, fields: Record<string, unknown>): void;
|
|
117
|
+
/**
|
|
118
|
+
* @description Tombstone an entity across every known field. The Rust engine
|
|
119
|
+
* excludes it from queries immediately; the TS bitmap is persisted on flush so
|
|
120
|
+
* the tombstone survives a reopen.
|
|
121
|
+
* @param entityIntId - The entity's u32 id.
|
|
122
|
+
*/
|
|
123
|
+
removeEntity(entityIntId: number): void;
|
|
124
|
+
/**
|
|
125
|
+
* @description Point filter: entity int ids whose `field` equals `value`.
|
|
126
|
+
* Routes to the numeric/float/string Rust filter by the field's value type.
|
|
127
|
+
* @param field - Field to filter.
|
|
128
|
+
* @param value - Exact value to match.
|
|
129
|
+
* @returns Roaring bitmap of matching entity int ids.
|
|
130
|
+
*/
|
|
131
|
+
filter(field: string, value: unknown): Promise<RoaringBitmap32>;
|
|
132
|
+
/**
|
|
133
|
+
* @description Decode a native column-store filter/range result into a
|
|
134
|
+
* `RoaringBitmap32`. Cor 3.0 Piece E switched the native `filter*` /
|
|
135
|
+
* `rangeQuery*` output to a croaring `Treemap` (Roaring64) portable bitmap,
|
|
136
|
+
* whose container layout is NOT interchangeable with the Roaring32 `Bitmap`
|
|
137
|
+
* portable format — decoding the Treemap buffer via `RoaringBitmap32` yields
|
|
138
|
+
* an empty set (croaring rejects the foreign cookie and returns empty). We
|
|
139
|
+
* therefore decode through `NativeRoaringBitmap64` and narrow each entity int
|
|
140
|
+
* to a `number`, lossless for the column store's entity-int range (≤ u32::MAX
|
|
141
|
+
* in every shipping deployment) and consistent with {@link sortTopK}'s
|
|
142
|
+
* `Number()` narrowing. U64 brains past that range use brainy 8.0's
|
|
143
|
+
* u64-capable wrapper.
|
|
144
|
+
* @param buf - A Roaring64 (Treemap) portable buffer from the native engine.
|
|
145
|
+
* @returns The matching entity int ids as a `RoaringBitmap32`.
|
|
146
|
+
*/
|
|
147
|
+
private decodeFilterResult;
|
|
148
|
+
/**
|
|
149
|
+
* @description Range filter: entity int ids whose `field` is within
|
|
150
|
+
* `[min, max]` (inclusive). Open bounds fall back to the type's extreme so a
|
|
151
|
+
* one-sided range still works. Routes by the field's value type.
|
|
152
|
+
* @param field - Field to filter.
|
|
153
|
+
* @param min - Lower bound, or undefined for no lower bound.
|
|
154
|
+
* @param max - Upper bound, or undefined for no upper bound.
|
|
155
|
+
* @returns Roaring bitmap of matching entity int ids.
|
|
156
|
+
*/
|
|
157
|
+
rangeQuery(field: string, min?: unknown, max?: unknown): Promise<RoaringBitmap32>;
|
|
158
|
+
/**
|
|
159
|
+
* @description Sort top-K: the K entity int ids with the highest (`desc`) or
|
|
160
|
+
* lowest (`asc`) values in `field`, in sort order. This is the method brainy's
|
|
161
|
+
* unfiltered `orderBy` path calls. The Rust engine performs the k-way merge
|
|
162
|
+
* across sorted segments + the (per-query sorted) tail buffer and excludes
|
|
163
|
+
* tombstoned ids.
|
|
164
|
+
* @param field - Field to sort by.
|
|
165
|
+
* @param order - 'asc' or 'desc'.
|
|
166
|
+
* @param k - Maximum number of ids to return.
|
|
167
|
+
* @returns Sorted array of entity int ids.
|
|
168
|
+
*/
|
|
169
|
+
sortTopK(field: string, order: 'asc' | 'desc', k: number): Promise<number[]>;
|
|
170
|
+
/**
|
|
171
|
+
* @description Filtered sort top-K: {@link sortTopK} restricted to ids present
|
|
172
|
+
* in `filterBitmap`. Implemented as sort-then-intersect: the native sort is
|
|
173
|
+
* pulled deep enough that the post-filter still yields up to K ids, then the
|
|
174
|
+
* filter bitmap is applied in order.
|
|
175
|
+
* @param filterBitmap - Candidate entity int ids.
|
|
176
|
+
* @param field - Field to sort by.
|
|
177
|
+
* @param order - 'asc' or 'desc'.
|
|
178
|
+
* @param k - Maximum number of ids to return.
|
|
179
|
+
* @returns Sorted array of entity int ids, all present in `filterBitmap`.
|
|
180
|
+
*/
|
|
181
|
+
filteredSortTopK(filterBitmap: RoaringBitmap32, field: string, order: 'asc' | 'desc', k: number): Promise<number[]>;
|
|
182
|
+
/**
|
|
183
|
+
* @description All distinct values for a field, as strings in ascending order
|
|
184
|
+
* (numeric by value, string by code point), backed by the Rust engine's
|
|
185
|
+
* segment + tail scan.
|
|
186
|
+
* @param field - Field name.
|
|
187
|
+
* @returns Distinct values stringified the way brainy renders them.
|
|
188
|
+
*/
|
|
189
|
+
getFilterValues(field: string): Promise<string[]>;
|
|
190
|
+
/**
|
|
191
|
+
* @description Whether a field has any indexed data (a persisted segment or
|
|
192
|
+
* buffered tail entry) in the Rust engine.
|
|
193
|
+
* @param field - Field name.
|
|
194
|
+
* @returns True if the field is queryable.
|
|
195
|
+
*/
|
|
196
|
+
hasField(field: string): boolean;
|
|
197
|
+
/**
|
|
198
|
+
* @description Flush every field's tail buffer to a new L0 `.cidx` segment,
|
|
199
|
+
* persist the segment as a raw binary blob, update the manifest, and persist
|
|
200
|
+
* the deleted bitmap. No-op if {@link init} has not run (no storage to write).
|
|
201
|
+
*/
|
|
202
|
+
flush(): Promise<void>;
|
|
203
|
+
/**
|
|
204
|
+
* @description Flush and release all resources. Persists pending writes first.
|
|
205
|
+
*/
|
|
206
|
+
close(): Promise<void>;
|
|
207
|
+
/**
|
|
208
|
+
* @description Discard all column-store state — in-memory AND persisted — and
|
|
209
|
+
* start fresh. Called when the owning metadata index rebuilds or clears: a
|
|
210
|
+
* rebuild reassigns entity int ids from scratch, so any persisted `.cidx`
|
|
211
|
+
* segments (which encode the OLD ids) would point at the wrong entities. This
|
|
212
|
+
* drops the native engine (a fresh instance discards all tail buffers and
|
|
213
|
+
* loaded segments), clears the manifests/value-types/deleted bitmaps, and
|
|
214
|
+
* deletes every persisted file under `_column_index/`. The store remains
|
|
215
|
+
* initialized and ready: the rebuild's subsequent `addEntity` calls repopulate
|
|
216
|
+
* it consistently with the new id assignments.
|
|
217
|
+
*/
|
|
218
|
+
reset(): Promise<void>;
|
|
219
|
+
/**
|
|
220
|
+
* @description Add a single value to a field, inferring + recording the value
|
|
221
|
+
* type on first write and routing to the matching Rust tail buffer.
|
|
222
|
+
* @param field - Field name.
|
|
223
|
+
* @param value - A non-null primitive value.
|
|
224
|
+
* @param entityIntId - The entity's u32 id.
|
|
225
|
+
*/
|
|
226
|
+
private addValue;
|
|
227
|
+
/**
|
|
228
|
+
* @description Flush a single field: drain its Rust tail buffer to a `.cidx`
|
|
229
|
+
* segment buffer, persist it as a binary blob, register it in the manifest,
|
|
230
|
+
* then persist the manifest and (if any) the deleted bitmap.
|
|
231
|
+
* @param field - Field to flush.
|
|
232
|
+
*/
|
|
233
|
+
private flushField;
|
|
234
|
+
/**
|
|
235
|
+
* @description Load all of a field's persisted segments into the Rust engine.
|
|
236
|
+
* Used on init, where no segment buffer is held in hand, so each segment is
|
|
237
|
+
* loaded by mmap path or read from storage.
|
|
238
|
+
* @param field - Field name.
|
|
239
|
+
* @param manifest - The field's loaded manifest.
|
|
240
|
+
*/
|
|
241
|
+
private loadFieldSegments;
|
|
242
|
+
/**
|
|
243
|
+
* @description Load one persisted segment into the Rust engine, preferring
|
|
244
|
+
* zero-copy mmap (`loadSegmentFromPath`) when the adapter exposes a local
|
|
245
|
+
* path. Falls back to `fallbackBuffer` if provided (the just-flushed bytes),
|
|
246
|
+
* otherwise reads the blob from storage. Used both on init (reload all
|
|
247
|
+
* segments) and on flush (re-attach the segment a drained tail just produced).
|
|
248
|
+
* @param field - Field name.
|
|
249
|
+
* @param segId - Segment id.
|
|
250
|
+
* @param level - Segment compaction level.
|
|
251
|
+
* @param fallbackBuffer - Segment bytes already in memory (flush path), if any.
|
|
252
|
+
*/
|
|
253
|
+
private loadSegmentIntoEngine;
|
|
254
|
+
/**
|
|
255
|
+
* @description Load and re-apply a field's persisted deleted bitmap by calling
|
|
256
|
+
* `deleteEntity` for each id, so tombstones survive a reopen.
|
|
257
|
+
* @param field - Field name.
|
|
258
|
+
*/
|
|
259
|
+
private loadDeletedBitmap;
|
|
260
|
+
/**
|
|
261
|
+
* @description Coerce a JS value to the i64 numeric column representation,
|
|
262
|
+
* matching brainy's `normalizeValue` for `Number`/`Boolean`.
|
|
263
|
+
* @param value - The raw value.
|
|
264
|
+
* @param type - The target value type (`Number` or `Boolean`).
|
|
265
|
+
* @returns The integer value, or undefined if not coercible.
|
|
266
|
+
*/
|
|
267
|
+
private toNumeric;
|
|
268
|
+
/**
|
|
269
|
+
* @description Coerce a JS value to the f64 float column representation,
|
|
270
|
+
* matching brainy's `normalizeValue` for `Float`.
|
|
271
|
+
* @param value - The raw value.
|
|
272
|
+
* @returns The float value, or undefined if not coercible.
|
|
273
|
+
*/
|
|
274
|
+
private toFloat;
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* @description Infer the column `ValueType` from a JS value, identical to
|
|
278
|
+
* brainy's `ColumnStore.inferValueType`: boolean → Boolean; integer number →
|
|
279
|
+
* Number; non-integer number → Float; everything else → String.
|
|
280
|
+
* @param value - A non-null value.
|
|
281
|
+
* @returns The inferred value type.
|
|
282
|
+
*/
|
|
283
|
+
export declare function inferValueType(value: unknown): ValueType;
|
|
284
|
+
//# sourceMappingURL=NativeColumnStore.d.ts.map
|