@nicia-ai/typegraph 0.28.1 → 0.30.0
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/dist/backend/postgres/index.cjs +24 -762
- package/dist/backend/postgres/index.cjs.map +1 -1
- package/dist/backend/postgres/index.d.cts +24 -5
- package/dist/backend/postgres/index.d.ts +24 -5
- package/dist/backend/postgres/index.js +11 -753
- package/dist/backend/postgres/index.js.map +1 -1
- package/dist/backend/postgres/pglite.cjs +52 -0
- package/dist/backend/postgres/pglite.cjs.map +1 -0
- package/dist/backend/postgres/pglite.d.cts +116 -0
- package/dist/backend/postgres/pglite.d.ts +116 -0
- package/dist/backend/postgres/pglite.js +50 -0
- package/dist/backend/postgres/pglite.js.map +1 -0
- package/dist/backend/sqlite/index.cjs +23 -27
- package/dist/backend/sqlite/index.d.cts +22 -15
- package/dist/backend/sqlite/index.d.ts +22 -15
- package/dist/backend/sqlite/index.js +10 -10
- package/dist/backend/sqlite/libsql.cjs +18 -14
- package/dist/backend/sqlite/libsql.cjs.map +1 -1
- package/dist/backend/sqlite/libsql.d.cts +3 -3
- package/dist/backend/sqlite/libsql.d.ts +3 -3
- package/dist/backend/sqlite/libsql.js +17 -13
- package/dist/backend/sqlite/libsql.js.map +1 -1
- package/dist/backend/sqlite/local.cjs +19 -17
- package/dist/backend/sqlite/local.cjs.map +1 -1
- package/dist/backend/sqlite/local.d.cts +9 -3
- package/dist/backend/sqlite/local.d.ts +9 -3
- package/dist/backend/sqlite/local.js +17 -15
- package/dist/backend/sqlite/local.js.map +1 -1
- package/dist/chunk-26HWIIVS.cjs +740 -0
- package/dist/chunk-26HWIIVS.cjs.map +1 -0
- package/dist/chunk-2QA5EHEN.js +181 -0
- package/dist/chunk-2QA5EHEN.js.map +1 -0
- package/dist/chunk-3IRMLJEK.js +206 -0
- package/dist/chunk-3IRMLJEK.js.map +1 -0
- package/dist/{chunk-5MTOKWDA.js → chunk-3JLRHQ67.js} +9 -9
- package/dist/{chunk-5MTOKWDA.js.map → chunk-3JLRHQ67.js.map} +1 -1
- package/dist/{chunk-WVQXAFP3.js → chunk-454I6GDF.js} +46 -5
- package/dist/chunk-454I6GDF.js.map +1 -0
- package/dist/{chunk-CTHNVU6K.cjs → chunk-4BXHVEFB.cjs} +16 -16
- package/dist/{chunk-CTHNVU6K.cjs.map → chunk-4BXHVEFB.cjs.map} +1 -1
- package/dist/{chunk-NMOE2ZEU.cjs → chunk-4LBO6KIF.cjs} +139 -128
- package/dist/chunk-4LBO6KIF.cjs.map +1 -0
- package/dist/chunk-633N7DSE.js +258 -0
- package/dist/chunk-633N7DSE.js.map +1 -0
- package/dist/{chunk-WFUN6DCM.cjs → chunk-7JGLPAFV.cjs} +11 -11
- package/dist/chunk-7JGLPAFV.cjs.map +1 -0
- package/dist/{chunk-ZTREJOGB.js → chunk-AI3RNREM.js} +140 -69
- package/dist/chunk-AI3RNREM.js.map +1 -0
- package/dist/{chunk-TSGBRPNE.js → chunk-ENDUY2CD.js} +46 -46
- package/dist/chunk-ENDUY2CD.js.map +1 -0
- package/dist/{chunk-7V335GHG.cjs → chunk-FLRCVI2G.cjs} +12 -2
- package/dist/chunk-FLRCVI2G.cjs.map +1 -0
- package/dist/chunk-FVIGJTS7.js +305 -0
- package/dist/chunk-FVIGJTS7.js.map +1 -0
- package/dist/chunk-FWBVPATK.cjs +208 -0
- package/dist/chunk-FWBVPATK.cjs.map +1 -0
- package/dist/chunk-IE6GN7L3.cjs +319 -0
- package/dist/chunk-IE6GN7L3.cjs.map +1 -0
- package/dist/{chunk-PGU2XKMA.js → chunk-IUIJJYPI.js} +117 -106
- package/dist/chunk-IUIJJYPI.js.map +1 -0
- package/dist/{chunk-E5ROA3MN.js → chunk-IX7RE7CG.js} +3 -3
- package/dist/{chunk-E5ROA3MN.js.map → chunk-IX7RE7CG.js.map} +1 -1
- package/dist/{chunk-YEDQLNVE.cjs → chunk-JIEJCTDS.cjs} +30 -2
- package/dist/chunk-JIEJCTDS.cjs.map +1 -0
- package/dist/{chunk-WZH2MKHW.cjs → chunk-K4N35E7B.cjs} +101 -101
- package/dist/{chunk-WZH2MKHW.cjs.map → chunk-K4N35E7B.cjs.map} +1 -1
- package/dist/{chunk-NZ5PMBTG.js → chunk-K5P5OZXS.js} +12 -3
- package/dist/chunk-K5P5OZXS.js.map +1 -0
- package/dist/chunk-K5VREXMZ.js +738 -0
- package/dist/chunk-K5VREXMZ.js.map +1 -0
- package/dist/chunk-LUHU4T2F.cjs +12 -0
- package/dist/{chunk-4FJVSF3K.cjs.map → chunk-LUHU4T2F.cjs.map} +1 -1
- package/dist/{chunk-O57GYGYK.js → chunk-M6OC455F.js} +32 -18
- package/dist/chunk-M6OC455F.js.map +1 -0
- package/dist/{chunk-OYLF3IC7.js → chunk-MAQJYNJR.js} +4 -4
- package/dist/chunk-MAQJYNJR.js.map +1 -0
- package/dist/{chunk-OOS2RKRH.js → chunk-MXE2U3EX.js} +4 -4
- package/dist/{chunk-OOS2RKRH.js.map → chunk-MXE2U3EX.js.map} +1 -1
- package/dist/{chunk-TKM2WDT4.js → chunk-N2OEOGBB.js} +386 -1093
- package/dist/chunk-N2OEOGBB.js.map +1 -0
- package/dist/{chunk-EJCDOA37.js → chunk-R4JOBEJU.js} +5 -5
- package/dist/{chunk-EJCDOA37.js.map → chunk-R4JOBEJU.js.map} +1 -1
- package/dist/{chunk-FFHW2JEK.cjs → chunk-RP7YITHX.cjs} +32 -17
- package/dist/chunk-RP7YITHX.cjs.map +1 -0
- package/dist/{chunk-PYJOBZR3.cjs → chunk-SGPXKJ4A.cjs} +60 -60
- package/dist/chunk-SGPXKJ4A.cjs.map +1 -0
- package/dist/{chunk-U5ROKVRF.js → chunk-TQOYM4LN.js} +29 -3
- package/dist/chunk-TQOYM4LN.js.map +1 -0
- package/dist/{chunk-PN37HVAV.cjs → chunk-UESTHN47.cjs} +153 -71
- package/dist/chunk-UESTHN47.cjs.map +1 -0
- package/dist/chunk-VFX3LC76.cjs +183 -0
- package/dist/chunk-VFX3LC76.cjs.map +1 -0
- package/dist/chunk-WEMZM7J7.cjs +270 -0
- package/dist/chunk-WEMZM7J7.cjs.map +1 -0
- package/dist/{chunk-NYQFPGYP.cjs → chunk-WFCECD6Q.cjs} +423 -1139
- package/dist/chunk-WFCECD6Q.cjs.map +1 -0
- package/dist/{chunk-EK6RJ373.cjs → chunk-YQGLVWB2.cjs} +7 -7
- package/dist/{chunk-EK6RJ373.cjs.map → chunk-YQGLVWB2.cjs.map} +1 -1
- package/dist/{chunk-X3244EKP.cjs → chunk-ZFWNO3GE.cjs} +48 -4
- package/dist/chunk-ZFWNO3GE.cjs.map +1 -0
- package/dist/ddl-BSFlbGQF.d.cts +38 -0
- package/dist/ddl-EdkIiFVU.d.ts +38 -0
- package/dist/{extension-types-B78CZfA5.d.cts → extension-types-CwHV-Uk4.d.cts} +4 -4
- package/dist/{extension-types-B78CZfA5.d.ts → extension-types-CwHV-Uk4.d.ts} +4 -4
- package/dist/graph-extension/index.cjs +19 -19
- package/dist/graph-extension/index.d.cts +4 -4
- package/dist/graph-extension/index.d.ts +4 -4
- package/dist/graph-extension/index.js +4 -4
- package/dist/{index-CWdBFWbI.d.ts → index-DzeIiMO3.d.ts} +30 -3
- package/dist/{index-Bilw0pYm.d.cts → index-Ln_KHTGA.d.cts} +30 -3
- package/dist/index.cjs +1542 -1011
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +142 -12
- package/dist/index.d.ts +142 -12
- package/dist/index.js +1138 -643
- package/dist/index.js.map +1 -1
- package/dist/indexes/index.cjs +19 -19
- package/dist/indexes/index.d.cts +9 -9
- package/dist/indexes/index.d.ts +9 -9
- package/dist/indexes/index.js +5 -5
- package/dist/interchange/index.cjs +8 -14
- package/dist/interchange/index.cjs.map +1 -1
- package/dist/interchange/index.d.cts +5 -16
- package/dist/interchange/index.d.ts +5 -16
- package/dist/interchange/index.js +3 -9
- package/dist/interchange/index.js.map +1 -1
- package/dist/{manager-ehLTTNq0.d.ts → manager-BbdyNhkv.d.ts} +2 -2
- package/dist/{manager-c1MGaVVR.d.cts → manager-wTDgjoXG.d.cts} +2 -2
- package/dist/{materialize-indexes-DFavFQah.d.ts → materialize-indexes-lkawaDor.d.ts} +1 -1
- package/dist/{materialize-indexes-BQfaAZKA.d.cts → materialize-indexes-oanE3RU0.d.cts} +1 -1
- package/dist/{ddl-0Ooc2dpL.d.ts → postgres-CK2ByP1q.d.cts} +2 -471
- package/dist/{ddl-Dza3kDrI.d.cts → postgres-ymXYC6Rr.d.ts} +2 -471
- package/dist/profiler/index.cjs +10 -10
- package/dist/profiler/index.d.cts +7 -7
- package/dist/profiler/index.d.ts +7 -7
- package/dist/profiler/index.js +3 -3
- package/dist/schema/index.cjs +33 -33
- package/dist/schema/index.d.cts +7 -7
- package/dist/schema/index.d.ts +7 -7
- package/dist/schema/index.js +10 -10
- package/dist/{sqlite-BxZUPbyD.d.ts → sqlite-DxH1lrwD.d.cts} +2 -468
- package/dist/{sqlite-mvpXjEeB.d.cts → sqlite-R8YGOqVQ.d.ts} +2 -468
- package/dist/{store-Ufc1Eoey.d.ts → store--z1Tmk7U.d.cts} +124 -5
- package/dist/{store-D7YEMg2v.d.cts → store-CNKM334w.d.ts} +124 -5
- package/dist/{types-CMQymcwk.d.cts → types-BBiDqJCU.d.ts} +252 -46
- package/dist/{types-B-sJJ4GQ.d.ts → types-CMvxx2kd.d.ts} +2 -2
- package/dist/{types-CmUA9sal.d.cts → types-D5B6Be5c.d.cts} +2 -2
- package/dist/{types-CQ7xXJMP.d.ts → types-dL-clxO3.d.cts} +252 -46
- package/dist/{where-CtrLu1Zf.d.ts → where-CAo6wsBZ.d.ts} +2 -2
- package/dist/{where-5zf15aFZ.d.cts → where-bJjuOCeV.d.cts} +2 -2
- package/package.json +20 -5
- package/dist/chunk-3EUKJEQ3.js +0 -77
- package/dist/chunk-3EUKJEQ3.js.map +0 -1
- package/dist/chunk-4FJVSF3K.cjs +0 -12
- package/dist/chunk-7V335GHG.cjs.map +0 -1
- package/dist/chunk-FFHW2JEK.cjs.map +0 -1
- package/dist/chunk-HTGDWIDE.cjs +0 -89
- package/dist/chunk-HTGDWIDE.cjs.map +0 -1
- package/dist/chunk-LYRUNG6E.cjs +0 -52
- package/dist/chunk-LYRUNG6E.cjs.map +0 -1
- package/dist/chunk-NMOE2ZEU.cjs.map +0 -1
- package/dist/chunk-NYQFPGYP.cjs.map +0 -1
- package/dist/chunk-NZ5PMBTG.js.map +0 -1
- package/dist/chunk-O57GYGYK.js.map +0 -1
- package/dist/chunk-OYLF3IC7.js.map +0 -1
- package/dist/chunk-PCHTTG42.js +0 -47
- package/dist/chunk-PCHTTG42.js.map +0 -1
- package/dist/chunk-PGU2XKMA.js.map +0 -1
- package/dist/chunk-PN37HVAV.cjs.map +0 -1
- package/dist/chunk-PYJOBZR3.cjs.map +0 -1
- package/dist/chunk-TKM2WDT4.js.map +0 -1
- package/dist/chunk-TSGBRPNE.js.map +0 -1
- package/dist/chunk-U5ROKVRF.js.map +0 -1
- package/dist/chunk-WFUN6DCM.cjs.map +0 -1
- package/dist/chunk-WVQXAFP3.js.map +0 -1
- package/dist/chunk-X3244EKP.cjs.map +0 -1
- package/dist/chunk-YEDQLNVE.cjs.map +0 -1
- package/dist/chunk-ZTREJOGB.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,25 +1,28 @@
|
|
|
1
|
-
|
|
2
|
-
import {
|
|
3
|
-
export {
|
|
4
|
-
import {
|
|
5
|
-
export {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
export {
|
|
9
|
-
import {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
import {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
1
|
+
export { libsqlVectorStrategy } from './chunk-3IRMLJEK.js';
|
|
2
|
+
import { quoteIdentifier, EMPTY_REQUIRED_COLUMNS, addRequiredColumn, markSelectiveFieldAsRequired, markFieldRefAsRequired, NODE_COLUMNS, shouldProjectColumn, mapSelectiveSystemFieldToColumn, isIdFieldRef, isAggregateExpr, EDGE_COLUMNS } from './chunk-FVIGJTS7.js';
|
|
3
|
+
export { pgvectorStrategy } from './chunk-FVIGJTS7.js';
|
|
4
|
+
import { META_EDGE_BRAND, loadAndMergeGraphExtensionDocument, ensureSchema, loadAndVerifyGraph, mergeGraphExtension, commitNewSchemaVersion, applyDeprecatedKinds, loadActiveSchemaWithBootstrap, parseSerializedSchema, computeClosuresFromOntology, createEmptyClosures, KindRegistry, extensionKindNames, serializeSchemaProperties, buildGraphExtensionOntologyKeySet, compileTimeOntologyKey, getTypeName, validateNodeProps, validateEdgeProps } from './chunk-3JLRHQ67.js';
|
|
5
|
+
export { broader, core, defineEdge, defineNode, differentFrom, disjointWith, equivalentTo, hasPart, implies, inverseOf, isMetaEdge, narrower, partOf, relatedTo, sameAs, subClassOf } from './chunk-3JLRHQ67.js';
|
|
6
|
+
import { nowIso, isKnownKind, validateOptionalIsoDate, getNodeKinds, getEdgeKinds } from './chunk-MAQJYNJR.js';
|
|
7
|
+
export { defineGraph, getEdgeKinds, getNodeKinds, isEdgeType, isEdgeTypeWithEndpoints, isGraphDef, isNodeType } from './chunk-MAQJYNJR.js';
|
|
8
|
+
export { defineGraphExtension } from './chunk-IX7RE7CG.js';
|
|
9
|
+
import { IncompatibleChangeError, filterReservedKeys, RESERVED_NODE_KEYS, RESERVED_EDGE_KEYS, RemoveCompileTimeKindError, KindHasReferentsError, validateProjectionField } from './chunk-ENDUY2CD.js';
|
|
10
|
+
export { CURRENT_GRAPH_EXTENSION_VERSION, GraphExtensionError, GraphExtensionUnresolvedEndpointError, GraphExtensionValidationError, GraphExtensionVersionUnsupportedError, IncompatibleChangeError, KindCollisionError, KindHasReferentsError, LEGACY_GRAPH_EXTENSION_VERSION, RemoveCompileTimeKindError, computeTransitiveClosure, invertClosure, isReachable, validateGraphExtension } from './chunk-ENDUY2CD.js';
|
|
11
|
+
export { sqliteVecStrategy } from './chunk-2QA5EHEN.js';
|
|
12
|
+
import { LEGACY_EMBEDDINGS_TABLE_NAME, runOptionallyInTransaction } from './chunk-633N7DSE.js';
|
|
13
|
+
import { isMissingTableError, sortedReplacer, canonicalEqual } from './chunk-M6OC455F.js';
|
|
14
|
+
import { quotedTableName, getDialect, assertVectorMinScore, compileNodeIndexFieldKeys, compileIndexWhere, generateIndexDDL, vectorScoreExpression, vectorMinScoreCondition } from './chunk-AI3RNREM.js';
|
|
15
|
+
export { andWhere, buildVectorCapabilities, fts5Strategy, notWhere, orWhere, tsvectorStrategy } from './chunk-AI3RNREM.js';
|
|
16
|
+
import { MAX_PG_IDENTIFIER_LENGTH, DEFAULT_PAGINATION_LIMIT, DEFAULT_STREAM_BATCH_SIZE, NODE_META_KEYS, EDGE_META_KEYS, sha256Hex, SQLITE_PATH_DELIMITER, PG_ARRAY_START, PG_ARRAY_END, PG_PATH_ELEMENT_SEPARATOR, fnv1aBase36 } from './chunk-R4JOBEJU.js';
|
|
17
|
+
export { defineEdgeIndex, defineNodeIndex } from './chunk-R4JOBEJU.js';
|
|
18
|
+
import { createSchemaIntrospector, resolveFieldTypeInfoAtJsonPointer, getSearchableMetadata, DEFAULT_SEARCHABLE_LANGUAGE } from './chunk-MXE2U3EX.js';
|
|
19
|
+
export { DEFAULT_SEARCHABLE_LANGUAGE, getSearchableMetadata, isSearchableSchema, searchable } from './chunk-MXE2U3EX.js';
|
|
20
|
+
import { DEFAULT_EMBEDDING_INDEX_TYPE, DEFAULT_EMBEDDING_METRIC, resolveEmbeddingFields } from './chunk-454I6GDF.js';
|
|
21
|
+
export { embedding, getEmbeddingDimensions, isEmbeddingSchema } from './chunk-454I6GDF.js';
|
|
22
|
+
import { ConfigurationError, EmbeddingDimensionChangedError, UnsupportedPredicateError, ValidationError, KindNotFoundError, EndpointError, EagerMaterializationError, CompilerInvariantError, DatabaseOperationError, EdgeNotFoundError, NodeIndexNotFoundError, NodeConstraintNotFoundError, RestrictedDeleteError, NodeNotFoundError, EndpointNotFoundError, UniquenessError, CardinalityError, DisjointError } from './chunk-TQOYM4LN.js';
|
|
23
|
+
export { BackendDisposedError, CardinalityError, CompilerInvariantError, ConfigurationError, DatabaseOperationError, DisjointError, EagerMaterializationError, EdgeNotFoundError, EmbeddingDimensionChangedError, EndpointError, EndpointNotFoundError, KindNotFoundError, MigrationError, NodeConstraintNotFoundError, NodeIndexNotFoundError, NodeNotFoundError, RestrictedDeleteError, SchemaContentConflictError, SchemaMismatchError, StaleVersionError, StoreNotInitializedError, TypeGraphError, UniquenessError, UnsupportedPredicateError, ValidationError, VersionConflictError, getErrorSuggestion, isConstraintError, isSystemError, isTypeGraphError, isUserRecoverable } from './chunk-TQOYM4LN.js';
|
|
24
|
+
import { jsonPointer, parseJsonPointer, resolveJsonPointer, normalizeJsonPointer, joinJsonPointers } from './chunk-K5P5OZXS.js';
|
|
25
|
+
export { MAX_JSON_POINTER_DEPTH, joinJsonPointers, jsonPointer, normalizeJsonPointer, parseJsonPointer } from './chunk-K5P5OZXS.js';
|
|
23
26
|
import { z } from 'zod';
|
|
24
27
|
import { sql, Placeholder } from 'drizzle-orm';
|
|
25
28
|
import { nanoid } from 'nanoid';
|
|
@@ -67,6 +70,184 @@ function getExternalRefTable(schema) {
|
|
|
67
70
|
function createExternalRef(table) {
|
|
68
71
|
return (id) => ({ table, id });
|
|
69
72
|
}
|
|
73
|
+
var DEFAULT_BATCH_SIZE = 500;
|
|
74
|
+
var DEFAULT_SLOT_METRIC = DEFAULT_EMBEDDING_METRIC;
|
|
75
|
+
var DEFAULT_SLOT_INDEX_TYPE = DEFAULT_EMBEDDING_INDEX_TYPE;
|
|
76
|
+
async function migrateLegacyEmbeddings(options) {
|
|
77
|
+
const {
|
|
78
|
+
backend,
|
|
79
|
+
graphId,
|
|
80
|
+
resolveSlotConfig,
|
|
81
|
+
batchSize = DEFAULT_BATCH_SIZE,
|
|
82
|
+
legacyTableName = LEGACY_EMBEDDINGS_TABLE_NAME
|
|
83
|
+
} = options;
|
|
84
|
+
if (backend.vectorStrategy === void 0 || backend.upsertEmbedding === void 0) {
|
|
85
|
+
throw new Error(
|
|
86
|
+
"migrateLegacyEmbeddings requires a backend wired with a vectorStrategy (the destination per-field storage). Pass the post-cutover backend."
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
if (!Number.isInteger(batchSize) || batchSize <= 0) {
|
|
90
|
+
throw new RangeError(
|
|
91
|
+
`batchSize must be a positive integer, got: ${batchSize}`
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
const upsertEmbedding = backend.upsertEmbedding;
|
|
95
|
+
const perField = {};
|
|
96
|
+
const skippedDimensionMismatch = {};
|
|
97
|
+
const skippedDecodeError = {};
|
|
98
|
+
let migrated = 0;
|
|
99
|
+
let cursor;
|
|
100
|
+
for (; ; ) {
|
|
101
|
+
let batch;
|
|
102
|
+
try {
|
|
103
|
+
batch = await readLegacyBatch(backend, {
|
|
104
|
+
legacyTableName,
|
|
105
|
+
graphId,
|
|
106
|
+
batchSize,
|
|
107
|
+
after: cursor
|
|
108
|
+
});
|
|
109
|
+
} catch (error) {
|
|
110
|
+
if (cursor === void 0 && isMissingTableError(error)) {
|
|
111
|
+
return {
|
|
112
|
+
migrated: 0,
|
|
113
|
+
perField: {},
|
|
114
|
+
skippedDimensionMismatch: {},
|
|
115
|
+
skippedDecodeError: {},
|
|
116
|
+
legacyTablePresent: false
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
throw error;
|
|
120
|
+
}
|
|
121
|
+
if (batch.length === 0) break;
|
|
122
|
+
for (const row of batch) {
|
|
123
|
+
const slotKey = `${row.node_kind}.${row.field_path}`;
|
|
124
|
+
let embedding2;
|
|
125
|
+
try {
|
|
126
|
+
embedding2 = decodeEmbeddingJson(row);
|
|
127
|
+
} catch {
|
|
128
|
+
skippedDecodeError[slotKey] = (skippedDecodeError[slotKey] ?? 0) + 1;
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
const config = resolveSlotConfig?.(row.node_kind, row.field_path);
|
|
132
|
+
try {
|
|
133
|
+
await upsertEmbedding({
|
|
134
|
+
graphId: row.graph_id,
|
|
135
|
+
nodeKind: row.node_kind,
|
|
136
|
+
nodeId: row.node_id,
|
|
137
|
+
fieldPath: row.field_path,
|
|
138
|
+
embedding: embedding2,
|
|
139
|
+
// The decoded length is the authoritative fixed dimension — the
|
|
140
|
+
// strategy provisions its column type (e.g. `F32_BLOB(N)`) from it.
|
|
141
|
+
dimensions: embedding2.length,
|
|
142
|
+
metric: config?.metric ?? DEFAULT_SLOT_METRIC,
|
|
143
|
+
indexType: config?.indexType ?? DEFAULT_SLOT_INDEX_TYPE
|
|
144
|
+
});
|
|
145
|
+
} catch (error) {
|
|
146
|
+
if (error instanceof EmbeddingDimensionChangedError) {
|
|
147
|
+
skippedDimensionMismatch[slotKey] = (skippedDimensionMismatch[slotKey] ?? 0) + 1;
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
throw error;
|
|
151
|
+
}
|
|
152
|
+
migrated += 1;
|
|
153
|
+
perField[slotKey] = (perField[slotKey] ?? 0) + 1;
|
|
154
|
+
}
|
|
155
|
+
if (batch.length < batchSize) break;
|
|
156
|
+
const last = batch.at(-1);
|
|
157
|
+
cursor = {
|
|
158
|
+
graphId: last.graph_id,
|
|
159
|
+
nodeKind: last.node_kind,
|
|
160
|
+
nodeId: last.node_id,
|
|
161
|
+
fieldPath: last.field_path
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
return {
|
|
165
|
+
migrated,
|
|
166
|
+
perField,
|
|
167
|
+
skippedDimensionMismatch,
|
|
168
|
+
skippedDecodeError,
|
|
169
|
+
legacyTablePresent: true
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
async function readLegacyBatch(backend, params) {
|
|
173
|
+
const table = quotedTableName(params.legacyTableName);
|
|
174
|
+
const embeddingJson = legacyEmbeddingDecodeExpression(backend);
|
|
175
|
+
const conditions = [];
|
|
176
|
+
if (params.graphId !== void 0) {
|
|
177
|
+
conditions.push(sql`"graph_id" = ${params.graphId}`);
|
|
178
|
+
}
|
|
179
|
+
if (params.after !== void 0) {
|
|
180
|
+
conditions.push(keysetAfterCondition(params.after));
|
|
181
|
+
}
|
|
182
|
+
const whereClause = conditions.length === 0 ? sql`` : sql` WHERE ${sql.join(conditions, sql` AND `)}`;
|
|
183
|
+
const query = sql`
|
|
184
|
+
SELECT
|
|
185
|
+
"graph_id" AS graph_id,
|
|
186
|
+
"node_kind" AS node_kind,
|
|
187
|
+
"node_id" AS node_id,
|
|
188
|
+
"field_path" AS field_path,
|
|
189
|
+
${embeddingJson} AS embedding_json
|
|
190
|
+
FROM ${table}${whereClause}
|
|
191
|
+
ORDER BY "graph_id" ASC, "node_kind" ASC, "node_id" ASC, "field_path" ASC
|
|
192
|
+
LIMIT ${params.batchSize}
|
|
193
|
+
`;
|
|
194
|
+
return backend.execute(query);
|
|
195
|
+
}
|
|
196
|
+
function keysetAfterCondition(after) {
|
|
197
|
+
return sql`
|
|
198
|
+
(
|
|
199
|
+
"graph_id" > ${after.graphId}
|
|
200
|
+
OR ("graph_id" = ${after.graphId} AND "node_kind" > ${after.nodeKind})
|
|
201
|
+
OR ("graph_id" = ${after.graphId} AND "node_kind" = ${after.nodeKind} AND "node_id" > ${after.nodeId})
|
|
202
|
+
OR (
|
|
203
|
+
"graph_id" = ${after.graphId}
|
|
204
|
+
AND "node_kind" = ${after.nodeKind}
|
|
205
|
+
AND "node_id" = ${after.nodeId}
|
|
206
|
+
AND "field_path" > ${after.fieldPath}
|
|
207
|
+
)
|
|
208
|
+
)
|
|
209
|
+
`;
|
|
210
|
+
}
|
|
211
|
+
function legacyEmbeddingDecodeExpression(backend) {
|
|
212
|
+
switch (backend.dialect) {
|
|
213
|
+
case "sqlite": {
|
|
214
|
+
return sql`vec_to_json("embedding")`;
|
|
215
|
+
}
|
|
216
|
+
case "postgres": {
|
|
217
|
+
return sql`"embedding"::text`;
|
|
218
|
+
}
|
|
219
|
+
default: {
|
|
220
|
+
const _exhaustive = backend.dialect;
|
|
221
|
+
throw new Error(
|
|
222
|
+
`migrateLegacyEmbeddings does not support dialect: ${String(_exhaustive)}`
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
function decodeEmbeddingJson(row) {
|
|
228
|
+
let parsed;
|
|
229
|
+
try {
|
|
230
|
+
parsed = JSON.parse(row.embedding_json);
|
|
231
|
+
} catch (error) {
|
|
232
|
+
throw new Error(
|
|
233
|
+
`Failed to decode legacy embedding for ${row.node_kind}.${row.field_path} (graph ${row.graph_id}, node ${row.node_id}): not valid JSON.`,
|
|
234
|
+
{ cause: error }
|
|
235
|
+
);
|
|
236
|
+
}
|
|
237
|
+
if (!Array.isArray(parsed) || parsed.length === 0) {
|
|
238
|
+
throw new Error(
|
|
239
|
+
`Legacy embedding for ${row.node_kind}.${row.field_path} (graph ${row.graph_id}, node ${row.node_id}) decoded to a non-array or empty value.`
|
|
240
|
+
);
|
|
241
|
+
}
|
|
242
|
+
for (const [index, value] of parsed.entries()) {
|
|
243
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
244
|
+
throw new TypeError(
|
|
245
|
+
`Legacy embedding for ${row.node_kind}.${row.field_path} (graph ${row.graph_id}, node ${row.node_id}) has a non-finite value at index ${index}: ${String(value)}.`
|
|
246
|
+
);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
return parsed;
|
|
250
|
+
}
|
|
70
251
|
|
|
71
252
|
// src/query/subquery-utils.ts
|
|
72
253
|
function normalizeValueType(valueType) {
|
|
@@ -371,7 +552,10 @@ function vectorSimilarity(field2, queryEmbedding, limit, options) {
|
|
|
371
552
|
__type: "vector_similarity",
|
|
372
553
|
field: field2,
|
|
373
554
|
queryEmbedding,
|
|
374
|
-
|
|
555
|
+
// Leave metric unset when the caller didn't specify one, so the compiler
|
|
556
|
+
// resolves the field's declared metric per kind. Defaulting to "cosine"
|
|
557
|
+
// here would mis-rank l2/inner_product fields and bypass their ANN index.
|
|
558
|
+
...options?.metric !== void 0 && { metric: options.metric },
|
|
375
559
|
limit,
|
|
376
560
|
...options?.minScore !== void 0 && { minScore: options.minScore }
|
|
377
561
|
};
|
|
@@ -1614,7 +1798,6 @@ function mergeEdgeKinds(traversal) {
|
|
|
1614
1798
|
var DEFAULT_TABLE_NAMES = {
|
|
1615
1799
|
nodes: "typegraph_nodes",
|
|
1616
1800
|
edges: "typegraph_edges",
|
|
1617
|
-
embeddings: "typegraph_node_embeddings",
|
|
1618
1801
|
fulltext: "typegraph_node_fulltext",
|
|
1619
1802
|
uniques: "typegraph_node_uniques"
|
|
1620
1803
|
};
|
|
@@ -1641,18 +1824,19 @@ function createSqlSchema(names = {}) {
|
|
|
1641
1824
|
const tables = { ...DEFAULT_TABLE_NAMES, ...names };
|
|
1642
1825
|
validateTableName(tables.nodes, "nodes");
|
|
1643
1826
|
validateTableName(tables.edges, "edges");
|
|
1644
|
-
validateTableName(tables.embeddings, "embeddings");
|
|
1645
1827
|
validateTableName(tables.fulltext, "fulltext");
|
|
1646
1828
|
validateTableName(tables.uniques, "uniques");
|
|
1647
1829
|
return {
|
|
1648
1830
|
tables,
|
|
1649
1831
|
nodesTable: sql.raw(quoteIdentifier2(tables.nodes)),
|
|
1650
1832
|
edgesTable: sql.raw(quoteIdentifier2(tables.edges)),
|
|
1651
|
-
embeddingsTable: sql.raw(quoteIdentifier2(tables.embeddings)),
|
|
1652
1833
|
fulltextTable: sql.raw(quoteIdentifier2(tables.fulltext))
|
|
1653
1834
|
};
|
|
1654
1835
|
}
|
|
1655
1836
|
var DEFAULT_SQL_SCHEMA = createSqlSchema();
|
|
1837
|
+
function vectorSlotKey(nodeKind, fieldPath) {
|
|
1838
|
+
return `${nodeKind}\0${fieldPath}`;
|
|
1839
|
+
}
|
|
1656
1840
|
var ALIAS_CTE_PREFIX = "cte_";
|
|
1657
1841
|
var EMBEDDINGS_CTE_ALIAS = "cte_embeddings";
|
|
1658
1842
|
var FULLTEXT_CTE_ALIAS = "cte_fulltext";
|
|
@@ -2159,35 +2343,61 @@ function buildStandardHaving(input) {
|
|
|
2159
2343
|
}
|
|
2160
2344
|
function buildStandardEmbeddingsCte(input) {
|
|
2161
2345
|
const { ctx, graphId, nodeKinds, vectorPredicate } = input;
|
|
2162
|
-
const { dialect } = ctx;
|
|
2163
2346
|
const { field: field2, metric, minScore, queryEmbedding } = vectorPredicate;
|
|
2164
|
-
const embeddingsTableName = ctx.schema.tables.embeddings;
|
|
2165
2347
|
if (nodeKinds.length === 0) {
|
|
2166
2348
|
throw new UnsupportedPredicateError(
|
|
2167
2349
|
"Vector predicate must resolve to at least one node kind"
|
|
2168
2350
|
);
|
|
2169
2351
|
}
|
|
2170
|
-
const
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
metric
|
|
2175
|
-
);
|
|
2176
|
-
const scopedNodes = buildScopedNodeIdsSubquery(field2.alias);
|
|
2177
|
-
const conditions = [
|
|
2178
|
-
sql`${qualifyColumn(embeddingsTableName, "graph_id")} = ${graphId}`,
|
|
2179
|
-
compileKindFilter(
|
|
2180
|
-
qualifyColumn(embeddingsTableName, "node_kind"),
|
|
2181
|
-
nodeKinds
|
|
2182
|
-
),
|
|
2183
|
-
sql`${qualifyColumn(embeddingsTableName, "field_path")} = ${fieldPath}`
|
|
2184
|
-
];
|
|
2185
|
-
if (minScore !== void 0) {
|
|
2186
|
-
conditions.push(
|
|
2187
|
-
compileVectorMinScoreCondition(distanceExpr, metric, minScore)
|
|
2352
|
+
const { vectorStrategy } = ctx;
|
|
2353
|
+
if (vectorStrategy === void 0) {
|
|
2354
|
+
throw new UnsupportedPredicateError(
|
|
2355
|
+
"Vector similarity predicate requires a backend with a vector strategy"
|
|
2188
2356
|
);
|
|
2189
2357
|
}
|
|
2190
|
-
const
|
|
2358
|
+
const fieldPath = resolveEmbeddingFieldPath(field2);
|
|
2359
|
+
const vectorSlots = ctx.vectorSlots;
|
|
2360
|
+
const scopedNodes = buildScopedNodeIdsSubquery(field2.alias);
|
|
2361
|
+
const declaringKinds = nodeKinds.filter(
|
|
2362
|
+
(kind) => vectorSlots?.has(vectorSlotKey(kind, fieldPath))
|
|
2363
|
+
);
|
|
2364
|
+
const branches = declaringKinds.map((kind) => {
|
|
2365
|
+
const tableName = vectorStrategy.tableName(graphId, kind, fieldPath);
|
|
2366
|
+
const branchMetric = metric ?? vectorSlots?.get(vectorSlotKey(kind, fieldPath))?.metric ?? "cosine";
|
|
2367
|
+
const distanceExpr = vectorStrategy.distanceExpression(
|
|
2368
|
+
qualifyColumn(tableName, "embedding"),
|
|
2369
|
+
queryEmbedding,
|
|
2370
|
+
branchMetric
|
|
2371
|
+
);
|
|
2372
|
+
const scoreExpr = vectorScoreExpression(distanceExpr, branchMetric);
|
|
2373
|
+
const conditions = [
|
|
2374
|
+
sql`${qualifyColumn(tableName, "graph_id")} = ${graphId}`
|
|
2375
|
+
];
|
|
2376
|
+
if (minScore !== void 0) {
|
|
2377
|
+
conditions.push(
|
|
2378
|
+
vectorMinScoreCondition(distanceExpr, branchMetric, minScore)
|
|
2379
|
+
);
|
|
2380
|
+
}
|
|
2381
|
+
return sql`
|
|
2382
|
+
SELECT
|
|
2383
|
+
${qualifyColumn(tableName, "node_id")} AS node_id,
|
|
2384
|
+
${kind} AS node_kind,
|
|
2385
|
+
${distanceExpr} AS distance,
|
|
2386
|
+
${scoreExpr} AS score
|
|
2387
|
+
FROM ${quoteIdentifier(tableName)}
|
|
2388
|
+
INNER JOIN ${scopedNodes}
|
|
2389
|
+
ON ${sql.raw(`${SCOPED_RELEVANCE_NODES_ALIAS}.node_id`)} =
|
|
2390
|
+
${qualifyColumn(tableName, "node_id")}
|
|
2391
|
+
AND ${sql.raw(`${SCOPED_RELEVANCE_NODES_ALIAS}.node_kind`)} = ${kind}
|
|
2392
|
+
WHERE ${sql.join(conditions, sql` AND `)}
|
|
2393
|
+
`;
|
|
2394
|
+
});
|
|
2395
|
+
const unionBody = branches.length === 0 ? emptyEmbeddingsBody() : sql.join(
|
|
2396
|
+
branches,
|
|
2397
|
+
sql`
|
|
2398
|
+
UNION ALL
|
|
2399
|
+
`
|
|
2400
|
+
);
|
|
2191
2401
|
return sql`
|
|
2192
2402
|
${sql.raw(EMBEDDINGS_CTE_ALIAS)} AS (
|
|
2193
2403
|
SELECT
|
|
@@ -2197,49 +2407,22 @@ function buildStandardEmbeddingsCte(input) {
|
|
|
2197
2407
|
score,
|
|
2198
2408
|
ROW_NUMBER() OVER (ORDER BY distance ASC) AS ord
|
|
2199
2409
|
FROM (
|
|
2200
|
-
|
|
2201
|
-
${qualifyColumn(embeddingsTableName, "node_id")} AS node_id,
|
|
2202
|
-
${qualifyColumn(embeddingsTableName, "node_kind")} AS node_kind,
|
|
2203
|
-
${distanceExpr} AS distance,
|
|
2204
|
-
${scoreExpr} AS score
|
|
2205
|
-
FROM ${ctx.schema.embeddingsTable}
|
|
2206
|
-
INNER JOIN ${scopedNodes}
|
|
2207
|
-
ON ${sql.raw(`${SCOPED_RELEVANCE_NODES_ALIAS}.node_id`)} =
|
|
2208
|
-
${qualifyColumn(embeddingsTableName, "node_id")}
|
|
2209
|
-
AND ${sql.raw(`${SCOPED_RELEVANCE_NODES_ALIAS}.node_kind`)} =
|
|
2210
|
-
${qualifyColumn(embeddingsTableName, "node_kind")}
|
|
2211
|
-
WHERE ${sql.join(conditions, sql` AND `)}
|
|
2410
|
+
${unionBody}
|
|
2212
2411
|
ORDER BY distance ASC
|
|
2213
2412
|
LIMIT ${vectorPredicate.limit}
|
|
2214
2413
|
) AS vec_inner
|
|
2215
2414
|
)
|
|
2216
2415
|
`;
|
|
2217
2416
|
}
|
|
2218
|
-
function
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
}
|
|
2228
|
-
}
|
|
2229
|
-
function compileVectorMinScoreCondition(distanceExpr, metric, minScore) {
|
|
2230
|
-
switch (metric) {
|
|
2231
|
-
case "cosine": {
|
|
2232
|
-
const threshold = 1 - minScore;
|
|
2233
|
-
return sql`${distanceExpr} <= ${threshold}`;
|
|
2234
|
-
}
|
|
2235
|
-
case "l2": {
|
|
2236
|
-
return sql`${distanceExpr} <= ${minScore}`;
|
|
2237
|
-
}
|
|
2238
|
-
case "inner_product": {
|
|
2239
|
-
const negativeThreshold = -minScore;
|
|
2240
|
-
return sql`${distanceExpr} <= ${negativeThreshold}`;
|
|
2241
|
-
}
|
|
2242
|
-
}
|
|
2417
|
+
function emptyEmbeddingsBody() {
|
|
2418
|
+
return sql`
|
|
2419
|
+
SELECT
|
|
2420
|
+
CAST(NULL AS TEXT) AS node_id,
|
|
2421
|
+
CAST(NULL AS TEXT) AS node_kind,
|
|
2422
|
+
CAST(NULL AS REAL) AS distance,
|
|
2423
|
+
CAST(NULL AS REAL) AS score
|
|
2424
|
+
WHERE 1 = 0
|
|
2425
|
+
`;
|
|
2243
2426
|
}
|
|
2244
2427
|
function buildStandardFulltextCte(input) {
|
|
2245
2428
|
const { ctx, fulltextPredicate, graphId, nodeKinds } = input;
|
|
@@ -2406,6 +2589,11 @@ function runFulltextPredicatePass(ast, dialect) {
|
|
|
2406
2589
|
`Fulltext query mode "${fulltextPredicate.mode}" is not supported by the "${strategy.name}" strategy. Supported modes: ${strategy.supportedModes.join(", ")}.`
|
|
2407
2590
|
);
|
|
2408
2591
|
}
|
|
2592
|
+
if (fulltextPredicate.language !== void 0 && !strategy.supportsLanguageOverride) {
|
|
2593
|
+
throw new UnsupportedPredicateError(
|
|
2594
|
+
`Fulltext strategy "${strategy.name}" does not honor a per-query \`language\` override (its tokenizer is fixed at table-create time). Drop the option, or use a strategy that advertises \`supportsLanguageOverride: true\`.`
|
|
2595
|
+
);
|
|
2596
|
+
}
|
|
2409
2597
|
if (!Number.isFinite(fulltextPredicate.limit) || fulltextPredicate.limit <= 0) {
|
|
2410
2598
|
throw new UnsupportedPredicateError(
|
|
2411
2599
|
`Fulltext match limit must be a positive finite number, got ${String(fulltextPredicate.limit)}`
|
|
@@ -2516,7 +2704,7 @@ function createTemporalFilterPass(ast, currentTimestamp) {
|
|
|
2516
2704
|
}
|
|
2517
2705
|
|
|
2518
2706
|
// src/query/compiler/passes/vector.ts
|
|
2519
|
-
function runVectorPredicatePass(ast, dialect) {
|
|
2707
|
+
function runVectorPredicatePass(ast, dialect, vectorStrategy) {
|
|
2520
2708
|
const vectorPredicates = extractVectorSimilarityPredicates(ast.predicates);
|
|
2521
2709
|
if (vectorPredicates.length > 1) {
|
|
2522
2710
|
throw new UnsupportedPredicateError(
|
|
@@ -2527,16 +2715,19 @@ function runVectorPredicatePass(ast, dialect) {
|
|
|
2527
2715
|
if (vectorPredicate === void 0) {
|
|
2528
2716
|
return { vectorPredicate: void 0 };
|
|
2529
2717
|
}
|
|
2530
|
-
const
|
|
2531
|
-
if (
|
|
2718
|
+
const predicateStrategy = dialect.capabilities.vectorPredicateStrategy;
|
|
2719
|
+
if (predicateStrategy === "unsupported" || !dialect.supportsVectors) {
|
|
2532
2720
|
throw new UnsupportedPredicateError(
|
|
2533
2721
|
`Vector similarity predicates are not supported for dialect "${dialect.name}"`
|
|
2534
2722
|
);
|
|
2535
2723
|
}
|
|
2536
|
-
if (
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2724
|
+
if (vectorPredicate.metric !== void 0) {
|
|
2725
|
+
const supportedMetrics = vectorStrategy?.capabilities.metrics ?? dialect.capabilities.vectorMetrics;
|
|
2726
|
+
if (!supportedMetrics.includes(vectorPredicate.metric)) {
|
|
2727
|
+
throw new UnsupportedPredicateError(
|
|
2728
|
+
vectorStrategy === void 0 ? `Vector metric "${vectorPredicate.metric}" is not supported for dialect "${dialect.name}"` : `Vector metric "${vectorPredicate.metric}" is not supported by vector strategy "${vectorStrategy.name}" (supported: ${supportedMetrics.join(", ")})`
|
|
2729
|
+
);
|
|
2730
|
+
}
|
|
2540
2731
|
}
|
|
2541
2732
|
if (!Number.isFinite(vectorPredicate.limit) || vectorPredicate.limit <= 0) {
|
|
2542
2733
|
throw new UnsupportedPredicateError(
|
|
@@ -2781,13 +2972,14 @@ function lowerRecursiveQueryToLogicalPlanNode(input) {
|
|
|
2781
2972
|
ast.limit
|
|
2782
2973
|
);
|
|
2783
2974
|
}
|
|
2784
|
-
function lowerComposableQueryToLogicalPlanNode(query, dialect, graphId, nextPlanNodeId) {
|
|
2975
|
+
function lowerComposableQueryToLogicalPlanNode(query, dialect, graphId, nextPlanNodeId, vectorStrategy) {
|
|
2785
2976
|
if ("__type" in query) {
|
|
2786
2977
|
return lowerSetOperationToLogicalPlanNode(
|
|
2787
2978
|
query,
|
|
2788
2979
|
graphId,
|
|
2789
2980
|
dialect,
|
|
2790
|
-
nextPlanNodeId
|
|
2981
|
+
nextPlanNodeId,
|
|
2982
|
+
vectorStrategy
|
|
2791
2983
|
);
|
|
2792
2984
|
}
|
|
2793
2985
|
const hasVariableLengthTraversal2 = query.traversals.some(
|
|
@@ -2803,7 +2995,8 @@ function lowerComposableQueryToLogicalPlanNode(query, dialect, graphId, nextPlan
|
|
|
2803
2995
|
const dialectAdapter = getDialect(dialect);
|
|
2804
2996
|
const vectorPredicate = runVectorPredicatePass(
|
|
2805
2997
|
query,
|
|
2806
|
-
dialectAdapter
|
|
2998
|
+
dialectAdapter,
|
|
2999
|
+
vectorStrategy
|
|
2807
3000
|
).vectorPredicate;
|
|
2808
3001
|
const fulltextPredicate = runFulltextPredicatePass(
|
|
2809
3002
|
query,
|
|
@@ -2827,14 +3020,15 @@ function lowerComposableQueryToLogicalPlanNode(query, dialect, graphId, nextPlan
|
|
|
2827
3020
|
};
|
|
2828
3021
|
return lowerStandardQueryToLogicalPlanNode(loweringInput);
|
|
2829
3022
|
}
|
|
2830
|
-
function lowerSetOperationToLogicalPlanNode(op, graphId, dialect, nextPlanNodeId) {
|
|
3023
|
+
function lowerSetOperationToLogicalPlanNode(op, graphId, dialect, nextPlanNodeId, vectorStrategy) {
|
|
2831
3024
|
let currentNode = {
|
|
2832
3025
|
id: nextPlanNodeId(),
|
|
2833
3026
|
left: lowerComposableQueryToLogicalPlanNode(
|
|
2834
3027
|
op.left,
|
|
2835
3028
|
dialect,
|
|
2836
3029
|
graphId,
|
|
2837
|
-
nextPlanNodeId
|
|
3030
|
+
nextPlanNodeId,
|
|
3031
|
+
vectorStrategy
|
|
2838
3032
|
),
|
|
2839
3033
|
op: "set_op",
|
|
2840
3034
|
operator: op.operator,
|
|
@@ -2842,7 +3036,8 @@ function lowerSetOperationToLogicalPlanNode(op, graphId, dialect, nextPlanNodeId
|
|
|
2842
3036
|
op.right,
|
|
2843
3037
|
dialect,
|
|
2844
3038
|
graphId,
|
|
2845
|
-
nextPlanNodeId
|
|
3039
|
+
nextPlanNodeId,
|
|
3040
|
+
vectorStrategy
|
|
2846
3041
|
)
|
|
2847
3042
|
};
|
|
2848
3043
|
if (op.orderBy !== void 0 && op.orderBy.length > 0) {
|
|
@@ -2906,7 +3101,8 @@ function lowerSetOperationToLogicalPlan(input) {
|
|
|
2906
3101
|
input.op,
|
|
2907
3102
|
input.graphId,
|
|
2908
3103
|
input.dialect,
|
|
2909
|
-
nextPlanNodeId
|
|
3104
|
+
nextPlanNodeId,
|
|
3105
|
+
input.vectorStrategy
|
|
2910
3106
|
)
|
|
2911
3107
|
};
|
|
2912
3108
|
}
|
|
@@ -4480,29 +4676,7 @@ var OPERATOR_MAP = {
|
|
|
4480
4676
|
intersect: "INTERSECT",
|
|
4481
4677
|
except: "EXCEPT"
|
|
4482
4678
|
};
|
|
4483
|
-
function
|
|
4484
|
-
return compileSetOperationStandard(
|
|
4485
|
-
op,
|
|
4486
|
-
graphId,
|
|
4487
|
-
dialect,
|
|
4488
|
-
logicalPlan,
|
|
4489
|
-
compileQuery2
|
|
4490
|
-
);
|
|
4491
|
-
}
|
|
4492
|
-
function compileSetOperationWithSqliteCompoundStrategy(op, graphId, dialect, logicalPlan, schema, _compileQuery) {
|
|
4493
|
-
return compileSetOperationForSqlite(
|
|
4494
|
-
op,
|
|
4495
|
-
graphId,
|
|
4496
|
-
dialect,
|
|
4497
|
-
logicalPlan,
|
|
4498
|
-
schema
|
|
4499
|
-
);
|
|
4500
|
-
}
|
|
4501
|
-
var SET_OPERATION_STRATEGY_HANDLERS = {
|
|
4502
|
-
standard_parenthesized: compileSetOperationWithStandardParenthesizedStrategy,
|
|
4503
|
-
sqlite_compound: compileSetOperationWithSqliteCompoundStrategy
|
|
4504
|
-
};
|
|
4505
|
-
function runSetOperationPassPipeline(op, graphId, dialect) {
|
|
4679
|
+
function runSetOperationPassPipeline(op, graphId, dialect, vectorStrategy) {
|
|
4506
4680
|
let state = {
|
|
4507
4681
|
dialect,
|
|
4508
4682
|
graphId,
|
|
@@ -4515,7 +4689,8 @@ function runSetOperationPassPipeline(op, graphId, dialect) {
|
|
|
4515
4689
|
return lowerSetOperationToLogicalPlan({
|
|
4516
4690
|
dialect: currentState.dialect.name,
|
|
4517
4691
|
graphId: currentState.graphId,
|
|
4518
|
-
op: currentState.op
|
|
4692
|
+
op: currentState.op,
|
|
4693
|
+
...vectorStrategy === void 0 ? {} : { vectorStrategy }
|
|
4519
4694
|
});
|
|
4520
4695
|
},
|
|
4521
4696
|
update(currentState, logicalPlan) {
|
|
@@ -4528,25 +4703,20 @@ function runSetOperationPassPipeline(op, graphId, dialect) {
|
|
|
4528
4703
|
state = logicalPlanPass.state;
|
|
4529
4704
|
return state;
|
|
4530
4705
|
}
|
|
4531
|
-
function compileSetOperation(op, graphId, dialect,
|
|
4532
|
-
const passState = runSetOperationPassPipeline(
|
|
4706
|
+
function compileSetOperation(op, graphId, dialect, compileQuery2, vectorStrategy) {
|
|
4707
|
+
const passState = runSetOperationPassPipeline(
|
|
4708
|
+
op,
|
|
4709
|
+
graphId,
|
|
4710
|
+
dialect,
|
|
4711
|
+
vectorStrategy
|
|
4712
|
+
);
|
|
4533
4713
|
const { logicalPlan } = passState;
|
|
4534
4714
|
if (logicalPlan === void 0) {
|
|
4535
4715
|
throw new CompilerInvariantError(
|
|
4536
4716
|
"Logical plan pass did not initialize plan state"
|
|
4537
4717
|
);
|
|
4538
4718
|
}
|
|
4539
|
-
const
|
|
4540
|
-
const handler = SET_OPERATION_STRATEGY_HANDLERS[strategy];
|
|
4541
|
-
return handler(op, graphId, dialect, logicalPlan, schema, compileQuery2);
|
|
4542
|
-
}
|
|
4543
|
-
function compileSetOperationStandard(op, graphId, dialect, logicalPlan, compileQuery2) {
|
|
4544
|
-
const coreSql = compileSetOperationCoreStandard(
|
|
4545
|
-
op,
|
|
4546
|
-
graphId,
|
|
4547
|
-
dialect,
|
|
4548
|
-
compileQuery2
|
|
4549
|
-
);
|
|
4719
|
+
const coreSql = compileSetOperationCore(op, graphId, compileQuery2, dialect);
|
|
4550
4720
|
const suffixClauses = buildSetOperationSuffixClauses(op, dialect);
|
|
4551
4721
|
return emitSetOperationQuerySql({
|
|
4552
4722
|
baseQuery: coreSql,
|
|
@@ -4554,439 +4724,106 @@ function compileSetOperationStandard(op, graphId, dialect, logicalPlan, compileQ
|
|
|
4554
4724
|
...suffixClauses.length === 0 ? {} : { suffixClauses }
|
|
4555
4725
|
});
|
|
4556
4726
|
}
|
|
4557
|
-
function
|
|
4558
|
-
const left =
|
|
4559
|
-
|
|
4560
|
-
graphId,
|
|
4561
|
-
dialect,
|
|
4562
|
-
compileQuery2
|
|
4563
|
-
);
|
|
4564
|
-
const right = compileComposableQueryStandard(
|
|
4727
|
+
function compileSetOperationCore(op, graphId, compileQuery2, dialect) {
|
|
4728
|
+
const left = compileComposableQuery(op.left, graphId, compileQuery2, dialect);
|
|
4729
|
+
const right = compileComposableQuery(
|
|
4565
4730
|
op.right,
|
|
4566
4731
|
graphId,
|
|
4567
|
-
|
|
4568
|
-
|
|
4732
|
+
compileQuery2,
|
|
4733
|
+
dialect
|
|
4569
4734
|
);
|
|
4570
4735
|
const opSql = sql.raw(OPERATOR_MAP[op.operator]);
|
|
4571
|
-
return sql
|
|
4736
|
+
return sql`${dialect.wrapSetOperationOperand(left)} ${opSql} ${dialect.wrapSetOperationOperand(right)}`;
|
|
4572
4737
|
}
|
|
4573
|
-
function
|
|
4738
|
+
function compileComposableQuery(query, graphId, compileQuery2, dialect) {
|
|
4574
4739
|
if ("__type" in query) {
|
|
4575
|
-
return
|
|
4576
|
-
query,
|
|
4577
|
-
graphId,
|
|
4578
|
-
dialect,
|
|
4579
|
-
compileQuery2
|
|
4580
|
-
);
|
|
4740
|
+
return compileSetOperationCompound(query, graphId, compileQuery2, dialect);
|
|
4581
4741
|
}
|
|
4582
4742
|
return compileQuery2(query, graphId);
|
|
4583
4743
|
}
|
|
4584
|
-
function
|
|
4585
|
-
const
|
|
4586
|
-
collectLeafQueries(op, leaves, "q");
|
|
4587
|
-
for (const leaf of leaves) {
|
|
4588
|
-
validateSqliteSetOpLeaf(leaf.ast);
|
|
4589
|
-
}
|
|
4590
|
-
return { leaves };
|
|
4591
|
-
}
|
|
4592
|
-
function compileSetOperationForSqlite(op, graphId, dialect, logicalPlan, schema) {
|
|
4593
|
-
const { leaves } = runSqliteSetOperationValidationPass(op);
|
|
4594
|
-
const allCtes = [];
|
|
4595
|
-
const ctx = {
|
|
4596
|
-
dialect,
|
|
4597
|
-
schema,
|
|
4598
|
-
compileQuery: () => {
|
|
4599
|
-
throw new CompilerInvariantError(
|
|
4600
|
-
"compileQuery is not available in set-operation CTE compilation"
|
|
4601
|
-
);
|
|
4602
|
-
}
|
|
4603
|
-
};
|
|
4604
|
-
for (const leaf of leaves) {
|
|
4605
|
-
const cte = compilePrefixedStartCte(leaf.ast, leaf.prefix, graphId, ctx);
|
|
4606
|
-
allCtes.push(cte);
|
|
4607
|
-
}
|
|
4608
|
-
const selectStatements = [];
|
|
4609
|
-
for (const leaf of leaves) {
|
|
4610
|
-
const select = compilePrefixedSelect(leaf.ast, leaf.prefix, dialect);
|
|
4611
|
-
selectStatements.push(select);
|
|
4612
|
-
}
|
|
4613
|
-
const compoundSelect = buildCompoundSelect(op, leaves, selectStatements);
|
|
4744
|
+
function compileSetOperationCompound(op, graphId, compileQuery2, dialect) {
|
|
4745
|
+
const core2 = compileSetOperationCore(op, graphId, compileQuery2, dialect);
|
|
4614
4746
|
const suffixClauses = buildSetOperationSuffixClauses(op, dialect);
|
|
4615
|
-
return
|
|
4616
|
-
|
|
4617
|
-
...allCtes.length === 0 ? {} : { ctes: allCtes },
|
|
4618
|
-
logicalPlan,
|
|
4619
|
-
...suffixClauses.length === 0 ? {} : { suffixClauses }
|
|
4620
|
-
});
|
|
4747
|
+
if (suffixClauses.length === 0) return core2;
|
|
4748
|
+
return sql.join([core2, ...suffixClauses], sql` `);
|
|
4621
4749
|
}
|
|
4622
|
-
function
|
|
4623
|
-
|
|
4624
|
-
|
|
4625
|
-
|
|
4626
|
-
}
|
|
4627
|
-
if (hasSubqueryPredicates(ast)) {
|
|
4628
|
-
unsupported.push("EXISTS/IN subqueries");
|
|
4629
|
-
}
|
|
4630
|
-
if (hasVectorSimilarityPredicates(ast)) {
|
|
4631
|
-
unsupported.push("vector similarity predicates");
|
|
4750
|
+
function getLeftmostProjection(op) {
|
|
4751
|
+
let current = op.left;
|
|
4752
|
+
while ("__type" in current) {
|
|
4753
|
+
current = current.left;
|
|
4632
4754
|
}
|
|
4633
|
-
|
|
4634
|
-
|
|
4755
|
+
return current.projection;
|
|
4756
|
+
}
|
|
4757
|
+
function normalizeFieldRefKey(field2) {
|
|
4758
|
+
let pointer = field2.jsonPointer;
|
|
4759
|
+
if (pointer === void 0 && field2.path.length > 1 && field2.path[0] === "props") {
|
|
4760
|
+
pointer = jsonPointer(field2.path.slice(1));
|
|
4635
4761
|
}
|
|
4636
|
-
|
|
4637
|
-
|
|
4762
|
+
const basePath = field2.path.length > 0 && field2.path[0] === "props" ? "props" : field2.path.join(".");
|
|
4763
|
+
return `${field2.alias}:${basePath}:${pointer ?? ""}`;
|
|
4764
|
+
}
|
|
4765
|
+
function matchFieldToProjection(field2, projection) {
|
|
4766
|
+
const targetKey = normalizeFieldRefKey(field2);
|
|
4767
|
+
for (const projected of projection.fields) {
|
|
4768
|
+
const source = projected.source;
|
|
4769
|
+
if (!("__type" in source) || source.__type !== "field_ref") continue;
|
|
4770
|
+
if (normalizeFieldRefKey(source) === targetKey) {
|
|
4771
|
+
return projected;
|
|
4772
|
+
}
|
|
4638
4773
|
}
|
|
4639
|
-
|
|
4640
|
-
|
|
4774
|
+
return void 0;
|
|
4775
|
+
}
|
|
4776
|
+
function buildSetOperationSuffixClauses(op, dialect) {
|
|
4777
|
+
const clauses = [];
|
|
4778
|
+
if (op.orderBy && op.orderBy.length > 0) {
|
|
4779
|
+
const projection = getLeftmostProjection(op);
|
|
4780
|
+
if (projection.fields.length === 0) {
|
|
4781
|
+
throw new UnsupportedPredicateError(
|
|
4782
|
+
"Set operation ORDER BY requires explicit field projection. SELECT * does not provide stable output column names for ordering. Use .select() to specify which fields to project."
|
|
4783
|
+
);
|
|
4784
|
+
}
|
|
4785
|
+
const orderParts = [];
|
|
4786
|
+
for (const orderSpec of op.orderBy) {
|
|
4787
|
+
const projected = matchFieldToProjection(orderSpec.field, projection);
|
|
4788
|
+
if (!projected) {
|
|
4789
|
+
const fieldDesc = orderSpec.field.jsonPointer ? `${orderSpec.field.alias}.props${orderSpec.field.jsonPointer}` : `${orderSpec.field.alias}.${orderSpec.field.path.join(".")}`;
|
|
4790
|
+
const availableFields = projection.fields.map((f) => f.outputName).join(", ");
|
|
4791
|
+
throw new UnsupportedPredicateError(
|
|
4792
|
+
`Set operation ORDER BY field "${fieldDesc}" is not in the projection. ORDER BY for UNION/INTERSECT/EXCEPT must reference projected columns. Available columns: ${availableFields}`
|
|
4793
|
+
);
|
|
4794
|
+
}
|
|
4795
|
+
const columnRef = sql.raw(dialect.quoteIdentifier(projected.outputName));
|
|
4796
|
+
const dir = sql.raw(orderSpec.direction.toUpperCase());
|
|
4797
|
+
const nulls = orderSpec.nulls ?? (orderSpec.direction === "asc" ? "last" : "first");
|
|
4798
|
+
const nullsDir = sql.raw(nulls === "first" ? "DESC" : "ASC");
|
|
4799
|
+
orderParts.push(
|
|
4800
|
+
sql`(${columnRef} IS NULL) ${nullsDir}`,
|
|
4801
|
+
sql`${columnRef} ${dir}`
|
|
4802
|
+
);
|
|
4803
|
+
}
|
|
4804
|
+
clauses.push(sql`ORDER BY ${sql.join(orderParts, sql`, `)}`);
|
|
4641
4805
|
}
|
|
4642
|
-
if (
|
|
4643
|
-
|
|
4644
|
-
"per-query ORDER BY (use set operation's orderBy instead)"
|
|
4645
|
-
);
|
|
4806
|
+
if (op.limit !== void 0) {
|
|
4807
|
+
clauses.push(sql`LIMIT ${op.limit}`);
|
|
4646
4808
|
}
|
|
4647
|
-
if (
|
|
4648
|
-
|
|
4809
|
+
if (op.offset !== void 0) {
|
|
4810
|
+
clauses.push(sql`OFFSET ${op.offset}`);
|
|
4649
4811
|
}
|
|
4650
|
-
|
|
4651
|
-
|
|
4812
|
+
return clauses;
|
|
4813
|
+
}
|
|
4814
|
+
|
|
4815
|
+
// src/query/compiler/standard-pass-pipeline.ts
|
|
4816
|
+
var TRAVERSAL_LIMIT_PUSHDOWN_MULTIPLIER = 8;
|
|
4817
|
+
var TRAVERSAL_LIMIT_PUSHDOWN_MAX = 1e4;
|
|
4818
|
+
function isColumnPruningEnabled(ast) {
|
|
4819
|
+
if (ast.selectiveFields && ast.selectiveFields.length > 0) {
|
|
4820
|
+
return true;
|
|
4652
4821
|
}
|
|
4653
|
-
if (
|
|
4654
|
-
|
|
4655
|
-
`SQLite set operations (UNION/INTERSECT/EXCEPT) do not support: ${unsupported.join(", ")}. Use PostgreSQL for complex set operations, or refactor to separate queries.`
|
|
4656
|
-
);
|
|
4822
|
+
if (ast.groupBy || ast.having) {
|
|
4823
|
+
return true;
|
|
4657
4824
|
}
|
|
4658
|
-
|
|
4659
|
-
|
|
4660
|
-
return ast.predicates.some(
|
|
4661
|
-
(predicate2) => hasVectorSimilarityInExpression(predicate2.expression)
|
|
4662
|
-
);
|
|
4663
|
-
}
|
|
4664
|
-
function hasVectorSimilarityInExpression(expr) {
|
|
4665
|
-
if ("__type" in expr) {
|
|
4666
|
-
switch (expr.__type) {
|
|
4667
|
-
case "and":
|
|
4668
|
-
case "or": {
|
|
4669
|
-
return expr.predicates.some((p) => hasVectorSimilarityInExpression(p));
|
|
4670
|
-
}
|
|
4671
|
-
case "not": {
|
|
4672
|
-
return hasVectorSimilarityInExpression(expr.predicate);
|
|
4673
|
-
}
|
|
4674
|
-
case "vector_similarity": {
|
|
4675
|
-
return true;
|
|
4676
|
-
}
|
|
4677
|
-
case "exists":
|
|
4678
|
-
case "in_subquery":
|
|
4679
|
-
case "comparison":
|
|
4680
|
-
case "string_op":
|
|
4681
|
-
case "null_check":
|
|
4682
|
-
case "between":
|
|
4683
|
-
case "array_op":
|
|
4684
|
-
case "object_op":
|
|
4685
|
-
case "aggregate_comparison":
|
|
4686
|
-
case "fulltext_match": {
|
|
4687
|
-
return false;
|
|
4688
|
-
}
|
|
4689
|
-
}
|
|
4690
|
-
}
|
|
4691
|
-
return false;
|
|
4692
|
-
}
|
|
4693
|
-
function hasFulltextMatchPredicates(ast) {
|
|
4694
|
-
return ast.predicates.some(
|
|
4695
|
-
(predicate2) => hasFulltextMatchInExpression(predicate2.expression)
|
|
4696
|
-
);
|
|
4697
|
-
}
|
|
4698
|
-
function hasFulltextMatchInExpression(expr) {
|
|
4699
|
-
if ("__type" in expr) {
|
|
4700
|
-
switch (expr.__type) {
|
|
4701
|
-
case "and":
|
|
4702
|
-
case "or": {
|
|
4703
|
-
return expr.predicates.some((p) => hasFulltextMatchInExpression(p));
|
|
4704
|
-
}
|
|
4705
|
-
case "not": {
|
|
4706
|
-
return hasFulltextMatchInExpression(expr.predicate);
|
|
4707
|
-
}
|
|
4708
|
-
case "fulltext_match": {
|
|
4709
|
-
return true;
|
|
4710
|
-
}
|
|
4711
|
-
case "exists":
|
|
4712
|
-
case "in_subquery":
|
|
4713
|
-
case "comparison":
|
|
4714
|
-
case "string_op":
|
|
4715
|
-
case "null_check":
|
|
4716
|
-
case "between":
|
|
4717
|
-
case "array_op":
|
|
4718
|
-
case "object_op":
|
|
4719
|
-
case "aggregate_comparison":
|
|
4720
|
-
case "vector_similarity": {
|
|
4721
|
-
return false;
|
|
4722
|
-
}
|
|
4723
|
-
}
|
|
4724
|
-
}
|
|
4725
|
-
return false;
|
|
4726
|
-
}
|
|
4727
|
-
function hasSubqueryPredicates(ast) {
|
|
4728
|
-
return ast.predicates.some(
|
|
4729
|
-
(predicate2) => hasSubqueryInExpression(predicate2.expression)
|
|
4730
|
-
);
|
|
4731
|
-
}
|
|
4732
|
-
function hasSubqueryInExpression(expr) {
|
|
4733
|
-
if ("__type" in expr) {
|
|
4734
|
-
switch (expr.__type) {
|
|
4735
|
-
case "and":
|
|
4736
|
-
case "or": {
|
|
4737
|
-
return expr.predicates.some((p) => hasSubqueryInExpression(p));
|
|
4738
|
-
}
|
|
4739
|
-
case "not": {
|
|
4740
|
-
return hasSubqueryInExpression(expr.predicate);
|
|
4741
|
-
}
|
|
4742
|
-
case "exists": {
|
|
4743
|
-
return true;
|
|
4744
|
-
}
|
|
4745
|
-
case "in_subquery": {
|
|
4746
|
-
return true;
|
|
4747
|
-
}
|
|
4748
|
-
// These expression types don't contain subqueries
|
|
4749
|
-
case "comparison":
|
|
4750
|
-
case "string_op":
|
|
4751
|
-
case "null_check":
|
|
4752
|
-
case "between":
|
|
4753
|
-
case "array_op":
|
|
4754
|
-
case "object_op":
|
|
4755
|
-
case "aggregate_comparison":
|
|
4756
|
-
case "vector_similarity":
|
|
4757
|
-
case "fulltext_match": {
|
|
4758
|
-
return false;
|
|
4759
|
-
}
|
|
4760
|
-
}
|
|
4761
|
-
}
|
|
4762
|
-
return false;
|
|
4763
|
-
}
|
|
4764
|
-
function collectLeafQueries(query, leaves, basePrefix) {
|
|
4765
|
-
if ("__type" in query) {
|
|
4766
|
-
collectLeafQueries(query.left, leaves, basePrefix);
|
|
4767
|
-
collectLeafQueries(query.right, leaves, basePrefix);
|
|
4768
|
-
} else {
|
|
4769
|
-
const index = leaves.length;
|
|
4770
|
-
leaves.push({ ast: query, prefix: `${basePrefix}${index}` });
|
|
4771
|
-
}
|
|
4772
|
-
}
|
|
4773
|
-
function compilePrefixedStartCte(ast, prefix, graphId, ctx) {
|
|
4774
|
-
const temporalFilterPass = createTemporalFilterPass(
|
|
4775
|
-
ast,
|
|
4776
|
-
ctx.dialect.currentTimestamp()
|
|
4777
|
-
);
|
|
4778
|
-
const alias = ast.start.alias;
|
|
4779
|
-
const kinds = ast.start.kinds;
|
|
4780
|
-
const kindFilter = kinds.length === 1 ? sql`kind = ${kinds[0]}` : sql`kind IN (${sql.join(
|
|
4781
|
-
kinds.map((k) => sql`${k}`),
|
|
4782
|
-
sql`, `
|
|
4783
|
-
)})`;
|
|
4784
|
-
const temporalFilter = temporalFilterPass.forAlias();
|
|
4785
|
-
const cteContext = { ...ctx, cteColumnPrefix: "" };
|
|
4786
|
-
const predicateClauses = ast.predicates.filter((p) => p.targetAlias === alias).map((p) => compilePredicateExpression(p.expression, cteContext));
|
|
4787
|
-
const whereClauses = [
|
|
4788
|
-
sql`graph_id = ${graphId}`,
|
|
4789
|
-
kindFilter,
|
|
4790
|
-
temporalFilter,
|
|
4791
|
-
...predicateClauses
|
|
4792
|
-
];
|
|
4793
|
-
const cteName = `cte_${prefix}_${alias}`;
|
|
4794
|
-
return sql`
|
|
4795
|
-
${sql.raw(cteName)} AS (
|
|
4796
|
-
SELECT
|
|
4797
|
-
id AS ${sql.raw(alias)}_id,
|
|
4798
|
-
kind AS ${sql.raw(alias)}_kind,
|
|
4799
|
-
props AS ${sql.raw(alias)}_props,
|
|
4800
|
-
version AS ${sql.raw(alias)}_version,
|
|
4801
|
-
valid_from AS ${sql.raw(alias)}_valid_from,
|
|
4802
|
-
valid_to AS ${sql.raw(alias)}_valid_to,
|
|
4803
|
-
created_at AS ${sql.raw(alias)}_created_at,
|
|
4804
|
-
updated_at AS ${sql.raw(alias)}_updated_at,
|
|
4805
|
-
deleted_at AS ${sql.raw(alias)}_deleted_at
|
|
4806
|
-
FROM ${ctx.schema.nodesTable}
|
|
4807
|
-
WHERE ${sql.join(whereClauses, sql` AND `)}
|
|
4808
|
-
)
|
|
4809
|
-
`;
|
|
4810
|
-
}
|
|
4811
|
-
function compilePrefixedSelect(ast, prefix, dialect) {
|
|
4812
|
-
const alias = ast.start.alias;
|
|
4813
|
-
const cteName = `cte_${prefix}_${alias}`;
|
|
4814
|
-
const fields = ast.projection.fields;
|
|
4815
|
-
const projection = fields.length === 0 ? sql.raw("*") : sql.join(
|
|
4816
|
-
fields.map((f) => {
|
|
4817
|
-
const source = compileFieldValueForSetOp(
|
|
4818
|
-
f.source,
|
|
4819
|
-
prefix,
|
|
4820
|
-
alias,
|
|
4821
|
-
dialect
|
|
4822
|
-
);
|
|
4823
|
-
return sql`${source} AS ${sql.raw(dialect.quoteIdentifier(f.outputName))}`;
|
|
4824
|
-
}),
|
|
4825
|
-
sql`, `
|
|
4826
|
-
);
|
|
4827
|
-
return sql`SELECT ${projection} FROM ${sql.raw(cteName)}`;
|
|
4828
|
-
}
|
|
4829
|
-
function compileFieldValueForSetOp(source, prefix, alias, dialect) {
|
|
4830
|
-
if ("__type" in source && source.__type === "aggregate") {
|
|
4831
|
-
const { field: field2, function: fn } = source;
|
|
4832
|
-
switch (fn) {
|
|
4833
|
-
case "count":
|
|
4834
|
-
case "countDistinct":
|
|
4835
|
-
case "sum":
|
|
4836
|
-
case "avg":
|
|
4837
|
-
case "min":
|
|
4838
|
-
case "max": {
|
|
4839
|
-
const column = compileFieldColumnForSetOp(field2, prefix, dialect);
|
|
4840
|
-
if (fn === "countDistinct") {
|
|
4841
|
-
return sql`COUNT(DISTINCT ${column})`;
|
|
4842
|
-
}
|
|
4843
|
-
return sql`${sql.raw(fn.toUpperCase())}(${column})`;
|
|
4844
|
-
}
|
|
4845
|
-
default: {
|
|
4846
|
-
throw new CompilerInvariantError(
|
|
4847
|
-
`Unknown aggregate function: ${String(fn)}`
|
|
4848
|
-
);
|
|
4849
|
-
}
|
|
4850
|
-
}
|
|
4851
|
-
}
|
|
4852
|
-
return compileFieldColumnForSetOp(source, prefix, dialect);
|
|
4853
|
-
}
|
|
4854
|
-
function compileFieldColumnForSetOp(field2, prefix, dialect) {
|
|
4855
|
-
const cteName = `cte_${prefix}_${field2.alias}`;
|
|
4856
|
-
const alias = field2.alias;
|
|
4857
|
-
if (field2.path.length === 1) {
|
|
4858
|
-
const columnName = field2.path[0];
|
|
4859
|
-
const columnMap = {
|
|
4860
|
-
id: "_id",
|
|
4861
|
-
kind: "_kind",
|
|
4862
|
-
props: "_props",
|
|
4863
|
-
version: "_version",
|
|
4864
|
-
valid_from: "_valid_from",
|
|
4865
|
-
valid_to: "_valid_to",
|
|
4866
|
-
created_at: "_created_at",
|
|
4867
|
-
updated_at: "_updated_at",
|
|
4868
|
-
deleted_at: "_deleted_at"
|
|
4869
|
-
};
|
|
4870
|
-
if (columnName === void 0) return sql.raw(`${cteName}.${alias}_props`);
|
|
4871
|
-
const suffix = columnMap[columnName];
|
|
4872
|
-
if (suffix) {
|
|
4873
|
-
return sql.raw(`${cteName}.${alias}${suffix}`);
|
|
4874
|
-
}
|
|
4875
|
-
}
|
|
4876
|
-
const column = sql.raw(`${cteName}.${alias}_props`);
|
|
4877
|
-
const pointer = field2.jsonPointer;
|
|
4878
|
-
if (!pointer) {
|
|
4879
|
-
return column;
|
|
4880
|
-
}
|
|
4881
|
-
return compileTypedJsonExtract({
|
|
4882
|
-
column,
|
|
4883
|
-
dialect,
|
|
4884
|
-
fallback: "text",
|
|
4885
|
-
pointer,
|
|
4886
|
-
valueType: field2.valueType
|
|
4887
|
-
});
|
|
4888
|
-
}
|
|
4889
|
-
function buildCompoundSelect(op, leaves, selectStatements) {
|
|
4890
|
-
const prefixToSelect = /* @__PURE__ */ new Map();
|
|
4891
|
-
for (const [index, leaf] of leaves.entries()) {
|
|
4892
|
-
prefixToSelect.set(leaf.prefix, selectStatements[index]);
|
|
4893
|
-
}
|
|
4894
|
-
return buildCompoundSelectRecursive(op, leaves, prefixToSelect);
|
|
4895
|
-
}
|
|
4896
|
-
function buildCompoundSelectRecursive(query, leaves, prefixToSelect) {
|
|
4897
|
-
if (!("__type" in query)) {
|
|
4898
|
-
const leaf = leaves.find((l) => l.ast === query);
|
|
4899
|
-
if (!leaf) {
|
|
4900
|
-
throw new CompilerInvariantError("Leaf query not found in leaves array");
|
|
4901
|
-
}
|
|
4902
|
-
return prefixToSelect.get(leaf.prefix);
|
|
4903
|
-
}
|
|
4904
|
-
const left = buildCompoundSelectRecursive(query.left, leaves, prefixToSelect);
|
|
4905
|
-
const right = buildCompoundSelectRecursive(
|
|
4906
|
-
query.right,
|
|
4907
|
-
leaves,
|
|
4908
|
-
prefixToSelect
|
|
4909
|
-
);
|
|
4910
|
-
const opSql = sql.raw(OPERATOR_MAP[query.operator]);
|
|
4911
|
-
return sql`${left} ${opSql} ${right}`;
|
|
4912
|
-
}
|
|
4913
|
-
function getLeftmostProjection(op) {
|
|
4914
|
-
let current = op.left;
|
|
4915
|
-
while ("__type" in current) {
|
|
4916
|
-
current = current.left;
|
|
4917
|
-
}
|
|
4918
|
-
return current.projection;
|
|
4919
|
-
}
|
|
4920
|
-
function normalizeFieldRefKey(field2) {
|
|
4921
|
-
let pointer = field2.jsonPointer;
|
|
4922
|
-
if (pointer === void 0 && field2.path.length > 1 && field2.path[0] === "props") {
|
|
4923
|
-
pointer = jsonPointer(field2.path.slice(1));
|
|
4924
|
-
}
|
|
4925
|
-
const basePath = field2.path.length > 0 && field2.path[0] === "props" ? "props" : field2.path.join(".");
|
|
4926
|
-
return `${field2.alias}:${basePath}:${pointer ?? ""}`;
|
|
4927
|
-
}
|
|
4928
|
-
function matchFieldToProjection(field2, projection) {
|
|
4929
|
-
const targetKey = normalizeFieldRefKey(field2);
|
|
4930
|
-
for (const projected of projection.fields) {
|
|
4931
|
-
const source = projected.source;
|
|
4932
|
-
if (!("__type" in source) || source.__type !== "field_ref") continue;
|
|
4933
|
-
if (normalizeFieldRefKey(source) === targetKey) {
|
|
4934
|
-
return projected;
|
|
4935
|
-
}
|
|
4936
|
-
}
|
|
4937
|
-
return void 0;
|
|
4938
|
-
}
|
|
4939
|
-
function buildSetOperationSuffixClauses(op, dialect) {
|
|
4940
|
-
const clauses = [];
|
|
4941
|
-
if (op.orderBy && op.orderBy.length > 0) {
|
|
4942
|
-
const projection = getLeftmostProjection(op);
|
|
4943
|
-
if (projection.fields.length === 0) {
|
|
4944
|
-
throw new UnsupportedPredicateError(
|
|
4945
|
-
"Set operation ORDER BY requires explicit field projection. SELECT * does not provide stable output column names for ordering. Use .select() to specify which fields to project."
|
|
4946
|
-
);
|
|
4947
|
-
}
|
|
4948
|
-
const orderParts = [];
|
|
4949
|
-
for (const orderSpec of op.orderBy) {
|
|
4950
|
-
const projected = matchFieldToProjection(orderSpec.field, projection);
|
|
4951
|
-
if (!projected) {
|
|
4952
|
-
const fieldDesc = orderSpec.field.jsonPointer ? `${orderSpec.field.alias}.props${orderSpec.field.jsonPointer}` : `${orderSpec.field.alias}.${orderSpec.field.path.join(".")}`;
|
|
4953
|
-
const availableFields = projection.fields.map((f) => f.outputName).join(", ");
|
|
4954
|
-
throw new UnsupportedPredicateError(
|
|
4955
|
-
`Set operation ORDER BY field "${fieldDesc}" is not in the projection. ORDER BY for UNION/INTERSECT/EXCEPT must reference projected columns. Available columns: ${availableFields}`
|
|
4956
|
-
);
|
|
4957
|
-
}
|
|
4958
|
-
const columnRef = sql.raw(dialect.quoteIdentifier(projected.outputName));
|
|
4959
|
-
const dir = sql.raw(orderSpec.direction.toUpperCase());
|
|
4960
|
-
const nulls = orderSpec.nulls ?? (orderSpec.direction === "asc" ? "last" : "first");
|
|
4961
|
-
const nullsDir = sql.raw(nulls === "first" ? "DESC" : "ASC");
|
|
4962
|
-
orderParts.push(
|
|
4963
|
-
sql`(${columnRef} IS NULL) ${nullsDir}`,
|
|
4964
|
-
sql`${columnRef} ${dir}`
|
|
4965
|
-
);
|
|
4966
|
-
}
|
|
4967
|
-
clauses.push(sql`ORDER BY ${sql.join(orderParts, sql`, `)}`);
|
|
4968
|
-
}
|
|
4969
|
-
if (op.limit !== void 0) {
|
|
4970
|
-
clauses.push(sql`LIMIT ${op.limit}`);
|
|
4971
|
-
}
|
|
4972
|
-
if (op.offset !== void 0) {
|
|
4973
|
-
clauses.push(sql`OFFSET ${op.offset}`);
|
|
4974
|
-
}
|
|
4975
|
-
return clauses;
|
|
4976
|
-
}
|
|
4977
|
-
|
|
4978
|
-
// src/query/compiler/standard-pass-pipeline.ts
|
|
4979
|
-
var TRAVERSAL_LIMIT_PUSHDOWN_MULTIPLIER = 8;
|
|
4980
|
-
var TRAVERSAL_LIMIT_PUSHDOWN_MAX = 1e4;
|
|
4981
|
-
function isColumnPruningEnabled(ast) {
|
|
4982
|
-
if (ast.selectiveFields && ast.selectiveFields.length > 0) {
|
|
4983
|
-
return true;
|
|
4984
|
-
}
|
|
4985
|
-
if (ast.groupBy || ast.having) {
|
|
4986
|
-
return true;
|
|
4987
|
-
}
|
|
4988
|
-
return ast.projection.fields.some(
|
|
4989
|
-
(field2) => field2.source.__type === "aggregate"
|
|
4825
|
+
return ast.projection.fields.some(
|
|
4826
|
+
(field2) => field2.source.__type === "aggregate"
|
|
4990
4827
|
);
|
|
4991
4828
|
}
|
|
4992
4829
|
function markPredicateFieldsAsRequired(requiredColumnsByAlias, expression) {
|
|
@@ -5201,7 +5038,11 @@ function runStandardQueryPassPipeline(ast, graphId, ctx) {
|
|
|
5201
5038
|
const vectorPass = runCompilerPass(state, {
|
|
5202
5039
|
name: "vector_predicate",
|
|
5203
5040
|
execute(currentState) {
|
|
5204
|
-
return runVectorPredicatePass(
|
|
5041
|
+
return runVectorPredicatePass(
|
|
5042
|
+
currentState.ast,
|
|
5043
|
+
currentState.ctx.dialect,
|
|
5044
|
+
currentState.ctx.vectorStrategy
|
|
5045
|
+
).vectorPredicate;
|
|
5205
5046
|
},
|
|
5206
5047
|
update(currentState, vectorPredicate) {
|
|
5207
5048
|
return {
|
|
@@ -5358,7 +5199,10 @@ function compileQuery(ast, graphId, options = "sqlite") {
|
|
|
5358
5199
|
const ctx = {
|
|
5359
5200
|
dialect: adapter,
|
|
5360
5201
|
schema,
|
|
5361
|
-
compileQuery: (subAst, subGraphId) => compileQuery(subAst, subGraphId, propagateOptions(options_))
|
|
5202
|
+
compileQuery: (subAst, subGraphId) => compileQuery(subAst, subGraphId, propagateOptions(options_)),
|
|
5203
|
+
...options_.vectorStrategy === void 0 ? {} : { vectorStrategy: options_.vectorStrategy },
|
|
5204
|
+
windowFunctions: options_.windowFunctions ?? true,
|
|
5205
|
+
...options_.vectorSlots === void 0 ? {} : { vectorSlots: options_.vectorSlots }
|
|
5362
5206
|
};
|
|
5363
5207
|
if (hasVariableLengthTraversal(ast)) {
|
|
5364
5208
|
const lowered = tryLowerSingleHopRecursiveTraversal(ast);
|
|
@@ -5393,14 +5237,13 @@ function tryLowerSingleHopRecursiveTraversal(ast) {
|
|
|
5393
5237
|
function compileSetOperation2(op, graphId, options = "sqlite") {
|
|
5394
5238
|
const options_ = typeof options === "string" ? { dialect: options } : options;
|
|
5395
5239
|
const dialect = options_.dialect ?? "sqlite";
|
|
5396
|
-
const schema = options_.schema ?? DEFAULT_SQL_SCHEMA;
|
|
5397
5240
|
const adapter = resolveDialectAdapter(dialect, options_.fulltextStrategy);
|
|
5398
5241
|
return compileSetOperation(
|
|
5399
5242
|
op,
|
|
5400
5243
|
graphId,
|
|
5401
5244
|
adapter,
|
|
5402
|
-
|
|
5403
|
-
|
|
5245
|
+
(ast, gid) => compileQuery(ast, gid, propagateOptions(options_)),
|
|
5246
|
+
options_.vectorStrategy
|
|
5404
5247
|
);
|
|
5405
5248
|
}
|
|
5406
5249
|
function resolveDialectAdapter(dialect, fulltextStrategy) {
|
|
@@ -5414,7 +5257,10 @@ function propagateOptions(options_) {
|
|
|
5414
5257
|
return {
|
|
5415
5258
|
dialect: options_.dialect ?? "sqlite",
|
|
5416
5259
|
...options_.schema === void 0 ? {} : { schema: options_.schema },
|
|
5417
|
-
...options_.fulltextStrategy === void 0 ? {} : { fulltextStrategy: options_.fulltextStrategy }
|
|
5260
|
+
...options_.fulltextStrategy === void 0 ? {} : { fulltextStrategy: options_.fulltextStrategy },
|
|
5261
|
+
...options_.vectorStrategy === void 0 ? {} : { vectorStrategy: options_.vectorStrategy },
|
|
5262
|
+
windowFunctions: options_.windowFunctions ?? true,
|
|
5263
|
+
...options_.vectorSlots === void 0 ? {} : { vectorSlots: options_.vectorSlots }
|
|
5418
5264
|
};
|
|
5419
5265
|
}
|
|
5420
5266
|
function resolveCountAggregateFastPath(ast) {
|
|
@@ -5694,6 +5540,27 @@ function selectStandardOrderBy(input) {
|
|
|
5694
5540
|
dialect
|
|
5695
5541
|
});
|
|
5696
5542
|
}
|
|
5543
|
+
function selectWindowFunctionOperation(vectorPredicate, fulltextPredicate) {
|
|
5544
|
+
if (vectorPredicate !== void 0 && fulltextPredicate !== void 0) {
|
|
5545
|
+
return "hybrid relevance ranking";
|
|
5546
|
+
}
|
|
5547
|
+
if (vectorPredicate !== void 0) return "vector relevance ranking";
|
|
5548
|
+
return "fulltext relevance ranking";
|
|
5549
|
+
}
|
|
5550
|
+
function assertWindowFunctionsSupported(ctx, operation) {
|
|
5551
|
+
if (ctx.windowFunctions) return;
|
|
5552
|
+
throw new ConfigurationError(
|
|
5553
|
+
`${operation} requires SQL window functions, but this backend profile declares windowFunctions: false.`,
|
|
5554
|
+
{
|
|
5555
|
+
capability: "windowFunctions",
|
|
5556
|
+
operation,
|
|
5557
|
+
windowFunctions: false
|
|
5558
|
+
},
|
|
5559
|
+
{
|
|
5560
|
+
suggestion: "Use a backend profile that supports SQL window functions, or avoid this query shape."
|
|
5561
|
+
}
|
|
5562
|
+
);
|
|
5563
|
+
}
|
|
5697
5564
|
function compileStandardQueryWithCteStrategy(ast, graphId, ctx) {
|
|
5698
5565
|
const { dialect } = ctx;
|
|
5699
5566
|
const {
|
|
@@ -5721,6 +5588,12 @@ function compileStandardQueryWithCteStrategy(ast, graphId, ctx) {
|
|
|
5721
5588
|
{ phase: "standard-pass-pipeline" }
|
|
5722
5589
|
);
|
|
5723
5590
|
}
|
|
5591
|
+
if (vectorPredicate !== void 0 || fulltextPredicate !== void 0) {
|
|
5592
|
+
assertWindowFunctionsSupported(
|
|
5593
|
+
ctx,
|
|
5594
|
+
selectWindowFunctionOperation(vectorPredicate, fulltextPredicate)
|
|
5595
|
+
);
|
|
5596
|
+
}
|
|
5724
5597
|
if (!vectorPredicate && !fulltextPredicate) {
|
|
5725
5598
|
const fastPathSql = compileCountAggregateFastPath(
|
|
5726
5599
|
ast,
|
|
@@ -5932,12 +5805,7 @@ function extractCursorValue(row, spec) {
|
|
|
5932
5805
|
current = record[segment];
|
|
5933
5806
|
}
|
|
5934
5807
|
if (jsonPointer2) {
|
|
5935
|
-
|
|
5936
|
-
for (const segment of segments) {
|
|
5937
|
-
if (current === null || current === void 0) return void 0;
|
|
5938
|
-
if (typeof current !== "object") return void 0;
|
|
5939
|
-
current = current[segment];
|
|
5940
|
-
}
|
|
5808
|
+
return resolveJsonPointer(current, jsonPointer2);
|
|
5941
5809
|
}
|
|
5942
5810
|
return current;
|
|
5943
5811
|
}
|
|
@@ -7005,12 +6873,32 @@ function buildQueryAst(config, state) {
|
|
|
7005
6873
|
// src/query/builder/compile-options.ts
|
|
7006
6874
|
function buildCompileOptions(config) {
|
|
7007
6875
|
const fulltextStrategy = config.backend?.fulltextStrategy;
|
|
6876
|
+
const vectorStrategy = config.backend?.vectorStrategy;
|
|
7008
6877
|
return {
|
|
7009
6878
|
dialect: config.dialect ?? "sqlite",
|
|
7010
6879
|
schema: config.schema,
|
|
7011
|
-
|
|
6880
|
+
windowFunctions: config.backend?.capabilities.windowFunctions ?? true,
|
|
6881
|
+
...fulltextStrategy === void 0 ? {} : { fulltextStrategy },
|
|
6882
|
+
...vectorStrategy === void 0 ? {} : { vectorStrategy, vectorSlots: buildVectorSlots(config.registry) }
|
|
7012
6883
|
};
|
|
7013
6884
|
}
|
|
6885
|
+
var vectorSlotsCache = /* @__PURE__ */ new WeakMap();
|
|
6886
|
+
function buildVectorSlots(registry) {
|
|
6887
|
+
const cached = vectorSlotsCache.get(registry);
|
|
6888
|
+
if (cached !== void 0) return cached;
|
|
6889
|
+
const slots = /* @__PURE__ */ new Map();
|
|
6890
|
+
for (const [nodeKind, nodeType] of registry.nodeKinds) {
|
|
6891
|
+
for (const field2 of resolveEmbeddingFields(nodeType.schema)) {
|
|
6892
|
+
slots.set(vectorSlotKey(nodeKind, field2.fieldPath), {
|
|
6893
|
+
dimensions: field2.dimensions,
|
|
6894
|
+
metric: field2.metric,
|
|
6895
|
+
indexType: field2.indexType
|
|
6896
|
+
});
|
|
6897
|
+
}
|
|
6898
|
+
}
|
|
6899
|
+
vectorSlotsCache.set(registry, slots);
|
|
6900
|
+
return slots;
|
|
6901
|
+
}
|
|
7014
6902
|
function toLiteral(value) {
|
|
7015
6903
|
if (value === null) {
|
|
7016
6904
|
throw new ConfigurationError(
|
|
@@ -10630,6 +10518,27 @@ function createEdgeCollection(config) {
|
|
|
10630
10518
|
};
|
|
10631
10519
|
}
|
|
10632
10520
|
|
|
10521
|
+
// src/store/node-fetch.ts
|
|
10522
|
+
async function getNodeRowsByIds(backend, graphId, kind, ids) {
|
|
10523
|
+
const rowsById = /* @__PURE__ */ new Map();
|
|
10524
|
+
if (ids.length === 0) return rowsById;
|
|
10525
|
+
if (backend.getNodes !== void 0) {
|
|
10526
|
+
const rows2 = await backend.getNodes(graphId, kind, ids);
|
|
10527
|
+
for (const row of rows2) {
|
|
10528
|
+
rowsById.set(row.id, row);
|
|
10529
|
+
}
|
|
10530
|
+
return rowsById;
|
|
10531
|
+
}
|
|
10532
|
+
const uniqueIds = [...new Set(ids)];
|
|
10533
|
+
const rows = await Promise.all(
|
|
10534
|
+
uniqueIds.map((id) => backend.getNode(graphId, kind, id))
|
|
10535
|
+
);
|
|
10536
|
+
for (const row of rows) {
|
|
10537
|
+
if (row !== void 0) rowsById.set(row.id, row);
|
|
10538
|
+
}
|
|
10539
|
+
return rowsById;
|
|
10540
|
+
}
|
|
10541
|
+
|
|
10633
10542
|
// src/store/collections/node-collection.ts
|
|
10634
10543
|
function narrowNode(node) {
|
|
10635
10544
|
return node;
|
|
@@ -10671,7 +10580,8 @@ function createNodeCollection(config) {
|
|
|
10671
10580
|
executeGetOrCreateByConstraint,
|
|
10672
10581
|
executeBulkGetOrCreateByConstraint,
|
|
10673
10582
|
executeFindByConstraint,
|
|
10674
|
-
executeBulkFindByConstraint
|
|
10583
|
+
executeBulkFindByConstraint,
|
|
10584
|
+
executeBulkFindByIndex
|
|
10675
10585
|
} = config;
|
|
10676
10586
|
return {
|
|
10677
10587
|
async create(props, options) {
|
|
@@ -10692,27 +10602,13 @@ function createNodeCollection(config) {
|
|
|
10692
10602
|
},
|
|
10693
10603
|
async getByIds(ids, options) {
|
|
10694
10604
|
if (ids.length === 0) return [];
|
|
10695
|
-
|
|
10696
|
-
|
|
10697
|
-
const
|
|
10698
|
-
|
|
10699
|
-
|
|
10700
|
-
|
|
10701
|
-
|
|
10702
|
-
const row = rowMap.get(id);
|
|
10703
|
-
if (!row) return;
|
|
10704
|
-
if (!matchesTemporalMode(row, options)) return;
|
|
10705
|
-
return narrowNode(rowToNode2(row));
|
|
10706
|
-
});
|
|
10707
|
-
}
|
|
10708
|
-
return Promise.all(
|
|
10709
|
-
ids.map(async (id) => {
|
|
10710
|
-
const row = await backend.getNode(graphId, kind, id);
|
|
10711
|
-
if (!row) return;
|
|
10712
|
-
if (!matchesTemporalMode(row, options)) return;
|
|
10713
|
-
return narrowNode(rowToNode2(row));
|
|
10714
|
-
})
|
|
10715
|
-
);
|
|
10605
|
+
const rowsById = await getNodeRowsByIds(backend, graphId, kind, ids);
|
|
10606
|
+
return ids.map((id) => {
|
|
10607
|
+
const row = rowsById.get(id);
|
|
10608
|
+
if (!row) return;
|
|
10609
|
+
if (!matchesTemporalMode(row, options)) return;
|
|
10610
|
+
return narrowNode(rowToNode2(row));
|
|
10611
|
+
});
|
|
10716
10612
|
},
|
|
10717
10613
|
async update(id, props, options) {
|
|
10718
10614
|
const result = await executeNodeUpdate2(
|
|
@@ -10909,6 +10805,20 @@ function createNodeCollection(config) {
|
|
|
10909
10805
|
(result) => result === void 0 ? void 0 : narrowNode(result)
|
|
10910
10806
|
);
|
|
10911
10807
|
},
|
|
10808
|
+
async bulkFindByIndex(indexName, items, options) {
|
|
10809
|
+
if (items.length === 0) return [];
|
|
10810
|
+
const mappedItems = items.map((item) => ({
|
|
10811
|
+
props: item.props
|
|
10812
|
+
}));
|
|
10813
|
+
const results = await executeBulkFindByIndex(
|
|
10814
|
+
kind,
|
|
10815
|
+
indexName,
|
|
10816
|
+
mappedItems,
|
|
10817
|
+
backend,
|
|
10818
|
+
options
|
|
10819
|
+
);
|
|
10820
|
+
return results.map((bucket) => narrowNodes(bucket));
|
|
10821
|
+
},
|
|
10912
10822
|
async getOrCreateByConstraint(constraintName, props, options) {
|
|
10913
10823
|
const execute = async (target) => {
|
|
10914
10824
|
const result = await executeGetOrCreateByConstraint(
|
|
@@ -11212,7 +11122,7 @@ async function materializeVectorIndex(declaration, backend, graphId, schemaVersi
|
|
|
11212
11122
|
);
|
|
11213
11123
|
}
|
|
11214
11124
|
const vectorCapability = backend.capabilities.vector;
|
|
11215
|
-
if (vectorCapability?.supported !== true || backend.createVectorIndex === void 0) {
|
|
11125
|
+
if (vectorCapability?.supported !== true || backend.createVectorIndex === void 0 || backend.vectorStrategy === void 0) {
|
|
11216
11126
|
return skippedEntry(
|
|
11217
11127
|
declaration,
|
|
11218
11128
|
`Backend (${backend.dialect}) does not support vector indexes in its current configuration`
|
|
@@ -11224,7 +11134,11 @@ async function materializeVectorIndex(declaration, backend, graphId, schemaVersi
|
|
|
11224
11134
|
`Backend (${backend.dialect}) does not support index type "${declaration.indexType}" for vector indexes; supported: ${vectorCapability.indexTypes.join(", ") || "(none)"}`
|
|
11225
11135
|
);
|
|
11226
11136
|
}
|
|
11227
|
-
const embeddingsTable = backend.
|
|
11137
|
+
const embeddingsTable = backend.vectorStrategy.tableName(
|
|
11138
|
+
graphId,
|
|
11139
|
+
declaration.kind,
|
|
11140
|
+
declaration.fieldPath
|
|
11141
|
+
);
|
|
11228
11142
|
const signature = await computeIndexSignature(
|
|
11229
11143
|
backend.dialect,
|
|
11230
11144
|
embeddingsTable,
|
|
@@ -11387,16 +11301,16 @@ async function materializeRemovals(context, options = {}) {
|
|
|
11387
11301
|
}
|
|
11388
11302
|
await ensureFocusedStatusTable(backend, backend.ensureKindRemovalsTable);
|
|
11389
11303
|
await reconcilePendingRemovals(context);
|
|
11304
|
+
const reclaimedVectorFields = await reclaimRemovedVectorFieldTables(context);
|
|
11390
11305
|
const pending = await backend.getPendingKindRemovals(graphId);
|
|
11391
11306
|
const kindFilter = options.kinds === void 0 ? void 0 : new Set(options.kinds);
|
|
11392
11307
|
const candidates = pending.filter(
|
|
11393
11308
|
(row) => kindFilter === void 0 ? true : kindFilter.has(row.kindName)
|
|
11394
11309
|
);
|
|
11395
|
-
if (candidates.length === 0) return { results: [] };
|
|
11310
|
+
if (candidates.length === 0) return { results: [], reclaimedVectorFields };
|
|
11396
11311
|
const tableNames = backend.tableNames;
|
|
11397
11312
|
const nodesTable = tableNames?.nodes ?? "typegraph_nodes";
|
|
11398
11313
|
const edgesTable = tableNames?.edges ?? "typegraph_edges";
|
|
11399
|
-
const embeddingsTable = tableNames?.embeddings ?? "typegraph_node_embeddings";
|
|
11400
11314
|
const fulltextTable = tableNames?.fulltext ?? "typegraph_node_fulltext";
|
|
11401
11315
|
const uniquesTable = tableNames?.uniques ?? "typegraph_node_uniques";
|
|
11402
11316
|
const ctx = {
|
|
@@ -11404,7 +11318,6 @@ async function materializeRemovals(context, options = {}) {
|
|
|
11404
11318
|
graphId,
|
|
11405
11319
|
nodesTable,
|
|
11406
11320
|
edgesTable,
|
|
11407
|
-
embeddingsTable,
|
|
11408
11321
|
fulltextTable,
|
|
11409
11322
|
uniquesTable
|
|
11410
11323
|
};
|
|
@@ -11413,7 +11326,8 @@ async function materializeRemovals(context, options = {}) {
|
|
|
11413
11326
|
candidates,
|
|
11414
11327
|
options,
|
|
11415
11328
|
(removal) => materializeOne2(removal, ctx)
|
|
11416
|
-
)
|
|
11329
|
+
),
|
|
11330
|
+
reclaimedVectorFields
|
|
11417
11331
|
};
|
|
11418
11332
|
}
|
|
11419
11333
|
async function reconcilePendingRemovals(context) {
|
|
@@ -11500,15 +11414,13 @@ async function materializeOne2(row, ctx) {
|
|
|
11500
11414
|
executeDdl(
|
|
11501
11415
|
`DELETE FROM ${quote(ctx.edgesTable)} WHERE graph_id = ${graphLit} AND (from_kind = ${kindLit} OR to_kind = ${kindLit})`
|
|
11502
11416
|
),
|
|
11503
|
-
executeDdl(
|
|
11504
|
-
`DELETE FROM ${quote(ctx.embeddingsTable)} WHERE graph_id = ${graphLit} AND node_kind = ${kindLit}`
|
|
11505
|
-
),
|
|
11506
11417
|
executeDdl(
|
|
11507
11418
|
`DELETE FROM ${quote(ctx.fulltextTable)} WHERE graph_id = ${graphLit} AND node_kind = ${kindLit}`
|
|
11508
11419
|
),
|
|
11509
11420
|
executeDdl(
|
|
11510
11421
|
`DELETE FROM ${quote(ctx.uniquesTable)} WHERE graph_id = ${graphLit} AND node_kind = ${kindLit}`
|
|
11511
|
-
)
|
|
11422
|
+
),
|
|
11423
|
+
...buildEmbeddingTableCleanup(ctx, row)
|
|
11512
11424
|
);
|
|
11513
11425
|
} else {
|
|
11514
11426
|
deletes.push(
|
|
@@ -11552,6 +11464,128 @@ async function materializeOne2(row, ctx) {
|
|
|
11552
11464
|
};
|
|
11553
11465
|
}
|
|
11554
11466
|
}
|
|
11467
|
+
function buildEmbeddingTableCleanup(ctx, row) {
|
|
11468
|
+
const vectorStrategy = ctx.backend.vectorStrategy;
|
|
11469
|
+
if (vectorStrategy === void 0) return [];
|
|
11470
|
+
const executeDdl = ctx.backend.executeDdl;
|
|
11471
|
+
const cleanup = (async () => {
|
|
11472
|
+
const slots = await resolveRemovedKindEmbeddingSlots(ctx.backend, row);
|
|
11473
|
+
await Promise.all(
|
|
11474
|
+
slots.map(
|
|
11475
|
+
(slot) => dropVectorSlotStorage(vectorStrategy, executeDdl, slot)
|
|
11476
|
+
)
|
|
11477
|
+
);
|
|
11478
|
+
})();
|
|
11479
|
+
return [cleanup];
|
|
11480
|
+
}
|
|
11481
|
+
async function dropVectorSlotStorage(strategy, executeDdl, slot) {
|
|
11482
|
+
try {
|
|
11483
|
+
for (const ddl of strategy.buildDropStorage(slot)) {
|
|
11484
|
+
await executeDdl(ddl);
|
|
11485
|
+
}
|
|
11486
|
+
} catch (error) {
|
|
11487
|
+
if (!isMissingTableError(error)) throw error;
|
|
11488
|
+
}
|
|
11489
|
+
}
|
|
11490
|
+
async function resolveRemovedKindEmbeddingSlots(backend, row) {
|
|
11491
|
+
const priorVersion = row.schemaVersion - 1;
|
|
11492
|
+
if (priorVersion < 1) return [];
|
|
11493
|
+
const priorRow = await backend.getSchemaVersion(row.graphId, priorVersion);
|
|
11494
|
+
if (priorRow === void 0) return [];
|
|
11495
|
+
const priorSchema = parseSerializedSchema(priorRow.schema_doc);
|
|
11496
|
+
const slots = /* @__PURE__ */ new Map();
|
|
11497
|
+
for (const declaration of priorSchema.indexes ?? []) {
|
|
11498
|
+
if (declaration.entity === "vector" && declaration.kind === row.kindName) {
|
|
11499
|
+
slots.set(declaration.fieldPath, {
|
|
11500
|
+
graphId: row.graphId,
|
|
11501
|
+
nodeKind: row.kindName,
|
|
11502
|
+
fieldPath: declaration.fieldPath,
|
|
11503
|
+
dimensions: declaration.dimensions,
|
|
11504
|
+
metric: declaration.metric,
|
|
11505
|
+
indexType: declaration.indexType
|
|
11506
|
+
});
|
|
11507
|
+
}
|
|
11508
|
+
}
|
|
11509
|
+
return [...slots.values()];
|
|
11510
|
+
}
|
|
11511
|
+
function vectorFieldKey(kind, fieldPath) {
|
|
11512
|
+
return `${kind}\0${fieldPath}`;
|
|
11513
|
+
}
|
|
11514
|
+
async function reclaimRemovedVectorFieldTables(context) {
|
|
11515
|
+
const { backend, graphId } = context;
|
|
11516
|
+
const vectorStrategy = backend.vectorStrategy;
|
|
11517
|
+
const executeDdl = backend.executeDdl;
|
|
11518
|
+
if (vectorStrategy === void 0 || executeDdl === void 0) return [];
|
|
11519
|
+
const activeRow = await backend.getActiveSchema(graphId);
|
|
11520
|
+
if (activeRow === void 0) return [];
|
|
11521
|
+
const cacheKey = `${graphId}\0${activeRow.version}`;
|
|
11522
|
+
let perBackend = reclaimCache.get(backend);
|
|
11523
|
+
const cached = perBackend?.get(cacheKey);
|
|
11524
|
+
if (cached !== void 0) return cached;
|
|
11525
|
+
const activeSchema = parseSerializedSchema(activeRow.schema_doc);
|
|
11526
|
+
const activeVectorFields = /* @__PURE__ */ new Set();
|
|
11527
|
+
for (const declaration of activeSchema.indexes ?? []) {
|
|
11528
|
+
if (declaration.entity === "vector") {
|
|
11529
|
+
activeVectorFields.add(
|
|
11530
|
+
vectorFieldKey(declaration.kind, declaration.fieldPath)
|
|
11531
|
+
);
|
|
11532
|
+
}
|
|
11533
|
+
}
|
|
11534
|
+
const survivingKinds = new Set(Object.keys(activeSchema.nodes));
|
|
11535
|
+
const historical = /* @__PURE__ */ new Map();
|
|
11536
|
+
for (let version = activeRow.version - 1; version >= 1; version -= 1) {
|
|
11537
|
+
const priorRow = await backend.getSchemaVersion(graphId, version);
|
|
11538
|
+
if (priorRow === void 0) continue;
|
|
11539
|
+
const priorSchema = parseSerializedSchema(priorRow.schema_doc);
|
|
11540
|
+
for (const declaration of priorSchema.indexes ?? []) {
|
|
11541
|
+
if (declaration.entity !== "vector") continue;
|
|
11542
|
+
const key = vectorFieldKey(declaration.kind, declaration.fieldPath);
|
|
11543
|
+
if (historical.has(key)) continue;
|
|
11544
|
+
historical.set(key, {
|
|
11545
|
+
kind: declaration.kind,
|
|
11546
|
+
fieldPath: declaration.fieldPath,
|
|
11547
|
+
slot: {
|
|
11548
|
+
graphId,
|
|
11549
|
+
nodeKind: declaration.kind,
|
|
11550
|
+
fieldPath: declaration.fieldPath,
|
|
11551
|
+
dimensions: declaration.dimensions,
|
|
11552
|
+
metric: declaration.metric,
|
|
11553
|
+
indexType: declaration.indexType
|
|
11554
|
+
}
|
|
11555
|
+
});
|
|
11556
|
+
}
|
|
11557
|
+
}
|
|
11558
|
+
const orphans = [...historical.values()].filter(
|
|
11559
|
+
(field2) => !activeVectorFields.has(vectorFieldKey(field2.kind, field2.fieldPath)) && survivingKinds.has(field2.kind)
|
|
11560
|
+
);
|
|
11561
|
+
const results = [];
|
|
11562
|
+
for (const field2 of orphans) {
|
|
11563
|
+
try {
|
|
11564
|
+
await dropVectorSlotStorage(vectorStrategy, executeDdl, field2.slot);
|
|
11565
|
+
results.push({
|
|
11566
|
+
kind: field2.kind,
|
|
11567
|
+
fieldPath: field2.fieldPath,
|
|
11568
|
+
status: "reclaimed"
|
|
11569
|
+
});
|
|
11570
|
+
} catch (error) {
|
|
11571
|
+
results.push({
|
|
11572
|
+
kind: field2.kind,
|
|
11573
|
+
fieldPath: field2.fieldPath,
|
|
11574
|
+
status: "failed",
|
|
11575
|
+
error: error instanceof Error ? error : new Error(String(error))
|
|
11576
|
+
});
|
|
11577
|
+
}
|
|
11578
|
+
}
|
|
11579
|
+
if (results.every((entry2) => entry2.status === "reclaimed")) {
|
|
11580
|
+
if (perBackend === void 0) {
|
|
11581
|
+
perBackend = /* @__PURE__ */ new Map();
|
|
11582
|
+
reclaimCache.set(backend, perBackend);
|
|
11583
|
+
}
|
|
11584
|
+
perBackend.set(cacheKey, results);
|
|
11585
|
+
}
|
|
11586
|
+
return results;
|
|
11587
|
+
}
|
|
11588
|
+
var reclaimCache = /* @__PURE__ */ new WeakMap();
|
|
11555
11589
|
function quote(identifier) {
|
|
11556
11590
|
return `"${identifier.replaceAll('"', '""')}"`;
|
|
11557
11591
|
}
|
|
@@ -12536,46 +12570,10 @@ var embeddingFieldsCache = /* @__PURE__ */ new WeakMap();
|
|
|
12536
12570
|
function getEmbeddingFields(schema) {
|
|
12537
12571
|
const cached = embeddingFieldsCache.get(schema);
|
|
12538
12572
|
if (cached) return cached;
|
|
12539
|
-
const fields =
|
|
12573
|
+
const fields = resolveEmbeddingFields(schema);
|
|
12540
12574
|
embeddingFieldsCache.set(schema, fields);
|
|
12541
12575
|
return fields;
|
|
12542
12576
|
}
|
|
12543
|
-
function computeEmbeddingFields(schema) {
|
|
12544
|
-
if (schema.type !== "object") return [];
|
|
12545
|
-
const def = schema.def;
|
|
12546
|
-
const shape = def.shape;
|
|
12547
|
-
if (!shape) return [];
|
|
12548
|
-
const fields = [];
|
|
12549
|
-
for (const [fieldPath, fieldSchema] of Object.entries(shape)) {
|
|
12550
|
-
const dimensions = getEmbeddingDimensionsFromField(fieldSchema);
|
|
12551
|
-
if (dimensions !== void 0) {
|
|
12552
|
-
fields.push({ fieldPath, dimensions });
|
|
12553
|
-
}
|
|
12554
|
-
}
|
|
12555
|
-
return fields;
|
|
12556
|
-
}
|
|
12557
|
-
function getEmbeddingDimensionsFromField(schema) {
|
|
12558
|
-
const directDimensions = getEmbeddingDimensions(schema);
|
|
12559
|
-
if (directDimensions !== void 0) {
|
|
12560
|
-
return directDimensions;
|
|
12561
|
-
}
|
|
12562
|
-
const unwrapped = unwrapToEmbedding(schema);
|
|
12563
|
-
if (unwrapped) {
|
|
12564
|
-
return getEmbeddingDimensions(unwrapped);
|
|
12565
|
-
}
|
|
12566
|
-
return void 0;
|
|
12567
|
-
}
|
|
12568
|
-
function unwrapToEmbedding(schema) {
|
|
12569
|
-
const type = schema.type;
|
|
12570
|
-
const def = schema.def;
|
|
12571
|
-
if ((type === "optional" || type === "nullable" || type === "default" || type === "readonly") && def.innerType) {
|
|
12572
|
-
if (isEmbeddingSchema(def.innerType)) {
|
|
12573
|
-
return def.innerType;
|
|
12574
|
-
}
|
|
12575
|
-
return unwrapToEmbedding(def.innerType);
|
|
12576
|
-
}
|
|
12577
|
-
return void 0;
|
|
12578
|
-
}
|
|
12579
12577
|
async function syncEmbeddings(ctx, schema, props) {
|
|
12580
12578
|
const { backend } = ctx;
|
|
12581
12579
|
if (!backend.upsertEmbedding || !backend.deleteEmbedding) {
|
|
@@ -12594,14 +12592,19 @@ async function syncEmbeddings(ctx, schema, props) {
|
|
|
12594
12592
|
nodeId: ctx.nodeId,
|
|
12595
12593
|
fieldPath: field2.fieldPath,
|
|
12596
12594
|
embedding: value,
|
|
12597
|
-
dimensions: field2.dimensions
|
|
12595
|
+
dimensions: field2.dimensions,
|
|
12596
|
+
metric: field2.metric,
|
|
12597
|
+
indexType: field2.indexType
|
|
12598
12598
|
});
|
|
12599
12599
|
} else if (value === void 0) {
|
|
12600
12600
|
await backend.deleteEmbedding({
|
|
12601
12601
|
graphId: ctx.graphId,
|
|
12602
12602
|
nodeKind: ctx.nodeKind,
|
|
12603
12603
|
nodeId: ctx.nodeId,
|
|
12604
|
-
fieldPath: field2.fieldPath
|
|
12604
|
+
fieldPath: field2.fieldPath,
|
|
12605
|
+
dimensions: field2.dimensions,
|
|
12606
|
+
metric: field2.metric,
|
|
12607
|
+
indexType: field2.indexType
|
|
12605
12608
|
});
|
|
12606
12609
|
}
|
|
12607
12610
|
}
|
|
@@ -12620,7 +12623,10 @@ async function deleteNodeEmbeddings(ctx, schema) {
|
|
|
12620
12623
|
graphId: ctx.graphId,
|
|
12621
12624
|
nodeKind: ctx.nodeKind,
|
|
12622
12625
|
nodeId: ctx.nodeId,
|
|
12623
|
-
fieldPath: field2.fieldPath
|
|
12626
|
+
fieldPath: field2.fieldPath,
|
|
12627
|
+
dimensions: field2.dimensions,
|
|
12628
|
+
metric: field2.metric,
|
|
12629
|
+
indexType: field2.indexType
|
|
12624
12630
|
});
|
|
12625
12631
|
}
|
|
12626
12632
|
}
|
|
@@ -13360,6 +13366,10 @@ async function executeNodeHardDelete(ctx, kind, id, backend) {
|
|
|
13360
13366
|
await enforceDeleteBehavior(ctx, kind, id, "hard", backend, registration);
|
|
13361
13367
|
const hardDelete = async (target) => {
|
|
13362
13368
|
await target.hardDeleteNode({ graphId: ctx.graphId, kind, id });
|
|
13369
|
+
await deleteNodeEmbeddings(
|
|
13370
|
+
createEmbeddingSyncContext(ctx.graphId, kind, id, target),
|
|
13371
|
+
registration.type.schema
|
|
13372
|
+
);
|
|
13363
13373
|
};
|
|
13364
13374
|
await ("transaction" in backend && backend.capabilities.transactions ? backend.transaction(async (tx) => hardDelete(tx)) : hardDelete(backend));
|
|
13365
13375
|
});
|
|
@@ -13549,6 +13559,212 @@ async function executeNodeBulkFindByConstraint(ctx, kind, constraintName, items,
|
|
|
13549
13559
|
}
|
|
13550
13560
|
return results;
|
|
13551
13561
|
}
|
|
13562
|
+
function resolveNodeIndex(graph, kind, indexName) {
|
|
13563
|
+
getNodeRegistration(graph, kind);
|
|
13564
|
+
const declaration = graph.indexes?.find(
|
|
13565
|
+
(candidate) => candidate.entity === "node" && candidate.kind === kind && candidate.name === indexName
|
|
13566
|
+
);
|
|
13567
|
+
if (declaration?.entity !== "node") {
|
|
13568
|
+
throw new NodeIndexNotFoundError(indexName, kind);
|
|
13569
|
+
}
|
|
13570
|
+
return declaration;
|
|
13571
|
+
}
|
|
13572
|
+
var INDEX_PROBE_EXPECTED_TYPEOF = {
|
|
13573
|
+
string: "string",
|
|
13574
|
+
number: "number",
|
|
13575
|
+
boolean: "boolean"
|
|
13576
|
+
};
|
|
13577
|
+
function validateIndexProbeValue(value, valueType, pointer, kind) {
|
|
13578
|
+
if (value === void 0 || value === null) return;
|
|
13579
|
+
if (typeof value !== "string" && typeof value !== "number" && typeof value !== "boolean" && !(value instanceof Date)) {
|
|
13580
|
+
throw indexProbeTypeError(
|
|
13581
|
+
pointer,
|
|
13582
|
+
kind,
|
|
13583
|
+
"a scalar (string, number, boolean, or Date)",
|
|
13584
|
+
value
|
|
13585
|
+
);
|
|
13586
|
+
}
|
|
13587
|
+
if (valueType === "date") {
|
|
13588
|
+
if (value instanceof Date || typeof value === "string") return;
|
|
13589
|
+
throw indexProbeTypeError(
|
|
13590
|
+
pointer,
|
|
13591
|
+
kind,
|
|
13592
|
+
"date (Date or ISO string)",
|
|
13593
|
+
value
|
|
13594
|
+
);
|
|
13595
|
+
}
|
|
13596
|
+
const expected = INDEX_PROBE_EXPECTED_TYPEOF[valueType ?? "unknown"];
|
|
13597
|
+
if (expected === void 0) return;
|
|
13598
|
+
if (typeof value !== expected) {
|
|
13599
|
+
throw indexProbeTypeError(pointer, kind, expected, value);
|
|
13600
|
+
}
|
|
13601
|
+
}
|
|
13602
|
+
function indexProbeTypeError(pointer, kind, expected, value) {
|
|
13603
|
+
return new ValidationError(
|
|
13604
|
+
`Index probe value for "${pointer}" on node kind "${kind}" has an incompatible type`,
|
|
13605
|
+
{
|
|
13606
|
+
entityType: "node",
|
|
13607
|
+
kind,
|
|
13608
|
+
issues: [
|
|
13609
|
+
{
|
|
13610
|
+
path: pointer,
|
|
13611
|
+
message: `Expected ${expected}, received ${typeof value}`,
|
|
13612
|
+
code: "invalid_type"
|
|
13613
|
+
}
|
|
13614
|
+
]
|
|
13615
|
+
}
|
|
13616
|
+
);
|
|
13617
|
+
}
|
|
13618
|
+
function coerceIndexProbeBind(value, adapter) {
|
|
13619
|
+
return adapter.bindValue(normalizeProbeScalar(value));
|
|
13620
|
+
}
|
|
13621
|
+
function normalizeProbeScalar(value) {
|
|
13622
|
+
return value instanceof Date ? value.toISOString() : value;
|
|
13623
|
+
}
|
|
13624
|
+
var PROBE_NULL_TAG = 0;
|
|
13625
|
+
var PROBE_VALUE_TAG = 1;
|
|
13626
|
+
function canonicalIndexProbeKey(probe) {
|
|
13627
|
+
return JSON.stringify(
|
|
13628
|
+
probe.map(
|
|
13629
|
+
(value) => value === void 0 || value === null ? [PROBE_NULL_TAG] : [PROBE_VALUE_TAG, normalizeProbeScalar(value)]
|
|
13630
|
+
)
|
|
13631
|
+
);
|
|
13632
|
+
}
|
|
13633
|
+
async function executeNodeBulkFindByIndex(ctx, kind, indexName, items, backend, options) {
|
|
13634
|
+
if (items.length === 0) return [];
|
|
13635
|
+
const index = resolveNodeIndex(ctx.graph, kind, indexName);
|
|
13636
|
+
if (index.fieldValueTypes.includes("date")) {
|
|
13637
|
+
throw new ConfigurationError(
|
|
13638
|
+
`bulkFindByIndex does not support date-typed key fields on index "${indexName}" (node kind "${kind}")`,
|
|
13639
|
+
{ indexName, kind },
|
|
13640
|
+
{
|
|
13641
|
+
suggestion: "Date index keys compare differently across SQLite and PostgreSQL. Use a string-encoded key field, or query date predicates via store.query(...).where(...)."
|
|
13642
|
+
}
|
|
13643
|
+
);
|
|
13644
|
+
}
|
|
13645
|
+
const limitPerInput = options?.limitPerInput;
|
|
13646
|
+
if (limitPerInput !== void 0 && (!Number.isInteger(limitPerInput) || limitPerInput <= 0)) {
|
|
13647
|
+
throw new ValidationError(
|
|
13648
|
+
"bulkFindByIndex limitPerInput must be a positive integer",
|
|
13649
|
+
{
|
|
13650
|
+
entityType: "node",
|
|
13651
|
+
kind,
|
|
13652
|
+
issues: [
|
|
13653
|
+
{
|
|
13654
|
+
path: "limitPerInput",
|
|
13655
|
+
message: `Expected a positive integer, received ${String(limitPerInput)}`,
|
|
13656
|
+
code: "invalid_value"
|
|
13657
|
+
}
|
|
13658
|
+
]
|
|
13659
|
+
}
|
|
13660
|
+
);
|
|
13661
|
+
}
|
|
13662
|
+
const adapter = getDialect(backend.dialect);
|
|
13663
|
+
const probes = items.map(
|
|
13664
|
+
(item) => index.fields.map((pointer, position) => {
|
|
13665
|
+
const value = resolveJsonPointer(item.props, pointer);
|
|
13666
|
+
validateIndexProbeValue(
|
|
13667
|
+
value,
|
|
13668
|
+
index.fieldValueTypes[position],
|
|
13669
|
+
pointer,
|
|
13670
|
+
kind
|
|
13671
|
+
);
|
|
13672
|
+
return value;
|
|
13673
|
+
})
|
|
13674
|
+
);
|
|
13675
|
+
const groupByKey = /* @__PURE__ */ new Map();
|
|
13676
|
+
const groupProbes = [];
|
|
13677
|
+
const groupToInputs = [];
|
|
13678
|
+
for (const [inputIndex, probe] of probes.entries()) {
|
|
13679
|
+
const key = canonicalIndexProbeKey(probe);
|
|
13680
|
+
const existing = groupByKey.get(key);
|
|
13681
|
+
if (existing === void 0) {
|
|
13682
|
+
groupByKey.set(key, groupProbes.length);
|
|
13683
|
+
groupProbes.push(probe);
|
|
13684
|
+
groupToInputs.push([inputIndex]);
|
|
13685
|
+
continue;
|
|
13686
|
+
}
|
|
13687
|
+
groupToInputs[existing]?.push(inputIndex);
|
|
13688
|
+
}
|
|
13689
|
+
const schema = backend.tableNames ? createSqlSchema(backend.tableNames) : DEFAULT_SQL_SCHEMA;
|
|
13690
|
+
const compileContext = {
|
|
13691
|
+
dialect: backend.dialect,
|
|
13692
|
+
propsColumn: sql.raw(`"props"`),
|
|
13693
|
+
systemColumn: (column) => sql.raw(`"${column}"`)
|
|
13694
|
+
};
|
|
13695
|
+
const fieldKeys = compileNodeIndexFieldKeys(index, compileContext);
|
|
13696
|
+
const groupPredicates = groupProbes.map(
|
|
13697
|
+
(probe) => sql`(${sql.join(
|
|
13698
|
+
fieldKeys.map((fieldKey, position) => {
|
|
13699
|
+
const value = probe[position];
|
|
13700
|
+
if (value === void 0 || value === null) {
|
|
13701
|
+
return sql`${fieldKey} IS NULL`;
|
|
13702
|
+
}
|
|
13703
|
+
return adapter.nullSafeEquals(
|
|
13704
|
+
fieldKey,
|
|
13705
|
+
sql`${coerceIndexProbeBind(value, adapter)}`
|
|
13706
|
+
);
|
|
13707
|
+
}),
|
|
13708
|
+
sql` AND `
|
|
13709
|
+
)})`
|
|
13710
|
+
);
|
|
13711
|
+
const caseBranches = groupPredicates.map(
|
|
13712
|
+
(predicate2, group) => sql`WHEN ${predicate2} THEN ${sql.raw(String(group))}`
|
|
13713
|
+
);
|
|
13714
|
+
const probeIndexExpr = sql`CASE ${sql.join(caseBranches, sql` `)} ELSE NULL END`;
|
|
13715
|
+
const conditions = [
|
|
13716
|
+
sql`"graph_id" = ${ctx.graphId}`,
|
|
13717
|
+
sql`"kind" = ${kind}`,
|
|
13718
|
+
sql`"deleted_at" IS NULL`
|
|
13719
|
+
];
|
|
13720
|
+
if (index.where !== void 0) {
|
|
13721
|
+
conditions.push(compileIndexWhere(compileContext, index.where));
|
|
13722
|
+
}
|
|
13723
|
+
conditions.push(sql`(${sql.join(groupPredicates, sql` OR `)})`);
|
|
13724
|
+
const whereClause = sql.join(conditions, sql` AND `);
|
|
13725
|
+
const probedSelect = sql`SELECT "id", ${probeIndexExpr} AS probe_idx FROM ${schema.nodesTable} WHERE ${whereClause}`;
|
|
13726
|
+
const capInSql = limitPerInput !== void 0 && backend.capabilities.windowFunctions;
|
|
13727
|
+
const query = capInSql ? sql`SELECT "id", probe_idx FROM (SELECT "id", probe_idx, ROW_NUMBER() OVER (PARTITION BY probe_idx ORDER BY "id") AS probe_rank FROM (${probedSelect}) AS probed) AS ranked WHERE probe_rank <= ${limitPerInput} ORDER BY probe_idx, "id"` : sql`${probedSelect} ORDER BY probe_idx, "id"`;
|
|
13728
|
+
const rawMatches = await backend.execute(query);
|
|
13729
|
+
const matches = limitPerInput !== void 0 && !capInSql ? capMatchesPerGroup(rawMatches, limitPerInput) : rawMatches;
|
|
13730
|
+
const nodesById = await hydrateNodesById(
|
|
13731
|
+
backend,
|
|
13732
|
+
ctx.graphId,
|
|
13733
|
+
kind,
|
|
13734
|
+
matches.map((match) => match.id)
|
|
13735
|
+
);
|
|
13736
|
+
const results = Array.from({ length: items.length }, () => []);
|
|
13737
|
+
for (const match of matches) {
|
|
13738
|
+
const node = nodesById.get(match.id);
|
|
13739
|
+
if (node === void 0) continue;
|
|
13740
|
+
const inputs = groupToInputs[match.probe_idx];
|
|
13741
|
+
if (inputs === void 0) continue;
|
|
13742
|
+
for (const inputIndex of inputs) {
|
|
13743
|
+
results[inputIndex]?.push(node);
|
|
13744
|
+
}
|
|
13745
|
+
}
|
|
13746
|
+
return results;
|
|
13747
|
+
}
|
|
13748
|
+
function capMatchesPerGroup(matches, limitPerInput) {
|
|
13749
|
+
const perGroupCount = /* @__PURE__ */ new Map();
|
|
13750
|
+
const capped = [];
|
|
13751
|
+
for (const match of matches) {
|
|
13752
|
+
const count2 = perGroupCount.get(match.probe_idx) ?? 0;
|
|
13753
|
+
if (count2 >= limitPerInput) continue;
|
|
13754
|
+
perGroupCount.set(match.probe_idx, count2 + 1);
|
|
13755
|
+
capped.push(match);
|
|
13756
|
+
}
|
|
13757
|
+
return capped;
|
|
13758
|
+
}
|
|
13759
|
+
async function hydrateNodesById(backend, graphId, kind, ids) {
|
|
13760
|
+
const rowsById = await getNodeRowsByIds(backend, graphId, kind, ids);
|
|
13761
|
+
const nodesById = /* @__PURE__ */ new Map();
|
|
13762
|
+
for (const [id, row] of rowsById) {
|
|
13763
|
+
if (row.deleted_at !== void 0) continue;
|
|
13764
|
+
nodesById.set(id, rowToNode(row));
|
|
13765
|
+
}
|
|
13766
|
+
return nodesById;
|
|
13767
|
+
}
|
|
13552
13768
|
async function executeNodeBulkGetOrCreateByConstraint(ctx, kind, constraintName, items, backend, options) {
|
|
13553
13769
|
if (items.length === 0) return [];
|
|
13554
13770
|
const ifExists = options?.ifExists ?? "return";
|
|
@@ -13829,6 +14045,24 @@ function processPage(schema, rows) {
|
|
|
13829
14045
|
}
|
|
13830
14046
|
|
|
13831
14047
|
// src/store/search.ts
|
|
14048
|
+
function resolveSearchSlot(ctx, nodeKind, fieldPath) {
|
|
14049
|
+
const nodeType = ctx.registry.getNodeType(nodeKind);
|
|
14050
|
+
if (nodeType !== void 0) {
|
|
14051
|
+
const fields = getEmbeddingFields(nodeType.schema);
|
|
14052
|
+
const field2 = fields.find((entry2) => entry2.fieldPath === fieldPath);
|
|
14053
|
+
if (field2 !== void 0) {
|
|
14054
|
+
return {
|
|
14055
|
+
dimensions: field2.dimensions,
|
|
14056
|
+
metric: field2.metric,
|
|
14057
|
+
indexType: field2.indexType
|
|
14058
|
+
};
|
|
14059
|
+
}
|
|
14060
|
+
}
|
|
14061
|
+
throw new ConfigurationError(
|
|
14062
|
+
`Node kind "${nodeKind}" has no embedding field "${fieldPath}". Declare it with embedding(dimensions) on the node schema.`,
|
|
14063
|
+
{ capability: "vector", graphId: ctx.graphId }
|
|
14064
|
+
);
|
|
14065
|
+
}
|
|
13832
14066
|
async function executeFulltextSearch(ctx, nodeKind, options) {
|
|
13833
14067
|
const { backend, graphId } = ctx;
|
|
13834
14068
|
if (!backend.fulltextSearch) {
|
|
@@ -13890,12 +14124,29 @@ async function executeVectorSearch(ctx, nodeKind, options) {
|
|
|
13890
14124
|
);
|
|
13891
14125
|
}
|
|
13892
14126
|
assertEfSearch(options.efSearch, "vectorSearch.efSearch");
|
|
14127
|
+
const slot = resolveSearchSlot(ctx, nodeKind, options.fieldPath);
|
|
14128
|
+
assertVectorQueryCompatible(
|
|
14129
|
+
backend,
|
|
14130
|
+
slot,
|
|
14131
|
+
{ metric: options.metric, queryEmbedding: options.queryEmbedding },
|
|
14132
|
+
"vectorSearch"
|
|
14133
|
+
);
|
|
14134
|
+
assertMinScore(
|
|
14135
|
+
options.minScore,
|
|
14136
|
+
options.metric ?? slot.metric,
|
|
14137
|
+
"vectorSearch.minScore"
|
|
14138
|
+
);
|
|
13893
14139
|
const rows = await backend.vectorSearch({
|
|
13894
14140
|
graphId,
|
|
13895
14141
|
nodeKind,
|
|
13896
14142
|
fieldPath: options.fieldPath,
|
|
13897
14143
|
queryEmbedding: options.queryEmbedding,
|
|
13898
|
-
metric
|
|
14144
|
+
// Default to the field's DECLARED metric (the metric its index was built
|
|
14145
|
+
// for); only an explicit caller override changes it. Defaulting to cosine
|
|
14146
|
+
// here would mis-rank l2 / inner_product fields and bypass their ANN index.
|
|
14147
|
+
metric: options.metric ?? slot.metric,
|
|
14148
|
+
dimensions: slot.dimensions,
|
|
14149
|
+
indexType: slot.indexType,
|
|
13899
14150
|
limit: options.limit,
|
|
13900
14151
|
...options.minScore === void 0 ? {} : { minScore: options.minScore },
|
|
13901
14152
|
...options.efSearch === void 0 ? {} : { efSearch: options.efSearch }
|
|
@@ -13947,12 +14198,30 @@ async function executeHybridSearch(ctx, nodeKind, options) {
|
|
|
13947
14198
|
const overFetchMultiplier = 4;
|
|
13948
14199
|
const vectorK = options.vector.k ?? options.limit * overFetchMultiplier;
|
|
13949
14200
|
const fulltextK = options.fulltext.k ?? options.limit * overFetchMultiplier;
|
|
14201
|
+
const vectorSlot = resolveSearchSlot(ctx, nodeKind, options.vector.fieldPath);
|
|
14202
|
+
assertVectorQueryCompatible(
|
|
14203
|
+
backend,
|
|
14204
|
+
vectorSlot,
|
|
14205
|
+
{
|
|
14206
|
+
metric: options.vector.metric,
|
|
14207
|
+
queryEmbedding: options.vector.queryEmbedding
|
|
14208
|
+
},
|
|
14209
|
+
"hybridSearch.vector"
|
|
14210
|
+
);
|
|
14211
|
+
assertMinScore(
|
|
14212
|
+
options.vector.minScore,
|
|
14213
|
+
options.vector.metric ?? vectorSlot.metric,
|
|
14214
|
+
"hybridSearch.vector.minScore"
|
|
14215
|
+
);
|
|
13950
14216
|
const vectorPromise = backend.vectorSearch({
|
|
13951
14217
|
graphId,
|
|
13952
14218
|
nodeKind,
|
|
13953
14219
|
fieldPath: options.vector.fieldPath,
|
|
13954
14220
|
queryEmbedding: options.vector.queryEmbedding,
|
|
13955
|
-
|
|
14221
|
+
// Default to the field's declared metric (see executeVectorSearch).
|
|
14222
|
+
metric: options.vector.metric ?? vectorSlot.metric,
|
|
14223
|
+
dimensions: vectorSlot.dimensions,
|
|
14224
|
+
indexType: vectorSlot.indexType,
|
|
13956
14225
|
limit: vectorK,
|
|
13957
14226
|
...options.vector.minScore === void 0 ? {} : { minScore: options.vector.minScore },
|
|
13958
14227
|
...options.vector.efSearch === void 0 ? {} : { efSearch: options.vector.efSearch }
|
|
@@ -14043,6 +14312,30 @@ function assertEfSearch(efSearch, label) {
|
|
|
14043
14312
|
);
|
|
14044
14313
|
}
|
|
14045
14314
|
}
|
|
14315
|
+
function assertMinScore(minScore, metric, label) {
|
|
14316
|
+
if (minScore === void 0) return;
|
|
14317
|
+
assertVectorMinScore(minScore, metric, label);
|
|
14318
|
+
}
|
|
14319
|
+
function assertVectorQueryCompatible(backend, slot, options, label) {
|
|
14320
|
+
if (options.metric !== void 0 && options.metric !== slot.metric) {
|
|
14321
|
+
throw new ConfigurationError(
|
|
14322
|
+
`${label}: metric "${options.metric}" does not match the field's declared metric "${slot.metric}". Vector storage is built for the declared metric \u2014 omit metric or pass "${slot.metric}".`,
|
|
14323
|
+
{ backend: backend.dialect, capability: "vector" }
|
|
14324
|
+
);
|
|
14325
|
+
}
|
|
14326
|
+
const supported = backend.capabilities.vector?.metrics;
|
|
14327
|
+
if (supported !== void 0 && !supported.includes(slot.metric)) {
|
|
14328
|
+
throw new ConfigurationError(
|
|
14329
|
+
`${label}: backend "${backend.dialect}" does not support the "${slot.metric}" metric (supported: ${supported.join(", ")}).`,
|
|
14330
|
+
{ backend: backend.dialect, capability: "vector" }
|
|
14331
|
+
);
|
|
14332
|
+
}
|
|
14333
|
+
if (options.queryEmbedding.length !== slot.dimensions) {
|
|
14334
|
+
throw new RangeError(
|
|
14335
|
+
`${label}: queryEmbedding has ${options.queryEmbedding.length} dimensions, but "${slot.dimensions}" are declared for this field.`
|
|
14336
|
+
);
|
|
14337
|
+
}
|
|
14338
|
+
}
|
|
14046
14339
|
function validateFulltextCallOptions(backend, options) {
|
|
14047
14340
|
const strategy = backend.fulltextStrategy;
|
|
14048
14341
|
const capabilities = backend.capabilities.fulltext;
|
|
@@ -14149,7 +14442,11 @@ var StoreSearch = class {
|
|
|
14149
14442
|
async fulltext(nodeKind, options) {
|
|
14150
14443
|
this.#assertKindRegistered(nodeKind);
|
|
14151
14444
|
return executeFulltextSearch(
|
|
14152
|
-
{
|
|
14445
|
+
{
|
|
14446
|
+
graphId: this.#context.graphId,
|
|
14447
|
+
backend: this.#context.backend,
|
|
14448
|
+
registry: this.#context.registry
|
|
14449
|
+
},
|
|
14153
14450
|
nodeKind,
|
|
14154
14451
|
options
|
|
14155
14452
|
);
|
|
@@ -14168,7 +14465,11 @@ var StoreSearch = class {
|
|
|
14168
14465
|
async vector(nodeKind, options) {
|
|
14169
14466
|
this.#assertKindRegistered(nodeKind);
|
|
14170
14467
|
return executeVectorSearch(
|
|
14171
|
-
{
|
|
14468
|
+
{
|
|
14469
|
+
graphId: this.#context.graphId,
|
|
14470
|
+
backend: this.#context.backend,
|
|
14471
|
+
registry: this.#context.registry
|
|
14472
|
+
},
|
|
14172
14473
|
nodeKind,
|
|
14173
14474
|
options
|
|
14174
14475
|
);
|
|
@@ -14185,7 +14486,11 @@ var StoreSearch = class {
|
|
|
14185
14486
|
async hybrid(nodeKind, options) {
|
|
14186
14487
|
this.#assertKindRegistered(nodeKind);
|
|
14187
14488
|
return executeHybridSearch(
|
|
14188
|
-
{
|
|
14489
|
+
{
|
|
14490
|
+
graphId: this.#context.graphId,
|
|
14491
|
+
backend: this.#context.backend,
|
|
14492
|
+
registry: this.#context.registry
|
|
14493
|
+
},
|
|
14189
14494
|
nodeKind,
|
|
14190
14495
|
options
|
|
14191
14496
|
);
|
|
@@ -14234,6 +14539,7 @@ var UNKNOWN_SCHEMA_METADATA = Object.freeze({
|
|
|
14234
14539
|
schemaVersion: void 0,
|
|
14235
14540
|
schemaHash: void 0
|
|
14236
14541
|
});
|
|
14542
|
+
var DEFAULT_REEMBED_BATCH_SIZE = 200;
|
|
14237
14543
|
var CAUGHT_UP_VERB_DETAILS = {
|
|
14238
14544
|
evolve: { phrase: "evolve", code: "EVOLVE_BEFORE_INITIALIZE" },
|
|
14239
14545
|
materialize: {
|
|
@@ -14248,6 +14554,10 @@ var CAUGHT_UP_VERB_DETAILS = {
|
|
|
14248
14554
|
undeprecate: {
|
|
14249
14555
|
phrase: "undeprecate kinds on",
|
|
14250
14556
|
code: "DEPRECATE_BEFORE_INITIALIZE"
|
|
14557
|
+
},
|
|
14558
|
+
reembed: {
|
|
14559
|
+
phrase: "re-embed a vector field on",
|
|
14560
|
+
code: "REEMBED_BEFORE_INITIALIZE"
|
|
14251
14561
|
}
|
|
14252
14562
|
};
|
|
14253
14563
|
var Store = class _Store {
|
|
@@ -14553,6 +14863,14 @@ var Store = class _Store {
|
|
|
14553
14863
|
constraintName,
|
|
14554
14864
|
items,
|
|
14555
14865
|
backend
|
|
14866
|
+
),
|
|
14867
|
+
executeBulkFindByIndex: (kind, indexName, items, backend, options) => executeNodeBulkFindByIndex(
|
|
14868
|
+
ctx,
|
|
14869
|
+
kind,
|
|
14870
|
+
indexName,
|
|
14871
|
+
items,
|
|
14872
|
+
backend,
|
|
14873
|
+
options
|
|
14556
14874
|
)
|
|
14557
14875
|
};
|
|
14558
14876
|
}
|
|
@@ -14799,7 +15117,7 @@ var Store = class _Store {
|
|
|
14799
15117
|
return fn({ nodes: this.nodes, edges: this.edges });
|
|
14800
15118
|
}
|
|
14801
15119
|
return this.#backend.transaction(
|
|
14802
|
-
async (txBackend,
|
|
15120
|
+
async (txBackend, sql19) => fn(this.#buildTransactionContext(txBackend, sql19))
|
|
14803
15121
|
);
|
|
14804
15122
|
}
|
|
14805
15123
|
/**
|
|
@@ -14876,7 +15194,7 @@ var Store = class _Store {
|
|
|
14876
15194
|
* raw Drizzle handle bound to the same transaction (#140);
|
|
14877
15195
|
* `undefined` only on the non-transactional fallback.
|
|
14878
15196
|
*/
|
|
14879
|
-
#buildTransactionContext(txBackend,
|
|
15197
|
+
#buildTransactionContext(txBackend, sql19) {
|
|
14880
15198
|
const txNodeOperations = {
|
|
14881
15199
|
...this.#nodeOperations,
|
|
14882
15200
|
createQuery: () => this.#createQueryForBackend(txBackend)
|
|
@@ -14899,7 +15217,7 @@ var Store = class _Store {
|
|
|
14899
15217
|
txBackend,
|
|
14900
15218
|
txEdgeOperations
|
|
14901
15219
|
);
|
|
14902
|
-
return
|
|
15220
|
+
return sql19 === void 0 ? { nodes, edges } : { nodes, edges, sql: sql19 };
|
|
14903
15221
|
}
|
|
14904
15222
|
// === Graph Lifecycle ===
|
|
14905
15223
|
/**
|
|
@@ -14916,11 +15234,44 @@ var Store = class _Store {
|
|
|
14916
15234
|
await target.clearGraph(this.graphId);
|
|
14917
15235
|
};
|
|
14918
15236
|
await (this.#backend.capabilities.transactions ? this.#backend.transaction(async (tx) => doClear(tx)) : doClear(this.#backend));
|
|
15237
|
+
await this.#clearVectorStorage();
|
|
14919
15238
|
this.#nodeCollections = void 0;
|
|
14920
15239
|
this.#edgeCollections = void 0;
|
|
14921
15240
|
this.#algorithms = void 0;
|
|
14922
15241
|
this.#search = void 0;
|
|
14923
15242
|
}
|
|
15243
|
+
/**
|
|
15244
|
+
* Resets every declared embedding field's per-field storage for this graph by
|
|
15245
|
+
* dropping and recreating it empty. Drop+recreate (rather than DELETE) mirrors
|
|
15246
|
+
* `reembedVectorField` and keeps the backend's storage-ensure latch valid: the
|
|
15247
|
+
* table still exists after clear, so a later write finds it. Enumerated from
|
|
15248
|
+
* the in-memory registry (not the DB schema, which `clearGraph` just wiped).
|
|
15249
|
+
*/
|
|
15250
|
+
async #clearVectorStorage() {
|
|
15251
|
+
const backend = this.#backend;
|
|
15252
|
+
const strategy = backend.vectorStrategy;
|
|
15253
|
+
if (strategy === void 0 || backend.executeDdl === void 0) return;
|
|
15254
|
+
for (const [nodeKind, nodeType] of this.#registry.nodeKinds) {
|
|
15255
|
+
for (const field2 of resolveEmbeddingFields(nodeType.schema)) {
|
|
15256
|
+
const slot = {
|
|
15257
|
+
graphId: this.graphId,
|
|
15258
|
+
nodeKind,
|
|
15259
|
+
fieldPath: field2.fieldPath,
|
|
15260
|
+
dimensions: field2.dimensions,
|
|
15261
|
+
metric: field2.metric,
|
|
15262
|
+
indexType: field2.indexType
|
|
15263
|
+
};
|
|
15264
|
+
for (const ddl of strategy.buildDropStorage(slot)) {
|
|
15265
|
+
await backend.executeDdl(ddl);
|
|
15266
|
+
}
|
|
15267
|
+
for (const contribution of strategy.ownedTables(slot)) {
|
|
15268
|
+
for (const ddl of contribution.createDdl) {
|
|
15269
|
+
await backend.executeDdl(ddl);
|
|
15270
|
+
}
|
|
15271
|
+
}
|
|
15272
|
+
}
|
|
15273
|
+
}
|
|
15274
|
+
}
|
|
14924
15275
|
// === Lifecycle ===
|
|
14925
15276
|
/**
|
|
14926
15277
|
* Refreshes the backend's query-planner statistics.
|
|
@@ -15144,6 +15495,150 @@ var Store = class _Store {
|
|
|
15144
15495
|
options ?? {}
|
|
15145
15496
|
);
|
|
15146
15497
|
}
|
|
15498
|
+
/**
|
|
15499
|
+
* Recreate a vector field's per-field storage at its current declared
|
|
15500
|
+
* dimension, then optionally re-embed existing rows.
|
|
15501
|
+
*
|
|
15502
|
+
* Use this after changing a field's `embedding(N)` to `embedding(M)` (e.g. a
|
|
15503
|
+
* new embedding model): the stored N-dim vectors are invalid under the new
|
|
15504
|
+
* dimension and must be recomputed, not converted. This drops and recreates
|
|
15505
|
+
* the per-`(graphId, kind, field)` table at the new dimension — a brief
|
|
15506
|
+
* window where the field returns no vector hits — resets its materialization
|
|
15507
|
+
* marker, then, when `options.embed` is supplied, pages the kind's nodes,
|
|
15508
|
+
* calls `embed(batch)`, and upserts the returned vectors. Without `embed`,
|
|
15509
|
+
* storage is recreated empty and the caller re-embeds via normal
|
|
15510
|
+
* `update()` / `upsertEmbedding` writes.
|
|
15511
|
+
*
|
|
15512
|
+
* @throws {ConfigurationError} when the backend has no vector strategy or
|
|
15513
|
+
* cannot execute DDL, or `(kind, fieldPath)` is not a declared embedding.
|
|
15514
|
+
*/
|
|
15515
|
+
async reembedVectorField(kind, fieldPath, options) {
|
|
15516
|
+
if (options?.batchSize !== void 0 && (!Number.isInteger(options.batchSize) || options.batchSize <= 0)) {
|
|
15517
|
+
throw new RangeError(
|
|
15518
|
+
`reembedVectorField batchSize must be a positive integer, got: ${options.batchSize}`
|
|
15519
|
+
);
|
|
15520
|
+
}
|
|
15521
|
+
const { activeRow, baseline } = await this.#loadCaughtUp("reembed");
|
|
15522
|
+
const backend = this.#backend;
|
|
15523
|
+
const strategy = backend.vectorStrategy;
|
|
15524
|
+
if (strategy === void 0 || backend.executeDdl === void 0) {
|
|
15525
|
+
throw new ConfigurationError(
|
|
15526
|
+
"reembedVectorField requires a backend with a vector strategy and executeDdl.",
|
|
15527
|
+
{
|
|
15528
|
+
backend: backend.dialect,
|
|
15529
|
+
capability: "vector",
|
|
15530
|
+
operation: "reembed"
|
|
15531
|
+
}
|
|
15532
|
+
);
|
|
15533
|
+
}
|
|
15534
|
+
const declaration = (baseline.indexes ?? []).find(
|
|
15535
|
+
(candidate) => candidate.entity === "vector" && candidate.kind === kind && candidate.fieldPath === fieldPath
|
|
15536
|
+
);
|
|
15537
|
+
if (declaration === void 0) {
|
|
15538
|
+
throw new ConfigurationError(
|
|
15539
|
+
`No embedding field "${kind}.${fieldPath}" is declared in the active schema.`,
|
|
15540
|
+
{ kind, fieldPath, operation: "reembed" }
|
|
15541
|
+
);
|
|
15542
|
+
}
|
|
15543
|
+
const slot = {
|
|
15544
|
+
graphId: this.graphId,
|
|
15545
|
+
nodeKind: kind,
|
|
15546
|
+
fieldPath,
|
|
15547
|
+
dimensions: declaration.dimensions,
|
|
15548
|
+
metric: declaration.metric,
|
|
15549
|
+
indexType: declaration.indexType
|
|
15550
|
+
};
|
|
15551
|
+
for (const ddl of strategy.buildDropStorage(slot)) {
|
|
15552
|
+
await backend.executeDdl(ddl);
|
|
15553
|
+
}
|
|
15554
|
+
for (const contribution of strategy.ownedTables(slot)) {
|
|
15555
|
+
for (const ddl of contribution.createDdl) {
|
|
15556
|
+
await backend.executeDdl(ddl);
|
|
15557
|
+
}
|
|
15558
|
+
}
|
|
15559
|
+
const declaredIndexMaterializes = declaration.indexType !== "none" && backend.capabilities.vector?.indexTypes.includes(
|
|
15560
|
+
declaration.indexType
|
|
15561
|
+
) === true;
|
|
15562
|
+
if (declaredIndexMaterializes && backend.createVectorIndex !== void 0) {
|
|
15563
|
+
await backend.createVectorIndex({
|
|
15564
|
+
graphId: this.graphId,
|
|
15565
|
+
nodeKind: kind,
|
|
15566
|
+
fieldPath,
|
|
15567
|
+
dimensions: declaration.dimensions,
|
|
15568
|
+
metric: declaration.metric,
|
|
15569
|
+
indexType: declaration.indexType,
|
|
15570
|
+
indexParams: {
|
|
15571
|
+
m: declaration.indexParams.m,
|
|
15572
|
+
efConstruction: declaration.indexParams.efConstruction,
|
|
15573
|
+
...declaration.indexParams.lists === void 0 ? {} : { lists: declaration.indexParams.lists }
|
|
15574
|
+
}
|
|
15575
|
+
});
|
|
15576
|
+
}
|
|
15577
|
+
if (declaredIndexMaterializes && backend.recordIndexMaterialization !== void 0) {
|
|
15578
|
+
const tableName = strategy.tableName(this.graphId, kind, fieldPath);
|
|
15579
|
+
const signature = await computeIndexSignature(
|
|
15580
|
+
backend.dialect,
|
|
15581
|
+
tableName,
|
|
15582
|
+
declaration
|
|
15583
|
+
);
|
|
15584
|
+
const now = nowIso();
|
|
15585
|
+
await backend.recordIndexMaterialization({
|
|
15586
|
+
indexName: vectorStatusKey(this.graphId, declaration.name),
|
|
15587
|
+
graphId: this.graphId,
|
|
15588
|
+
entity: "vector",
|
|
15589
|
+
kind,
|
|
15590
|
+
signature,
|
|
15591
|
+
schemaVersion: activeRow.version,
|
|
15592
|
+
attemptedAt: now,
|
|
15593
|
+
materializedAt: now,
|
|
15594
|
+
error: void 0
|
|
15595
|
+
});
|
|
15596
|
+
}
|
|
15597
|
+
if (options?.embed === void 0) {
|
|
15598
|
+
return { recreated: true, reembedded: 0 };
|
|
15599
|
+
}
|
|
15600
|
+
if (backend.upsertEmbedding === void 0) {
|
|
15601
|
+
throw new ConfigurationError(
|
|
15602
|
+
"reembedVectorField with an `embed` callback requires a backend that supports upsertEmbedding.",
|
|
15603
|
+
{
|
|
15604
|
+
backend: backend.dialect,
|
|
15605
|
+
capability: "vector",
|
|
15606
|
+
operation: "reembed"
|
|
15607
|
+
}
|
|
15608
|
+
);
|
|
15609
|
+
}
|
|
15610
|
+
const upsertEmbedding = backend.upsertEmbedding;
|
|
15611
|
+
const batchSize = options.batchSize ?? DEFAULT_REEMBED_BATCH_SIZE;
|
|
15612
|
+
const collection = this.getNodeCollectionOrThrow(kind);
|
|
15613
|
+
let reembedded = 0;
|
|
15614
|
+
let offset = 0;
|
|
15615
|
+
for (; ; ) {
|
|
15616
|
+
const batch = await collection.find({
|
|
15617
|
+
limit: batchSize,
|
|
15618
|
+
offset
|
|
15619
|
+
});
|
|
15620
|
+
if (batch.length === 0) break;
|
|
15621
|
+
const vectors = await options.embed(batch);
|
|
15622
|
+
for (const node of batch) {
|
|
15623
|
+
const embedding2 = vectors.get(node.id);
|
|
15624
|
+
if (embedding2 === void 0) continue;
|
|
15625
|
+
await upsertEmbedding({
|
|
15626
|
+
graphId: this.graphId,
|
|
15627
|
+
nodeKind: kind,
|
|
15628
|
+
nodeId: node.id,
|
|
15629
|
+
fieldPath,
|
|
15630
|
+
embedding: embedding2,
|
|
15631
|
+
dimensions: declaration.dimensions,
|
|
15632
|
+
metric: declaration.metric,
|
|
15633
|
+
indexType: declaration.indexType
|
|
15634
|
+
});
|
|
15635
|
+
reembedded += 1;
|
|
15636
|
+
}
|
|
15637
|
+
offset += batch.length;
|
|
15638
|
+
if (batch.length < batchSize) break;
|
|
15639
|
+
}
|
|
15640
|
+
return { recreated: true, reembedded };
|
|
15641
|
+
}
|
|
15147
15642
|
/**
|
|
15148
15643
|
* Removes extension kinds from the schema with cascading edge and
|
|
15149
15644
|
* ontology cleanup. Two-phase by design:
|
|
@@ -15521,6 +16016,6 @@ async function createVerifiedStore(graph, backend, options) {
|
|
|
15521
16016
|
return [store, result];
|
|
15522
16017
|
}
|
|
15523
16018
|
|
|
15524
|
-
export { DEFAULT_SQL_SCHEMA, ExecutableAggregateQuery, ExecutableQuery, MAX_EXPLICIT_RECURSIVE_DEPTH, MAX_RECURSIVE_DEPTH, PreparedQuery, QueryBuilder, StoreSearch, UnionableQuery, avg, composeFragments, count, countDistinct, countDistinctEdges, countEdges, createExternalRef, createFragment, createQueryBuilder, createSqlSchema, createStore, createStoreWithSchema, createVerifiedStore, defineSubgraphProject, exists, externalRef, field, fieldRef, generateId, getExternalRefTable, having, havingEq, havingGt, havingGte, havingLt, havingLte, inSubquery, isExternalRefSchema, isParameterRef, limitFragment, max, metaEdge, min, notExists, notInSubquery, offsetFragment, orderByFragment, param, sum };
|
|
16019
|
+
export { DEFAULT_SQL_SCHEMA, ExecutableAggregateQuery, ExecutableQuery, MAX_EXPLICIT_RECURSIVE_DEPTH, MAX_RECURSIVE_DEPTH, PreparedQuery, QueryBuilder, StoreSearch, UnionableQuery, avg, composeFragments, count, countDistinct, countDistinctEdges, countEdges, createExternalRef, createFragment, createQueryBuilder, createSqlSchema, createStore, createStoreWithSchema, createVerifiedStore, defineSubgraphProject, exists, externalRef, field, fieldRef, generateId, getExternalRefTable, having, havingEq, havingGt, havingGte, havingLt, havingLte, inSubquery, isExternalRefSchema, isParameterRef, limitFragment, max, metaEdge, migrateLegacyEmbeddings, min, notExists, notInSubquery, offsetFragment, orderByFragment, param, sum };
|
|
15525
16020
|
//# sourceMappingURL=index.js.map
|
|
15526
16021
|
//# sourceMappingURL=index.js.map
|