@nicia-ai/typegraph 0.19.0 → 0.21.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/{ast-Bh2NDeaK.d.cts → ast-CSLbPIOC.d.ts} +56 -3
- package/dist/{ast-COMyNeMn.d.ts → ast-DogwbBqA.d.cts} +56 -3
- package/dist/backend/postgres/index.cjs +123 -45
- package/dist/backend/postgres/index.cjs.map +1 -1
- package/dist/backend/postgres/index.d.cts +15 -7
- package/dist/backend/postgres/index.d.ts +15 -7
- package/dist/backend/postgres/index.js +101 -23
- package/dist/backend/postgres/index.js.map +1 -1
- package/dist/backend/sqlite/index.cjs +15 -15
- package/dist/backend/sqlite/index.d.cts +11 -7
- package/dist/backend/sqlite/index.d.ts +11 -7
- package/dist/backend/sqlite/index.js +5 -5
- package/dist/backend/sqlite/libsql.cjs +8 -8
- package/dist/backend/sqlite/libsql.d.cts +4 -5
- package/dist/backend/sqlite/libsql.d.ts +4 -5
- package/dist/backend/sqlite/libsql.js +5 -5
- package/dist/backend/sqlite/local.cjs +9 -9
- package/dist/backend/sqlite/local.d.cts +4 -5
- package/dist/backend/sqlite/local.d.ts +4 -5
- package/dist/backend/sqlite/local.js +5 -5
- package/dist/{chunk-LELLOHJK.cjs → chunk-2X7P7AD5.cjs} +35 -35
- package/dist/{chunk-LELLOHJK.cjs.map → chunk-2X7P7AD5.cjs.map} +1 -1
- package/dist/{chunk-GNIYZKBI.js → chunk-4XNE2JG3.js} +376 -8
- package/dist/chunk-4XNE2JG3.js.map +1 -0
- package/dist/{chunk-HXJXTOXR.cjs → chunk-CQL6FOTT.cjs} +82 -21
- package/dist/chunk-CQL6FOTT.cjs.map +1 -0
- package/dist/{chunk-K7SQ3SWP.js → chunk-EYELZHKY.js} +120 -12
- package/dist/chunk-EYELZHKY.js.map +1 -0
- package/dist/{chunk-MME3H4ZF.cjs → chunk-FQCQM3KG.cjs} +385 -9
- package/dist/chunk-FQCQM3KG.cjs.map +1 -0
- package/dist/{chunk-X33WV2UF.cjs → chunk-HRNQ3DVJ.cjs} +24 -21
- package/dist/chunk-HRNQ3DVJ.cjs.map +1 -0
- package/dist/{chunk-W6M7XWKX.js → chunk-JYUQA77I.js} +21 -18
- package/dist/chunk-JYUQA77I.js.map +1 -0
- package/dist/{chunk-LLHFS53Q.js → chunk-NRNJ52AA.js} +72 -11
- package/dist/chunk-NRNJ52AA.js.map +1 -0
- package/dist/{chunk-IYWWXOVK.cjs → chunk-Q73OWTJY.cjs} +204 -89
- package/dist/chunk-Q73OWTJY.cjs.map +1 -0
- package/dist/{chunk-G5CYWKRF.js → chunk-RS5OLGEW.js} +2 -2
- package/dist/{chunk-G5CYWKRF.js.map → chunk-RS5OLGEW.js.map} +1 -1
- package/dist/{chunk-6GWJH6AR.js → chunk-SMMOAVJ5.js} +4 -4
- package/dist/{chunk-6GWJH6AR.js.map → chunk-SMMOAVJ5.js.map} +1 -1
- package/dist/{chunk-QFZQGEQ3.js → chunk-UQVBVBVG.js} +151 -36
- package/dist/chunk-UQVBVBVG.js.map +1 -0
- package/dist/{chunk-P5CNM325.cjs → chunk-UVZHTBZ5.cjs} +123 -11
- package/dist/chunk-UVZHTBZ5.cjs.map +1 -0
- package/dist/{chunk-5GZMPJOP.cjs → chunk-WRWVWKQL.cjs} +2 -2
- package/dist/{chunk-5GZMPJOP.cjs.map → chunk-WRWVWKQL.cjs.map} +1 -1
- package/dist/{ddl-BBo2EcFc.d.cts → ddl-BqkTShcR.d.ts} +28 -6
- package/dist/{ddl-D4jlcOH_.d.ts → ddl-Dk1NDN3M.d.cts} +28 -6
- package/dist/index.cjs +2102 -556
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +8 -10
- package/dist/index.d.ts +8 -10
- package/dist/index.js +1961 -439
- package/dist/index.js.map +1 -1
- package/dist/indexes/index.cjs +17 -17
- package/dist/indexes/index.d.cts +5 -5
- package/dist/indexes/index.d.ts +5 -5
- package/dist/indexes/index.js +3 -3
- package/dist/interchange/index.cjs +6 -6
- package/dist/interchange/index.d.cts +4 -5
- package/dist/interchange/index.d.ts +4 -5
- package/dist/interchange/index.js +1 -1
- package/dist/{manager-Chhrq1vl.d.ts → manager-0jVZtiE8.d.ts} +1 -2
- package/dist/{manager-Bj1UEnhE.d.cts → manager-BPK-2PUI.d.cts} +1 -2
- package/dist/profiler/index.cjs +15 -8
- package/dist/profiler/index.cjs.map +1 -1
- package/dist/profiler/index.d.cts +7 -8
- package/dist/profiler/index.d.ts +7 -8
- package/dist/profiler/index.js +8 -1
- package/dist/profiler/index.js.map +1 -1
- package/dist/schema/index.cjs +21 -21
- package/dist/schema/index.d.cts +4 -5
- package/dist/schema/index.d.ts +4 -5
- package/dist/schema/index.js +3 -3
- package/dist/{sqlite-DE-6NWtC.d.cts → sqlite-D1rrwcir.d.cts} +14 -1
- package/dist/{sqlite-Blq-AhmK.d.ts → sqlite-W3rmJMlc.d.ts} +14 -1
- package/dist/{store-BnUu4qo5.d.cts → store-2QW2SIRk.d.cts} +626 -7
- package/dist/{store-CCOwNft_.d.ts → store-C0q13mtJ.d.ts} +626 -7
- package/dist/{types-B3mmOMJV.d.cts → types-CQF88McG.d.cts} +475 -2
- package/dist/{types-ThB4cFLp.d.ts → types-CQF88McG.d.ts} +475 -2
- package/dist/{types-COPePE8_.d.ts → types-CglvhPKz.d.ts} +1 -1
- package/dist/{types-CVtGFpB9.d.cts → types-D23slCOA.d.cts} +1 -1
- package/dist/{types-Ceb01czq.d.ts → types-IMQCuser.d.ts} +2 -2
- package/dist/{types-CZd2PEOc.d.cts → types-vgjar7Ch.d.cts} +2 -2
- package/package.json +10 -10
- package/dist/chunk-GNIYZKBI.js.map +0 -1
- package/dist/chunk-HXJXTOXR.cjs.map +0 -1
- package/dist/chunk-IYWWXOVK.cjs.map +0 -1
- package/dist/chunk-K7SQ3SWP.js.map +0 -1
- package/dist/chunk-LLHFS53Q.js.map +0 -1
- package/dist/chunk-MME3H4ZF.cjs.map +0 -1
- package/dist/chunk-P5CNM325.cjs.map +0 -1
- package/dist/chunk-QFZQGEQ3.js.map +0 -1
- package/dist/chunk-W6M7XWKX.js.map +0 -1
- package/dist/chunk-X33WV2UF.cjs.map +0 -1
- package/dist/types-DMzKq0d5.d.cts +0 -181
- package/dist/types-DMzKq0d5.d.ts +0 -181
package/dist/index.cjs
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
4
|
-
var
|
|
3
|
+
var chunkHRNQ3DVJ_cjs = require('./chunk-HRNQ3DVJ.cjs');
|
|
4
|
+
var chunkWRWVWKQL_cjs = require('./chunk-WRWVWKQL.cjs');
|
|
5
5
|
var chunkO5PST6TB_cjs = require('./chunk-O5PST6TB.cjs');
|
|
6
|
-
var
|
|
7
|
-
var
|
|
6
|
+
var chunkFQCQM3KG_cjs = require('./chunk-FQCQM3KG.cjs');
|
|
7
|
+
var chunkUVZHTBZ5_cjs = require('./chunk-UVZHTBZ5.cjs');
|
|
8
8
|
var zod = require('zod');
|
|
9
9
|
var drizzleOrm = require('drizzle-orm');
|
|
10
10
|
var nanoid = require('nanoid');
|
|
@@ -29,6 +29,44 @@ var PROTOTYPE_POLLUTION_KEYS = /* @__PURE__ */ new Set([
|
|
|
29
29
|
"constructor",
|
|
30
30
|
"prototype"
|
|
31
31
|
]);
|
|
32
|
+
var RESERVED_PROPERTY_PREFIX = "$";
|
|
33
|
+
function isReservedPropertyName(name) {
|
|
34
|
+
return name.startsWith(RESERVED_PROPERTY_PREFIX);
|
|
35
|
+
}
|
|
36
|
+
function assertSchemaKeysAreFree(entityKind, name, keys, reservedStructuralKeys) {
|
|
37
|
+
const structuralConflicts = keys.filter(
|
|
38
|
+
(key) => reservedStructuralKeys.has(key)
|
|
39
|
+
);
|
|
40
|
+
if (structuralConflicts.length > 0) {
|
|
41
|
+
const label = entityKind.toLowerCase();
|
|
42
|
+
throw new chunkO5PST6TB_cjs.ConfigurationError(
|
|
43
|
+
`${entityKind} "${name}" schema contains reserved property names: ${structuralConflicts.join(", ")}`,
|
|
44
|
+
{
|
|
45
|
+
[`${label}Type`]: name,
|
|
46
|
+
conflicts: structuralConflicts,
|
|
47
|
+
reservedKeys: [...reservedStructuralKeys]
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
suggestion: `Rename the conflicting properties. Reserved names (${[...reservedStructuralKeys].join(", ")}) are added automatically to all ${label}s.`
|
|
51
|
+
}
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
const prefixConflicts = keys.filter((key) => isReservedPropertyName(key));
|
|
55
|
+
if (prefixConflicts.length > 0) {
|
|
56
|
+
const label = entityKind.toLowerCase();
|
|
57
|
+
throw new chunkO5PST6TB_cjs.ConfigurationError(
|
|
58
|
+
`${entityKind} "${name}" schema uses the reserved "${RESERVED_PROPERTY_PREFIX}" prefix on: ${prefixConflicts.join(", ")}`,
|
|
59
|
+
{
|
|
60
|
+
[`${label}Type`]: name,
|
|
61
|
+
conflicts: prefixConflicts,
|
|
62
|
+
reservedPrefix: RESERVED_PROPERTY_PREFIX
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
suggestion: `Property names starting with "${RESERVED_PROPERTY_PREFIX}" are reserved for TypeGraph accessors (e.g. $fulltext). Rename each field.`
|
|
66
|
+
}
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
32
70
|
function validateProjectionField(field2, entityType, kind) {
|
|
33
71
|
const reserved = entityType === "node" ? RESERVED_NODE_KEYS : RESERVED_EDGE_KEYS;
|
|
34
72
|
if (reserved.has(field2)) {
|
|
@@ -62,24 +100,17 @@ function filterReservedKeys(props, reservedKeys) {
|
|
|
62
100
|
|
|
63
101
|
// src/core/node.ts
|
|
64
102
|
function validateSchemaKeys(schema, name) {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
103
|
+
assertSchemaKeysAreFree(
|
|
104
|
+
"Node",
|
|
105
|
+
name,
|
|
106
|
+
Object.keys(schema.shape),
|
|
107
|
+
RESERVED_NODE_KEYS
|
|
68
108
|
);
|
|
69
|
-
if (conflicts.length > 0) {
|
|
70
|
-
throw new chunkO5PST6TB_cjs.ConfigurationError(
|
|
71
|
-
`Node "${name}" schema contains reserved property names: ${conflicts.join(", ")}`,
|
|
72
|
-
{ nodeType: name, conflicts, reservedKeys: [...RESERVED_NODE_KEYS] },
|
|
73
|
-
{
|
|
74
|
-
suggestion: `Rename the conflicting properties. Reserved names (id, kind, meta) are added automatically to all nodes.`
|
|
75
|
-
}
|
|
76
|
-
);
|
|
77
|
-
}
|
|
78
109
|
}
|
|
79
110
|
function defineNode(name, options) {
|
|
80
111
|
validateSchemaKeys(options.schema, name);
|
|
81
112
|
return Object.freeze({
|
|
82
|
-
[
|
|
113
|
+
[chunkWRWVWKQL_cjs.NODE_TYPE_BRAND]: true,
|
|
83
114
|
kind: name,
|
|
84
115
|
schema: options.schema,
|
|
85
116
|
description: options.description
|
|
@@ -87,25 +118,18 @@ function defineNode(name, options) {
|
|
|
87
118
|
}
|
|
88
119
|
var EMPTY_SCHEMA = zod.z.object({});
|
|
89
120
|
function validateSchemaKeys2(schema, name) {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
121
|
+
assertSchemaKeysAreFree(
|
|
122
|
+
"Edge",
|
|
123
|
+
name,
|
|
124
|
+
Object.keys(schema.shape),
|
|
125
|
+
RESERVED_EDGE_KEYS
|
|
93
126
|
);
|
|
94
|
-
if (conflicts.length > 0) {
|
|
95
|
-
throw new chunkO5PST6TB_cjs.ConfigurationError(
|
|
96
|
-
`Edge "${name}" schema contains reserved property names: ${conflicts.join(", ")}`,
|
|
97
|
-
{ edgeType: name, conflicts, reservedKeys: [...RESERVED_EDGE_KEYS] },
|
|
98
|
-
{
|
|
99
|
-
suggestion: `Rename the conflicting properties. Reserved names are added automatically to all edges.`
|
|
100
|
-
}
|
|
101
|
-
);
|
|
102
|
-
}
|
|
103
127
|
}
|
|
104
128
|
function defineEdge(name, options) {
|
|
105
129
|
const schema = options?.schema ?? EMPTY_SCHEMA;
|
|
106
130
|
validateSchemaKeys2(schema, name);
|
|
107
131
|
return Object.freeze({
|
|
108
|
-
[
|
|
132
|
+
[chunkWRWVWKQL_cjs.EDGE_TYPE_BRAND]: true,
|
|
109
133
|
kind: name,
|
|
110
134
|
schema,
|
|
111
135
|
description: options?.description,
|
|
@@ -125,7 +149,7 @@ function metaEdge(name, options = {}) {
|
|
|
125
149
|
description: options.description
|
|
126
150
|
};
|
|
127
151
|
return Object.freeze({
|
|
128
|
-
[
|
|
152
|
+
[chunkHRNQ3DVJ_cjs.META_EDGE_BRAND]: true,
|
|
129
153
|
name,
|
|
130
154
|
properties
|
|
131
155
|
});
|
|
@@ -161,7 +185,7 @@ function createExternalRef(table) {
|
|
|
161
185
|
// src/ontology/core-meta-edges.ts
|
|
162
186
|
function createMetaEdge(name, properties) {
|
|
163
187
|
return Object.freeze({
|
|
164
|
-
[
|
|
188
|
+
[chunkHRNQ3DVJ_cjs.META_EDGE_BRAND]: true,
|
|
165
189
|
name,
|
|
166
190
|
properties: {
|
|
167
191
|
transitive: properties.transitive ?? false,
|
|
@@ -173,7 +197,7 @@ function createMetaEdge(name, properties) {
|
|
|
173
197
|
}
|
|
174
198
|
});
|
|
175
199
|
}
|
|
176
|
-
var subClassOfMetaEdge = createMetaEdge(
|
|
200
|
+
var subClassOfMetaEdge = createMetaEdge(chunkHRNQ3DVJ_cjs.META_EDGE_SUB_CLASS_OF, {
|
|
177
201
|
transitive: true,
|
|
178
202
|
inference: "subsumption",
|
|
179
203
|
description: "Type inheritance (Podcast subClassOf Media)"
|
|
@@ -185,9 +209,9 @@ function subClassOf(child, parent) {
|
|
|
185
209
|
to: parent
|
|
186
210
|
};
|
|
187
211
|
}
|
|
188
|
-
var broaderMetaEdge = createMetaEdge(
|
|
212
|
+
var broaderMetaEdge = createMetaEdge(chunkHRNQ3DVJ_cjs.META_EDGE_BROADER, {
|
|
189
213
|
transitive: true,
|
|
190
|
-
inverse:
|
|
214
|
+
inverse: chunkHRNQ3DVJ_cjs.META_EDGE_NARROWER,
|
|
191
215
|
inference: "hierarchy",
|
|
192
216
|
description: "Broader concept (ML broader AI)"
|
|
193
217
|
});
|
|
@@ -198,9 +222,9 @@ function broader(narrowerConcept, broaderConcept) {
|
|
|
198
222
|
to: broaderConcept
|
|
199
223
|
};
|
|
200
224
|
}
|
|
201
|
-
var narrowerMetaEdge = createMetaEdge(
|
|
225
|
+
var narrowerMetaEdge = createMetaEdge(chunkHRNQ3DVJ_cjs.META_EDGE_NARROWER, {
|
|
202
226
|
transitive: true,
|
|
203
|
-
inverse:
|
|
227
|
+
inverse: chunkHRNQ3DVJ_cjs.META_EDGE_BROADER,
|
|
204
228
|
inference: "hierarchy",
|
|
205
229
|
description: "Narrower concept (AI narrower ML)"
|
|
206
230
|
});
|
|
@@ -211,7 +235,7 @@ function narrower(broaderConcept, narrowerConcept) {
|
|
|
211
235
|
to: narrowerConcept
|
|
212
236
|
};
|
|
213
237
|
}
|
|
214
|
-
var relatedToMetaEdge = createMetaEdge(
|
|
238
|
+
var relatedToMetaEdge = createMetaEdge(chunkHRNQ3DVJ_cjs.META_EDGE_RELATED_TO, {
|
|
215
239
|
symmetric: true,
|
|
216
240
|
inference: "association",
|
|
217
241
|
description: "Non-hierarchical association"
|
|
@@ -223,7 +247,7 @@ function relatedTo(conceptA, conceptB) {
|
|
|
223
247
|
to: conceptB
|
|
224
248
|
};
|
|
225
249
|
}
|
|
226
|
-
var equivalentToMetaEdge = createMetaEdge(
|
|
250
|
+
var equivalentToMetaEdge = createMetaEdge(chunkHRNQ3DVJ_cjs.META_EDGE_EQUIVALENT_TO, {
|
|
227
251
|
symmetric: true,
|
|
228
252
|
transitive: true,
|
|
229
253
|
inference: "substitution",
|
|
@@ -236,7 +260,7 @@ function equivalentTo(kindA, kindBOrIri) {
|
|
|
236
260
|
to: kindBOrIri
|
|
237
261
|
};
|
|
238
262
|
}
|
|
239
|
-
var sameAsMetaEdge = createMetaEdge(
|
|
263
|
+
var sameAsMetaEdge = createMetaEdge(chunkHRNQ3DVJ_cjs.META_EDGE_SAME_AS, {
|
|
240
264
|
symmetric: true,
|
|
241
265
|
transitive: true,
|
|
242
266
|
inference: "substitution",
|
|
@@ -249,7 +273,7 @@ function sameAs(kindA, kindBOrIri) {
|
|
|
249
273
|
to: kindBOrIri
|
|
250
274
|
};
|
|
251
275
|
}
|
|
252
|
-
var differentFromMetaEdge = createMetaEdge(
|
|
276
|
+
var differentFromMetaEdge = createMetaEdge(chunkHRNQ3DVJ_cjs.META_EDGE_DIFFERENT_FROM, {
|
|
253
277
|
symmetric: true,
|
|
254
278
|
inference: "constraint",
|
|
255
279
|
description: "Explicitly different individuals"
|
|
@@ -261,7 +285,7 @@ function differentFrom(kindA, kindB) {
|
|
|
261
285
|
to: kindB
|
|
262
286
|
};
|
|
263
287
|
}
|
|
264
|
-
var disjointWithMetaEdge = createMetaEdge(
|
|
288
|
+
var disjointWithMetaEdge = createMetaEdge(chunkHRNQ3DVJ_cjs.META_EDGE_DISJOINT_WITH, {
|
|
265
289
|
symmetric: true,
|
|
266
290
|
inference: "constraint",
|
|
267
291
|
description: "Mutually exclusive types"
|
|
@@ -273,9 +297,9 @@ function disjointWith(kindA, kindB) {
|
|
|
273
297
|
to: kindB
|
|
274
298
|
};
|
|
275
299
|
}
|
|
276
|
-
var partOfMetaEdge = createMetaEdge(
|
|
300
|
+
var partOfMetaEdge = createMetaEdge(chunkHRNQ3DVJ_cjs.META_EDGE_PART_OF, {
|
|
277
301
|
transitive: true,
|
|
278
|
-
inverse:
|
|
302
|
+
inverse: chunkHRNQ3DVJ_cjs.META_EDGE_HAS_PART,
|
|
279
303
|
inference: "composition",
|
|
280
304
|
description: "X is part of Y"
|
|
281
305
|
});
|
|
@@ -286,9 +310,9 @@ function partOf(part, whole) {
|
|
|
286
310
|
to: whole
|
|
287
311
|
};
|
|
288
312
|
}
|
|
289
|
-
var hasPartMetaEdge = createMetaEdge(
|
|
313
|
+
var hasPartMetaEdge = createMetaEdge(chunkHRNQ3DVJ_cjs.META_EDGE_HAS_PART, {
|
|
290
314
|
transitive: true,
|
|
291
|
-
inverse:
|
|
315
|
+
inverse: chunkHRNQ3DVJ_cjs.META_EDGE_PART_OF,
|
|
292
316
|
inference: "composition",
|
|
293
317
|
description: "Y has part X"
|
|
294
318
|
});
|
|
@@ -299,12 +323,12 @@ function hasPart(whole, part) {
|
|
|
299
323
|
to: part
|
|
300
324
|
};
|
|
301
325
|
}
|
|
302
|
-
var inverseOfMetaEdge = createMetaEdge(
|
|
326
|
+
var inverseOfMetaEdge = createMetaEdge(chunkHRNQ3DVJ_cjs.META_EDGE_INVERSE_OF, {
|
|
303
327
|
symmetric: true,
|
|
304
328
|
inference: "none",
|
|
305
329
|
description: "Edge A is inverse of edge B"
|
|
306
330
|
});
|
|
307
|
-
var impliesMetaEdge = createMetaEdge(
|
|
331
|
+
var impliesMetaEdge = createMetaEdge(chunkHRNQ3DVJ_cjs.META_EDGE_IMPLIES, {
|
|
308
332
|
transitive: true,
|
|
309
333
|
inference: "none",
|
|
310
334
|
description: "Edge A implies edge B exists"
|
|
@@ -565,6 +589,41 @@ function stringField(field2) {
|
|
|
565
589
|
ilike: (pattern) => stringOp("ilike", field2, pattern)
|
|
566
590
|
};
|
|
567
591
|
}
|
|
592
|
+
var DEFAULT_FULLTEXT_MATCH_K = 50;
|
|
593
|
+
function createFulltextAccessor(alias, hasSearchableField) {
|
|
594
|
+
return {
|
|
595
|
+
matches: (query, k, options) => {
|
|
596
|
+
if (!hasSearchableField()) {
|
|
597
|
+
throw new chunkO5PST6TB_cjs.UnsupportedPredicateError(
|
|
598
|
+
`Cannot call .$fulltext.matches() on alias "${alias}" \u2014 its node kind has no fields declared with searchable(). Add at least one: \`title: searchable({ language: "english" })\`.`
|
|
599
|
+
);
|
|
600
|
+
}
|
|
601
|
+
return fulltextMatch(
|
|
602
|
+
fieldRef(alias, ["$fulltext"]),
|
|
603
|
+
query,
|
|
604
|
+
k ?? DEFAULT_FULLTEXT_MATCH_K,
|
|
605
|
+
options
|
|
606
|
+
);
|
|
607
|
+
}
|
|
608
|
+
};
|
|
609
|
+
}
|
|
610
|
+
function fulltextMatch(field2, query, limit, options) {
|
|
611
|
+
if (!Number.isFinite(limit) || limit <= 0) {
|
|
612
|
+
throw new chunkO5PST6TB_cjs.UnsupportedPredicateError(
|
|
613
|
+
`matches() limit must be a positive finite number, got ${String(limit)}`
|
|
614
|
+
);
|
|
615
|
+
}
|
|
616
|
+
const expr = {
|
|
617
|
+
__type: "fulltext_match",
|
|
618
|
+
field: field2,
|
|
619
|
+
query,
|
|
620
|
+
mode: options?.mode ?? "websearch",
|
|
621
|
+
limit,
|
|
622
|
+
...options?.language === void 0 ? {} : { language: options.language },
|
|
623
|
+
...options?.minScore === void 0 ? {} : { minScore: options.minScore }
|
|
624
|
+
};
|
|
625
|
+
return predicate(expr);
|
|
626
|
+
}
|
|
568
627
|
function numberField(field2) {
|
|
569
628
|
return {
|
|
570
629
|
...baseFieldBuilder(field2),
|
|
@@ -664,7 +723,7 @@ function buildFieldBuilderForTypeInfo(field2, typeInfo) {
|
|
|
664
723
|
}
|
|
665
724
|
}
|
|
666
725
|
function resolvePointerTypeInfo(typeInfo, pointer) {
|
|
667
|
-
return
|
|
726
|
+
return chunkUVZHTBZ5_cjs.resolveFieldTypeInfoAtJsonPointer(typeInfo, pointer);
|
|
668
727
|
}
|
|
669
728
|
function objectOp(op, field2, pointer, value, options) {
|
|
670
729
|
const expr = {
|
|
@@ -687,8 +746,8 @@ function objectField(field2, options) {
|
|
|
687
746
|
return {
|
|
688
747
|
...baseFieldBuilder(field2),
|
|
689
748
|
get: (key) => {
|
|
690
|
-
const pointer =
|
|
691
|
-
const nestedPointer =
|
|
749
|
+
const pointer = chunkUVZHTBZ5_cjs.jsonPointer([key]);
|
|
750
|
+
const nestedPointer = chunkUVZHTBZ5_cjs.joinJsonPointers(basePointer, pointer);
|
|
692
751
|
const resolved = resolvePointerTypeInfo(options?.typeInfo, pointer);
|
|
693
752
|
const nestedRef = fieldRef(field2.alias, field2.path, {
|
|
694
753
|
jsonPointer: nestedPointer,
|
|
@@ -702,17 +761,17 @@ function objectField(field2, options) {
|
|
|
702
761
|
return buildFieldBuilderForTypeInfo(nestedRef, resolved);
|
|
703
762
|
},
|
|
704
763
|
hasKey: (key) => {
|
|
705
|
-
const pointer =
|
|
764
|
+
const pointer = chunkUVZHTBZ5_cjs.jsonPointer([key]);
|
|
706
765
|
resolvePointerTypeInfo(options?.typeInfo, pointer);
|
|
707
766
|
return objectOp("hasKey", field2, pointer);
|
|
708
767
|
},
|
|
709
768
|
hasPath: (pointer) => {
|
|
710
|
-
const normalized =
|
|
769
|
+
const normalized = chunkUVZHTBZ5_cjs.normalizeJsonPointer(pointer);
|
|
711
770
|
resolvePointerTypeInfo(options?.typeInfo, normalized);
|
|
712
771
|
return objectOp("hasPath", field2, normalized);
|
|
713
772
|
},
|
|
714
773
|
pathEquals: (pointer, value) => {
|
|
715
|
-
const normalized =
|
|
774
|
+
const normalized = chunkUVZHTBZ5_cjs.normalizeJsonPointer(pointer);
|
|
716
775
|
const resolved = resolvePointerTypeInfo(options?.typeInfo, normalized);
|
|
717
776
|
if (resolved && (resolved.valueType === "array" || resolved.valueType === "object")) {
|
|
718
777
|
throw new Error("pathEquals is only supported for scalar JSON values");
|
|
@@ -721,7 +780,7 @@ function objectField(field2, options) {
|
|
|
721
780
|
return objectOp("pathEquals", field2, normalized, value, predicateOptions);
|
|
722
781
|
},
|
|
723
782
|
pathContains: (pointer, value) => {
|
|
724
|
-
const normalized =
|
|
783
|
+
const normalized = chunkUVZHTBZ5_cjs.normalizeJsonPointer(pointer);
|
|
725
784
|
const resolved = resolvePointerTypeInfo(options?.typeInfo, normalized);
|
|
726
785
|
if (resolved && resolved.valueType !== "array") {
|
|
727
786
|
throw new Error("pathContains is only supported for JSON array values");
|
|
@@ -736,7 +795,7 @@ function objectField(field2, options) {
|
|
|
736
795
|
);
|
|
737
796
|
},
|
|
738
797
|
pathIsNull: (pointer) => {
|
|
739
|
-
const normalized =
|
|
798
|
+
const normalized = chunkUVZHTBZ5_cjs.normalizeJsonPointer(pointer);
|
|
740
799
|
const resolved = resolvePointerTypeInfo(options?.typeInfo, normalized);
|
|
741
800
|
const predicateOptions = resolved?.valueType === void 0 ? void 0 : { valueType: resolved.valueType };
|
|
742
801
|
return objectOp(
|
|
@@ -748,7 +807,7 @@ function objectField(field2, options) {
|
|
|
748
807
|
);
|
|
749
808
|
},
|
|
750
809
|
pathIsNotNull: (pointer) => {
|
|
751
|
-
const normalized =
|
|
810
|
+
const normalized = chunkUVZHTBZ5_cjs.normalizeJsonPointer(pointer);
|
|
752
811
|
const resolved = resolvePointerTypeInfo(options?.typeInfo, normalized);
|
|
753
812
|
const predicateOptions = resolved?.valueType === void 0 ? void 0 : { valueType: resolved.valueType };
|
|
754
813
|
return objectOp(
|
|
@@ -760,8 +819,8 @@ function objectField(field2, options) {
|
|
|
760
819
|
);
|
|
761
820
|
},
|
|
762
821
|
field: (pointer) => {
|
|
763
|
-
const normalized =
|
|
764
|
-
const nestedPointer =
|
|
822
|
+
const normalized = chunkUVZHTBZ5_cjs.normalizeJsonPointer(pointer);
|
|
823
|
+
const nestedPointer = chunkUVZHTBZ5_cjs.joinJsonPointers(basePointer, normalized);
|
|
765
824
|
const resolved = resolvePointerTypeInfo(options?.typeInfo, normalized);
|
|
766
825
|
const nestedRef = fieldRef(field2.alias, field2.path, {
|
|
767
826
|
jsonPointer: nestedPointer,
|
|
@@ -891,7 +950,7 @@ function getFieldPointer(field2) {
|
|
|
891
950
|
return field2.jsonPointer;
|
|
892
951
|
}
|
|
893
952
|
if (field2.path.length > 1 && field2.path[0] === "props") {
|
|
894
|
-
return
|
|
953
|
+
return chunkUVZHTBZ5_cjs.jsonPointer(field2.path.slice(1));
|
|
895
954
|
}
|
|
896
955
|
return void 0;
|
|
897
956
|
}
|
|
@@ -1140,11 +1199,13 @@ function compilePredicateExpression(expr, ctx) {
|
|
|
1140
1199
|
case "in_subquery": {
|
|
1141
1200
|
return compileInSubquery(expr, ctx);
|
|
1142
1201
|
}
|
|
1143
|
-
case "vector_similarity":
|
|
1144
|
-
|
|
1202
|
+
case "vector_similarity":
|
|
1203
|
+
case "fulltext_match": {
|
|
1204
|
+
return STRUCTURAL_PREDICATE_NOOP;
|
|
1145
1205
|
}
|
|
1146
1206
|
}
|
|
1147
1207
|
}
|
|
1208
|
+
var STRUCTURAL_PREDICATE_NOOP = drizzleOrm.sql.raw("1=1");
|
|
1148
1209
|
function compileComparisonPredicate(expr, dialect, cteColumnPrefix) {
|
|
1149
1210
|
if (isParameterRef(expr.right)) {
|
|
1150
1211
|
const parameterValueType = normalizeValueType2(expr.right.valueType) ?? normalizeValueType2(expr.left.valueType);
|
|
@@ -1248,7 +1309,7 @@ function compileArrayPredicate(expr, dialect, cteColumnPrefix) {
|
|
|
1248
1309
|
}
|
|
1249
1310
|
function compileObjectPredicate(expr, dialect, cteColumnPrefix) {
|
|
1250
1311
|
const basePointer = getFieldPointer(expr.field);
|
|
1251
|
-
const pointer =
|
|
1312
|
+
const pointer = chunkUVZHTBZ5_cjs.joinJsonPointers(basePointer, expr.pointer);
|
|
1252
1313
|
const column = compileFieldColumn(expr.field, void 0, cteColumnPrefix);
|
|
1253
1314
|
switch (expr.op) {
|
|
1254
1315
|
case "hasKey":
|
|
@@ -1398,45 +1459,61 @@ function compileInSubquery(expr, ctx) {
|
|
|
1398
1459
|
const subSql = ctx.compileQuery(expr.subquery, expr.subquery.graphId);
|
|
1399
1460
|
return expr.negated ? drizzleOrm.sql`${fieldSql} NOT IN (${subSql})` : drizzleOrm.sql`${fieldSql} IN (${subSql})`;
|
|
1400
1461
|
}
|
|
1401
|
-
function
|
|
1402
|
-
return drizzleOrm.sql.raw("1=1");
|
|
1403
|
-
}
|
|
1404
|
-
function extractVectorSimilarityPredicates(predicates) {
|
|
1462
|
+
function extractStructuralPredicates(predicates, isMatch, rejectionMessage) {
|
|
1405
1463
|
const results = [];
|
|
1406
1464
|
function visit(expr, inDisallowedBranch) {
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
throw new chunkO5PST6TB_cjs.UnsupportedPredicateError(
|
|
1411
|
-
"Vector similarity predicates cannot be nested under OR or NOT. Use top-level AND combinations instead."
|
|
1412
|
-
);
|
|
1413
|
-
}
|
|
1414
|
-
results.push(expr);
|
|
1415
|
-
break;
|
|
1465
|
+
if (isMatch(expr)) {
|
|
1466
|
+
if (inDisallowedBranch) {
|
|
1467
|
+
throw new chunkO5PST6TB_cjs.UnsupportedPredicateError(rejectionMessage);
|
|
1416
1468
|
}
|
|
1469
|
+
results.push(expr);
|
|
1470
|
+
return;
|
|
1471
|
+
}
|
|
1472
|
+
switch (expr.__type) {
|
|
1417
1473
|
case "and": {
|
|
1418
|
-
for (const
|
|
1419
|
-
|
|
1420
|
-
}
|
|
1421
|
-
break;
|
|
1474
|
+
for (const child of expr.predicates) visit(child, inDisallowedBranch);
|
|
1475
|
+
return;
|
|
1422
1476
|
}
|
|
1423
1477
|
case "or": {
|
|
1424
|
-
for (const
|
|
1425
|
-
|
|
1426
|
-
}
|
|
1427
|
-
break;
|
|
1478
|
+
for (const child of expr.predicates) visit(child, true);
|
|
1479
|
+
return;
|
|
1428
1480
|
}
|
|
1429
1481
|
case "not": {
|
|
1430
1482
|
visit(expr.predicate, true);
|
|
1431
|
-
|
|
1483
|
+
return;
|
|
1484
|
+
}
|
|
1485
|
+
case "comparison":
|
|
1486
|
+
case "string_op":
|
|
1487
|
+
case "null_check":
|
|
1488
|
+
case "between":
|
|
1489
|
+
case "array_op":
|
|
1490
|
+
case "object_op":
|
|
1491
|
+
case "aggregate_comparison":
|
|
1492
|
+
case "exists":
|
|
1493
|
+
case "in_subquery":
|
|
1494
|
+
case "vector_similarity":
|
|
1495
|
+
case "fulltext_match": {
|
|
1496
|
+
return;
|
|
1432
1497
|
}
|
|
1433
1498
|
}
|
|
1434
1499
|
}
|
|
1435
|
-
for (const pred of predicates)
|
|
1436
|
-
visit(pred.expression, false);
|
|
1437
|
-
}
|
|
1500
|
+
for (const pred of predicates) visit(pred.expression, false);
|
|
1438
1501
|
return results;
|
|
1439
1502
|
}
|
|
1503
|
+
function extractVectorSimilarityPredicates(predicates) {
|
|
1504
|
+
return extractStructuralPredicates(
|
|
1505
|
+
predicates,
|
|
1506
|
+
(expr) => expr.__type === "vector_similarity",
|
|
1507
|
+
"Vector similarity predicates cannot be nested under OR or NOT. Use top-level AND combinations instead."
|
|
1508
|
+
);
|
|
1509
|
+
}
|
|
1510
|
+
function extractFulltextMatchPredicates(predicates) {
|
|
1511
|
+
return extractStructuralPredicates(
|
|
1512
|
+
predicates,
|
|
1513
|
+
(expr) => expr.__type === "fulltext_match",
|
|
1514
|
+
"Fulltext match predicates cannot be nested under OR or NOT. Use top-level AND combinations instead."
|
|
1515
|
+
);
|
|
1516
|
+
}
|
|
1440
1517
|
|
|
1441
1518
|
// src/query/compiler/predicate-utils.ts
|
|
1442
1519
|
var EMPTY_PREDICATES = [];
|
|
@@ -1495,6 +1572,10 @@ function getNodeKindsForAlias(ast, alias) {
|
|
|
1495
1572
|
}
|
|
1496
1573
|
throw new chunkO5PST6TB_cjs.CompilerInvariantError(`Unknown traversal source alias: ${alias}`);
|
|
1497
1574
|
}
|
|
1575
|
+
function getHybridTargetAlias(vectorPredicate, fulltextPredicate) {
|
|
1576
|
+
if (!vectorPredicate || !fulltextPredicate) return void 0;
|
|
1577
|
+
return vectorPredicate.field.alias === fulltextPredicate.field.alias ? vectorPredicate.field.alias : void 0;
|
|
1578
|
+
}
|
|
1498
1579
|
|
|
1499
1580
|
// src/query/compiler/emitter/plan-inspector.ts
|
|
1500
1581
|
function collectPlanOperations(node, ops) {
|
|
@@ -1502,6 +1583,7 @@ function collectPlanOperations(node, ops) {
|
|
|
1502
1583
|
switch (node.op) {
|
|
1503
1584
|
case "aggregate":
|
|
1504
1585
|
case "filter":
|
|
1586
|
+
case "fulltext_match":
|
|
1505
1587
|
case "join":
|
|
1506
1588
|
case "limit_offset":
|
|
1507
1589
|
case "project":
|
|
@@ -1530,6 +1612,7 @@ function findUnaryNodeInProjectChain(rootNode, op) {
|
|
|
1530
1612
|
switch (currentNode.op) {
|
|
1531
1613
|
case "aggregate":
|
|
1532
1614
|
case "filter":
|
|
1615
|
+
case "fulltext_match":
|
|
1533
1616
|
case "join":
|
|
1534
1617
|
case "limit_offset":
|
|
1535
1618
|
case "recursive_expand":
|
|
@@ -1565,6 +1648,7 @@ function inspectProjectPlan(logicalPlan) {
|
|
|
1565
1648
|
);
|
|
1566
1649
|
return {
|
|
1567
1650
|
hasAggregate: operations.has("aggregate"),
|
|
1651
|
+
hasFulltextMatch: operations.has("fulltext_match"),
|
|
1568
1652
|
hasLimitOffset: operations.has("limit_offset"),
|
|
1569
1653
|
hasRecursiveExpand: operations.has("recursive_expand"),
|
|
1570
1654
|
hasSetOperation: operations.has("set_op"),
|
|
@@ -1616,6 +1700,7 @@ function findTopLevelLimitOffsetNode(rootNode) {
|
|
|
1616
1700
|
switch (currentNode.op) {
|
|
1617
1701
|
case "aggregate":
|
|
1618
1702
|
case "filter":
|
|
1703
|
+
case "fulltext_match":
|
|
1619
1704
|
case "join":
|
|
1620
1705
|
case "project":
|
|
1621
1706
|
case "recursive_expand":
|
|
@@ -1640,6 +1725,7 @@ function findTopLevelSortNode(rootNode) {
|
|
|
1640
1725
|
switch (currentNode.op) {
|
|
1641
1726
|
case "aggregate":
|
|
1642
1727
|
case "filter":
|
|
1728
|
+
case "fulltext_match":
|
|
1643
1729
|
case "join":
|
|
1644
1730
|
case "limit_offset":
|
|
1645
1731
|
case "project":
|
|
@@ -1774,16 +1860,16 @@ function assertStandardEmitterClauseAlignment(logicalPlan, input) {
|
|
|
1774
1860
|
{ component: "standard-emitter" }
|
|
1775
1861
|
);
|
|
1776
1862
|
}
|
|
1777
|
-
const expectsOrderBy = planShape.hasSort || planShape.hasVectorKnn;
|
|
1863
|
+
const expectsOrderBy = planShape.hasSort || planShape.hasVectorKnn || planShape.hasFulltextMatch;
|
|
1778
1864
|
if (expectsOrderBy && input.orderBy === void 0) {
|
|
1779
1865
|
throw new chunkO5PST6TB_cjs.CompilerInvariantError(
|
|
1780
|
-
"Standard SQL emitter expected ORDER BY clause for plan containing a sort or
|
|
1866
|
+
"Standard SQL emitter expected ORDER BY clause for plan containing a sort, vector_knn, or fulltext_match node",
|
|
1781
1867
|
{ component: "standard-emitter" }
|
|
1782
1868
|
);
|
|
1783
1869
|
}
|
|
1784
1870
|
if (!expectsOrderBy && input.orderBy !== void 0) {
|
|
1785
1871
|
throw new chunkO5PST6TB_cjs.CompilerInvariantError(
|
|
1786
|
-
"Standard SQL emitter received ORDER BY clause for a plan without sort or
|
|
1872
|
+
"Standard SQL emitter received ORDER BY clause for a plan without sort, vector_knn, or fulltext_match nodes",
|
|
1787
1873
|
{ component: "standard-emitter" }
|
|
1788
1874
|
);
|
|
1789
1875
|
}
|
|
@@ -1822,6 +1908,62 @@ function emitStandardQuerySql(input) {
|
|
|
1822
1908
|
return drizzleOrm.sql.join(parts, drizzleOrm.sql` `);
|
|
1823
1909
|
}
|
|
1824
1910
|
|
|
1911
|
+
// src/query/ast.ts
|
|
1912
|
+
var DEFAULT_RRF_K = 60;
|
|
1913
|
+
var DEFAULT_RRF_WEIGHT = 1;
|
|
1914
|
+
function mergeEdgeKinds(traversal) {
|
|
1915
|
+
const inverse = traversal.inverseEdgeKinds;
|
|
1916
|
+
if (inverse === void 0 || inverse.length === 0) return traversal.edgeKinds;
|
|
1917
|
+
return [
|
|
1918
|
+
...traversal.edgeKinds,
|
|
1919
|
+
...inverse.filter((kind) => !traversal.edgeKinds.includes(kind))
|
|
1920
|
+
];
|
|
1921
|
+
}
|
|
1922
|
+
var DEFAULT_TABLE_NAMES = {
|
|
1923
|
+
nodes: "typegraph_nodes",
|
|
1924
|
+
edges: "typegraph_edges",
|
|
1925
|
+
embeddings: "typegraph_node_embeddings",
|
|
1926
|
+
fulltext: "typegraph_node_fulltext"
|
|
1927
|
+
};
|
|
1928
|
+
var VALID_IDENTIFIER_PATTERN = /^[a-z_][a-z0-9_$]*$/i;
|
|
1929
|
+
function validateTableName(name, label) {
|
|
1930
|
+
if (!name || name.length === 0) {
|
|
1931
|
+
throw new chunkO5PST6TB_cjs.ConfigurationError(`${label} table name cannot be empty`);
|
|
1932
|
+
}
|
|
1933
|
+
if (name.length > chunkFQCQM3KG_cjs.MAX_PG_IDENTIFIER_LENGTH) {
|
|
1934
|
+
throw new chunkO5PST6TB_cjs.ConfigurationError(
|
|
1935
|
+
`${label} table name exceeds maximum length of ${chunkFQCQM3KG_cjs.MAX_PG_IDENTIFIER_LENGTH} characters`
|
|
1936
|
+
);
|
|
1937
|
+
}
|
|
1938
|
+
if (!VALID_IDENTIFIER_PATTERN.test(name)) {
|
|
1939
|
+
throw new chunkO5PST6TB_cjs.ConfigurationError(
|
|
1940
|
+
`${label} table name "${name}" is not a valid SQL identifier. Table names must start with a letter or underscore and contain only letters, digits, underscores, or dollar signs.`
|
|
1941
|
+
);
|
|
1942
|
+
}
|
|
1943
|
+
}
|
|
1944
|
+
function quoteIdentifier(name) {
|
|
1945
|
+
return `"${name.replaceAll('"', '""')}"`;
|
|
1946
|
+
}
|
|
1947
|
+
function createSqlSchema(names = {}) {
|
|
1948
|
+
const tables = { ...DEFAULT_TABLE_NAMES, ...names };
|
|
1949
|
+
validateTableName(tables.nodes, "nodes");
|
|
1950
|
+
validateTableName(tables.edges, "edges");
|
|
1951
|
+
validateTableName(tables.embeddings, "embeddings");
|
|
1952
|
+
validateTableName(tables.fulltext, "fulltext");
|
|
1953
|
+
return {
|
|
1954
|
+
tables,
|
|
1955
|
+
nodesTable: drizzleOrm.sql.raw(quoteIdentifier(tables.nodes)),
|
|
1956
|
+
edgesTable: drizzleOrm.sql.raw(quoteIdentifier(tables.edges)),
|
|
1957
|
+
embeddingsTable: drizzleOrm.sql.raw(quoteIdentifier(tables.embeddings)),
|
|
1958
|
+
fulltextTable: drizzleOrm.sql.raw(quoteIdentifier(tables.fulltext))
|
|
1959
|
+
};
|
|
1960
|
+
}
|
|
1961
|
+
var DEFAULT_SQL_SCHEMA = createSqlSchema();
|
|
1962
|
+
var ALIAS_CTE_PREFIX = "cte_";
|
|
1963
|
+
var EMBEDDINGS_CTE_ALIAS = "cte_embeddings";
|
|
1964
|
+
var FULLTEXT_CTE_ALIAS = "cte_fulltext";
|
|
1965
|
+
var HYBRID_CANDIDATES_CTE_ALIAS = "cte_relevance_candidates";
|
|
1966
|
+
|
|
1825
1967
|
// src/query/compiler/typed-json-extract.ts
|
|
1826
1968
|
function compileTypedJsonExtract(input) {
|
|
1827
1969
|
const { column, dialect, pointer, valueType } = input;
|
|
@@ -1875,7 +2017,7 @@ var EDGE_COLUMNS = [
|
|
|
1875
2017
|
"deleted_at"
|
|
1876
2018
|
];
|
|
1877
2019
|
var EMPTY_REQUIRED_COLUMNS = /* @__PURE__ */ new Set();
|
|
1878
|
-
function
|
|
2020
|
+
function quoteIdentifier2(identifier) {
|
|
1879
2021
|
return drizzleOrm.sql.raw(`"${identifier.replaceAll('"', '""')}"`);
|
|
1880
2022
|
}
|
|
1881
2023
|
function shouldProjectColumn(requiredColumns, column, alwaysRequiredColumns) {
|
|
@@ -1929,6 +2071,21 @@ function compileColumnReference(tableAlias, column) {
|
|
|
1929
2071
|
}
|
|
1930
2072
|
return drizzleOrm.sql`${drizzleOrm.sql.raw(tableAlias)}.${drizzleOrm.sql.raw(column)}`;
|
|
1931
2073
|
}
|
|
2074
|
+
function qualifyColumn(owner, name) {
|
|
2075
|
+
return drizzleOrm.sql`${quoteIdentifier2(owner)}.${quoteIdentifier2(name)}`;
|
|
2076
|
+
}
|
|
2077
|
+
var SCOPED_RELEVANCE_NODES_ALIAS = "scoped_relevance_nodes";
|
|
2078
|
+
function buildScopedNodeIdsSubquery(nodeAlias) {
|
|
2079
|
+
const cteAlias = `${ALIAS_CTE_PREFIX}${nodeAlias}`;
|
|
2080
|
+
return drizzleOrm.sql`
|
|
2081
|
+
(
|
|
2082
|
+
SELECT DISTINCT
|
|
2083
|
+
${qualifyColumn(cteAlias, `${nodeAlias}_id`)} AS node_id,
|
|
2084
|
+
${qualifyColumn(cteAlias, `${nodeAlias}_kind`)} AS node_kind
|
|
2085
|
+
FROM ${quoteIdentifier2(cteAlias)}
|
|
2086
|
+
) AS ${drizzleOrm.sql.raw(SCOPED_RELEVANCE_NODES_ALIAS)}
|
|
2087
|
+
`;
|
|
2088
|
+
}
|
|
1932
2089
|
function compileNodeSelectColumns(tableAlias, alias, requiredColumns) {
|
|
1933
2090
|
return NODE_COLUMNS.filter(
|
|
1934
2091
|
(column) => column === "id" || column === "kind" || shouldProjectColumn(requiredColumns, column)
|
|
@@ -2181,7 +2338,7 @@ function buildStandardProjection(input) {
|
|
|
2181
2338
|
}
|
|
2182
2339
|
const projectedFields = fields.map((field2) => {
|
|
2183
2340
|
const source = compileProjectedSource(field2, dialect);
|
|
2184
|
-
return drizzleOrm.sql`${source} AS ${
|
|
2341
|
+
return drizzleOrm.sql`${source} AS ${quoteIdentifier2(field2.outputName)}`;
|
|
2185
2342
|
});
|
|
2186
2343
|
return drizzleOrm.sql.join(projectedFields, drizzleOrm.sql`, `);
|
|
2187
2344
|
}
|
|
@@ -2201,23 +2358,67 @@ function compileSelectiveProjection(fields, dialect, ast, collapsedTraversalCteA
|
|
|
2201
2358
|
const cteAlias = collapsedTraversalCteAlias ?? aliasToCte.get(field2.alias) ?? `cte_${field2.alias}`;
|
|
2202
2359
|
if (field2.isSystemField) {
|
|
2203
2360
|
const dbColumn = mapSelectiveSystemFieldToColumn(field2.field);
|
|
2204
|
-
return drizzleOrm.sql`${drizzleOrm.sql.raw(cteAlias)}.${drizzleOrm.sql.raw(`${field2.alias}_${dbColumn}`)} AS ${
|
|
2361
|
+
return drizzleOrm.sql`${drizzleOrm.sql.raw(cteAlias)}.${drizzleOrm.sql.raw(`${field2.alias}_${dbColumn}`)} AS ${quoteIdentifier2(field2.outputName)}`;
|
|
2205
2362
|
}
|
|
2206
2363
|
const propsColumn = `${field2.alias}_props`;
|
|
2207
2364
|
const column = drizzleOrm.sql`${drizzleOrm.sql.raw(cteAlias)}.${drizzleOrm.sql.raw(propsColumn)}`;
|
|
2208
|
-
const pointer =
|
|
2365
|
+
const pointer = chunkUVZHTBZ5_cjs.jsonPointer([field2.field]);
|
|
2209
2366
|
const extracted = compileTypedJsonExtract({
|
|
2210
2367
|
column,
|
|
2211
2368
|
dialect,
|
|
2212
2369
|
pointer,
|
|
2213
2370
|
valueType: field2.valueType
|
|
2214
2371
|
});
|
|
2215
|
-
return drizzleOrm.sql`${extracted} AS ${
|
|
2372
|
+
return drizzleOrm.sql`${extracted} AS ${quoteIdentifier2(field2.outputName)}`;
|
|
2216
2373
|
});
|
|
2217
2374
|
return drizzleOrm.sql.join(columns, drizzleOrm.sql`, `);
|
|
2218
2375
|
}
|
|
2376
|
+
function buildRelevanceJoins(vectorPredicate, fulltextPredicate) {
|
|
2377
|
+
const hybridTargetAlias = getHybridTargetAlias(
|
|
2378
|
+
vectorPredicate,
|
|
2379
|
+
fulltextPredicate
|
|
2380
|
+
);
|
|
2381
|
+
if (hybridTargetAlias !== void 0) {
|
|
2382
|
+
return [
|
|
2383
|
+
buildHybridCandidateJoin(hybridTargetAlias),
|
|
2384
|
+
buildRelevanceJoin(EMBEDDINGS_CTE_ALIAS, hybridTargetAlias, "LEFT JOIN"),
|
|
2385
|
+
buildRelevanceJoin(FULLTEXT_CTE_ALIAS, hybridTargetAlias, "LEFT JOIN")
|
|
2386
|
+
];
|
|
2387
|
+
}
|
|
2388
|
+
const joins = [];
|
|
2389
|
+
if (vectorPredicate) {
|
|
2390
|
+
joins.push(
|
|
2391
|
+
buildRelevanceJoin(EMBEDDINGS_CTE_ALIAS, vectorPredicate.field.alias)
|
|
2392
|
+
);
|
|
2393
|
+
}
|
|
2394
|
+
if (fulltextPredicate) {
|
|
2395
|
+
joins.push(
|
|
2396
|
+
buildRelevanceJoin(FULLTEXT_CTE_ALIAS, fulltextPredicate.field.alias)
|
|
2397
|
+
);
|
|
2398
|
+
}
|
|
2399
|
+
return joins;
|
|
2400
|
+
}
|
|
2401
|
+
function buildHybridCandidateJoin(nodeAlias) {
|
|
2402
|
+
const candidateCte = drizzleOrm.sql.raw(HYBRID_CANDIDATES_CTE_ALIAS);
|
|
2403
|
+
const node = drizzleOrm.sql.raw(`${ALIAS_CTE_PREFIX}${nodeAlias}`);
|
|
2404
|
+
const idColumn = drizzleOrm.sql.raw(`${nodeAlias}_id`);
|
|
2405
|
+
const kindColumn = drizzleOrm.sql.raw(`${nodeAlias}_kind`);
|
|
2406
|
+
return drizzleOrm.sql`INNER JOIN ${candidateCte} ON ${candidateCte}.node_id = ${node}.${idColumn} AND ${candidateCte}.node_kind = ${node}.${kindColumn}`;
|
|
2407
|
+
}
|
|
2408
|
+
function buildRelevanceJoin(cteAlias, nodeAlias, joinType = "INNER JOIN") {
|
|
2409
|
+
const cte = drizzleOrm.sql.raw(cteAlias);
|
|
2410
|
+
const node = drizzleOrm.sql.raw(`${ALIAS_CTE_PREFIX}${nodeAlias}`);
|
|
2411
|
+
const idColumn = drizzleOrm.sql.raw(`${nodeAlias}_id`);
|
|
2412
|
+
const kindColumn = drizzleOrm.sql.raw(`${nodeAlias}_kind`);
|
|
2413
|
+
return drizzleOrm.sql`${drizzleOrm.sql.raw(joinType)} ${cte} ON ${cte}.node_id = ${node}.${idColumn} AND ${cte}.node_kind = ${node}.${kindColumn}`;
|
|
2414
|
+
}
|
|
2219
2415
|
function buildStandardFromClause(input) {
|
|
2220
|
-
const {
|
|
2416
|
+
const {
|
|
2417
|
+
ast,
|
|
2418
|
+
collapsedTraversalCteAlias,
|
|
2419
|
+
fulltextPredicate,
|
|
2420
|
+
vectorPredicate
|
|
2421
|
+
} = input;
|
|
2221
2422
|
if (collapsedTraversalCteAlias !== void 0) {
|
|
2222
2423
|
return drizzleOrm.sql`FROM ${drizzleOrm.sql.raw(collapsedTraversalCteAlias)}`;
|
|
2223
2424
|
}
|
|
@@ -2232,11 +2433,8 @@ function buildStandardFromClause(input) {
|
|
|
2232
2433
|
drizzleOrm.sql`${drizzleOrm.sql.raw(joinType)} ${drizzleOrm.sql.raw(cteAlias)} ON ${drizzleOrm.sql.raw(cteAlias)}.${drizzleOrm.sql.raw(previousAlias)}_id = cte_${drizzleOrm.sql.raw(previousAlias)}.${drizzleOrm.sql.raw(previousAlias)}_id AND ${drizzleOrm.sql.raw(cteAlias)}.${drizzleOrm.sql.raw(previousAlias)}_kind = cte_${drizzleOrm.sql.raw(previousAlias)}.${drizzleOrm.sql.raw(previousAlias)}_kind`
|
|
2233
2434
|
);
|
|
2234
2435
|
}
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
joins.push(
|
|
2238
|
-
drizzleOrm.sql`INNER JOIN cte_embeddings ON cte_embeddings.node_id = cte_${drizzleOrm.sql.raw(nodeAlias)}.${drizzleOrm.sql.raw(nodeAlias)}_id`
|
|
2239
|
-
);
|
|
2436
|
+
for (const join of buildRelevanceJoins(vectorPredicate, fulltextPredicate)) {
|
|
2437
|
+
joins.push(join);
|
|
2240
2438
|
}
|
|
2241
2439
|
return joins.length === 0 ? fromClause : drizzleOrm.sql`${fromClause} ${drizzleOrm.sql.join(joins, drizzleOrm.sql` `)}`;
|
|
2242
2440
|
}
|
|
@@ -2275,6 +2473,15 @@ function fieldRefKey(field2) {
|
|
|
2275
2473
|
const pointer = field2.jsonPointer ?? "";
|
|
2276
2474
|
return `${field2.alias}:${field2.path.join(".")}:${pointer}`;
|
|
2277
2475
|
}
|
|
2476
|
+
function resolveEmbeddingFieldPath(field2) {
|
|
2477
|
+
if (field2.jsonPointer !== void 0 && field2.jsonPointer !== "" && field2.jsonPointer !== "/") {
|
|
2478
|
+
return field2.jsonPointer.replace(/^\//u, "").replaceAll("/", ".");
|
|
2479
|
+
}
|
|
2480
|
+
if (field2.path[0] === "props") {
|
|
2481
|
+
return field2.path.slice(1).join(".");
|
|
2482
|
+
}
|
|
2483
|
+
return field2.path.join(".");
|
|
2484
|
+
}
|
|
2278
2485
|
function buildStandardGroupBy(input) {
|
|
2279
2486
|
const { ast, dialect } = input;
|
|
2280
2487
|
if (!ast.groupBy || ast.groupBy.fields.length === 0) {
|
|
@@ -2321,18 +2528,29 @@ function buildStandardHaving(input) {
|
|
|
2321
2528
|
return drizzleOrm.sql`HAVING ${condition}`;
|
|
2322
2529
|
}
|
|
2323
2530
|
function buildStandardEmbeddingsCte(input) {
|
|
2324
|
-
const { ctx, graphId, vectorPredicate } = input;
|
|
2531
|
+
const { ctx, graphId, nodeKinds, vectorPredicate } = input;
|
|
2325
2532
|
const { dialect } = ctx;
|
|
2326
2533
|
const { field: field2, metric, minScore, queryEmbedding } = vectorPredicate;
|
|
2327
|
-
const
|
|
2534
|
+
const embeddingsTableName = ctx.schema.tables.embeddings;
|
|
2535
|
+
if (nodeKinds.length === 0) {
|
|
2536
|
+
throw new chunkO5PST6TB_cjs.UnsupportedPredicateError(
|
|
2537
|
+
"Vector predicate must resolve to at least one node kind"
|
|
2538
|
+
);
|
|
2539
|
+
}
|
|
2540
|
+
const fieldPath = resolveEmbeddingFieldPath(field2);
|
|
2328
2541
|
const distanceExpr = dialect.vectorDistance(
|
|
2329
|
-
|
|
2542
|
+
qualifyColumn(embeddingsTableName, "embedding"),
|
|
2330
2543
|
queryEmbedding,
|
|
2331
2544
|
metric
|
|
2332
2545
|
);
|
|
2546
|
+
const scopedNodes = buildScopedNodeIdsSubquery(field2.alias);
|
|
2333
2547
|
const conditions = [
|
|
2334
|
-
drizzleOrm.sql
|
|
2335
|
-
|
|
2548
|
+
drizzleOrm.sql`${qualifyColumn(embeddingsTableName, "graph_id")} = ${graphId}`,
|
|
2549
|
+
compileKindFilter(
|
|
2550
|
+
qualifyColumn(embeddingsTableName, "node_kind"),
|
|
2551
|
+
nodeKinds
|
|
2552
|
+
),
|
|
2553
|
+
drizzleOrm.sql`${qualifyColumn(embeddingsTableName, "field_path")} = ${fieldPath}`
|
|
2336
2554
|
];
|
|
2337
2555
|
if (minScore !== void 0) {
|
|
2338
2556
|
conditions.push(
|
|
@@ -2341,14 +2559,29 @@ function buildStandardEmbeddingsCte(input) {
|
|
|
2341
2559
|
}
|
|
2342
2560
|
const scoreExpr = compileVectorScoreExpression(distanceExpr, metric);
|
|
2343
2561
|
return drizzleOrm.sql`
|
|
2344
|
-
|
|
2562
|
+
${drizzleOrm.sql.raw(EMBEDDINGS_CTE_ALIAS)} AS (
|
|
2345
2563
|
SELECT
|
|
2346
2564
|
node_id,
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2565
|
+
node_kind,
|
|
2566
|
+
distance,
|
|
2567
|
+
score,
|
|
2568
|
+
ROW_NUMBER() OVER (ORDER BY distance ASC) AS ord
|
|
2569
|
+
FROM (
|
|
2570
|
+
SELECT
|
|
2571
|
+
${qualifyColumn(embeddingsTableName, "node_id")} AS node_id,
|
|
2572
|
+
${qualifyColumn(embeddingsTableName, "node_kind")} AS node_kind,
|
|
2573
|
+
${distanceExpr} AS distance,
|
|
2574
|
+
${scoreExpr} AS score
|
|
2575
|
+
FROM ${ctx.schema.embeddingsTable}
|
|
2576
|
+
INNER JOIN ${scopedNodes}
|
|
2577
|
+
ON ${drizzleOrm.sql.raw(`${SCOPED_RELEVANCE_NODES_ALIAS}.node_id`)} =
|
|
2578
|
+
${qualifyColumn(embeddingsTableName, "node_id")}
|
|
2579
|
+
AND ${drizzleOrm.sql.raw(`${SCOPED_RELEVANCE_NODES_ALIAS}.node_kind`)} =
|
|
2580
|
+
${qualifyColumn(embeddingsTableName, "node_kind")}
|
|
2581
|
+
WHERE ${drizzleOrm.sql.join(conditions, drizzleOrm.sql` AND `)}
|
|
2582
|
+
ORDER BY distance ASC
|
|
2583
|
+
LIMIT ${vectorPredicate.limit}
|
|
2584
|
+
) AS vec_inner
|
|
2352
2585
|
)
|
|
2353
2586
|
`;
|
|
2354
2587
|
}
|
|
@@ -2378,37 +2611,130 @@ function compileVectorMinScoreCondition(distanceExpr, metric, minScore) {
|
|
|
2378
2611
|
}
|
|
2379
2612
|
}
|
|
2380
2613
|
}
|
|
2381
|
-
function
|
|
2382
|
-
const {
|
|
2383
|
-
const
|
|
2384
|
-
const
|
|
2385
|
-
if (
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2614
|
+
function buildStandardFulltextCte(input) {
|
|
2615
|
+
const { ctx, fulltextPredicate, graphId, nodeKinds } = input;
|
|
2616
|
+
const { dialect, schema } = ctx;
|
|
2617
|
+
const { query, mode, language, limit, minScore } = fulltextPredicate;
|
|
2618
|
+
if (nodeKinds.length === 0) {
|
|
2619
|
+
throw new chunkO5PST6TB_cjs.UnsupportedPredicateError(
|
|
2620
|
+
"Fulltext predicate must resolve to at least one node kind"
|
|
2621
|
+
);
|
|
2622
|
+
}
|
|
2623
|
+
const tableName = schema.tables.fulltext;
|
|
2624
|
+
const scopedNodes = buildScopedNodeIdsSubquery(fulltextPredicate.field.alias);
|
|
2625
|
+
const fulltextStrategy = dialect.fulltext;
|
|
2626
|
+
if (fulltextStrategy === void 0) {
|
|
2627
|
+
throw new chunkO5PST6TB_cjs.UnsupportedPredicateError(
|
|
2628
|
+
`Fulltext match predicates are not supported for dialect "${dialect.name}"`
|
|
2629
|
+
);
|
|
2630
|
+
}
|
|
2631
|
+
const matchCondition = fulltextStrategy.matchCondition(
|
|
2632
|
+
tableName,
|
|
2633
|
+
query,
|
|
2634
|
+
mode,
|
|
2635
|
+
language
|
|
2636
|
+
);
|
|
2637
|
+
const rankExpression = fulltextStrategy.rankExpression(
|
|
2638
|
+
tableName,
|
|
2639
|
+
query,
|
|
2640
|
+
mode,
|
|
2641
|
+
language
|
|
2642
|
+
);
|
|
2643
|
+
const conditions = [
|
|
2644
|
+
drizzleOrm.sql`${qualifyColumn(tableName, "graph_id")} = ${graphId}`,
|
|
2645
|
+
compileKindFilter(qualifyColumn(tableName, "node_kind"), nodeKinds),
|
|
2646
|
+
matchCondition
|
|
2647
|
+
];
|
|
2648
|
+
if (minScore !== void 0) {
|
|
2649
|
+
conditions.push(drizzleOrm.sql`${rankExpression} >= ${minScore}`);
|
|
2650
|
+
}
|
|
2651
|
+
return drizzleOrm.sql`
|
|
2652
|
+
${drizzleOrm.sql.raw(FULLTEXT_CTE_ALIAS)} AS (
|
|
2653
|
+
SELECT
|
|
2654
|
+
node_id,
|
|
2655
|
+
node_kind,
|
|
2656
|
+
rank,
|
|
2657
|
+
ROW_NUMBER() OVER (ORDER BY rank DESC, node_id ASC) AS ord
|
|
2658
|
+
FROM (
|
|
2659
|
+
SELECT
|
|
2660
|
+
${qualifyColumn(tableName, "node_id")} AS node_id,
|
|
2661
|
+
${qualifyColumn(tableName, "node_kind")} AS node_kind,
|
|
2662
|
+
${rankExpression} AS rank
|
|
2663
|
+
FROM ${schema.fulltextTable}
|
|
2664
|
+
INNER JOIN ${scopedNodes}
|
|
2665
|
+
ON ${drizzleOrm.sql.raw(`${SCOPED_RELEVANCE_NODES_ALIAS}.node_id`)} =
|
|
2666
|
+
${qualifyColumn(tableName, "node_id")}
|
|
2667
|
+
AND ${drizzleOrm.sql.raw(`${SCOPED_RELEVANCE_NODES_ALIAS}.node_kind`)} =
|
|
2668
|
+
${qualifyColumn(tableName, "node_kind")}
|
|
2669
|
+
WHERE ${drizzleOrm.sql.join(conditions, drizzleOrm.sql` AND `)}
|
|
2670
|
+
ORDER BY rank DESC, ${qualifyColumn(tableName, "node_id")} ASC
|
|
2671
|
+
LIMIT ${limit}
|
|
2672
|
+
) AS fts_inner
|
|
2673
|
+
)
|
|
2674
|
+
`;
|
|
2675
|
+
}
|
|
2676
|
+
function buildStandardHybridCandidateCte() {
|
|
2677
|
+
return drizzleOrm.sql`
|
|
2678
|
+
${drizzleOrm.sql.raw(HYBRID_CANDIDATES_CTE_ALIAS)} AS (
|
|
2679
|
+
SELECT node_id, node_kind FROM ${drizzleOrm.sql.raw(EMBEDDINGS_CTE_ALIAS)}
|
|
2680
|
+
UNION
|
|
2681
|
+
SELECT node_id, node_kind FROM ${drizzleOrm.sql.raw(FULLTEXT_CTE_ALIAS)}
|
|
2682
|
+
)
|
|
2683
|
+
`;
|
|
2684
|
+
}
|
|
2685
|
+
function compileUserOrderBy(ast, dialect) {
|
|
2686
|
+
if (!ast.orderBy || ast.orderBy.length === 0) return [];
|
|
2687
|
+
const aliasToCte = buildAliasToCteMap(ast);
|
|
2688
|
+
const fragments = [];
|
|
2689
|
+
for (const orderSpec of ast.orderBy) {
|
|
2690
|
+
const valueType = orderSpec.field.valueType;
|
|
2691
|
+
if (valueType === "array" || valueType === "object") {
|
|
2692
|
+
throw new chunkO5PST6TB_cjs.UnsupportedPredicateError(
|
|
2693
|
+
"Ordering by JSON arrays or objects is not supported"
|
|
2407
2694
|
);
|
|
2408
2695
|
}
|
|
2696
|
+
const cteAlias = aliasToCte.get(orderSpec.field.alias) ?? `${ALIAS_CTE_PREFIX}${orderSpec.field.alias}`;
|
|
2697
|
+
const field2 = compileFieldValue(
|
|
2698
|
+
orderSpec.field,
|
|
2699
|
+
dialect,
|
|
2700
|
+
valueType,
|
|
2701
|
+
cteAlias
|
|
2702
|
+
);
|
|
2703
|
+
const direction = drizzleOrm.sql.raw(orderSpec.direction.toUpperCase());
|
|
2704
|
+
const nulls = orderSpec.nulls ?? (orderSpec.direction === "asc" ? "last" : "first");
|
|
2705
|
+
const nullsDirection = drizzleOrm.sql.raw(nulls === "first" ? "DESC" : "ASC");
|
|
2706
|
+
fragments.push(
|
|
2707
|
+
drizzleOrm.sql`(${field2} IS NULL) ${nullsDirection}`,
|
|
2708
|
+
drizzleOrm.sql`${field2} ${direction}`
|
|
2709
|
+
);
|
|
2409
2710
|
}
|
|
2410
|
-
|
|
2411
|
-
|
|
2711
|
+
return fragments;
|
|
2712
|
+
}
|
|
2713
|
+
function buildStandardFulltextOrderBy(input) {
|
|
2714
|
+
const { ast, dialect } = input;
|
|
2715
|
+
const rankOrder = drizzleOrm.sql.raw(`${FULLTEXT_CTE_ALIAS}.rank DESC`);
|
|
2716
|
+
const userOrders = compileUserOrderBy(ast, dialect);
|
|
2717
|
+
return drizzleOrm.sql`ORDER BY ${drizzleOrm.sql.join([rankOrder, ...userOrders], drizzleOrm.sql`, `)}`;
|
|
2718
|
+
}
|
|
2719
|
+
function buildStandardHybridRrfOrderBy(input) {
|
|
2720
|
+
const { ast, dialect, fusion } = input;
|
|
2721
|
+
const k = fusion?.k ?? DEFAULT_RRF_K;
|
|
2722
|
+
const vectorWeight = fusion?.weights?.vector ?? DEFAULT_RRF_WEIGHT;
|
|
2723
|
+
const fulltextWeight = fusion?.weights?.fulltext ?? DEFAULT_RRF_WEIGHT;
|
|
2724
|
+
const rrfOrder = drizzleOrm.sql.raw(
|
|
2725
|
+
`(COALESCE(${vectorWeight} / (${k} + ${EMBEDDINGS_CTE_ALIAS}.ord), 0) + COALESCE(${fulltextWeight} / (${k} + ${FULLTEXT_CTE_ALIAS}.ord), 0)) DESC`
|
|
2726
|
+
);
|
|
2727
|
+
const idTiebreak = drizzleOrm.sql.raw(
|
|
2728
|
+
`COALESCE(${FULLTEXT_CTE_ALIAS}.node_id, ${EMBEDDINGS_CTE_ALIAS}.node_id) ASC`
|
|
2729
|
+
);
|
|
2730
|
+
const userOrders = compileUserOrderBy(ast, dialect);
|
|
2731
|
+
return drizzleOrm.sql`ORDER BY ${drizzleOrm.sql.join([rrfOrder, ...userOrders, idTiebreak], drizzleOrm.sql`, `)}`;
|
|
2732
|
+
}
|
|
2733
|
+
function buildStandardVectorOrderBy(input) {
|
|
2734
|
+
const { ast, dialect } = input;
|
|
2735
|
+
const distanceOrder = drizzleOrm.sql.raw(`${EMBEDDINGS_CTE_ALIAS}.distance ASC`);
|
|
2736
|
+
const userOrders = compileUserOrderBy(ast, dialect);
|
|
2737
|
+
return drizzleOrm.sql`ORDER BY ${drizzleOrm.sql.join([distanceOrder, ...userOrders], drizzleOrm.sql`, `)}`;
|
|
2412
2738
|
}
|
|
2413
2739
|
function buildLimitOffsetClause(input) {
|
|
2414
2740
|
const { limit, offset } = input;
|
|
@@ -2422,6 +2748,74 @@ function buildLimitOffsetClause(input) {
|
|
|
2422
2748
|
return parts.length > 0 ? drizzleOrm.sql.join(parts, drizzleOrm.sql` `) : void 0;
|
|
2423
2749
|
}
|
|
2424
2750
|
|
|
2751
|
+
// src/query/compiler/passes/fulltext.ts
|
|
2752
|
+
function runFulltextPredicatePass(ast, dialect) {
|
|
2753
|
+
const fulltextPredicates = extractFulltextMatchPredicates(ast.predicates);
|
|
2754
|
+
if (fulltextPredicates.length > 1) {
|
|
2755
|
+
throw new chunkO5PST6TB_cjs.UnsupportedPredicateError(
|
|
2756
|
+
"Multiple fulltext match predicates in a single query are not supported"
|
|
2757
|
+
);
|
|
2758
|
+
}
|
|
2759
|
+
const fulltextPredicate = fulltextPredicates[0];
|
|
2760
|
+
if (fulltextPredicate === void 0) {
|
|
2761
|
+
return { fulltextPredicate: void 0 };
|
|
2762
|
+
}
|
|
2763
|
+
if (!dialect.capabilities.supportsFulltext) {
|
|
2764
|
+
throw new chunkO5PST6TB_cjs.UnsupportedPredicateError(
|
|
2765
|
+
`Fulltext match predicates are not supported for dialect "${dialect.name}"`
|
|
2766
|
+
);
|
|
2767
|
+
}
|
|
2768
|
+
const strategy = dialect.fulltext;
|
|
2769
|
+
if (strategy === void 0) {
|
|
2770
|
+
throw new chunkO5PST6TB_cjs.UnsupportedPredicateError(
|
|
2771
|
+
`Dialect "${dialect.name}" advertises fulltext support but has no strategy configured`
|
|
2772
|
+
);
|
|
2773
|
+
}
|
|
2774
|
+
if (!strategy.supportedModes.includes(fulltextPredicate.mode)) {
|
|
2775
|
+
throw new chunkO5PST6TB_cjs.UnsupportedPredicateError(
|
|
2776
|
+
`Fulltext query mode "${fulltextPredicate.mode}" is not supported by the "${strategy.name}" strategy. Supported modes: ${strategy.supportedModes.join(", ")}.`
|
|
2777
|
+
);
|
|
2778
|
+
}
|
|
2779
|
+
if (!Number.isFinite(fulltextPredicate.limit) || fulltextPredicate.limit <= 0) {
|
|
2780
|
+
throw new chunkO5PST6TB_cjs.UnsupportedPredicateError(
|
|
2781
|
+
`Fulltext match limit must be a positive finite number, got ${String(fulltextPredicate.limit)}`
|
|
2782
|
+
);
|
|
2783
|
+
}
|
|
2784
|
+
const { minScore } = fulltextPredicate;
|
|
2785
|
+
if (minScore !== void 0 && !Number.isFinite(minScore)) {
|
|
2786
|
+
throw new chunkO5PST6TB_cjs.UnsupportedPredicateError(
|
|
2787
|
+
`Fulltext minScore must be a finite number, got ${String(minScore)}`
|
|
2788
|
+
);
|
|
2789
|
+
}
|
|
2790
|
+
return { fulltextPredicate };
|
|
2791
|
+
}
|
|
2792
|
+
function resolveFulltextAwareLimit(astLimit, fulltextPredicate) {
|
|
2793
|
+
if (fulltextPredicate === void 0) {
|
|
2794
|
+
return astLimit;
|
|
2795
|
+
}
|
|
2796
|
+
if (astLimit === void 0) {
|
|
2797
|
+
return fulltextPredicate.limit;
|
|
2798
|
+
}
|
|
2799
|
+
return Math.min(astLimit, fulltextPredicate.limit);
|
|
2800
|
+
}
|
|
2801
|
+
|
|
2802
|
+
// src/query/compiler/passes/fusion.ts
|
|
2803
|
+
function runFusionConfigPass(ast, vectorPredicate, fulltextPredicate) {
|
|
2804
|
+
const fusion = ast.fusion;
|
|
2805
|
+
if (fusion === void 0) return { fusion: void 0 };
|
|
2806
|
+
if (vectorPredicate === void 0 || fulltextPredicate === void 0) {
|
|
2807
|
+
throw new chunkO5PST6TB_cjs.UnsupportedPredicateError(
|
|
2808
|
+
`.fuseWith() requires both a .similarTo() and a .$fulltext.matches() predicate in the same query. Remove .fuseWith(), or add the missing predicate.`
|
|
2809
|
+
);
|
|
2810
|
+
}
|
|
2811
|
+
if (vectorPredicate.field.alias !== fulltextPredicate.field.alias) {
|
|
2812
|
+
throw new chunkO5PST6TB_cjs.UnsupportedPredicateError(
|
|
2813
|
+
`.fuseWith() requires .similarTo() and .$fulltext.matches() to target the same node alias. Got vector=${vectorPredicate.field.alias}, fulltext=${fulltextPredicate.field.alias}.`
|
|
2814
|
+
);
|
|
2815
|
+
}
|
|
2816
|
+
return { fusion };
|
|
2817
|
+
}
|
|
2818
|
+
|
|
2425
2819
|
// src/query/compiler/passes/recursive.ts
|
|
2426
2820
|
function runRecursiveTraversalSelectionPass(ast) {
|
|
2427
2821
|
const variableLengthTraversal = ast.traversals.find(
|
|
@@ -2691,6 +3085,14 @@ function lowerStandardQueryToLogicalPlanNode(input) {
|
|
|
2691
3085
|
predicate: input.vectorPredicate
|
|
2692
3086
|
};
|
|
2693
3087
|
}
|
|
3088
|
+
if (input.fulltextPredicate !== void 0) {
|
|
3089
|
+
currentNode = {
|
|
3090
|
+
id: nextPlanNodeId(),
|
|
3091
|
+
input: currentNode,
|
|
3092
|
+
op: "fulltext_match",
|
|
3093
|
+
predicate: input.fulltextPredicate
|
|
3094
|
+
};
|
|
3095
|
+
}
|
|
2694
3096
|
return appendAggregateSortLimitAndProjectNodes(
|
|
2695
3097
|
currentNode,
|
|
2696
3098
|
ast,
|
|
@@ -2768,17 +3170,30 @@ function lowerComposableQueryToLogicalPlanNode(query, dialect, graphId, nextPlan
|
|
|
2768
3170
|
nextPlanNodeId
|
|
2769
3171
|
});
|
|
2770
3172
|
}
|
|
3173
|
+
const dialectAdapter = chunkFQCQM3KG_cjs.getDialect(dialect);
|
|
2771
3174
|
const vectorPredicate = runVectorPredicatePass(
|
|
2772
3175
|
query,
|
|
2773
|
-
|
|
3176
|
+
dialectAdapter
|
|
2774
3177
|
).vectorPredicate;
|
|
2775
|
-
const
|
|
3178
|
+
const fulltextPredicate = runFulltextPredicatePass(
|
|
3179
|
+
query,
|
|
3180
|
+
dialectAdapter
|
|
3181
|
+
).fulltextPredicate;
|
|
3182
|
+
const limitAfterVector = resolveVectorAwareLimit(
|
|
3183
|
+
query.limit,
|
|
3184
|
+
vectorPredicate
|
|
3185
|
+
);
|
|
3186
|
+
const effectiveLimit = resolveFulltextAwareLimit(
|
|
3187
|
+
limitAfterVector,
|
|
3188
|
+
fulltextPredicate
|
|
3189
|
+
);
|
|
2776
3190
|
const loweringInput = {
|
|
2777
3191
|
ast: query,
|
|
2778
3192
|
graphId,
|
|
2779
3193
|
nextPlanNodeId,
|
|
2780
3194
|
...effectiveLimit === void 0 ? {} : { effectiveLimit },
|
|
2781
|
-
...vectorPredicate === void 0 ? {} : { vectorPredicate }
|
|
3195
|
+
...vectorPredicate === void 0 ? {} : { vectorPredicate },
|
|
3196
|
+
...fulltextPredicate === void 0 ? {} : { fulltextPredicate }
|
|
2782
3197
|
};
|
|
2783
3198
|
return lowerStandardQueryToLogicalPlanNode(loweringInput);
|
|
2784
3199
|
}
|
|
@@ -3285,10 +3700,10 @@ function compileRecursiveProjection(ast, traversal, dialect) {
|
|
|
3285
3700
|
drizzleOrm.sql`${drizzleOrm.sql.raw(nodeAlias)}_deleted_at`
|
|
3286
3701
|
];
|
|
3287
3702
|
if (vl.depthAlias !== void 0) {
|
|
3288
|
-
fields.push(drizzleOrm.sql`depth AS ${
|
|
3703
|
+
fields.push(drizzleOrm.sql`depth AS ${quoteIdentifier2(vl.depthAlias)}`);
|
|
3289
3704
|
}
|
|
3290
3705
|
if (vl.pathAlias !== void 0) {
|
|
3291
|
-
fields.push(drizzleOrm.sql`path AS ${
|
|
3706
|
+
fields.push(drizzleOrm.sql`path AS ${quoteIdentifier2(vl.pathAlias)}`);
|
|
3292
3707
|
}
|
|
3293
3708
|
return drizzleOrm.sql.join(fields, drizzleOrm.sql`, `);
|
|
3294
3709
|
}
|
|
@@ -3302,23 +3717,23 @@ function compileRecursiveSelectiveProjection(fields, ast, traversal, dialect) {
|
|
|
3302
3717
|
}
|
|
3303
3718
|
if (field2.isSystemField) {
|
|
3304
3719
|
const dbColumn = mapSelectiveSystemFieldToColumn(field2.field);
|
|
3305
|
-
return drizzleOrm.sql`${drizzleOrm.sql.raw(`${field2.alias}_${dbColumn}`)} AS ${
|
|
3720
|
+
return drizzleOrm.sql`${drizzleOrm.sql.raw(`${field2.alias}_${dbColumn}`)} AS ${quoteIdentifier2(field2.outputName)}`;
|
|
3306
3721
|
}
|
|
3307
3722
|
const column = drizzleOrm.sql.raw(`${field2.alias}_props`);
|
|
3308
3723
|
const extracted = compileTypedJsonExtract({
|
|
3309
3724
|
column,
|
|
3310
3725
|
dialect,
|
|
3311
|
-
pointer:
|
|
3726
|
+
pointer: chunkUVZHTBZ5_cjs.jsonPointer([field2.field]),
|
|
3312
3727
|
valueType: field2.valueType
|
|
3313
3728
|
});
|
|
3314
|
-
return drizzleOrm.sql`${extracted} AS ${
|
|
3729
|
+
return drizzleOrm.sql`${extracted} AS ${quoteIdentifier2(field2.outputName)}`;
|
|
3315
3730
|
});
|
|
3316
3731
|
const vl = traversal.variableLength;
|
|
3317
3732
|
if (vl.depthAlias !== void 0) {
|
|
3318
|
-
columns.push(drizzleOrm.sql`depth AS ${
|
|
3733
|
+
columns.push(drizzleOrm.sql`depth AS ${quoteIdentifier2(vl.depthAlias)}`);
|
|
3319
3734
|
}
|
|
3320
3735
|
if (vl.pathAlias !== void 0) {
|
|
3321
|
-
columns.push(drizzleOrm.sql`path AS ${
|
|
3736
|
+
columns.push(drizzleOrm.sql`path AS ${quoteIdentifier2(vl.pathAlias)}`);
|
|
3322
3737
|
}
|
|
3323
3738
|
return drizzleOrm.sql.join(columns, drizzleOrm.sql`, `);
|
|
3324
3739
|
}
|
|
@@ -3355,43 +3770,6 @@ function compileLimitOffset(ast) {
|
|
|
3355
3770
|
}
|
|
3356
3771
|
return parts.length > 0 ? drizzleOrm.sql.join(parts, drizzleOrm.sql` `) : void 0;
|
|
3357
3772
|
}
|
|
3358
|
-
var DEFAULT_TABLE_NAMES = {
|
|
3359
|
-
nodes: "typegraph_nodes",
|
|
3360
|
-
edges: "typegraph_edges",
|
|
3361
|
-
embeddings: "typegraph_node_embeddings"
|
|
3362
|
-
};
|
|
3363
|
-
var VALID_IDENTIFIER_PATTERN = /^[a-z_][a-z0-9_$]*$/i;
|
|
3364
|
-
function validateTableName(name, label) {
|
|
3365
|
-
if (!name || name.length === 0) {
|
|
3366
|
-
throw new chunkO5PST6TB_cjs.ConfigurationError(`${label} table name cannot be empty`);
|
|
3367
|
-
}
|
|
3368
|
-
if (name.length > chunkMME3H4ZF_cjs.MAX_PG_IDENTIFIER_LENGTH) {
|
|
3369
|
-
throw new chunkO5PST6TB_cjs.ConfigurationError(
|
|
3370
|
-
`${label} table name exceeds maximum length of ${chunkMME3H4ZF_cjs.MAX_PG_IDENTIFIER_LENGTH} characters`
|
|
3371
|
-
);
|
|
3372
|
-
}
|
|
3373
|
-
if (!VALID_IDENTIFIER_PATTERN.test(name)) {
|
|
3374
|
-
throw new chunkO5PST6TB_cjs.ConfigurationError(
|
|
3375
|
-
`${label} table name "${name}" is not a valid SQL identifier. Table names must start with a letter or underscore and contain only letters, digits, underscores, or dollar signs.`
|
|
3376
|
-
);
|
|
3377
|
-
}
|
|
3378
|
-
}
|
|
3379
|
-
function quoteIdentifier2(name) {
|
|
3380
|
-
return `"${name.replaceAll('"', '""')}"`;
|
|
3381
|
-
}
|
|
3382
|
-
function createSqlSchema(names = {}) {
|
|
3383
|
-
const tables = { ...DEFAULT_TABLE_NAMES, ...names };
|
|
3384
|
-
validateTableName(tables.nodes, "nodes");
|
|
3385
|
-
validateTableName(tables.edges, "edges");
|
|
3386
|
-
validateTableName(tables.embeddings, "embeddings");
|
|
3387
|
-
return {
|
|
3388
|
-
tables,
|
|
3389
|
-
nodesTable: drizzleOrm.sql.raw(quoteIdentifier2(tables.nodes)),
|
|
3390
|
-
edgesTable: drizzleOrm.sql.raw(quoteIdentifier2(tables.edges)),
|
|
3391
|
-
embeddingsTable: drizzleOrm.sql.raw(quoteIdentifier2(tables.embeddings))
|
|
3392
|
-
};
|
|
3393
|
-
}
|
|
3394
|
-
var DEFAULT_SQL_SCHEMA = createSqlSchema();
|
|
3395
3773
|
|
|
3396
3774
|
// src/query/execution/value-decoder.ts
|
|
3397
3775
|
function nullToUndefined(value) {
|
|
@@ -3449,6 +3827,116 @@ function decodeByValueType(value, valueType) {
|
|
|
3449
3827
|
}
|
|
3450
3828
|
}
|
|
3451
3829
|
}
|
|
3830
|
+
function buildReachableCte(options) {
|
|
3831
|
+
const trackPath = options.cyclePolicy === "prevent" || options.includePath;
|
|
3832
|
+
const edgeKindFilter = compileKindFilter(
|
|
3833
|
+
drizzleOrm.sql.raw("e.kind"),
|
|
3834
|
+
options.edgeKinds
|
|
3835
|
+
);
|
|
3836
|
+
const currentTimestamp = options.dialect.currentTimestamp();
|
|
3837
|
+
const nodeTemporalFilter = compileTemporalFilter({
|
|
3838
|
+
mode: options.temporalMode,
|
|
3839
|
+
asOf: options.asOf,
|
|
3840
|
+
tableAlias: "n",
|
|
3841
|
+
currentTimestamp
|
|
3842
|
+
});
|
|
3843
|
+
const edgeTemporalFilter = compileTemporalFilter({
|
|
3844
|
+
mode: options.temporalMode,
|
|
3845
|
+
asOf: options.asOf,
|
|
3846
|
+
tableAlias: "e",
|
|
3847
|
+
currentTimestamp
|
|
3848
|
+
});
|
|
3849
|
+
const initialPath = trackPath ? options.dialect.initializePath(drizzleOrm.sql.raw("n.id")) : void 0;
|
|
3850
|
+
const pathExtension = trackPath ? options.dialect.extendPath(drizzleOrm.sql.raw("r.path"), drizzleOrm.sql.raw("n.id")) : void 0;
|
|
3851
|
+
const cycleCheck = options.cyclePolicy === "prevent" ? options.dialect.cycleCheck(drizzleOrm.sql.raw("n.id"), drizzleOrm.sql.raw("r.path")) : void 0;
|
|
3852
|
+
const baseColumns = [drizzleOrm.sql`n.id`, drizzleOrm.sql`n.kind`, drizzleOrm.sql`0 AS depth`];
|
|
3853
|
+
if (initialPath !== void 0) {
|
|
3854
|
+
baseColumns.push(drizzleOrm.sql`${initialPath} AS path`);
|
|
3855
|
+
}
|
|
3856
|
+
const baseCase = drizzleOrm.sql`SELECT ${drizzleOrm.sql.join(baseColumns, drizzleOrm.sql`, `)} FROM ${options.schema.nodesTable} n WHERE n.graph_id = ${options.graphId} AND n.id = ${options.sourceId} AND ${nodeTemporalFilter}`;
|
|
3857
|
+
const recursiveColumns = [
|
|
3858
|
+
drizzleOrm.sql`n.id`,
|
|
3859
|
+
drizzleOrm.sql`n.kind`,
|
|
3860
|
+
drizzleOrm.sql`r.depth + 1 AS depth`
|
|
3861
|
+
];
|
|
3862
|
+
if (pathExtension !== void 0) {
|
|
3863
|
+
recursiveColumns.push(drizzleOrm.sql`${pathExtension} AS path`);
|
|
3864
|
+
}
|
|
3865
|
+
const recursiveWhere = [
|
|
3866
|
+
drizzleOrm.sql`e.graph_id = ${options.graphId}`,
|
|
3867
|
+
edgeKindFilter,
|
|
3868
|
+
edgeTemporalFilter,
|
|
3869
|
+
nodeTemporalFilter,
|
|
3870
|
+
drizzleOrm.sql`r.depth < ${options.maxHops}`
|
|
3871
|
+
];
|
|
3872
|
+
if (cycleCheck !== void 0) recursiveWhere.push(cycleCheck);
|
|
3873
|
+
const forceWorktableOuterJoinOrder = options.dialect.capabilities.forceRecursiveWorktableOuterJoinOrder;
|
|
3874
|
+
const recursiveCase = compileRecursiveBranch({
|
|
3875
|
+
recursiveColumns,
|
|
3876
|
+
whereClauses: recursiveWhere,
|
|
3877
|
+
direction: options.direction,
|
|
3878
|
+
forceWorktableOuterJoinOrder,
|
|
3879
|
+
schema: options.schema
|
|
3880
|
+
});
|
|
3881
|
+
return drizzleOrm.sql`WITH RECURSIVE reachable AS (${baseCase} UNION ALL ${recursiveCase})`;
|
|
3882
|
+
}
|
|
3883
|
+
function compileRecursiveBranch(options) {
|
|
3884
|
+
const selectClause = drizzleOrm.sql`SELECT ${drizzleOrm.sql.join([...options.recursiveColumns], drizzleOrm.sql`, `)}`;
|
|
3885
|
+
switch (options.direction) {
|
|
3886
|
+
case "out": {
|
|
3887
|
+
return buildDirectionalBranch({
|
|
3888
|
+
selectClause,
|
|
3889
|
+
whereClauses: options.whereClauses,
|
|
3890
|
+
joinField: "from_id",
|
|
3891
|
+
targetField: "to_id",
|
|
3892
|
+
targetKindField: "to_kind",
|
|
3893
|
+
forceWorktableOuterJoinOrder: options.forceWorktableOuterJoinOrder,
|
|
3894
|
+
schema: options.schema
|
|
3895
|
+
});
|
|
3896
|
+
}
|
|
3897
|
+
case "in": {
|
|
3898
|
+
return buildDirectionalBranch({
|
|
3899
|
+
selectClause,
|
|
3900
|
+
whereClauses: options.whereClauses,
|
|
3901
|
+
joinField: "to_id",
|
|
3902
|
+
targetField: "from_id",
|
|
3903
|
+
targetKindField: "from_kind",
|
|
3904
|
+
forceWorktableOuterJoinOrder: options.forceWorktableOuterJoinOrder,
|
|
3905
|
+
schema: options.schema
|
|
3906
|
+
});
|
|
3907
|
+
}
|
|
3908
|
+
case "both": {
|
|
3909
|
+
return buildBidirectionalBranch({
|
|
3910
|
+
selectClause,
|
|
3911
|
+
whereClauses: options.whereClauses,
|
|
3912
|
+
forceWorktableOuterJoinOrder: options.forceWorktableOuterJoinOrder,
|
|
3913
|
+
schema: options.schema
|
|
3914
|
+
});
|
|
3915
|
+
}
|
|
3916
|
+
}
|
|
3917
|
+
}
|
|
3918
|
+
function buildDirectionalBranch(options) {
|
|
3919
|
+
const nodeJoin = drizzleOrm.sql`JOIN ${options.schema.nodesTable} n ON n.graph_id = e.graph_id AND n.id = e.${drizzleOrm.sql.raw(options.targetField)} AND n.kind = e.${drizzleOrm.sql.raw(options.targetKindField)}`;
|
|
3920
|
+
if (options.forceWorktableOuterJoinOrder) {
|
|
3921
|
+
const allWhere = [
|
|
3922
|
+
...options.whereClauses,
|
|
3923
|
+
drizzleOrm.sql`e.${drizzleOrm.sql.raw(options.joinField)} = r.id`
|
|
3924
|
+
];
|
|
3925
|
+
return drizzleOrm.sql`${options.selectClause} FROM reachable r CROSS JOIN ${options.schema.edgesTable} e ${nodeJoin} WHERE ${drizzleOrm.sql.join(allWhere, drizzleOrm.sql` AND `)}`;
|
|
3926
|
+
}
|
|
3927
|
+
return drizzleOrm.sql`${options.selectClause} FROM reachable r JOIN ${options.schema.edgesTable} e ON e.${drizzleOrm.sql.raw(options.joinField)} = r.id ${nodeJoin} WHERE ${drizzleOrm.sql.join([...options.whereClauses], drizzleOrm.sql` AND `)}`;
|
|
3928
|
+
}
|
|
3929
|
+
function buildBidirectionalBranch(options) {
|
|
3930
|
+
const nodeJoin = drizzleOrm.sql`JOIN ${options.schema.nodesTable} n ON n.graph_id = e.graph_id AND ((e.to_id = r.id AND n.id = e.from_id AND n.kind = e.from_kind) OR (e.from_id = r.id AND n.id = e.to_id AND n.kind = e.to_kind))`;
|
|
3931
|
+
if (options.forceWorktableOuterJoinOrder) {
|
|
3932
|
+
const allWhere = [
|
|
3933
|
+
...options.whereClauses,
|
|
3934
|
+
drizzleOrm.sql`(e.from_id = r.id OR e.to_id = r.id)`
|
|
3935
|
+
];
|
|
3936
|
+
return drizzleOrm.sql`${options.selectClause} FROM reachable r CROSS JOIN ${options.schema.edgesTable} e ${nodeJoin} WHERE ${drizzleOrm.sql.join(allWhere, drizzleOrm.sql` AND `)}`;
|
|
3937
|
+
}
|
|
3938
|
+
return drizzleOrm.sql`${options.selectClause} FROM reachable r JOIN ${options.schema.edgesTable} e ON (e.from_id = r.id OR e.to_id = r.id) ${nodeJoin} WHERE ${drizzleOrm.sql.join([...options.whereClauses], drizzleOrm.sql` AND `)}`;
|
|
3939
|
+
}
|
|
3452
3940
|
|
|
3453
3941
|
// src/store/row-mappers.ts
|
|
3454
3942
|
function nullToUndefined2(value) {
|
|
@@ -3512,9 +4000,9 @@ function truncateToBytes(value, maxBytes) {
|
|
|
3512
4000
|
}
|
|
3513
4001
|
function projectionAlias(entityPrefix, kind, field2) {
|
|
3514
4002
|
const prefix = entityPrefix === "node" ? "sg_n" : "sg_e";
|
|
3515
|
-
const hash =
|
|
4003
|
+
const hash = chunkFQCQM3KG_cjs.fnv1aBase36(`${kind}\0${field2}`);
|
|
3516
4004
|
const fixedBytes = prefix.length + 1 + 1 + hash.length;
|
|
3517
|
-
const maxKindBytes =
|
|
4005
|
+
const maxKindBytes = chunkFQCQM3KG_cjs.MAX_PG_IDENTIFIER_LENGTH - fixedBytes;
|
|
3518
4006
|
const truncatedKind = truncateToBytes(kind, maxKindBytes);
|
|
3519
4007
|
return `${prefix}_${truncatedKind}_${hash}`;
|
|
3520
4008
|
}
|
|
@@ -3539,6 +4027,8 @@ async function executeSubgraph(params) {
|
|
|
3539
4027
|
excludeRoot: options.excludeRoot ?? false,
|
|
3540
4028
|
direction: options.direction ?? "out",
|
|
3541
4029
|
cyclePolicy: options.cyclePolicy ?? "prevent",
|
|
4030
|
+
temporalMode: options.temporalMode ?? params.graph.defaults.temporalMode,
|
|
4031
|
+
asOf: options.asOf,
|
|
3542
4032
|
dialect: params.dialect,
|
|
3543
4033
|
schema: params.schema ?? DEFAULT_SQL_SCHEMA,
|
|
3544
4034
|
backend: params.backend
|
|
@@ -3556,7 +4046,19 @@ async function executeSubgraph(params) {
|
|
|
3556
4046
|
(kind, field2) => schemaIntrospector.getEdgeFieldTypeInfo(kind, field2),
|
|
3557
4047
|
"edge"
|
|
3558
4048
|
);
|
|
3559
|
-
const reachableCte = buildReachableCte(
|
|
4049
|
+
const reachableCte = buildReachableCte({
|
|
4050
|
+
graphId: ctx.graphId,
|
|
4051
|
+
sourceId: ctx.rootId,
|
|
4052
|
+
edgeKinds: ctx.edgeKinds,
|
|
4053
|
+
maxHops: ctx.maxDepth,
|
|
4054
|
+
direction: ctx.direction,
|
|
4055
|
+
cyclePolicy: ctx.cyclePolicy,
|
|
4056
|
+
includePath: false,
|
|
4057
|
+
temporalMode: ctx.temporalMode,
|
|
4058
|
+
...ctx.asOf !== void 0 && { asOf: ctx.asOf },
|
|
4059
|
+
dialect: ctx.dialect,
|
|
4060
|
+
schema: ctx.schema
|
|
4061
|
+
});
|
|
3560
4062
|
const includedIdsCte = buildIncludedIdsCte(ctx);
|
|
3561
4063
|
const [nodeRows, edgeRows] = await Promise.all([
|
|
3562
4064
|
fetchSubgraphNodes(ctx, reachableCte, includedIdsCte, nodeProjectionPlan),
|
|
@@ -3572,12 +4074,7 @@ async function executeSubgraph(params) {
|
|
|
3572
4074
|
for (const row of edgeRows) {
|
|
3573
4075
|
const edge = mapSubgraphEdgeRow(row, edgeProjectionPlan);
|
|
3574
4076
|
insertAdjacencyEntry(adjacency, edge.fromId, edge.kind, edge);
|
|
3575
|
-
insertAdjacencyEntry(
|
|
3576
|
-
reverseAdjacency,
|
|
3577
|
-
edge.toId,
|
|
3578
|
-
edge.kind,
|
|
3579
|
-
edge
|
|
3580
|
-
);
|
|
4077
|
+
insertAdjacencyEntry(reverseAdjacency, edge.toId, edge.kind, edge);
|
|
3581
4078
|
}
|
|
3582
4079
|
const root = nodesMap.get(ctx.rootId);
|
|
3583
4080
|
return {
|
|
@@ -3603,7 +4100,7 @@ function getSubgraphSchemaIntrospector(graph) {
|
|
|
3603
4100
|
{ schema: definition.type.schema }
|
|
3604
4101
|
])
|
|
3605
4102
|
);
|
|
3606
|
-
const introspector =
|
|
4103
|
+
const introspector = chunkUVZHTBZ5_cjs.createSchemaIntrospector(nodeKinds, edgeKinds);
|
|
3607
4104
|
introspectorCache.set(graph, introspector);
|
|
3608
4105
|
return introspector;
|
|
3609
4106
|
}
|
|
@@ -3654,79 +4151,6 @@ function getIncludedNodeKinds(graph, includeKinds) {
|
|
|
3654
4151
|
function dedupeStrings(values) {
|
|
3655
4152
|
return [...new Set(values)];
|
|
3656
4153
|
}
|
|
3657
|
-
function buildReachableCte(ctx) {
|
|
3658
|
-
const shouldTrackPath = ctx.cyclePolicy === "prevent";
|
|
3659
|
-
const edgeKindFilter = compileKindFilter(drizzleOrm.sql.raw("e.kind"), ctx.edgeKinds);
|
|
3660
|
-
const initialPath = shouldTrackPath ? ctx.dialect.initializePath(drizzleOrm.sql.raw("n.id")) : void 0;
|
|
3661
|
-
const pathExtension = shouldTrackPath ? ctx.dialect.extendPath(drizzleOrm.sql.raw("r.path"), drizzleOrm.sql.raw("n.id")) : void 0;
|
|
3662
|
-
const cycleCheck = shouldTrackPath ? ctx.dialect.cycleCheck(drizzleOrm.sql.raw("n.id"), drizzleOrm.sql.raw("r.path")) : void 0;
|
|
3663
|
-
const baseColumns = [drizzleOrm.sql`n.id`, drizzleOrm.sql`n.kind`, drizzleOrm.sql`0 AS depth`];
|
|
3664
|
-
if (initialPath !== void 0) {
|
|
3665
|
-
baseColumns.push(drizzleOrm.sql`${initialPath} AS path`);
|
|
3666
|
-
}
|
|
3667
|
-
const baseCase = drizzleOrm.sql`SELECT ${drizzleOrm.sql.join(baseColumns, drizzleOrm.sql`, `)} FROM ${ctx.schema.nodesTable} n WHERE n.graph_id = ${ctx.graphId} AND n.id = ${ctx.rootId} AND n.deleted_at IS NULL`;
|
|
3668
|
-
const recursiveColumns = [
|
|
3669
|
-
drizzleOrm.sql`n.id`,
|
|
3670
|
-
drizzleOrm.sql`n.kind`,
|
|
3671
|
-
drizzleOrm.sql`r.depth + 1 AS depth`
|
|
3672
|
-
];
|
|
3673
|
-
if (pathExtension !== void 0) {
|
|
3674
|
-
recursiveColumns.push(drizzleOrm.sql`${pathExtension} AS path`);
|
|
3675
|
-
}
|
|
3676
|
-
const recursiveWhereClauses = [
|
|
3677
|
-
drizzleOrm.sql`e.graph_id = ${ctx.graphId}`,
|
|
3678
|
-
edgeKindFilter,
|
|
3679
|
-
drizzleOrm.sql`e.deleted_at IS NULL`,
|
|
3680
|
-
drizzleOrm.sql`n.deleted_at IS NULL`,
|
|
3681
|
-
drizzleOrm.sql`r.depth < ${ctx.maxDepth}`
|
|
3682
|
-
];
|
|
3683
|
-
if (cycleCheck !== void 0) {
|
|
3684
|
-
recursiveWhereClauses.push(cycleCheck);
|
|
3685
|
-
}
|
|
3686
|
-
const forceWorktableOuterJoinOrder = ctx.dialect.capabilities.forceRecursiveWorktableOuterJoinOrder;
|
|
3687
|
-
const recursiveCase = ctx.direction === "both" ? compileBidirectionalBranch({
|
|
3688
|
-
recursiveColumns,
|
|
3689
|
-
whereClauses: recursiveWhereClauses,
|
|
3690
|
-
forceWorktableOuterJoinOrder,
|
|
3691
|
-
schema: ctx.schema
|
|
3692
|
-
}) : compileRecursiveBranch({
|
|
3693
|
-
recursiveColumns,
|
|
3694
|
-
whereClauses: recursiveWhereClauses,
|
|
3695
|
-
joinField: "from_id",
|
|
3696
|
-
targetField: "to_id",
|
|
3697
|
-
targetKindField: "to_kind",
|
|
3698
|
-
forceWorktableOuterJoinOrder,
|
|
3699
|
-
schema: ctx.schema
|
|
3700
|
-
});
|
|
3701
|
-
return drizzleOrm.sql`WITH RECURSIVE reachable AS (${baseCase} UNION ALL ${recursiveCase})`;
|
|
3702
|
-
}
|
|
3703
|
-
function compileRecursiveBranch(params) {
|
|
3704
|
-
const columns = [...params.recursiveColumns];
|
|
3705
|
-
const selectClause = drizzleOrm.sql`SELECT ${drizzleOrm.sql.join(columns, drizzleOrm.sql`, `)}`;
|
|
3706
|
-
const nodeJoin = drizzleOrm.sql`JOIN ${params.schema.nodesTable} n ON n.graph_id = e.graph_id AND n.id = e.${drizzleOrm.sql.raw(params.targetField)} AND n.kind = e.${drizzleOrm.sql.raw(params.targetKindField)}`;
|
|
3707
|
-
if (params.forceWorktableOuterJoinOrder) {
|
|
3708
|
-
const allWhere = [
|
|
3709
|
-
...params.whereClauses,
|
|
3710
|
-
drizzleOrm.sql`e.${drizzleOrm.sql.raw(params.joinField)} = r.id`
|
|
3711
|
-
];
|
|
3712
|
-
return drizzleOrm.sql`${selectClause} FROM reachable r CROSS JOIN ${params.schema.edgesTable} e ${nodeJoin} WHERE ${drizzleOrm.sql.join(allWhere, drizzleOrm.sql` AND `)}`;
|
|
3713
|
-
}
|
|
3714
|
-
const where = [...params.whereClauses];
|
|
3715
|
-
return drizzleOrm.sql`${selectClause} FROM reachable r JOIN ${params.schema.edgesTable} e ON e.${drizzleOrm.sql.raw(params.joinField)} = r.id ${nodeJoin} WHERE ${drizzleOrm.sql.join(where, drizzleOrm.sql` AND `)}`;
|
|
3716
|
-
}
|
|
3717
|
-
function compileBidirectionalBranch(params) {
|
|
3718
|
-
const columns = [...params.recursiveColumns];
|
|
3719
|
-
const selectClause = drizzleOrm.sql`SELECT ${drizzleOrm.sql.join(columns, drizzleOrm.sql`, `)}`;
|
|
3720
|
-
const nodeJoin = drizzleOrm.sql`JOIN ${params.schema.nodesTable} n ON n.graph_id = e.graph_id AND ((e.to_id = r.id AND n.id = e.from_id AND n.kind = e.from_kind) OR (e.from_id = r.id AND n.id = e.to_id AND n.kind = e.to_kind))`;
|
|
3721
|
-
if (params.forceWorktableOuterJoinOrder) {
|
|
3722
|
-
const allWhere = [
|
|
3723
|
-
...params.whereClauses,
|
|
3724
|
-
drizzleOrm.sql`(e.from_id = r.id OR e.to_id = r.id)`
|
|
3725
|
-
];
|
|
3726
|
-
return drizzleOrm.sql`${selectClause} FROM reachable r CROSS JOIN ${params.schema.edgesTable} e ${nodeJoin} WHERE ${drizzleOrm.sql.join(allWhere, drizzleOrm.sql` AND `)}`;
|
|
3727
|
-
}
|
|
3728
|
-
return drizzleOrm.sql`${selectClause} FROM reachable r JOIN ${params.schema.edgesTable} e ON (e.from_id = r.id OR e.to_id = r.id) ${nodeJoin} WHERE ${drizzleOrm.sql.join([...params.whereClauses], drizzleOrm.sql` AND `)}`;
|
|
3729
|
-
}
|
|
3730
4154
|
function buildIncludedIdsCte(ctx) {
|
|
3731
4155
|
const filters = [];
|
|
3732
4156
|
if (ctx.includeKinds !== void 0 && ctx.includeKinds.length > 0) {
|
|
@@ -3758,6 +4182,12 @@ async function fetchSubgraphNodes(ctx, reachableCte, includedIdsCte, projectionP
|
|
|
3758
4182
|
}
|
|
3759
4183
|
async function fetchSubgraphEdges(ctx, reachableCte, includedIdsCte, projectionPlan) {
|
|
3760
4184
|
const edgeKindFilter = compileKindFilter(drizzleOrm.sql.raw("e.kind"), ctx.edgeKinds);
|
|
4185
|
+
const edgeTemporalFilter = compileTemporalFilter({
|
|
4186
|
+
mode: ctx.temporalMode,
|
|
4187
|
+
asOf: ctx.asOf,
|
|
4188
|
+
tableAlias: "e",
|
|
4189
|
+
currentTimestamp: ctx.dialect.currentTimestamp()
|
|
4190
|
+
});
|
|
3761
4191
|
const columns = [
|
|
3762
4192
|
drizzleOrm.sql`e.id`,
|
|
3763
4193
|
drizzleOrm.sql`e.kind`,
|
|
@@ -3775,7 +4205,7 @@ async function fetchSubgraphEdges(ctx, reachableCte, includedIdsCte, projectionP
|
|
|
3775
4205
|
]),
|
|
3776
4206
|
...buildProjectedPropertyColumns("e", projectionPlan, ctx.dialect)
|
|
3777
4207
|
];
|
|
3778
|
-
const query = drizzleOrm.sql`${reachableCte}${includedIdsCte} SELECT ${drizzleOrm.sql.join(columns, drizzleOrm.sql`, `)} FROM ${ctx.schema.edgesTable} e WHERE e.graph_id = ${ctx.graphId} AND ${edgeKindFilter} AND
|
|
4208
|
+
const query = drizzleOrm.sql`${reachableCte}${includedIdsCte} SELECT ${drizzleOrm.sql.join(columns, drizzleOrm.sql`, `)} FROM ${ctx.schema.edgesTable} e WHERE e.graph_id = ${ctx.graphId} AND ${edgeKindFilter} AND ${edgeTemporalFilter} AND e.from_id IN (SELECT id FROM included_ids) AND e.to_id IN (SELECT id FROM included_ids)`;
|
|
3779
4209
|
return ctx.backend.execute(query);
|
|
3780
4210
|
}
|
|
3781
4211
|
function buildMetadataColumns(alias, plan, columns) {
|
|
@@ -3814,11 +4244,11 @@ function buildProjectedPropertyColumns(alias, plan, dialect) {
|
|
|
3814
4244
|
const extracted = compileTypedJsonExtract({
|
|
3815
4245
|
column: drizzleOrm.sql.raw(`${alias}.props`),
|
|
3816
4246
|
dialect,
|
|
3817
|
-
pointer:
|
|
4247
|
+
pointer: chunkUVZHTBZ5_cjs.jsonPointer([fieldPlan.field]),
|
|
3818
4248
|
valueType: fieldPlan.typeInfo?.valueType
|
|
3819
4249
|
});
|
|
3820
4250
|
columns.push(
|
|
3821
|
-
drizzleOrm.sql`CASE WHEN ${drizzleOrm.sql.raw(alias)}.kind = ${kind} THEN ${extracted} ELSE NULL END AS ${
|
|
4251
|
+
drizzleOrm.sql`CASE WHEN ${drizzleOrm.sql.raw(alias)}.kind = ${kind} THEN ${extracted} ELSE NULL END AS ${quoteIdentifier2(fieldPlan.outputName)}`
|
|
3822
4252
|
);
|
|
3823
4253
|
}
|
|
3824
4254
|
}
|
|
@@ -3885,16 +4315,6 @@ function mapSubgraphEdgeRow(row, projectionPlan) {
|
|
|
3885
4315
|
applyProjectedFields(projectedEdge, row, kindPlan);
|
|
3886
4316
|
return projectedEdge;
|
|
3887
4317
|
}
|
|
3888
|
-
|
|
3889
|
-
// src/query/ast.ts
|
|
3890
|
-
function mergeEdgeKinds(traversal) {
|
|
3891
|
-
const inverse = traversal.inverseEdgeKinds;
|
|
3892
|
-
if (inverse === void 0 || inverse.length === 0) return traversal.edgeKinds;
|
|
3893
|
-
return [
|
|
3894
|
-
...traversal.edgeKinds,
|
|
3895
|
-
...inverse.filter((kind) => !traversal.edgeKinds.includes(kind))
|
|
3896
|
-
];
|
|
3897
|
-
}
|
|
3898
4318
|
var OPERATOR_MAP = {
|
|
3899
4319
|
union: "UNION",
|
|
3900
4320
|
unionAll: "UNION ALL",
|
|
@@ -4051,6 +4471,9 @@ function validateSqliteSetOpLeaf(ast) {
|
|
|
4051
4471
|
if (hasVectorSimilarityPredicates(ast)) {
|
|
4052
4472
|
unsupported.push("vector similarity predicates");
|
|
4053
4473
|
}
|
|
4474
|
+
if (hasFulltextMatchPredicates(ast)) {
|
|
4475
|
+
unsupported.push("fulltext match predicates");
|
|
4476
|
+
}
|
|
4054
4477
|
if (ast.groupBy !== void 0) {
|
|
4055
4478
|
unsupported.push("GROUP BY");
|
|
4056
4479
|
}
|
|
@@ -4100,7 +4523,42 @@ function hasVectorSimilarityInExpression(expr) {
|
|
|
4100
4523
|
case "between":
|
|
4101
4524
|
case "array_op":
|
|
4102
4525
|
case "object_op":
|
|
4103
|
-
case "aggregate_comparison":
|
|
4526
|
+
case "aggregate_comparison":
|
|
4527
|
+
case "fulltext_match": {
|
|
4528
|
+
return false;
|
|
4529
|
+
}
|
|
4530
|
+
}
|
|
4531
|
+
}
|
|
4532
|
+
return false;
|
|
4533
|
+
}
|
|
4534
|
+
function hasFulltextMatchPredicates(ast) {
|
|
4535
|
+
return ast.predicates.some(
|
|
4536
|
+
(predicate2) => hasFulltextMatchInExpression(predicate2.expression)
|
|
4537
|
+
);
|
|
4538
|
+
}
|
|
4539
|
+
function hasFulltextMatchInExpression(expr) {
|
|
4540
|
+
if ("__type" in expr) {
|
|
4541
|
+
switch (expr.__type) {
|
|
4542
|
+
case "and":
|
|
4543
|
+
case "or": {
|
|
4544
|
+
return expr.predicates.some((p) => hasFulltextMatchInExpression(p));
|
|
4545
|
+
}
|
|
4546
|
+
case "not": {
|
|
4547
|
+
return hasFulltextMatchInExpression(expr.predicate);
|
|
4548
|
+
}
|
|
4549
|
+
case "fulltext_match": {
|
|
4550
|
+
return true;
|
|
4551
|
+
}
|
|
4552
|
+
case "exists":
|
|
4553
|
+
case "in_subquery":
|
|
4554
|
+
case "comparison":
|
|
4555
|
+
case "string_op":
|
|
4556
|
+
case "null_check":
|
|
4557
|
+
case "between":
|
|
4558
|
+
case "array_op":
|
|
4559
|
+
case "object_op":
|
|
4560
|
+
case "aggregate_comparison":
|
|
4561
|
+
case "vector_similarity": {
|
|
4104
4562
|
return false;
|
|
4105
4563
|
}
|
|
4106
4564
|
}
|
|
@@ -4136,7 +4594,8 @@ function hasSubqueryInExpression(expr) {
|
|
|
4136
4594
|
case "array_op":
|
|
4137
4595
|
case "object_op":
|
|
4138
4596
|
case "aggregate_comparison":
|
|
4139
|
-
case "vector_similarity":
|
|
4597
|
+
case "vector_similarity":
|
|
4598
|
+
case "fulltext_match": {
|
|
4140
4599
|
return false;
|
|
4141
4600
|
}
|
|
4142
4601
|
}
|
|
@@ -4302,7 +4761,7 @@ function getLeftmostProjection(op) {
|
|
|
4302
4761
|
function normalizeFieldRefKey(field2) {
|
|
4303
4762
|
let pointer = field2.jsonPointer;
|
|
4304
4763
|
if (pointer === void 0 && field2.path.length > 1 && field2.path[0] === "props") {
|
|
4305
|
-
pointer =
|
|
4764
|
+
pointer = chunkUVZHTBZ5_cjs.jsonPointer(field2.path.slice(1));
|
|
4306
4765
|
}
|
|
4307
4766
|
const basePath = field2.path.length > 0 && field2.path[0] === "props" ? "props" : field2.path.join(".");
|
|
4308
4767
|
return `${field2.alias}:${basePath}:${pointer ?? ""}`;
|
|
@@ -4414,6 +4873,10 @@ function markPredicateFieldsAsRequired(requiredColumnsByAlias, expression) {
|
|
|
4414
4873
|
markFieldRefAsRequired(requiredColumnsByAlias, expression.field);
|
|
4415
4874
|
return;
|
|
4416
4875
|
}
|
|
4876
|
+
case "fulltext_match": {
|
|
4877
|
+
markFieldRefAsRequired(requiredColumnsByAlias, expression.field);
|
|
4878
|
+
return;
|
|
4879
|
+
}
|
|
4417
4880
|
case "exists": {
|
|
4418
4881
|
return;
|
|
4419
4882
|
}
|
|
@@ -4478,7 +4941,8 @@ function hasIdEqualityPredicate(expression, alias) {
|
|
|
4478
4941
|
case "aggregate_comparison":
|
|
4479
4942
|
case "exists":
|
|
4480
4943
|
case "in_subquery":
|
|
4481
|
-
case "vector_similarity":
|
|
4944
|
+
case "vector_similarity":
|
|
4945
|
+
case "fulltext_match": {
|
|
4482
4946
|
return false;
|
|
4483
4947
|
}
|
|
4484
4948
|
}
|
|
@@ -4519,10 +4983,13 @@ function resolveTraversalCteLimit(ast, predicateIndex) {
|
|
|
4519
4983
|
);
|
|
4520
4984
|
return Math.max(ast.limit, pushdownLimit);
|
|
4521
4985
|
}
|
|
4522
|
-
function canCollapseSelectiveTraversalRowset(ast, vectorPredicate) {
|
|
4986
|
+
function canCollapseSelectiveTraversalRowset(ast, vectorPredicate, fulltextPredicate) {
|
|
4523
4987
|
if (vectorPredicate !== void 0) {
|
|
4524
4988
|
return false;
|
|
4525
4989
|
}
|
|
4990
|
+
if (fulltextPredicate !== void 0) {
|
|
4991
|
+
return false;
|
|
4992
|
+
}
|
|
4526
4993
|
if (ast.traversals.length === 0) {
|
|
4527
4994
|
return false;
|
|
4528
4995
|
}
|
|
@@ -4562,6 +5029,8 @@ function runStandardQueryPassPipeline(ast, graphId, ctx) {
|
|
|
4562
5029
|
collapsedTraversalCteAlias: void 0,
|
|
4563
5030
|
ctx,
|
|
4564
5031
|
effectiveLimit: void 0,
|
|
5032
|
+
fulltextPredicate: void 0,
|
|
5033
|
+
fusion: void 0,
|
|
4565
5034
|
logicalPlan: void 0,
|
|
4566
5035
|
predicateIndex: buildPredicateIndex(ast),
|
|
4567
5036
|
requiredColumnsByAlias: void 0,
|
|
@@ -4583,6 +5052,39 @@ function runStandardQueryPassPipeline(ast, graphId, ctx) {
|
|
|
4583
5052
|
}
|
|
4584
5053
|
});
|
|
4585
5054
|
state = vectorPass.state;
|
|
5055
|
+
const fulltextPass = runCompilerPass(state, {
|
|
5056
|
+
name: "fulltext_predicate",
|
|
5057
|
+
execute(currentState) {
|
|
5058
|
+
return runFulltextPredicatePass(
|
|
5059
|
+
currentState.ast,
|
|
5060
|
+
currentState.ctx.dialect
|
|
5061
|
+
).fulltextPredicate;
|
|
5062
|
+
},
|
|
5063
|
+
update(currentState, fulltextPredicate) {
|
|
5064
|
+
return {
|
|
5065
|
+
...currentState,
|
|
5066
|
+
fulltextPredicate
|
|
5067
|
+
};
|
|
5068
|
+
}
|
|
5069
|
+
});
|
|
5070
|
+
state = fulltextPass.state;
|
|
5071
|
+
const fusionPass = runCompilerPass(state, {
|
|
5072
|
+
name: "fusion_config",
|
|
5073
|
+
execute(currentState) {
|
|
5074
|
+
return runFusionConfigPass(
|
|
5075
|
+
currentState.ast,
|
|
5076
|
+
currentState.vectorPredicate,
|
|
5077
|
+
currentState.fulltextPredicate
|
|
5078
|
+
).fusion;
|
|
5079
|
+
},
|
|
5080
|
+
update(currentState, fusion) {
|
|
5081
|
+
return {
|
|
5082
|
+
...currentState,
|
|
5083
|
+
fusion
|
|
5084
|
+
};
|
|
5085
|
+
}
|
|
5086
|
+
});
|
|
5087
|
+
state = fusionPass.state;
|
|
4586
5088
|
const temporalPass = runCompilerPass(state, {
|
|
4587
5089
|
name: "temporal_filters",
|
|
4588
5090
|
execute(currentState) {
|
|
@@ -4617,7 +5119,8 @@ function runStandardQueryPassPipeline(ast, graphId, ctx) {
|
|
|
4617
5119
|
execute(currentState) {
|
|
4618
5120
|
const shouldCollapseSelectiveTraversalRowset = canCollapseSelectiveTraversalRowset(
|
|
4619
5121
|
currentState.ast,
|
|
4620
|
-
currentState.vectorPredicate
|
|
5122
|
+
currentState.vectorPredicate,
|
|
5123
|
+
currentState.fulltextPredicate
|
|
4621
5124
|
);
|
|
4622
5125
|
const lastTraversal = currentState.ast.traversals.at(-1);
|
|
4623
5126
|
const collapsedTraversalCteAlias = shouldCollapseSelectiveTraversalRowset && lastTraversal !== void 0 ? `cte_${lastTraversal.nodeAlias}` : void 0;
|
|
@@ -4653,9 +5156,9 @@ function runStandardQueryPassPipeline(ast, graphId, ctx) {
|
|
|
4653
5156
|
state = traversalLimitPass.state;
|
|
4654
5157
|
state = {
|
|
4655
5158
|
...state,
|
|
4656
|
-
effectiveLimit:
|
|
4657
|
-
state.ast.limit,
|
|
4658
|
-
state.
|
|
5159
|
+
effectiveLimit: resolveFulltextAwareLimit(
|
|
5160
|
+
resolveVectorAwareLimit(state.ast.limit, state.vectorPredicate),
|
|
5161
|
+
state.fulltextPredicate
|
|
4659
5162
|
)
|
|
4660
5163
|
};
|
|
4661
5164
|
const logicalPlanPass = runCompilerPass(state, {
|
|
@@ -4672,7 +5175,8 @@ function runStandardQueryPassPipeline(ast, graphId, ctx) {
|
|
|
4672
5175
|
collapsedTraversalCteAlias: currentState.collapsedTraversalCteAlias
|
|
4673
5176
|
},
|
|
4674
5177
|
...currentState.effectiveLimit === void 0 ? {} : { effectiveLimit: currentState.effectiveLimit },
|
|
4675
|
-
...currentState.vectorPredicate === void 0 ? {} : { vectorPredicate: currentState.vectorPredicate }
|
|
5178
|
+
...currentState.vectorPredicate === void 0 ? {} : { vectorPredicate: currentState.vectorPredicate },
|
|
5179
|
+
...currentState.fulltextPredicate === void 0 ? {} : { fulltextPredicate: currentState.fulltextPredicate }
|
|
4676
5180
|
});
|
|
4677
5181
|
},
|
|
4678
5182
|
update(currentState, logicalPlan) {
|
|
@@ -4691,11 +5195,11 @@ function compileQuery(ast, graphId, options = "sqlite") {
|
|
|
4691
5195
|
const options_ = typeof options === "string" ? { dialect: options } : options;
|
|
4692
5196
|
const dialect = options_.dialect ?? "sqlite";
|
|
4693
5197
|
const schema = options_.schema ?? DEFAULT_SQL_SCHEMA;
|
|
4694
|
-
const adapter =
|
|
5198
|
+
const adapter = resolveDialectAdapter(dialect, options_.fulltextStrategy);
|
|
4695
5199
|
const ctx = {
|
|
4696
5200
|
dialect: adapter,
|
|
4697
5201
|
schema,
|
|
4698
|
-
compileQuery: (subAst, subGraphId) => compileQuery(subAst, subGraphId,
|
|
5202
|
+
compileQuery: (subAst, subGraphId) => compileQuery(subAst, subGraphId, propagateOptions(options_))
|
|
4699
5203
|
};
|
|
4700
5204
|
if (hasVariableLengthTraversal(ast)) {
|
|
4701
5205
|
const lowered = tryLowerSingleHopRecursiveTraversal(ast);
|
|
@@ -4731,15 +5235,29 @@ function compileSetOperation2(op, graphId, options = "sqlite") {
|
|
|
4731
5235
|
const options_ = typeof options === "string" ? { dialect: options } : options;
|
|
4732
5236
|
const dialect = options_.dialect ?? "sqlite";
|
|
4733
5237
|
const schema = options_.schema ?? DEFAULT_SQL_SCHEMA;
|
|
4734
|
-
const adapter =
|
|
5238
|
+
const adapter = resolveDialectAdapter(dialect, options_.fulltextStrategy);
|
|
4735
5239
|
return compileSetOperation(
|
|
4736
5240
|
op,
|
|
4737
5241
|
graphId,
|
|
4738
5242
|
adapter,
|
|
4739
5243
|
schema,
|
|
4740
|
-
(ast, gid) => compileQuery(ast, gid,
|
|
5244
|
+
(ast, gid) => compileQuery(ast, gid, propagateOptions(options_))
|
|
4741
5245
|
);
|
|
4742
5246
|
}
|
|
5247
|
+
function resolveDialectAdapter(dialect, fulltextStrategy) {
|
|
5248
|
+
const baseAdapter = chunkFQCQM3KG_cjs.getDialect(dialect);
|
|
5249
|
+
if (fulltextStrategy === void 0 || fulltextStrategy === baseAdapter.fulltext) {
|
|
5250
|
+
return baseAdapter;
|
|
5251
|
+
}
|
|
5252
|
+
return { ...baseAdapter, fulltext: fulltextStrategy };
|
|
5253
|
+
}
|
|
5254
|
+
function propagateOptions(options_) {
|
|
5255
|
+
return {
|
|
5256
|
+
dialect: options_.dialect ?? "sqlite",
|
|
5257
|
+
...options_.schema === void 0 ? {} : { schema: options_.schema },
|
|
5258
|
+
...options_.fulltextStrategy === void 0 ? {} : { fulltextStrategy: options_.fulltextStrategy }
|
|
5259
|
+
};
|
|
5260
|
+
}
|
|
4743
5261
|
function resolveCountAggregateFastPath(ast) {
|
|
4744
5262
|
if (ast.traversals.length !== 1) {
|
|
4745
5263
|
return void 0;
|
|
@@ -4888,12 +5406,12 @@ function compileCountAggregateFastPath(ast, graphId, ctx, logicalPlan, requiredC
|
|
|
4888
5406
|
source.valueType,
|
|
4889
5407
|
`cte_${source.alias}`
|
|
4890
5408
|
);
|
|
4891
|
-
return drizzleOrm.sql`${value} AS ${
|
|
5409
|
+
return drizzleOrm.sql`${value} AS ${quoteIdentifier2(projectedField.outputName)}`;
|
|
4892
5410
|
}
|
|
4893
5411
|
const projectedCountColumn = source.function === "countDistinct" ? countDistinctColumn : countColumn;
|
|
4894
5412
|
const countValue = drizzleOrm.sql`${drizzleOrm.sql.raw(countCteAlias)}.${drizzleOrm.sql.raw(projectedCountColumn)}`;
|
|
4895
5413
|
const aggregateValue = traversal.optional ? drizzleOrm.sql`COALESCE(${countValue}, 0)` : countValue;
|
|
4896
|
-
return drizzleOrm.sql`${aggregateValue} AS ${
|
|
5414
|
+
return drizzleOrm.sql`${aggregateValue} AS ${quoteIdentifier2(projectedField.outputName)}`;
|
|
4897
5415
|
}),
|
|
4898
5416
|
drizzleOrm.sql`, `
|
|
4899
5417
|
);
|
|
@@ -4918,19 +5436,45 @@ function compileCountAggregateFastPath(ast, graphId, ctx, logicalPlan, requiredC
|
|
|
4918
5436
|
projection
|
|
4919
5437
|
});
|
|
4920
5438
|
}
|
|
4921
|
-
var STANDARD_QUERY_STRATEGY_HANDLERS = {
|
|
4922
|
-
cte_project: compileStandardQueryWithCteStrategy
|
|
4923
|
-
};
|
|
4924
|
-
function compileStandardQuery(ast, graphId, ctx) {
|
|
4925
|
-
const strategy = ctx.dialect.capabilities.standardQueryStrategy;
|
|
4926
|
-
const handler = STANDARD_QUERY_STRATEGY_HANDLERS[strategy];
|
|
4927
|
-
return handler(ast, graphId, ctx);
|
|
4928
|
-
}
|
|
5439
|
+
var STANDARD_QUERY_STRATEGY_HANDLERS = {
|
|
5440
|
+
cte_project: compileStandardQueryWithCteStrategy
|
|
5441
|
+
};
|
|
5442
|
+
function compileStandardQuery(ast, graphId, ctx) {
|
|
5443
|
+
const strategy = ctx.dialect.capabilities.standardQueryStrategy;
|
|
5444
|
+
const handler = STANDARD_QUERY_STRATEGY_HANDLERS[strategy];
|
|
5445
|
+
return handler(ast, graphId, ctx);
|
|
5446
|
+
}
|
|
5447
|
+
function selectStandardOrderBy(input) {
|
|
5448
|
+
const {
|
|
5449
|
+
ast,
|
|
5450
|
+
collapsedTraversalCteAlias,
|
|
5451
|
+
dialect,
|
|
5452
|
+
fulltextPredicate,
|
|
5453
|
+
fusion,
|
|
5454
|
+
vectorPredicate
|
|
5455
|
+
} = input;
|
|
5456
|
+
if (vectorPredicate && fulltextPredicate) {
|
|
5457
|
+
return buildStandardHybridRrfOrderBy({ ast, dialect, fusion });
|
|
5458
|
+
}
|
|
5459
|
+
if (vectorPredicate) {
|
|
5460
|
+
return buildStandardVectorOrderBy({ ast, dialect });
|
|
5461
|
+
}
|
|
5462
|
+
if (fulltextPredicate) {
|
|
5463
|
+
return buildStandardFulltextOrderBy({ ast, dialect });
|
|
5464
|
+
}
|
|
5465
|
+
return buildStandardOrderBy({
|
|
5466
|
+
ast,
|
|
5467
|
+
...collapsedTraversalCteAlias === void 0 ? {} : { collapsedTraversalCteAlias },
|
|
5468
|
+
dialect
|
|
5469
|
+
});
|
|
5470
|
+
}
|
|
4929
5471
|
function compileStandardQueryWithCteStrategy(ast, graphId, ctx) {
|
|
4930
5472
|
const { dialect } = ctx;
|
|
4931
5473
|
const {
|
|
4932
5474
|
collapsedTraversalCteAlias,
|
|
4933
5475
|
effectiveLimit,
|
|
5476
|
+
fulltextPredicate,
|
|
5477
|
+
fusion,
|
|
4934
5478
|
logicalPlan,
|
|
4935
5479
|
predicateIndex,
|
|
4936
5480
|
requiredColumnsByAlias,
|
|
@@ -4951,7 +5495,7 @@ function compileStandardQueryWithCteStrategy(ast, graphId, ctx) {
|
|
|
4951
5495
|
{ phase: "standard-pass-pipeline" }
|
|
4952
5496
|
);
|
|
4953
5497
|
}
|
|
4954
|
-
if (!vectorPredicate) {
|
|
5498
|
+
if (!vectorPredicate && !fulltextPredicate) {
|
|
4955
5499
|
const fastPathSql = compileCountAggregateFastPath(
|
|
4956
5500
|
ast,
|
|
4957
5501
|
graphId,
|
|
@@ -4997,7 +5541,29 @@ function compileStandardQueryWithCteStrategy(ast, graphId, ctx) {
|
|
|
4997
5541
|
);
|
|
4998
5542
|
}
|
|
4999
5543
|
if (vectorPredicate) {
|
|
5000
|
-
|
|
5544
|
+
const nodeKinds = getNodeKindsForAlias(ast, vectorPredicate.field.alias);
|
|
5545
|
+
ctes.push(
|
|
5546
|
+
buildStandardEmbeddingsCte({
|
|
5547
|
+
ctx,
|
|
5548
|
+
graphId,
|
|
5549
|
+
nodeKinds,
|
|
5550
|
+
vectorPredicate
|
|
5551
|
+
})
|
|
5552
|
+
);
|
|
5553
|
+
}
|
|
5554
|
+
if (fulltextPredicate) {
|
|
5555
|
+
const nodeKinds = getNodeKindsForAlias(ast, fulltextPredicate.field.alias);
|
|
5556
|
+
ctes.push(
|
|
5557
|
+
buildStandardFulltextCte({
|
|
5558
|
+
ctx,
|
|
5559
|
+
fulltextPredicate,
|
|
5560
|
+
graphId,
|
|
5561
|
+
nodeKinds
|
|
5562
|
+
})
|
|
5563
|
+
);
|
|
5564
|
+
}
|
|
5565
|
+
if (getHybridTargetAlias(vectorPredicate, fulltextPredicate) !== void 0) {
|
|
5566
|
+
ctes.push(buildStandardHybridCandidateCte());
|
|
5001
5567
|
}
|
|
5002
5568
|
const projection = buildStandardProjection({
|
|
5003
5569
|
ast,
|
|
@@ -5007,14 +5573,18 @@ function compileStandardQueryWithCteStrategy(ast, graphId, ctx) {
|
|
|
5007
5573
|
const fromClause = buildStandardFromClause({
|
|
5008
5574
|
ast,
|
|
5009
5575
|
...collapsedTraversalCteAlias === void 0 ? {} : { collapsedTraversalCteAlias },
|
|
5010
|
-
...vectorPredicate === void 0 ? {} : { vectorPredicate }
|
|
5576
|
+
...vectorPredicate === void 0 ? {} : { vectorPredicate },
|
|
5577
|
+
...fulltextPredicate === void 0 ? {} : { fulltextPredicate }
|
|
5011
5578
|
});
|
|
5012
5579
|
const groupBy = buildStandardGroupBy({ ast, dialect });
|
|
5013
5580
|
const having2 = buildStandardHaving({ ast, ctx });
|
|
5014
|
-
const orderBy =
|
|
5581
|
+
const orderBy = selectStandardOrderBy({
|
|
5015
5582
|
ast,
|
|
5016
|
-
|
|
5017
|
-
dialect
|
|
5583
|
+
collapsedTraversalCteAlias,
|
|
5584
|
+
dialect,
|
|
5585
|
+
fulltextPredicate,
|
|
5586
|
+
fusion,
|
|
5587
|
+
vectorPredicate
|
|
5018
5588
|
});
|
|
5019
5589
|
const limitOffset = buildLimitOffsetClause({
|
|
5020
5590
|
limit: effectiveLimit,
|
|
@@ -5136,7 +5706,7 @@ function extractCursorValue(row, spec) {
|
|
|
5136
5706
|
current = record[segment];
|
|
5137
5707
|
}
|
|
5138
5708
|
if (jsonPointer2) {
|
|
5139
|
-
const segments =
|
|
5709
|
+
const segments = chunkUVZHTBZ5_cjs.parseJsonPointer(jsonPointer2);
|
|
5140
5710
|
for (const segment of segments) {
|
|
5141
5711
|
if (current === null || current === void 0) return void 0;
|
|
5142
5712
|
if (typeof current !== "object") return void 0;
|
|
@@ -5206,14 +5776,14 @@ function generateId() {
|
|
|
5206
5776
|
|
|
5207
5777
|
// src/utils/path.ts
|
|
5208
5778
|
function parseSqlitePath(path) {
|
|
5209
|
-
const emptyPath = `${
|
|
5779
|
+
const emptyPath = `${chunkFQCQM3KG_cjs.SQLITE_PATH_DELIMITER}${chunkFQCQM3KG_cjs.SQLITE_PATH_DELIMITER}`;
|
|
5210
5780
|
if (!path || path === emptyPath) return [];
|
|
5211
5781
|
const trimmed = path.slice(1, -1);
|
|
5212
5782
|
if (trimmed === "") return [];
|
|
5213
|
-
return trimmed.split(
|
|
5783
|
+
return trimmed.split(chunkFQCQM3KG_cjs.SQLITE_PATH_DELIMITER);
|
|
5214
5784
|
}
|
|
5215
5785
|
function isSqlitePath(value) {
|
|
5216
|
-
return typeof value === "string" && value.startsWith(
|
|
5786
|
+
return typeof value === "string" && value.startsWith(chunkFQCQM3KG_cjs.SQLITE_PATH_DELIMITER) && value.endsWith(chunkFQCQM3KG_cjs.SQLITE_PATH_DELIMITER);
|
|
5217
5787
|
}
|
|
5218
5788
|
function normalizePath(value) {
|
|
5219
5789
|
if (Array.isArray(value)) {
|
|
@@ -5228,12 +5798,12 @@ function normalizePath(value) {
|
|
|
5228
5798
|
return [];
|
|
5229
5799
|
}
|
|
5230
5800
|
function isPostgresTextArray(value) {
|
|
5231
|
-
return typeof value === "string" && value.startsWith(
|
|
5801
|
+
return typeof value === "string" && value.startsWith(chunkFQCQM3KG_cjs.PG_ARRAY_START) && value.endsWith(chunkFQCQM3KG_cjs.PG_ARRAY_END);
|
|
5232
5802
|
}
|
|
5233
5803
|
function parsePostgresTextArray(value) {
|
|
5234
5804
|
const inner = value.slice(1, -1);
|
|
5235
5805
|
if (inner === "") return [];
|
|
5236
|
-
return inner.split(
|
|
5806
|
+
return inner.split(chunkFQCQM3KG_cjs.PG_PATH_ELEMENT_SEPARATOR);
|
|
5237
5807
|
}
|
|
5238
5808
|
|
|
5239
5809
|
// src/query/execution/result-mapper.ts
|
|
@@ -5435,11 +6005,7 @@ function buildComparisonPredicate(field2, op, value) {
|
|
|
5435
6005
|
};
|
|
5436
6006
|
}
|
|
5437
6007
|
function buildCursorFromContext(context, orderBy, direction) {
|
|
5438
|
-
return buildCursorFromRow(
|
|
5439
|
-
context,
|
|
5440
|
-
orderBy,
|
|
5441
|
-
direction
|
|
5442
|
-
);
|
|
6008
|
+
return buildCursorFromRow(context, orderBy, direction);
|
|
5443
6009
|
}
|
|
5444
6010
|
function buildPaginatedResult(data, orderedRows, orderBy, limit, hasMore, isBackward, cursor, buildContext) {
|
|
5445
6011
|
let nextCursor;
|
|
@@ -5474,7 +6040,7 @@ async function* createStreamIterable(batchSize, paginate) {
|
|
|
5474
6040
|
}
|
|
5475
6041
|
}
|
|
5476
6042
|
function getStreamBatchSize(options) {
|
|
5477
|
-
return options?.batchSize ??
|
|
6043
|
+
return options?.batchSize ?? chunkFQCQM3KG_cjs.DEFAULT_STREAM_BATCH_SIZE;
|
|
5478
6044
|
}
|
|
5479
6045
|
|
|
5480
6046
|
// src/query/execution/field-tracker.ts
|
|
@@ -5556,7 +6122,7 @@ function createNodeTrackingProxy(alias, kindNames, tracker, options) {
|
|
|
5556
6122
|
return getPlaceholderForSystemField(property, options.mode);
|
|
5557
6123
|
}
|
|
5558
6124
|
if (property === "meta") {
|
|
5559
|
-
for (const key of
|
|
6125
|
+
for (const key of chunkFQCQM3KG_cjs.NODE_META_KEYS) {
|
|
5560
6126
|
tracker.record(alias, `meta.${key}`, true);
|
|
5561
6127
|
}
|
|
5562
6128
|
return buildNodeMetaPlaceholder(options.mode);
|
|
@@ -5589,7 +6155,7 @@ function createEdgeTrackingProxy(alias, edgeKindNames, tracker, options) {
|
|
|
5589
6155
|
return getPlaceholderForSystemField(property, options.mode);
|
|
5590
6156
|
}
|
|
5591
6157
|
if (property === "meta") {
|
|
5592
|
-
for (const key of
|
|
6158
|
+
for (const key of chunkFQCQM3KG_cjs.EDGE_META_KEYS) {
|
|
5593
6159
|
tracker.record(alias, `meta.${key}`, true);
|
|
5594
6160
|
}
|
|
5595
6161
|
return buildEdgeMetaPlaceholder(options.mode);
|
|
@@ -6032,59 +6598,41 @@ function validateSqlIdentifier(alias) {
|
|
|
6032
6598
|
);
|
|
6033
6599
|
}
|
|
6034
6600
|
}
|
|
6035
|
-
function
|
|
6036
|
-
|
|
6037
|
-
|
|
6038
|
-
|
|
6039
|
-
|
|
6040
|
-
{
|
|
6041
|
-
path,
|
|
6042
|
-
message: "Vector similarity predicates are only supported at top-level or inside AND groups."
|
|
6043
|
-
}
|
|
6044
|
-
]
|
|
6045
|
-
},
|
|
6046
|
-
{
|
|
6047
|
-
suggestion: "Rewrite the predicate to keep vector similarity at top-level or combine with additional filters using AND."
|
|
6048
|
-
}
|
|
6049
|
-
);
|
|
6050
|
-
}
|
|
6051
|
-
function validateVectorPredicateExpression(expression, inDisallowedBranch, path) {
|
|
6601
|
+
function validateStructuralPredicatePlacement(expression, matchType, buildError, inDisallowedBranch, path) {
|
|
6602
|
+
if (expression.__type === matchType) {
|
|
6603
|
+
if (inDisallowedBranch) buildError(path);
|
|
6604
|
+
return;
|
|
6605
|
+
}
|
|
6052
6606
|
switch (expression.__type) {
|
|
6053
|
-
case "vector_similarity": {
|
|
6054
|
-
if (inDisallowedBranch) {
|
|
6055
|
-
throwInvalidVectorPredicatePlacement(path);
|
|
6056
|
-
}
|
|
6057
|
-
return;
|
|
6058
|
-
}
|
|
6059
6607
|
case "and": {
|
|
6060
|
-
for (const [
|
|
6061
|
-
|
|
6062
|
-
|
|
6063
|
-
|
|
6064
|
-
|
|
6065
|
-
predicate2,
|
|
6608
|
+
for (const [index, child] of expression.predicates.entries()) {
|
|
6609
|
+
validateStructuralPredicatePlacement(
|
|
6610
|
+
child,
|
|
6611
|
+
matchType,
|
|
6612
|
+
buildError,
|
|
6066
6613
|
inDisallowedBranch,
|
|
6067
|
-
`${path}.predicates[${
|
|
6614
|
+
`${path}.predicates[${index}]`
|
|
6068
6615
|
);
|
|
6069
6616
|
}
|
|
6070
6617
|
return;
|
|
6071
6618
|
}
|
|
6072
6619
|
case "or": {
|
|
6073
|
-
for (const [
|
|
6074
|
-
|
|
6075
|
-
|
|
6076
|
-
|
|
6077
|
-
|
|
6078
|
-
predicate2,
|
|
6620
|
+
for (const [index, child] of expression.predicates.entries()) {
|
|
6621
|
+
validateStructuralPredicatePlacement(
|
|
6622
|
+
child,
|
|
6623
|
+
matchType,
|
|
6624
|
+
buildError,
|
|
6079
6625
|
true,
|
|
6080
|
-
`${path}.predicates[${
|
|
6626
|
+
`${path}.predicates[${index}]`
|
|
6081
6627
|
);
|
|
6082
6628
|
}
|
|
6083
6629
|
return;
|
|
6084
6630
|
}
|
|
6085
6631
|
case "not": {
|
|
6086
|
-
|
|
6632
|
+
validateStructuralPredicatePlacement(
|
|
6087
6633
|
expression.predicate,
|
|
6634
|
+
matchType,
|
|
6635
|
+
buildError,
|
|
6088
6636
|
true,
|
|
6089
6637
|
`${path}.predicate`
|
|
6090
6638
|
);
|
|
@@ -6098,24 +6646,108 @@ function validateVectorPredicateExpression(expression, inDisallowedBranch, path)
|
|
|
6098
6646
|
case "object_op":
|
|
6099
6647
|
case "aggregate_comparison":
|
|
6100
6648
|
case "exists":
|
|
6101
|
-
case "in_subquery":
|
|
6649
|
+
case "in_subquery":
|
|
6650
|
+
case "vector_similarity":
|
|
6651
|
+
case "fulltext_match": {
|
|
6102
6652
|
return;
|
|
6103
6653
|
}
|
|
6104
6654
|
}
|
|
6105
6655
|
}
|
|
6656
|
+
function throwInvalidVectorPredicatePlacement(path) {
|
|
6657
|
+
throw new chunkO5PST6TB_cjs.ValidationError(
|
|
6658
|
+
"Vector similarity predicates cannot be nested under OR or NOT. Use top-level AND combinations instead.",
|
|
6659
|
+
{
|
|
6660
|
+
issues: [
|
|
6661
|
+
{
|
|
6662
|
+
path,
|
|
6663
|
+
message: "Vector similarity predicates are only supported at top-level or inside AND groups."
|
|
6664
|
+
}
|
|
6665
|
+
]
|
|
6666
|
+
},
|
|
6667
|
+
{
|
|
6668
|
+
suggestion: "Rewrite the predicate to keep vector similarity at top-level or combine with additional filters using AND."
|
|
6669
|
+
}
|
|
6670
|
+
);
|
|
6671
|
+
}
|
|
6672
|
+
function throwInvalidFulltextPredicatePlacement(path) {
|
|
6673
|
+
throw new chunkO5PST6TB_cjs.ValidationError(
|
|
6674
|
+
"Fulltext match predicates (.matches()) cannot be nested under OR or NOT. Use top-level AND combinations instead.",
|
|
6675
|
+
{
|
|
6676
|
+
issues: [
|
|
6677
|
+
{
|
|
6678
|
+
path,
|
|
6679
|
+
message: "Fulltext match predicates are only supported at top-level or inside AND groups."
|
|
6680
|
+
}
|
|
6681
|
+
]
|
|
6682
|
+
},
|
|
6683
|
+
{
|
|
6684
|
+
suggestion: "Restructure your query so the fulltext match appears at the top level of a whereNode() or inside an .and() combination."
|
|
6685
|
+
}
|
|
6686
|
+
);
|
|
6687
|
+
}
|
|
6106
6688
|
function validateVectorPredicatePlacement(predicates) {
|
|
6107
|
-
for (const [
|
|
6108
|
-
|
|
6689
|
+
for (const [index, predicate2] of predicates.entries()) {
|
|
6690
|
+
validateStructuralPredicatePlacement(
|
|
6691
|
+
predicate2.expression,
|
|
6692
|
+
"vector_similarity",
|
|
6693
|
+
throwInvalidVectorPredicatePlacement,
|
|
6694
|
+
false,
|
|
6695
|
+
`predicates[${index}].expression`
|
|
6696
|
+
);
|
|
6697
|
+
}
|
|
6698
|
+
}
|
|
6699
|
+
function validateFulltextPredicatePlacement(predicates) {
|
|
6700
|
+
for (const [index, predicate2] of predicates.entries()) {
|
|
6701
|
+
validateStructuralPredicatePlacement(
|
|
6109
6702
|
predicate2.expression,
|
|
6703
|
+
"fulltext_match",
|
|
6704
|
+
throwInvalidFulltextPredicatePlacement,
|
|
6110
6705
|
false,
|
|
6111
|
-
`predicates[${
|
|
6706
|
+
`predicates[${index}].expression`
|
|
6707
|
+
);
|
|
6708
|
+
}
|
|
6709
|
+
}
|
|
6710
|
+
function validateHybridFusionOptions(options) {
|
|
6711
|
+
const method = options.method;
|
|
6712
|
+
if (method !== void 0 && method !== "rrf") {
|
|
6713
|
+
throw new chunkO5PST6TB_cjs.ValidationError(`Unsupported fusion method: ${method}`, {
|
|
6714
|
+
issues: [{ path: "fusion.method", message: `Only "rrf" is supported.` }]
|
|
6715
|
+
});
|
|
6716
|
+
}
|
|
6717
|
+
const { k } = options;
|
|
6718
|
+
if (k !== void 0 && (!Number.isFinite(k) || k <= 0)) {
|
|
6719
|
+
throw new chunkO5PST6TB_cjs.ValidationError(
|
|
6720
|
+
`Fusion k must be a positive finite number, got: ${String(k)}`,
|
|
6721
|
+
{
|
|
6722
|
+
issues: [{ path: "fusion.k", message: "Must be a positive number." }]
|
|
6723
|
+
}
|
|
6112
6724
|
);
|
|
6113
6725
|
}
|
|
6726
|
+
const weights = options.weights;
|
|
6727
|
+
if (weights !== void 0) {
|
|
6728
|
+
for (const key of ["vector", "fulltext"]) {
|
|
6729
|
+
const weight = weights[key];
|
|
6730
|
+
if (weight !== void 0 && (!Number.isFinite(weight) || weight < 0)) {
|
|
6731
|
+
throw new chunkO5PST6TB_cjs.ValidationError(
|
|
6732
|
+
`Fusion weight for "${key}" must be a non-negative finite number, got: ${String(weight)}`,
|
|
6733
|
+
{
|
|
6734
|
+
issues: [
|
|
6735
|
+
{
|
|
6736
|
+
path: `fusion.weights.${key}`,
|
|
6737
|
+
message: "Must be a non-negative number."
|
|
6738
|
+
}
|
|
6739
|
+
]
|
|
6740
|
+
}
|
|
6741
|
+
);
|
|
6742
|
+
}
|
|
6743
|
+
}
|
|
6744
|
+
}
|
|
6114
6745
|
}
|
|
6115
6746
|
|
|
6116
6747
|
// src/query/builder/ast-builder.ts
|
|
6117
6748
|
function buildQueryAst(config, state) {
|
|
6118
6749
|
validateVectorPredicatePlacement(state.predicates);
|
|
6750
|
+
validateFulltextPredicatePlacement(state.predicates);
|
|
6119
6751
|
const temporalMode = {
|
|
6120
6752
|
mode: state.temporalMode
|
|
6121
6753
|
};
|
|
@@ -6139,7 +6771,18 @@ function buildQueryAst(config, state) {
|
|
|
6139
6771
|
...state.limit !== void 0 && { limit: state.limit },
|
|
6140
6772
|
...state.offset !== void 0 && { offset: state.offset },
|
|
6141
6773
|
...state.groupBy !== void 0 && { groupBy: state.groupBy },
|
|
6142
|
-
...state.having !== void 0 && { having: state.having }
|
|
6774
|
+
...state.having !== void 0 && { having: state.having },
|
|
6775
|
+
...state.fusion !== void 0 && { fusion: state.fusion }
|
|
6776
|
+
};
|
|
6777
|
+
}
|
|
6778
|
+
|
|
6779
|
+
// src/query/builder/compile-options.ts
|
|
6780
|
+
function buildCompileOptions(config) {
|
|
6781
|
+
const fulltextStrategy = config.backend?.fulltextStrategy;
|
|
6782
|
+
return {
|
|
6783
|
+
dialect: config.dialect ?? "sqlite",
|
|
6784
|
+
schema: config.schema,
|
|
6785
|
+
...fulltextStrategy === void 0 ? {} : { fulltextStrategy }
|
|
6143
6786
|
};
|
|
6144
6787
|
}
|
|
6145
6788
|
function toLiteral(value) {
|
|
@@ -6241,7 +6884,8 @@ function substitutePredicateExpression(expr, bindings) {
|
|
|
6241
6884
|
case "array_op":
|
|
6242
6885
|
case "object_op":
|
|
6243
6886
|
case "aggregate_comparison":
|
|
6244
|
-
case "vector_similarity":
|
|
6887
|
+
case "vector_similarity":
|
|
6888
|
+
case "fulltext_match": {
|
|
6245
6889
|
return expr;
|
|
6246
6890
|
}
|
|
6247
6891
|
case "exists": {
|
|
@@ -6280,7 +6924,7 @@ function substituteParameters(ast, bindings) {
|
|
|
6280
6924
|
};
|
|
6281
6925
|
}
|
|
6282
6926
|
function fillPlaceholders(params, bindings, dialect) {
|
|
6283
|
-
const adapter =
|
|
6927
|
+
const adapter = chunkFQCQM3KG_cjs.getDialect(dialect);
|
|
6284
6928
|
return params.map((parameter) => {
|
|
6285
6929
|
if (parameter instanceof drizzleOrm.Placeholder) {
|
|
6286
6930
|
const name = parameter.name;
|
|
@@ -6493,7 +7137,8 @@ function collectParameterMetadataFromExpression(expression, names, stringOpParam
|
|
|
6493
7137
|
case "array_op":
|
|
6494
7138
|
case "object_op":
|
|
6495
7139
|
case "aggregate_comparison":
|
|
6496
|
-
case "vector_similarity":
|
|
7140
|
+
case "vector_similarity":
|
|
7141
|
+
case "fulltext_match": {
|
|
6497
7142
|
return;
|
|
6498
7143
|
}
|
|
6499
7144
|
case "exists": {
|
|
@@ -6624,7 +7269,7 @@ var ExecutableQuery = class _ExecutableQuery {
|
|
|
6624
7269
|
direction
|
|
6625
7270
|
} : {
|
|
6626
7271
|
field: fieldRef(alias, ["props"], {
|
|
6627
|
-
jsonPointer:
|
|
7272
|
+
jsonPointer: chunkUVZHTBZ5_cjs.jsonPointer([field2]),
|
|
6628
7273
|
valueType: typeInfo?.valueType,
|
|
6629
7274
|
elementType: typeInfo?.elementType
|
|
6630
7275
|
}),
|
|
@@ -6848,10 +7493,7 @@ var ExecutableQuery = class _ExecutableQuery {
|
|
|
6848
7493
|
* Builds compile options from the config.
|
|
6849
7494
|
*/
|
|
6850
7495
|
#compileOptions() {
|
|
6851
|
-
return
|
|
6852
|
-
dialect: this.#config.dialect ?? "sqlite",
|
|
6853
|
-
schema: this.#config.schema
|
|
6854
|
-
};
|
|
7496
|
+
return buildCompileOptions(this.#config);
|
|
6855
7497
|
}
|
|
6856
7498
|
#requireBackend() {
|
|
6857
7499
|
const { backend } = this.#config;
|
|
@@ -7233,7 +7875,7 @@ var ExecutableQuery = class _ExecutableQuery {
|
|
|
7233
7875
|
if (field2.jsonPointer === void 0) {
|
|
7234
7876
|
return false;
|
|
7235
7877
|
}
|
|
7236
|
-
const segments =
|
|
7878
|
+
const segments = chunkUVZHTBZ5_cjs.parseJsonPointer(field2.jsonPointer);
|
|
7237
7879
|
if (segments.length !== 1) {
|
|
7238
7880
|
return false;
|
|
7239
7881
|
}
|
|
@@ -7281,7 +7923,7 @@ var ExecutableQuery = class _ExecutableQuery {
|
|
|
7281
7923
|
aliasObject2[fieldName] = nullToUndefined(row[outputName2]);
|
|
7282
7924
|
continue;
|
|
7283
7925
|
}
|
|
7284
|
-
const segments =
|
|
7926
|
+
const segments = chunkUVZHTBZ5_cjs.parseJsonPointer(jsonPointer2);
|
|
7285
7927
|
if (segments.length === 0) {
|
|
7286
7928
|
throw new MissingSelectiveFieldError(alias, "orderBy");
|
|
7287
7929
|
}
|
|
@@ -7377,7 +8019,7 @@ var ExecutableQuery = class _ExecutableQuery {
|
|
|
7377
8019
|
);
|
|
7378
8020
|
}
|
|
7379
8021
|
const isBackward = options.last !== void 0 || options.before !== void 0;
|
|
7380
|
-
const limit = options.first ?? options.last ??
|
|
8022
|
+
const limit = options.first ?? options.last ?? chunkFQCQM3KG_cjs.DEFAULT_PAGINATION_LIMIT;
|
|
7381
8023
|
const cursor = options.after ?? options.before;
|
|
7382
8024
|
let cursorData;
|
|
7383
8025
|
if (cursor) {
|
|
@@ -7566,14 +8208,8 @@ var ExecutableAggregateQuery = class _ExecutableAggregateQuery {
|
|
|
7566
8208
|
this.#cachedCompiled = compiled;
|
|
7567
8209
|
return compiled;
|
|
7568
8210
|
}
|
|
7569
|
-
/**
|
|
7570
|
-
* Builds compile options from the config.
|
|
7571
|
-
*/
|
|
7572
8211
|
#compileOptions() {
|
|
7573
|
-
return
|
|
7574
|
-
dialect: this.#config.dialect ?? "sqlite",
|
|
7575
|
-
schema: this.#config.schema
|
|
7576
|
-
};
|
|
8212
|
+
return buildCompileOptions(this.#config);
|
|
7577
8213
|
}
|
|
7578
8214
|
/**
|
|
7579
8215
|
* Executes the query and returns typed results.
|
|
@@ -7804,7 +8440,7 @@ var TraversalBuilder = class _TraversalBuilder {
|
|
|
7804
8440
|
const valueType = typeInfo?.valueType;
|
|
7805
8441
|
const elementType = typeInfo?.elementType;
|
|
7806
8442
|
const ref = fieldRef(alias, ["props"], {
|
|
7807
|
-
jsonPointer:
|
|
8443
|
+
jsonPointer: chunkUVZHTBZ5_cjs.jsonPointer([propertyName]),
|
|
7808
8444
|
valueType,
|
|
7809
8445
|
elementType
|
|
7810
8446
|
});
|
|
@@ -8124,7 +8760,7 @@ var QueryBuilder = class _QueryBuilder {
|
|
|
8124
8760
|
if (source.valueType !== void 0 || source.path.length !== 1 || source.path[0] !== "props" || source.jsonPointer === void 0) {
|
|
8125
8761
|
return [outputName, source];
|
|
8126
8762
|
}
|
|
8127
|
-
const segments =
|
|
8763
|
+
const segments = chunkUVZHTBZ5_cjs.parseJsonPointer(source.jsonPointer);
|
|
8128
8764
|
if (segments.length !== 1) {
|
|
8129
8765
|
return [outputName, source];
|
|
8130
8766
|
}
|
|
@@ -8178,7 +8814,7 @@ var QueryBuilder = class _QueryBuilder {
|
|
|
8178
8814
|
direction
|
|
8179
8815
|
} : {
|
|
8180
8816
|
field: fieldRef(alias, ["props"], {
|
|
8181
|
-
jsonPointer:
|
|
8817
|
+
jsonPointer: chunkUVZHTBZ5_cjs.jsonPointer([field2]),
|
|
8182
8818
|
valueType: typeInfo?.valueType,
|
|
8183
8819
|
elementType: typeInfo?.elementType
|
|
8184
8820
|
}),
|
|
@@ -8253,7 +8889,7 @@ var QueryBuilder = class _QueryBuilder {
|
|
|
8253
8889
|
__type: "field_ref",
|
|
8254
8890
|
alias,
|
|
8255
8891
|
path: ["props"],
|
|
8256
|
-
jsonPointer:
|
|
8892
|
+
jsonPointer: chunkUVZHTBZ5_cjs.jsonPointer([field2]),
|
|
8257
8893
|
valueType: typeInfo?.valueType,
|
|
8258
8894
|
elementType: typeInfo?.elementType
|
|
8259
8895
|
};
|
|
@@ -8300,6 +8936,36 @@ var QueryBuilder = class _QueryBuilder {
|
|
|
8300
8936
|
having: predicate2
|
|
8301
8937
|
});
|
|
8302
8938
|
}
|
|
8939
|
+
/**
|
|
8940
|
+
* Sets fusion parameters for hybrid (vector + fulltext) queries.
|
|
8941
|
+
*
|
|
8942
|
+
* Applies only when the query contains both a `.similarTo()` and a
|
|
8943
|
+
* `.$fulltext.matches()` predicate. Without this call, the default is
|
|
8944
|
+
* RRF with k=60 and equal weights. A mismatch between `.fuseWith()`
|
|
8945
|
+
* configuration and the predicates on the query is caught during
|
|
8946
|
+
* compilation, not here.
|
|
8947
|
+
*
|
|
8948
|
+
* @example
|
|
8949
|
+
* ```typescript
|
|
8950
|
+
* store.query()
|
|
8951
|
+
* .from("Document", "d")
|
|
8952
|
+
* .whereNode("d", d =>
|
|
8953
|
+
* d.$fulltext.matches("renewable energy", 50)
|
|
8954
|
+
* .and(d.embedding.similarTo(vec, 50))
|
|
8955
|
+
* .and(d.tenantId.eq(tenant))
|
|
8956
|
+
* )
|
|
8957
|
+
* .fuseWith({ k: 60, weights: { fulltext: 1.5 } })
|
|
8958
|
+
* .limit(10)
|
|
8959
|
+
* .execute();
|
|
8960
|
+
* ```
|
|
8961
|
+
*/
|
|
8962
|
+
fuseWith(options) {
|
|
8963
|
+
validateHybridFusionOptions(options);
|
|
8964
|
+
return new _QueryBuilder(this.#config, {
|
|
8965
|
+
...this.#state,
|
|
8966
|
+
fusion: options
|
|
8967
|
+
});
|
|
8968
|
+
}
|
|
8303
8969
|
/**
|
|
8304
8970
|
* Applies a query fragment to transform this builder.
|
|
8305
8971
|
*
|
|
@@ -8351,42 +9017,14 @@ var QueryBuilder = class _QueryBuilder {
|
|
|
8351
9017
|
property
|
|
8352
9018
|
) : void 0;
|
|
8353
9019
|
const ref = fieldRef(alias, ["props"], {
|
|
8354
|
-
jsonPointer:
|
|
9020
|
+
jsonPointer: chunkUVZHTBZ5_cjs.jsonPointer([property]),
|
|
8355
9021
|
valueType: typeInfo?.valueType,
|
|
8356
9022
|
elementType: typeInfo?.elementType
|
|
8357
9023
|
});
|
|
8358
9024
|
return this.#buildFieldBuilderForTypeInfo(ref, typeInfo);
|
|
8359
9025
|
}
|
|
8360
9026
|
#buildFieldBuilderForTypeInfo(ref, typeInfo) {
|
|
8361
|
-
|
|
8362
|
-
return baseField(ref);
|
|
8363
|
-
}
|
|
8364
|
-
switch (typeInfo.valueType) {
|
|
8365
|
-
case "string": {
|
|
8366
|
-
return stringField(ref);
|
|
8367
|
-
}
|
|
8368
|
-
case "number": {
|
|
8369
|
-
return numberField(ref);
|
|
8370
|
-
}
|
|
8371
|
-
case "boolean": {
|
|
8372
|
-
return baseField(ref);
|
|
8373
|
-
}
|
|
8374
|
-
case "date": {
|
|
8375
|
-
return dateField(ref);
|
|
8376
|
-
}
|
|
8377
|
-
case "array": {
|
|
8378
|
-
return arrayField(ref);
|
|
8379
|
-
}
|
|
8380
|
-
case "object": {
|
|
8381
|
-
return objectField(ref, { typeInfo });
|
|
8382
|
-
}
|
|
8383
|
-
case "embedding": {
|
|
8384
|
-
return embeddingField(ref);
|
|
8385
|
-
}
|
|
8386
|
-
case "unknown": {
|
|
8387
|
-
return baseField(ref);
|
|
8388
|
-
}
|
|
8389
|
-
}
|
|
9027
|
+
return buildFieldBuilderForTypeInfo(ref, typeInfo);
|
|
8390
9028
|
}
|
|
8391
9029
|
#createNodeAccessor(alias) {
|
|
8392
9030
|
const kindNames = this.#getKindNamesForAlias(alias);
|
|
@@ -8396,6 +9034,10 @@ var QueryBuilder = class _QueryBuilder {
|
|
|
8396
9034
|
const kindAccessor = stringField(
|
|
8397
9035
|
fieldRef(alias, ["kind"], { valueType: "string" })
|
|
8398
9036
|
);
|
|
9037
|
+
const fulltextAccessor = createFulltextAccessor(
|
|
9038
|
+
alias,
|
|
9039
|
+
() => this.#hasSearchableField(kindNames)
|
|
9040
|
+
);
|
|
8399
9041
|
return new Proxy({}, {
|
|
8400
9042
|
get: (_, property) => {
|
|
8401
9043
|
if (typeof property === "symbol") return;
|
|
@@ -8403,10 +9045,15 @@ var QueryBuilder = class _QueryBuilder {
|
|
|
8403
9045
|
if (property === "toJSON") return;
|
|
8404
9046
|
if (property === "id") return idAccessor;
|
|
8405
9047
|
if (property === "kind") return kindAccessor;
|
|
9048
|
+
if (property === "$fulltext") return fulltextAccessor;
|
|
8406
9049
|
return this.#getFieldBuilderForProperty(kindNames, property, alias);
|
|
8407
9050
|
}
|
|
8408
9051
|
});
|
|
8409
9052
|
}
|
|
9053
|
+
#hasSearchableField(kindNames) {
|
|
9054
|
+
if (!kindNames) return false;
|
|
9055
|
+
return this.#config.schemaIntrospector.hasSearchableField(kindNames);
|
|
9056
|
+
}
|
|
8410
9057
|
#expandTraversalEdgeKinds(edgeKind, includeImplyingEdges) {
|
|
8411
9058
|
return includeImplyingEdges ? this.#config.registry.expandImplyingEdges(edgeKind) : [edgeKind];
|
|
8412
9059
|
}
|
|
@@ -8449,7 +9096,7 @@ var QueryBuilder = class _QueryBuilder {
|
|
|
8449
9096
|
property
|
|
8450
9097
|
) : void 0;
|
|
8451
9098
|
const ref = fieldRef(alias, ["props"], {
|
|
8452
|
-
jsonPointer:
|
|
9099
|
+
jsonPointer: chunkUVZHTBZ5_cjs.jsonPointer([property]),
|
|
8453
9100
|
valueType: typeInfo?.valueType,
|
|
8454
9101
|
elementType: typeInfo?.elementType
|
|
8455
9102
|
});
|
|
@@ -8632,14 +9279,8 @@ var UnionableQuery = class _UnionableQuery {
|
|
|
8632
9279
|
this.#cachedCompiled = compiled;
|
|
8633
9280
|
return compiled;
|
|
8634
9281
|
}
|
|
8635
|
-
/**
|
|
8636
|
-
* Builds compile options from the config.
|
|
8637
|
-
*/
|
|
8638
9282
|
#compileOptions() {
|
|
8639
|
-
return
|
|
8640
|
-
dialect: this.#config.dialect ?? "sqlite",
|
|
8641
|
-
schema: this.#config.schema
|
|
8642
|
-
};
|
|
9283
|
+
return buildCompileOptions(this.#config);
|
|
8643
9284
|
}
|
|
8644
9285
|
/**
|
|
8645
9286
|
* Executes the combined query.
|
|
@@ -8701,7 +9342,7 @@ function count(alias, field2) {
|
|
|
8701
9342
|
__type: "field_ref",
|
|
8702
9343
|
alias,
|
|
8703
9344
|
path: field2 ? ["props"] : ["id"],
|
|
8704
|
-
jsonPointer: field2 ?
|
|
9345
|
+
jsonPointer: field2 ? chunkUVZHTBZ5_cjs.jsonPointer([field2]) : void 0,
|
|
8705
9346
|
valueType: field2 ? void 0 : "string"
|
|
8706
9347
|
}
|
|
8707
9348
|
};
|
|
@@ -8714,7 +9355,7 @@ function countDistinct(alias, field2) {
|
|
|
8714
9355
|
__type: "field_ref",
|
|
8715
9356
|
alias,
|
|
8716
9357
|
path: field2 ? ["props"] : ["id"],
|
|
8717
|
-
jsonPointer: field2 ?
|
|
9358
|
+
jsonPointer: field2 ? chunkUVZHTBZ5_cjs.jsonPointer([field2]) : void 0,
|
|
8718
9359
|
valueType: field2 ? void 0 : "string"
|
|
8719
9360
|
}
|
|
8720
9361
|
};
|
|
@@ -8727,7 +9368,7 @@ function sum(alias, field2) {
|
|
|
8727
9368
|
__type: "field_ref",
|
|
8728
9369
|
alias,
|
|
8729
9370
|
path: ["props"],
|
|
8730
|
-
jsonPointer:
|
|
9371
|
+
jsonPointer: chunkUVZHTBZ5_cjs.jsonPointer([field2]),
|
|
8731
9372
|
valueType: "number"
|
|
8732
9373
|
}
|
|
8733
9374
|
};
|
|
@@ -8740,7 +9381,7 @@ function avg(alias, field2) {
|
|
|
8740
9381
|
__type: "field_ref",
|
|
8741
9382
|
alias,
|
|
8742
9383
|
path: ["props"],
|
|
8743
|
-
jsonPointer:
|
|
9384
|
+
jsonPointer: chunkUVZHTBZ5_cjs.jsonPointer([field2]),
|
|
8744
9385
|
valueType: "number"
|
|
8745
9386
|
}
|
|
8746
9387
|
};
|
|
@@ -8753,7 +9394,7 @@ function min(alias, field2) {
|
|
|
8753
9394
|
__type: "field_ref",
|
|
8754
9395
|
alias,
|
|
8755
9396
|
path: ["props"],
|
|
8756
|
-
jsonPointer:
|
|
9397
|
+
jsonPointer: chunkUVZHTBZ5_cjs.jsonPointer([field2]),
|
|
8757
9398
|
valueType: "number"
|
|
8758
9399
|
}
|
|
8759
9400
|
};
|
|
@@ -8766,7 +9407,7 @@ function max(alias, field2) {
|
|
|
8766
9407
|
__type: "field_ref",
|
|
8767
9408
|
alias,
|
|
8768
9409
|
path: ["props"],
|
|
8769
|
-
jsonPointer:
|
|
9410
|
+
jsonPointer: chunkUVZHTBZ5_cjs.jsonPointer([field2]),
|
|
8770
9411
|
valueType: "number"
|
|
8771
9412
|
}
|
|
8772
9413
|
};
|
|
@@ -8797,7 +9438,7 @@ function field(alias, ...path) {
|
|
|
8797
9438
|
__type: "field_ref",
|
|
8798
9439
|
alias,
|
|
8799
9440
|
path: ["props"],
|
|
8800
|
-
jsonPointer:
|
|
9441
|
+
jsonPointer: chunkUVZHTBZ5_cjs.jsonPointer(path)
|
|
8801
9442
|
};
|
|
8802
9443
|
}
|
|
8803
9444
|
function having(aggregate, op, value) {
|
|
@@ -8855,16 +9496,12 @@ function offsetFragment(n) {
|
|
|
8855
9496
|
}
|
|
8856
9497
|
|
|
8857
9498
|
// src/query/builder/index.ts
|
|
8858
|
-
setQueryBuilderClass(
|
|
8859
|
-
|
|
8860
|
-
);
|
|
8861
|
-
setUnionableQueryClass(
|
|
8862
|
-
UnionableQuery
|
|
8863
|
-
);
|
|
9499
|
+
setQueryBuilderClass(QueryBuilder);
|
|
9500
|
+
setUnionableQueryClass(UnionableQuery);
|
|
8864
9501
|
|
|
8865
9502
|
// src/query/builder.ts
|
|
8866
9503
|
function createQueryBuilder(graphId, registry, options) {
|
|
8867
|
-
const schemaIntrospector =
|
|
9504
|
+
const schemaIntrospector = chunkUVZHTBZ5_cjs.createSchemaIntrospector(
|
|
8868
9505
|
registry.nodeKinds,
|
|
8869
9506
|
registry.edgeKinds
|
|
8870
9507
|
);
|
|
@@ -8891,7 +9528,8 @@ function createQueryBuilder(graphId, registry, options) {
|
|
|
8891
9528
|
temporalMode: "current",
|
|
8892
9529
|
asOf: void 0,
|
|
8893
9530
|
groupBy: void 0,
|
|
8894
|
-
having: void 0
|
|
9531
|
+
having: void 0,
|
|
9532
|
+
fusion: void 0
|
|
8895
9533
|
};
|
|
8896
9534
|
return new QueryBuilder(config, initialState);
|
|
8897
9535
|
}
|
|
@@ -8900,12 +9538,12 @@ function createQueryBuilder(graphId, registry, options) {
|
|
|
8900
9538
|
function buildKindRegistry(graph) {
|
|
8901
9539
|
const nodeTypes = extractNodeTypes(graph);
|
|
8902
9540
|
const edgeTypes = extractEdgeTypes(graph);
|
|
8903
|
-
const closures = graph.ontology.length > 0 ?
|
|
8904
|
-
return new
|
|
9541
|
+
const closures = graph.ontology.length > 0 ? chunkHRNQ3DVJ_cjs.computeClosuresFromOntology(graph.ontology) : chunkHRNQ3DVJ_cjs.createEmptyClosures();
|
|
9542
|
+
return new chunkHRNQ3DVJ_cjs.KindRegistry(nodeTypes, edgeTypes, closures);
|
|
8905
9543
|
}
|
|
8906
9544
|
function extractNodeTypes(graph) {
|
|
8907
9545
|
const result = /* @__PURE__ */ new Map();
|
|
8908
|
-
for (const typeName of
|
|
9546
|
+
for (const typeName of chunkWRWVWKQL_cjs.getNodeKinds(graph)) {
|
|
8909
9547
|
const registration = graph.nodes[typeName];
|
|
8910
9548
|
result.set(typeName, registration.type);
|
|
8911
9549
|
}
|
|
@@ -8913,13 +9551,253 @@ function extractNodeTypes(graph) {
|
|
|
8913
9551
|
}
|
|
8914
9552
|
function extractEdgeTypes(graph) {
|
|
8915
9553
|
const result = /* @__PURE__ */ new Map();
|
|
8916
|
-
for (const typeName of
|
|
9554
|
+
for (const typeName of chunkWRWVWKQL_cjs.getEdgeKinds(graph)) {
|
|
8917
9555
|
const registration = graph.edges[typeName];
|
|
8918
9556
|
result.set(typeName, registration.type);
|
|
8919
9557
|
}
|
|
8920
9558
|
return result;
|
|
8921
9559
|
}
|
|
8922
9560
|
|
|
9561
|
+
// src/store/algorithms/context.ts
|
|
9562
|
+
var DEFAULT_ALGORITHM_MAX_HOPS = 10;
|
|
9563
|
+
var DEFAULT_NEIGHBOR_DEPTH = 1;
|
|
9564
|
+
function resolveTemporalOptions(ctx, options) {
|
|
9565
|
+
return {
|
|
9566
|
+
temporalMode: options.temporalMode ?? ctx.defaultTemporalMode,
|
|
9567
|
+
...options.asOf !== void 0 && { asOf: options.asOf }
|
|
9568
|
+
};
|
|
9569
|
+
}
|
|
9570
|
+
function resolveTemporalFilter(ctx, options, tableAlias) {
|
|
9571
|
+
const resolved = resolveTemporalOptions(ctx, options);
|
|
9572
|
+
const filterOptions = {
|
|
9573
|
+
mode: resolved.temporalMode,
|
|
9574
|
+
asOf: resolved.asOf,
|
|
9575
|
+
tableAlias,
|
|
9576
|
+
currentTimestamp: ctx.dialect.currentTimestamp()
|
|
9577
|
+
};
|
|
9578
|
+
return compileTemporalFilter(filterOptions);
|
|
9579
|
+
}
|
|
9580
|
+
function resolveMaxHops(rawMaxHops, fallback, optionName) {
|
|
9581
|
+
const value = rawMaxHops ?? fallback;
|
|
9582
|
+
if (!Number.isFinite(value) || !Number.isInteger(value)) {
|
|
9583
|
+
throw new chunkO5PST6TB_cjs.ConfigurationError(
|
|
9584
|
+
`Graph algorithm ${optionName} must be a finite integer, got ${String(value)}.`,
|
|
9585
|
+
{ option: optionName, value }
|
|
9586
|
+
);
|
|
9587
|
+
}
|
|
9588
|
+
if (value < 1) {
|
|
9589
|
+
throw new chunkO5PST6TB_cjs.ConfigurationError(
|
|
9590
|
+
`Graph algorithm ${optionName} must be at least 1, got ${value}.`,
|
|
9591
|
+
{ option: optionName, value }
|
|
9592
|
+
);
|
|
9593
|
+
}
|
|
9594
|
+
if (value > MAX_EXPLICIT_RECURSIVE_DEPTH) {
|
|
9595
|
+
throw new chunkO5PST6TB_cjs.ConfigurationError(
|
|
9596
|
+
`Graph algorithm ${optionName} (${value}) exceeds the maximum of ${MAX_EXPLICIT_RECURSIVE_DEPTH}.`,
|
|
9597
|
+
{ option: optionName, value, limit: MAX_EXPLICIT_RECURSIVE_DEPTH }
|
|
9598
|
+
);
|
|
9599
|
+
}
|
|
9600
|
+
return value;
|
|
9601
|
+
}
|
|
9602
|
+
function assertEdgeKinds(edges) {
|
|
9603
|
+
if (edges.length === 0) {
|
|
9604
|
+
throw new chunkO5PST6TB_cjs.ConfigurationError(
|
|
9605
|
+
`Graph algorithms require at least one edge kind in 'edges'.`,
|
|
9606
|
+
{ edges }
|
|
9607
|
+
);
|
|
9608
|
+
}
|
|
9609
|
+
}
|
|
9610
|
+
|
|
9611
|
+
// src/store/algorithms/degree.ts
|
|
9612
|
+
async function executeDegree(ctx, nodeId, options = {}) {
|
|
9613
|
+
const direction = options.direction ?? "both";
|
|
9614
|
+
const whereClauses = [
|
|
9615
|
+
drizzleOrm.sql`graph_id = ${ctx.graphId}`,
|
|
9616
|
+
resolveTemporalFilter(ctx, options),
|
|
9617
|
+
compileDirectionFilter(direction, nodeId)
|
|
9618
|
+
];
|
|
9619
|
+
if (options.edges !== void 0) {
|
|
9620
|
+
whereClauses.push(compileKindFilter(drizzleOrm.sql.raw("kind"), options.edges));
|
|
9621
|
+
}
|
|
9622
|
+
const countExpr = direction === "both" ? drizzleOrm.sql`COUNT(DISTINCT id)` : drizzleOrm.sql`COUNT(*)`;
|
|
9623
|
+
const query = drizzleOrm.sql`SELECT ${countExpr} AS count FROM ${ctx.schema.edgesTable} WHERE ${drizzleOrm.sql.join(whereClauses, drizzleOrm.sql` AND `)}`;
|
|
9624
|
+
const rows = await ctx.backend.execute(query);
|
|
9625
|
+
return Number(rows[0]?.count ?? 0);
|
|
9626
|
+
}
|
|
9627
|
+
function compileDirectionFilter(direction, nodeId) {
|
|
9628
|
+
switch (direction) {
|
|
9629
|
+
case "out": {
|
|
9630
|
+
return drizzleOrm.sql`from_id = ${nodeId}`;
|
|
9631
|
+
}
|
|
9632
|
+
case "in": {
|
|
9633
|
+
return drizzleOrm.sql`to_id = ${nodeId}`;
|
|
9634
|
+
}
|
|
9635
|
+
case "both": {
|
|
9636
|
+
return drizzleOrm.sql`(from_id = ${nodeId} OR to_id = ${nodeId})`;
|
|
9637
|
+
}
|
|
9638
|
+
}
|
|
9639
|
+
}
|
|
9640
|
+
async function executeReachable(ctx, sourceId, options) {
|
|
9641
|
+
assertEdgeKinds(options.edges);
|
|
9642
|
+
const maxHops = resolveMaxHops(
|
|
9643
|
+
options.maxHops,
|
|
9644
|
+
DEFAULT_ALGORITHM_MAX_HOPS,
|
|
9645
|
+
"maxHops"
|
|
9646
|
+
);
|
|
9647
|
+
const cte = buildReachableCte({
|
|
9648
|
+
graphId: ctx.graphId,
|
|
9649
|
+
sourceId,
|
|
9650
|
+
edgeKinds: options.edges,
|
|
9651
|
+
maxHops,
|
|
9652
|
+
direction: options.direction ?? "out",
|
|
9653
|
+
cyclePolicy: options.cyclePolicy ?? "prevent",
|
|
9654
|
+
includePath: false,
|
|
9655
|
+
...resolveTemporalOptions(ctx, options),
|
|
9656
|
+
dialect: ctx.dialect,
|
|
9657
|
+
schema: ctx.schema
|
|
9658
|
+
});
|
|
9659
|
+
const sourceFilter = options.excludeSource === true ? drizzleOrm.sql` WHERE id != ${sourceId}` : drizzleOrm.sql``;
|
|
9660
|
+
const query = drizzleOrm.sql`${cte} SELECT id, kind, MIN(depth) AS depth FROM reachable${sourceFilter} GROUP BY id, kind ORDER BY depth ASC, id ASC`;
|
|
9661
|
+
const rows = await ctx.backend.execute(query);
|
|
9662
|
+
return rows.map((row) => ({
|
|
9663
|
+
id: row.id,
|
|
9664
|
+
kind: row.kind,
|
|
9665
|
+
depth: Number(row.depth)
|
|
9666
|
+
}));
|
|
9667
|
+
}
|
|
9668
|
+
async function executeCanReach(ctx, sourceId, targetId, options) {
|
|
9669
|
+
assertEdgeKinds(options.edges);
|
|
9670
|
+
const maxHops = resolveMaxHops(
|
|
9671
|
+
options.maxHops,
|
|
9672
|
+
DEFAULT_ALGORITHM_MAX_HOPS,
|
|
9673
|
+
"maxHops"
|
|
9674
|
+
);
|
|
9675
|
+
const cte = buildReachableCte({
|
|
9676
|
+
graphId: ctx.graphId,
|
|
9677
|
+
sourceId,
|
|
9678
|
+
edgeKinds: options.edges,
|
|
9679
|
+
maxHops,
|
|
9680
|
+
direction: options.direction ?? "out",
|
|
9681
|
+
cyclePolicy: options.cyclePolicy ?? "prevent",
|
|
9682
|
+
includePath: false,
|
|
9683
|
+
...resolveTemporalOptions(ctx, options),
|
|
9684
|
+
dialect: ctx.dialect,
|
|
9685
|
+
schema: ctx.schema
|
|
9686
|
+
});
|
|
9687
|
+
const query = drizzleOrm.sql`${cte} SELECT 1 AS hit FROM reachable WHERE id = ${targetId} LIMIT 1`;
|
|
9688
|
+
const rows = await ctx.backend.execute(query);
|
|
9689
|
+
return rows.length > 0;
|
|
9690
|
+
}
|
|
9691
|
+
async function executeNeighbors(ctx, sourceId, options) {
|
|
9692
|
+
const depth = resolveMaxHops(options.depth, DEFAULT_NEIGHBOR_DEPTH, "depth");
|
|
9693
|
+
return executeReachable(ctx, sourceId, {
|
|
9694
|
+
edges: options.edges,
|
|
9695
|
+
maxHops: depth,
|
|
9696
|
+
direction: options.direction ?? "out",
|
|
9697
|
+
cyclePolicy: options.cyclePolicy ?? "prevent",
|
|
9698
|
+
...options.temporalMode !== void 0 && {
|
|
9699
|
+
temporalMode: options.temporalMode
|
|
9700
|
+
},
|
|
9701
|
+
...options.asOf !== void 0 && { asOf: options.asOf },
|
|
9702
|
+
excludeSource: true
|
|
9703
|
+
});
|
|
9704
|
+
}
|
|
9705
|
+
async function executeShortestPath(ctx, sourceId, targetId, options) {
|
|
9706
|
+
assertEdgeKinds(options.edges);
|
|
9707
|
+
const maxHops = resolveMaxHops(
|
|
9708
|
+
options.maxHops,
|
|
9709
|
+
DEFAULT_ALGORITHM_MAX_HOPS,
|
|
9710
|
+
"maxHops"
|
|
9711
|
+
);
|
|
9712
|
+
if (sourceId === targetId) {
|
|
9713
|
+
const source = await fetchNodeKind(ctx, sourceId, options);
|
|
9714
|
+
if (source === void 0) return void 0;
|
|
9715
|
+
return { nodes: [source], depth: 0 };
|
|
9716
|
+
}
|
|
9717
|
+
const cte = buildReachableCte({
|
|
9718
|
+
graphId: ctx.graphId,
|
|
9719
|
+
sourceId,
|
|
9720
|
+
edgeKinds: options.edges,
|
|
9721
|
+
maxHops,
|
|
9722
|
+
direction: options.direction ?? "out",
|
|
9723
|
+
cyclePolicy: options.cyclePolicy ?? "prevent",
|
|
9724
|
+
includePath: true,
|
|
9725
|
+
...resolveTemporalOptions(ctx, options),
|
|
9726
|
+
dialect: ctx.dialect,
|
|
9727
|
+
schema: ctx.schema
|
|
9728
|
+
});
|
|
9729
|
+
const pathContainsCheck = drizzleOrm.sql`NOT (${ctx.dialect.cycleCheck(drizzleOrm.sql.raw("r.id"), drizzleOrm.sql.raw("h.path"))})`;
|
|
9730
|
+
const query = drizzleOrm.sql`${cte}, hit AS (SELECT id, kind, depth, path FROM reachable WHERE id = ${targetId} ORDER BY depth ASC LIMIT 1) SELECT h.id AS hit_id, h.kind AS hit_kind, h.depth AS hit_depth, h.path AS hit_path, r.id AS node_id, r.kind AS node_kind FROM hit h LEFT JOIN reachable r ON ${pathContainsCheck}`;
|
|
9731
|
+
const rows = await ctx.backend.execute(query);
|
|
9732
|
+
const first = rows[0];
|
|
9733
|
+
if (first === void 0) return void 0;
|
|
9734
|
+
const pathIds = normalizePath(first.hit_path);
|
|
9735
|
+
const kindById = buildKindIndex(rows);
|
|
9736
|
+
const pathNodes = pathIds.map((id) => ({
|
|
9737
|
+
id,
|
|
9738
|
+
kind: kindById.get(id) ?? ""
|
|
9739
|
+
}));
|
|
9740
|
+
return { nodes: pathNodes, depth: Number(first.hit_depth) };
|
|
9741
|
+
}
|
|
9742
|
+
function buildKindIndex(rows) {
|
|
9743
|
+
const map = /* @__PURE__ */ new Map();
|
|
9744
|
+
for (const row of rows) {
|
|
9745
|
+
if (row.node_id !== void 0 && row.node_kind !== void 0) {
|
|
9746
|
+
map.set(row.node_id, row.node_kind);
|
|
9747
|
+
}
|
|
9748
|
+
}
|
|
9749
|
+
return map;
|
|
9750
|
+
}
|
|
9751
|
+
async function fetchNodeKind(ctx, nodeId, options) {
|
|
9752
|
+
const temporalFilter = resolveTemporalFilter(ctx, options);
|
|
9753
|
+
const query = drizzleOrm.sql`SELECT id, kind FROM ${ctx.schema.nodesTable} WHERE graph_id = ${ctx.graphId} AND id = ${nodeId} AND ${temporalFilter} LIMIT 1`;
|
|
9754
|
+
const rows = await ctx.backend.execute(query);
|
|
9755
|
+
const row = rows[0];
|
|
9756
|
+
if (row === void 0) return void 0;
|
|
9757
|
+
return { id: row.id, kind: row.kind };
|
|
9758
|
+
}
|
|
9759
|
+
|
|
9760
|
+
// src/store/algorithms/index.ts
|
|
9761
|
+
function resolveNodeId(value) {
|
|
9762
|
+
return typeof value === "string" ? value : value.id;
|
|
9763
|
+
}
|
|
9764
|
+
function createGraphAlgorithms(params) {
|
|
9765
|
+
const ctx = {
|
|
9766
|
+
graphId: params.graphId,
|
|
9767
|
+
backend: params.backend,
|
|
9768
|
+
dialect: chunkFQCQM3KG_cjs.getDialect(params.backend.dialect),
|
|
9769
|
+
schema: params.schema ?? (params.backend.tableNames ? createSqlSchema(params.backend.tableNames) : createSqlSchema()),
|
|
9770
|
+
defaultTemporalMode: params.defaultTemporalMode
|
|
9771
|
+
};
|
|
9772
|
+
return {
|
|
9773
|
+
shortestPath(from, to, options) {
|
|
9774
|
+
return executeShortestPath(
|
|
9775
|
+
ctx,
|
|
9776
|
+
resolveNodeId(from),
|
|
9777
|
+
resolveNodeId(to),
|
|
9778
|
+
options
|
|
9779
|
+
);
|
|
9780
|
+
},
|
|
9781
|
+
reachable(from, options) {
|
|
9782
|
+
return executeReachable(ctx, resolveNodeId(from), options);
|
|
9783
|
+
},
|
|
9784
|
+
canReach(from, to, options) {
|
|
9785
|
+
return executeCanReach(
|
|
9786
|
+
ctx,
|
|
9787
|
+
resolveNodeId(from),
|
|
9788
|
+
resolveNodeId(to),
|
|
9789
|
+
options
|
|
9790
|
+
);
|
|
9791
|
+
},
|
|
9792
|
+
neighbors(node, options) {
|
|
9793
|
+
return executeNeighbors(ctx, resolveNodeId(node), options);
|
|
9794
|
+
},
|
|
9795
|
+
degree(node, options) {
|
|
9796
|
+
return executeDegree(ctx, resolveNodeId(node), options ?? {});
|
|
9797
|
+
}
|
|
9798
|
+
};
|
|
9799
|
+
}
|
|
9800
|
+
|
|
8923
9801
|
// src/store/collections/edge-collection.ts
|
|
8924
9802
|
function narrowEdge(edge) {
|
|
8925
9803
|
return edge;
|
|
@@ -8992,20 +9870,13 @@ function createEdgeCollection(config) {
|
|
|
8992
9870
|
const result = {};
|
|
8993
9871
|
if (options?.matchOn !== void 0)
|
|
8994
9872
|
result.matchOn = options.matchOn;
|
|
8995
|
-
if (options?.props !== void 0)
|
|
8996
|
-
result.props = options.props;
|
|
9873
|
+
if (options?.props !== void 0) result.props = options.props;
|
|
8997
9874
|
return result;
|
|
8998
9875
|
}
|
|
8999
9876
|
return {
|
|
9000
9877
|
async create(from, to, props, options) {
|
|
9001
9878
|
const result = await executeEdgeCreate2(
|
|
9002
|
-
buildCreateEdgeInput(
|
|
9003
|
-
kind,
|
|
9004
|
-
from,
|
|
9005
|
-
to,
|
|
9006
|
-
props ?? {},
|
|
9007
|
-
options
|
|
9008
|
-
),
|
|
9879
|
+
buildCreateEdgeInput(kind, from, to, props ?? {}, options),
|
|
9009
9880
|
backend
|
|
9010
9881
|
);
|
|
9011
9882
|
return narrowEdge(result);
|
|
@@ -9100,7 +9971,7 @@ function createEdgeCollection(config) {
|
|
|
9100
9971
|
temporalMode: mode
|
|
9101
9972
|
};
|
|
9102
9973
|
if (mode === "current" || mode === "asOf") {
|
|
9103
|
-
params.asOf = options?.asOf ??
|
|
9974
|
+
params.asOf = options?.asOf ?? chunkWRWVWKQL_cjs.nowIso();
|
|
9104
9975
|
}
|
|
9105
9976
|
if (options?.from?.kind !== void 0)
|
|
9106
9977
|
params.fromKind = options.from.kind;
|
|
@@ -9121,7 +9992,7 @@ function createEdgeCollection(config) {
|
|
|
9121
9992
|
temporalMode: mode
|
|
9122
9993
|
};
|
|
9123
9994
|
if (mode === "current" || mode === "asOf") {
|
|
9124
|
-
params.asOf = options?.asOf ??
|
|
9995
|
+
params.asOf = options?.asOf ?? chunkWRWVWKQL_cjs.nowIso();
|
|
9125
9996
|
}
|
|
9126
9997
|
if (options?.from?.kind !== void 0)
|
|
9127
9998
|
params.fromKind = options.from.kind;
|
|
@@ -9131,10 +10002,7 @@ function createEdgeCollection(config) {
|
|
|
9131
10002
|
return backend.countEdgesByKind(params);
|
|
9132
10003
|
},
|
|
9133
10004
|
async bulkCreate(items) {
|
|
9134
|
-
const batchInputs = mapBulkEdgeInputs(
|
|
9135
|
-
kind,
|
|
9136
|
-
items
|
|
9137
|
-
);
|
|
10005
|
+
const batchInputs = mapBulkEdgeInputs(kind, items);
|
|
9138
10006
|
if (backend.capabilities.transactions && "transaction" in backend) {
|
|
9139
10007
|
const results2 = await backend.transaction(
|
|
9140
10008
|
async (txBackend) => executeEdgeCreateBatch2(batchInputs, txBackend)
|
|
@@ -9170,11 +10038,7 @@ function createEdgeCollection(config) {
|
|
|
9170
10038
|
if (existing) {
|
|
9171
10039
|
toUpdate.push({
|
|
9172
10040
|
index: itemIndex,
|
|
9173
|
-
input: buildUpdateEdgeInput(
|
|
9174
|
-
item.id,
|
|
9175
|
-
item.props ?? {},
|
|
9176
|
-
item
|
|
9177
|
-
),
|
|
10041
|
+
input: buildUpdateEdgeInput(item.id, item.props ?? {}, item),
|
|
9178
10042
|
clearDeleted: existing.deleted_at !== void 0
|
|
9179
10043
|
});
|
|
9180
10044
|
} else {
|
|
@@ -9213,10 +10077,7 @@ function createEdgeCollection(config) {
|
|
|
9213
10077
|
return upsertAll(backend);
|
|
9214
10078
|
},
|
|
9215
10079
|
async bulkInsert(items) {
|
|
9216
|
-
const batchInputs = mapBulkEdgeInputs(
|
|
9217
|
-
kind,
|
|
9218
|
-
items
|
|
9219
|
-
);
|
|
10080
|
+
const batchInputs = mapBulkEdgeInputs(kind, items);
|
|
9220
10081
|
if (backend.capabilities.transactions && "transaction" in backend) {
|
|
9221
10082
|
await backend.transaction(async (txBackend) => {
|
|
9222
10083
|
await executeEdgeCreateNoReturnBatch2(batchInputs, txBackend);
|
|
@@ -9367,11 +10228,7 @@ function createNodeCollection(config) {
|
|
|
9367
10228
|
async getByIds(ids, options) {
|
|
9368
10229
|
if (ids.length === 0) return [];
|
|
9369
10230
|
if (backend.getNodes !== void 0) {
|
|
9370
|
-
const rows = await backend.getNodes(
|
|
9371
|
-
graphId,
|
|
9372
|
-
kind,
|
|
9373
|
-
ids
|
|
9374
|
-
);
|
|
10231
|
+
const rows = await backend.getNodes(graphId, kind, ids);
|
|
9375
10232
|
const rowMap = /* @__PURE__ */ new Map();
|
|
9376
10233
|
for (const row of rows) {
|
|
9377
10234
|
rowMap.set(row.id, row);
|
|
@@ -9416,7 +10273,7 @@ function createNodeCollection(config) {
|
|
|
9416
10273
|
const mode2 = options.temporalMode ?? defaultTemporalMode;
|
|
9417
10274
|
let query = createQuery().from(kind, "_n").temporal(
|
|
9418
10275
|
mode2,
|
|
9419
|
-
mode2 === "asOf" ? options.asOf ??
|
|
10276
|
+
mode2 === "asOf" ? options.asOf ?? chunkWRWVWKQL_cjs.nowIso() : void 0
|
|
9420
10277
|
).whereNode("_n", options.where).select((ctx) => ctx._n);
|
|
9421
10278
|
if (options.limit !== void 0) query = query.limit(options.limit);
|
|
9422
10279
|
if (options.offset !== void 0) query = query.offset(options.offset);
|
|
@@ -9431,7 +10288,7 @@ function createNodeCollection(config) {
|
|
|
9431
10288
|
temporalMode: mode
|
|
9432
10289
|
};
|
|
9433
10290
|
if (mode === "current" || mode === "asOf") {
|
|
9434
|
-
params.asOf = options?.asOf ??
|
|
10291
|
+
params.asOf = options?.asOf ?? chunkWRWVWKQL_cjs.nowIso();
|
|
9435
10292
|
}
|
|
9436
10293
|
if (options?.limit !== void 0) params.limit = options.limit;
|
|
9437
10294
|
if (options?.offset !== void 0) params.offset = options.offset;
|
|
@@ -9447,16 +10304,12 @@ function createNodeCollection(config) {
|
|
|
9447
10304
|
temporalMode: mode
|
|
9448
10305
|
};
|
|
9449
10306
|
if (mode === "current" || mode === "asOf") {
|
|
9450
|
-
params.asOf = options?.asOf ??
|
|
10307
|
+
params.asOf = options?.asOf ?? chunkWRWVWKQL_cjs.nowIso();
|
|
9451
10308
|
}
|
|
9452
10309
|
return backend.countNodesByKind(params);
|
|
9453
10310
|
},
|
|
9454
10311
|
async upsertById(id, props, options) {
|
|
9455
|
-
return this.upsertByIdFromRecord(
|
|
9456
|
-
id,
|
|
9457
|
-
props,
|
|
9458
|
-
options
|
|
9459
|
-
);
|
|
10312
|
+
return this.upsertByIdFromRecord(id, props, options);
|
|
9460
10313
|
},
|
|
9461
10314
|
async upsertByIdFromRecord(id, data, options) {
|
|
9462
10315
|
const existing = await backend.getNode(graphId, kind, id);
|
|
@@ -9476,10 +10329,7 @@ function createNodeCollection(config) {
|
|
|
9476
10329
|
return narrowNode(result);
|
|
9477
10330
|
},
|
|
9478
10331
|
async bulkCreate(items) {
|
|
9479
|
-
const batchInputs = mapBulkNodeInputs(
|
|
9480
|
-
kind,
|
|
9481
|
-
items
|
|
9482
|
-
);
|
|
10332
|
+
const batchInputs = mapBulkNodeInputs(kind, items);
|
|
9483
10333
|
if (backend.capabilities.transactions && "transaction" in backend) {
|
|
9484
10334
|
const results2 = await backend.transaction(
|
|
9485
10335
|
async (txBackend) => executeNodeCreateBatch2(batchInputs, txBackend)
|
|
@@ -9502,11 +10352,7 @@ function createNodeCollection(config) {
|
|
|
9502
10352
|
if (row !== void 0) existingMap.set(row.id, row);
|
|
9503
10353
|
}
|
|
9504
10354
|
} else {
|
|
9505
|
-
const rows = await target.getNodes(
|
|
9506
|
-
graphId,
|
|
9507
|
-
kind,
|
|
9508
|
-
ids
|
|
9509
|
-
);
|
|
10355
|
+
const rows = await target.getNodes(graphId, kind, ids);
|
|
9510
10356
|
for (const row of rows) {
|
|
9511
10357
|
existingMap.set(row.id, row);
|
|
9512
10358
|
}
|
|
@@ -9519,22 +10365,13 @@ function createNodeCollection(config) {
|
|
|
9519
10365
|
if (existing) {
|
|
9520
10366
|
toUpdate.push({
|
|
9521
10367
|
index: itemIndex,
|
|
9522
|
-
input: buildUpdateInput(
|
|
9523
|
-
kind,
|
|
9524
|
-
item.id,
|
|
9525
|
-
item.props,
|
|
9526
|
-
item
|
|
9527
|
-
),
|
|
10368
|
+
input: buildUpdateInput(kind, item.id, item.props, item),
|
|
9528
10369
|
clearDeleted: existing.deleted_at !== void 0
|
|
9529
10370
|
});
|
|
9530
10371
|
} else {
|
|
9531
10372
|
toCreate.push({
|
|
9532
10373
|
index: itemIndex,
|
|
9533
|
-
input: buildCreateInput(
|
|
9534
|
-
kind,
|
|
9535
|
-
item.props,
|
|
9536
|
-
item
|
|
9537
|
-
)
|
|
10374
|
+
input: buildCreateInput(kind, item.props, item)
|
|
9538
10375
|
});
|
|
9539
10376
|
}
|
|
9540
10377
|
itemIndex++;
|
|
@@ -9561,10 +10398,7 @@ function createNodeCollection(config) {
|
|
|
9561
10398
|
return upsertAll(backend);
|
|
9562
10399
|
},
|
|
9563
10400
|
async bulkInsert(items) {
|
|
9564
|
-
const batchInputs = mapBulkNodeInputs(
|
|
9565
|
-
kind,
|
|
9566
|
-
items
|
|
9567
|
-
);
|
|
10401
|
+
const batchInputs = mapBulkNodeInputs(kind, items);
|
|
9568
10402
|
if (backend.capabilities.transactions && "transaction" in backend) {
|
|
9569
10403
|
await backend.transaction(async (txBackend) => {
|
|
9570
10404
|
await executeNodeCreateNoReturnBatch2(batchInputs, txBackend);
|
|
@@ -10097,12 +10931,12 @@ async function validateAndPrepareEdgeCreate(ctx, input, id, backend) {
|
|
|
10097
10931
|
nodeId: input.toId
|
|
10098
10932
|
});
|
|
10099
10933
|
}
|
|
10100
|
-
const validatedProps =
|
|
10934
|
+
const validatedProps = chunkHRNQ3DVJ_cjs.validateEdgeProps(edgeKind.schema, input.props, {
|
|
10101
10935
|
kind,
|
|
10102
10936
|
operation: "create"
|
|
10103
10937
|
});
|
|
10104
|
-
const validFrom =
|
|
10105
|
-
const validTo =
|
|
10938
|
+
const validFrom = chunkWRWVWKQL_cjs.validateOptionalIsoDate(input.validFrom, "validFrom");
|
|
10939
|
+
const validTo = chunkWRWVWKQL_cjs.validateOptionalIsoDate(input.validTo, "validTo");
|
|
10106
10940
|
const cardinality = registration.cardinality ?? "many";
|
|
10107
10941
|
const constraintContext = {
|
|
10108
10942
|
graphId: ctx.graphId,
|
|
@@ -10247,12 +11081,12 @@ async function executeEdgeUpdate(ctx, input, backend) {
|
|
|
10247
11081
|
const edgeKind = registration.type;
|
|
10248
11082
|
const existingProps = JSON.parse(existing.props);
|
|
10249
11083
|
const mergedProps = { ...existingProps, ...input.props };
|
|
10250
|
-
const validatedProps =
|
|
11084
|
+
const validatedProps = chunkHRNQ3DVJ_cjs.validateEdgeProps(edgeKind.schema, mergedProps, {
|
|
10251
11085
|
kind: existing.kind,
|
|
10252
11086
|
operation: "update",
|
|
10253
11087
|
id
|
|
10254
11088
|
});
|
|
10255
|
-
const validTo =
|
|
11089
|
+
const validTo = chunkWRWVWKQL_cjs.validateOptionalIsoDate(input.validTo, "validTo");
|
|
10256
11090
|
const updateParams = {
|
|
10257
11091
|
graphId: ctx.graphId,
|
|
10258
11092
|
id,
|
|
@@ -10273,12 +11107,12 @@ async function executeEdgeUpsertUpdate(ctx, input, backend, options) {
|
|
|
10273
11107
|
const edgeKind = registration.type;
|
|
10274
11108
|
const existingProps = JSON.parse(existing.props);
|
|
10275
11109
|
const mergedProps = { ...existingProps, ...input.props };
|
|
10276
|
-
const validatedProps =
|
|
11110
|
+
const validatedProps = chunkHRNQ3DVJ_cjs.validateEdgeProps(edgeKind.schema, mergedProps, {
|
|
10277
11111
|
kind: existing.kind,
|
|
10278
11112
|
operation: "update",
|
|
10279
11113
|
id
|
|
10280
11114
|
});
|
|
10281
|
-
const validTo =
|
|
11115
|
+
const validTo = chunkWRWVWKQL_cjs.validateOptionalIsoDate(input.validTo, "validTo");
|
|
10282
11116
|
const updateParams = {
|
|
10283
11117
|
graphId: ctx.graphId,
|
|
10284
11118
|
id,
|
|
@@ -10430,7 +11264,7 @@ async function executeEdgeGetOrCreateByEndpoints(ctx, kind, fromKind, fromId, to
|
|
|
10430
11264
|
const matchOn = options?.matchOn ?? [];
|
|
10431
11265
|
const registration = getEdgeRegistration(ctx.graph, kind);
|
|
10432
11266
|
const edgeKind = registration.type;
|
|
10433
|
-
const validatedProps =
|
|
11267
|
+
const validatedProps = chunkHRNQ3DVJ_cjs.validateEdgeProps(edgeKind.schema, props, {
|
|
10434
11268
|
kind,
|
|
10435
11269
|
operation: "create"
|
|
10436
11270
|
});
|
|
@@ -10512,7 +11346,7 @@ async function executeEdgeBulkGetOrCreateByEndpoints(ctx, kind, items, backend,
|
|
|
10512
11346
|
validateMatchOnFields(edgeKind.schema, matchOn, kind);
|
|
10513
11347
|
const validated = [];
|
|
10514
11348
|
for (const item of items) {
|
|
10515
|
-
const validatedProps =
|
|
11349
|
+
const validatedProps = chunkHRNQ3DVJ_cjs.validateEdgeProps(edgeKind.schema, item.props, {
|
|
10516
11350
|
kind,
|
|
10517
11351
|
operation: "create"
|
|
10518
11352
|
});
|
|
@@ -10670,15 +11504,19 @@ async function executeEdgeBulkGetOrCreateByEndpoints(ctx, kind, items, backend,
|
|
|
10670
11504
|
}
|
|
10671
11505
|
|
|
10672
11506
|
// src/store/embedding-sync.ts
|
|
11507
|
+
var embeddingFieldsCache = /* @__PURE__ */ new WeakMap();
|
|
10673
11508
|
function getEmbeddingFields(schema) {
|
|
10674
|
-
|
|
10675
|
-
|
|
10676
|
-
|
|
11509
|
+
const cached = embeddingFieldsCache.get(schema);
|
|
11510
|
+
if (cached) return cached;
|
|
11511
|
+
const fields = computeEmbeddingFields(schema);
|
|
11512
|
+
embeddingFieldsCache.set(schema, fields);
|
|
11513
|
+
return fields;
|
|
11514
|
+
}
|
|
11515
|
+
function computeEmbeddingFields(schema) {
|
|
11516
|
+
if (schema.type !== "object") return [];
|
|
10677
11517
|
const def = schema.def;
|
|
10678
11518
|
const shape = def.shape;
|
|
10679
|
-
if (!shape)
|
|
10680
|
-
return [];
|
|
10681
|
-
}
|
|
11519
|
+
if (!shape) return [];
|
|
10682
11520
|
const fields = [];
|
|
10683
11521
|
for (const [fieldPath, fieldSchema] of Object.entries(shape)) {
|
|
10684
11522
|
const dimensions = getEmbeddingDimensionsFromField(fieldSchema);
|
|
@@ -10689,13 +11527,13 @@ function getEmbeddingFields(schema) {
|
|
|
10689
11527
|
return fields;
|
|
10690
11528
|
}
|
|
10691
11529
|
function getEmbeddingDimensionsFromField(schema) {
|
|
10692
|
-
const directDimensions =
|
|
11530
|
+
const directDimensions = chunkUVZHTBZ5_cjs.getEmbeddingDimensions(schema);
|
|
10693
11531
|
if (directDimensions !== void 0) {
|
|
10694
11532
|
return directDimensions;
|
|
10695
11533
|
}
|
|
10696
11534
|
const unwrapped = unwrapToEmbedding(schema);
|
|
10697
11535
|
if (unwrapped) {
|
|
10698
|
-
return
|
|
11536
|
+
return chunkUVZHTBZ5_cjs.getEmbeddingDimensions(unwrapped);
|
|
10699
11537
|
}
|
|
10700
11538
|
return void 0;
|
|
10701
11539
|
}
|
|
@@ -10703,7 +11541,7 @@ function unwrapToEmbedding(schema) {
|
|
|
10703
11541
|
const type = schema.type;
|
|
10704
11542
|
const def = schema.def;
|
|
10705
11543
|
if ((type === "optional" || type === "nullable" || type === "default" || type === "readonly") && def.innerType) {
|
|
10706
|
-
if (
|
|
11544
|
+
if (chunkUVZHTBZ5_cjs.isEmbeddingSchema(def.innerType)) {
|
|
10707
11545
|
return def.innerType;
|
|
10708
11546
|
}
|
|
10709
11547
|
return unwrapToEmbedding(def.innerType);
|
|
@@ -10765,6 +11603,101 @@ function isValidEmbeddingValue(value) {
|
|
|
10765
11603
|
return value.every((n) => typeof n === "number" && Number.isFinite(n));
|
|
10766
11604
|
}
|
|
10767
11605
|
|
|
11606
|
+
// src/store/fulltext-sync.ts
|
|
11607
|
+
var searchableFieldsCache = /* @__PURE__ */ new WeakMap();
|
|
11608
|
+
function getSearchableFields(schema) {
|
|
11609
|
+
const cached = searchableFieldsCache.get(schema);
|
|
11610
|
+
if (cached) return cached;
|
|
11611
|
+
const fields = computeSearchableFields(schema);
|
|
11612
|
+
searchableFieldsCache.set(schema, fields);
|
|
11613
|
+
return fields;
|
|
11614
|
+
}
|
|
11615
|
+
function computeSearchableFields(schema) {
|
|
11616
|
+
if (schema.type !== "object") return [];
|
|
11617
|
+
const def = schema.def;
|
|
11618
|
+
const shape = def.shape;
|
|
11619
|
+
if (!shape) return [];
|
|
11620
|
+
const fields = [];
|
|
11621
|
+
for (const [fieldPath, fieldSchema] of Object.entries(shape)) {
|
|
11622
|
+
const metadata = chunkUVZHTBZ5_cjs.getSearchableMetadata(fieldSchema);
|
|
11623
|
+
if (metadata !== void 0) {
|
|
11624
|
+
fields.push({ fieldPath, metadata });
|
|
11625
|
+
}
|
|
11626
|
+
}
|
|
11627
|
+
warnIfConflictingLanguages(fields);
|
|
11628
|
+
return fields;
|
|
11629
|
+
}
|
|
11630
|
+
function warnIfConflictingLanguages(fields) {
|
|
11631
|
+
if (fields.length < 2) return;
|
|
11632
|
+
const languages = new Set(fields.map((field2) => field2.metadata.language));
|
|
11633
|
+
if (languages.size < 2) return;
|
|
11634
|
+
if (typeof console === "undefined" || typeof console.warn !== "function") {
|
|
11635
|
+
return;
|
|
11636
|
+
}
|
|
11637
|
+
const fieldSummary = fields.map((field2) => `${field2.fieldPath}=${field2.metadata.language}`).join(", ");
|
|
11638
|
+
const winning = fields[0]?.metadata.language ?? chunkUVZHTBZ5_cjs.DEFAULT_SEARCHABLE_LANGUAGE;
|
|
11639
|
+
console.warn(
|
|
11640
|
+
`[typegraph] searchable() fields declare conflicting languages (${fieldSummary}). The first field's language ("${winning}") is recorded on the fulltext row; true multilingual indexing requires a dedicated node kind per language.`
|
|
11641
|
+
);
|
|
11642
|
+
}
|
|
11643
|
+
var FIELD_SEPARATOR = "\n";
|
|
11644
|
+
function resolveCombinedLanguage(fields) {
|
|
11645
|
+
return fields[0]?.metadata.language ?? chunkUVZHTBZ5_cjs.DEFAULT_SEARCHABLE_LANGUAGE;
|
|
11646
|
+
}
|
|
11647
|
+
function computeFulltextContent(schema, props) {
|
|
11648
|
+
const searchableFields = getSearchableFields(schema);
|
|
11649
|
+
if (searchableFields.length === 0) return void 0;
|
|
11650
|
+
const parts = [];
|
|
11651
|
+
for (const field2 of searchableFields) {
|
|
11652
|
+
const value = props[field2.fieldPath];
|
|
11653
|
+
if (typeof value === "string" && value.length > 0) {
|
|
11654
|
+
parts.push(value);
|
|
11655
|
+
}
|
|
11656
|
+
}
|
|
11657
|
+
if (parts.length === 0) return void 0;
|
|
11658
|
+
return {
|
|
11659
|
+
content: parts.join(FIELD_SEPARATOR),
|
|
11660
|
+
language: resolveCombinedLanguage(searchableFields)
|
|
11661
|
+
};
|
|
11662
|
+
}
|
|
11663
|
+
async function syncFulltext(ctx, schema, props) {
|
|
11664
|
+
const { backend } = ctx;
|
|
11665
|
+
if (!backend.upsertFulltext || !backend.deleteFulltext) {
|
|
11666
|
+
return;
|
|
11667
|
+
}
|
|
11668
|
+
const computed = computeFulltextContent(schema, props);
|
|
11669
|
+
if (computed !== void 0) {
|
|
11670
|
+
await backend.upsertFulltext({
|
|
11671
|
+
graphId: ctx.graphId,
|
|
11672
|
+
nodeKind: ctx.nodeKind,
|
|
11673
|
+
nodeId: ctx.nodeId,
|
|
11674
|
+
content: computed.content,
|
|
11675
|
+
language: computed.language
|
|
11676
|
+
});
|
|
11677
|
+
return;
|
|
11678
|
+
}
|
|
11679
|
+
if (getSearchableFields(schema).length === 0) return;
|
|
11680
|
+
await backend.deleteFulltext({
|
|
11681
|
+
graphId: ctx.graphId,
|
|
11682
|
+
nodeKind: ctx.nodeKind,
|
|
11683
|
+
nodeId: ctx.nodeId
|
|
11684
|
+
});
|
|
11685
|
+
}
|
|
11686
|
+
async function deleteNodeFulltext(ctx, schema) {
|
|
11687
|
+
const { backend } = ctx;
|
|
11688
|
+
if (!backend.deleteFulltext) {
|
|
11689
|
+
return;
|
|
11690
|
+
}
|
|
11691
|
+
if (getSearchableFields(schema).length === 0) {
|
|
11692
|
+
return;
|
|
11693
|
+
}
|
|
11694
|
+
await backend.deleteFulltext({
|
|
11695
|
+
graphId: ctx.graphId,
|
|
11696
|
+
nodeKind: ctx.nodeKind,
|
|
11697
|
+
nodeId: ctx.nodeId
|
|
11698
|
+
});
|
|
11699
|
+
}
|
|
11700
|
+
|
|
10768
11701
|
// src/store/uniqueness.ts
|
|
10769
11702
|
async function checkUniquenessConstraints(ctx, kind, id, props, constraints) {
|
|
10770
11703
|
for (const constraint of constraints) {
|
|
@@ -10958,6 +11891,9 @@ function createUniquenessContext(graphId, registry, backend) {
|
|
|
10958
11891
|
function createEmbeddingSyncContext(graphId, nodeKind, nodeId, backend) {
|
|
10959
11892
|
return { graphId, nodeKind, nodeId, backend };
|
|
10960
11893
|
}
|
|
11894
|
+
function createFulltextSyncContext(graphId, nodeKind, nodeId, backend) {
|
|
11895
|
+
return { graphId, nodeKind, nodeId, backend };
|
|
11896
|
+
}
|
|
10961
11897
|
function createNodeBatchValidationBackend(graphId, registry, backend) {
|
|
10962
11898
|
const nodeCache = /* @__PURE__ */ new Map();
|
|
10963
11899
|
const pendingNodes = /* @__PURE__ */ new Map();
|
|
@@ -11057,12 +11993,12 @@ async function validateAndPrepareNodeCreate(ctx, input, id, backend) {
|
|
|
11057
11993
|
const kind = input.kind;
|
|
11058
11994
|
const registration = getNodeRegistration(ctx.graph, kind);
|
|
11059
11995
|
const nodeKind = registration.type;
|
|
11060
|
-
const validatedProps =
|
|
11996
|
+
const validatedProps = chunkHRNQ3DVJ_cjs.validateNodeProps(nodeKind.schema, input.props, {
|
|
11061
11997
|
kind,
|
|
11062
11998
|
operation: "create"
|
|
11063
11999
|
});
|
|
11064
|
-
const validFrom =
|
|
11065
|
-
const validTo =
|
|
12000
|
+
const validFrom = chunkWRWVWKQL_cjs.validateOptionalIsoDate(input.validFrom, "validFrom");
|
|
12001
|
+
const validTo = chunkWRWVWKQL_cjs.validateOptionalIsoDate(input.validTo, "validTo");
|
|
11066
12002
|
const existingNode = await backend.getNode(ctx.graphId, kind, id);
|
|
11067
12003
|
if (existingNode && !existingNode.deleted_at) {
|
|
11068
12004
|
throw createNodeAlreadyExistsError(kind, id);
|
|
@@ -11115,6 +12051,11 @@ async function finalizeNodeCreate(ctx, prepared, backend) {
|
|
|
11115
12051
|
prepared.nodeKind.schema,
|
|
11116
12052
|
prepared.validatedProps
|
|
11117
12053
|
);
|
|
12054
|
+
await syncFulltext(
|
|
12055
|
+
createFulltextSyncContext(ctx.graphId, prepared.kind, prepared.id, backend),
|
|
12056
|
+
prepared.nodeKind.schema,
|
|
12057
|
+
prepared.validatedProps
|
|
12058
|
+
);
|
|
11118
12059
|
}
|
|
11119
12060
|
async function performNodeUpdate(ctx, input, backend, options) {
|
|
11120
12061
|
const { kind, id } = input;
|
|
@@ -11126,12 +12067,12 @@ async function performNodeUpdate(ctx, input, backend, options) {
|
|
|
11126
12067
|
const existingProps = JSON.parse(existing.props);
|
|
11127
12068
|
const mergedProps = { ...existingProps, ...input.props };
|
|
11128
12069
|
const nodeKind = registration.type;
|
|
11129
|
-
const validatedProps =
|
|
12070
|
+
const validatedProps = chunkHRNQ3DVJ_cjs.validateNodeProps(nodeKind.schema, mergedProps, {
|
|
11130
12071
|
kind,
|
|
11131
12072
|
operation: "update",
|
|
11132
12073
|
id
|
|
11133
12074
|
});
|
|
11134
|
-
const validTo =
|
|
12075
|
+
const validTo = chunkWRWVWKQL_cjs.validateOptionalIsoDate(input.validTo, "validTo");
|
|
11135
12076
|
await updateUniquenessEntries(
|
|
11136
12077
|
createUniquenessContext(ctx.graphId, ctx.registry, backend),
|
|
11137
12078
|
kind,
|
|
@@ -11155,6 +12096,11 @@ async function performNodeUpdate(ctx, input, backend, options) {
|
|
|
11155
12096
|
nodeKind.schema,
|
|
11156
12097
|
validatedProps
|
|
11157
12098
|
);
|
|
12099
|
+
await syncFulltext(
|
|
12100
|
+
createFulltextSyncContext(ctx.graphId, kind, id, backend),
|
|
12101
|
+
nodeKind.schema,
|
|
12102
|
+
validatedProps
|
|
12103
|
+
);
|
|
11158
12104
|
return rowToNode(row);
|
|
11159
12105
|
}
|
|
11160
12106
|
async function enforceDeleteBehavior(ctx, kind, id, mode, backend, registration) {
|
|
@@ -11371,6 +12317,10 @@ async function executeNodeDelete(ctx, kind, id, backend) {
|
|
|
11371
12317
|
createEmbeddingSyncContext(ctx.graphId, kind, id, backend),
|
|
11372
12318
|
nodeKind.schema
|
|
11373
12319
|
);
|
|
12320
|
+
await deleteNodeFulltext(
|
|
12321
|
+
createFulltextSyncContext(ctx.graphId, kind, id, backend),
|
|
12322
|
+
nodeKind.schema
|
|
12323
|
+
);
|
|
11374
12324
|
});
|
|
11375
12325
|
}
|
|
11376
12326
|
async function executeNodeHardDelete(ctx, kind, id, backend) {
|
|
@@ -11390,7 +12340,7 @@ async function executeNodeGetOrCreateByConstraint(ctx, kind, constraintName, pro
|
|
|
11390
12340
|
const ifExists = options?.ifExists ?? "return";
|
|
11391
12341
|
const registration = getNodeRegistration(ctx.graph, kind);
|
|
11392
12342
|
const nodeKind = registration.type;
|
|
11393
|
-
const validatedProps =
|
|
12343
|
+
const validatedProps = chunkHRNQ3DVJ_cjs.validateNodeProps(nodeKind.schema, props, {
|
|
11394
12344
|
kind,
|
|
11395
12345
|
operation: "create"
|
|
11396
12346
|
});
|
|
@@ -11462,7 +12412,7 @@ async function executeNodeGetOrCreateByConstraint(ctx, kind, constraintName, pro
|
|
|
11462
12412
|
async function executeNodeFindByConstraint(ctx, kind, constraintName, props, backend) {
|
|
11463
12413
|
const registration = getNodeRegistration(ctx.graph, kind);
|
|
11464
12414
|
const nodeKind = registration.type;
|
|
11465
|
-
const validatedProps =
|
|
12415
|
+
const validatedProps = chunkHRNQ3DVJ_cjs.validateNodeProps(nodeKind.schema, props, {
|
|
11466
12416
|
kind,
|
|
11467
12417
|
operation: "create"
|
|
11468
12418
|
});
|
|
@@ -11499,7 +12449,7 @@ async function executeNodeFindByConstraint(ctx, kind, constraintName, props, bac
|
|
|
11499
12449
|
function validateAndComputeKeys(nodeKind, kind, constraint, items) {
|
|
11500
12450
|
const validated = [];
|
|
11501
12451
|
for (const item of items) {
|
|
11502
|
-
const validatedProps =
|
|
12452
|
+
const validatedProps = chunkHRNQ3DVJ_cjs.validateNodeProps(nodeKind.schema, item.props, {
|
|
11503
12453
|
kind,
|
|
11504
12454
|
operation: "create"
|
|
11505
12455
|
});
|
|
@@ -11677,6 +12627,503 @@ async function executeNodeBulkGetOrCreateByConstraint(ctx, kind, constraintName,
|
|
|
11677
12627
|
return results;
|
|
11678
12628
|
}
|
|
11679
12629
|
|
|
12630
|
+
// src/store/fulltext-rebuild.ts
|
|
12631
|
+
var DEFAULT_PAGE_SIZE = 500;
|
|
12632
|
+
var DEFAULT_MAX_SKIPPED_IDS = 1e4;
|
|
12633
|
+
function validatePageSize(value) {
|
|
12634
|
+
if (value === void 0) return DEFAULT_PAGE_SIZE;
|
|
12635
|
+
if (!Number.isInteger(value) || value <= 0) {
|
|
12636
|
+
throw new chunkO5PST6TB_cjs.ValidationError(
|
|
12637
|
+
`pageSize must be a positive integer, got: ${String(value)}`,
|
|
12638
|
+
{
|
|
12639
|
+
issues: [{ path: "pageSize", message: "Must be a positive integer." }]
|
|
12640
|
+
}
|
|
12641
|
+
);
|
|
12642
|
+
}
|
|
12643
|
+
return value;
|
|
12644
|
+
}
|
|
12645
|
+
function validateMaxSkippedIds(value) {
|
|
12646
|
+
if (value === void 0) return DEFAULT_MAX_SKIPPED_IDS;
|
|
12647
|
+
if (!Number.isInteger(value) || value < 0) {
|
|
12648
|
+
throw new chunkO5PST6TB_cjs.ValidationError(
|
|
12649
|
+
`maxSkippedIds must be a non-negative integer, got: ${String(value)}`,
|
|
12650
|
+
{
|
|
12651
|
+
issues: [
|
|
12652
|
+
{
|
|
12653
|
+
path: "maxSkippedIds",
|
|
12654
|
+
message: "Must be a non-negative integer."
|
|
12655
|
+
}
|
|
12656
|
+
]
|
|
12657
|
+
}
|
|
12658
|
+
);
|
|
12659
|
+
}
|
|
12660
|
+
return value;
|
|
12661
|
+
}
|
|
12662
|
+
function isPropsObject(value) {
|
|
12663
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
12664
|
+
}
|
|
12665
|
+
async function rebuildFulltextIndex(ctx, nodeKind, options) {
|
|
12666
|
+
const { backend, registry } = ctx;
|
|
12667
|
+
if (!backend.upsertFulltext || !backend.deleteFulltext) {
|
|
12668
|
+
throw new chunkO5PST6TB_cjs.ConfigurationError(
|
|
12669
|
+
"Backend does not support fulltext; cannot rebuild index",
|
|
12670
|
+
{ backend: backend.dialect, capability: "fulltext" }
|
|
12671
|
+
);
|
|
12672
|
+
}
|
|
12673
|
+
const pageSize = validatePageSize(options.pageSize);
|
|
12674
|
+
const maxSkippedIds = validateMaxSkippedIds(options.maxSkippedIds);
|
|
12675
|
+
const targetKinds = nodeKind === void 0 ? [...registry.nodeKinds.keys()] : [nodeKind];
|
|
12676
|
+
const rebuiltKinds = [];
|
|
12677
|
+
let processed = 0;
|
|
12678
|
+
let upserted = 0;
|
|
12679
|
+
let cleared = 0;
|
|
12680
|
+
let skipped = 0;
|
|
12681
|
+
const skippedIds = [];
|
|
12682
|
+
for (const kind of targetKinds) {
|
|
12683
|
+
const nodeType = registry.getNodeType(kind);
|
|
12684
|
+
if (!nodeType) {
|
|
12685
|
+
throw new chunkO5PST6TB_cjs.ConfigurationError(`Unknown node kind: ${kind}`, { kind });
|
|
12686
|
+
}
|
|
12687
|
+
if (getSearchableFields(nodeType.schema).length === 0) {
|
|
12688
|
+
continue;
|
|
12689
|
+
}
|
|
12690
|
+
rebuiltKinds.push(kind);
|
|
12691
|
+
let cursor;
|
|
12692
|
+
for (; ; ) {
|
|
12693
|
+
const rows = await backend.findNodesByKind({
|
|
12694
|
+
graphId: ctx.graphId,
|
|
12695
|
+
kind,
|
|
12696
|
+
limit: pageSize,
|
|
12697
|
+
excludeDeleted: false,
|
|
12698
|
+
orderBy: "id",
|
|
12699
|
+
...cursor === void 0 ? {} : { after: cursor }
|
|
12700
|
+
});
|
|
12701
|
+
if (rows.length === 0) break;
|
|
12702
|
+
const pageResult = processPage(nodeType.schema, rows);
|
|
12703
|
+
skipped += pageResult.skipped;
|
|
12704
|
+
const remaining = maxSkippedIds - skippedIds.length;
|
|
12705
|
+
if (remaining > 0 && pageResult.skippedIds.length > 0) {
|
|
12706
|
+
skippedIds.push(...pageResult.skippedIds.slice(0, remaining));
|
|
12707
|
+
}
|
|
12708
|
+
await backend.transaction(async (tx) => {
|
|
12709
|
+
if (pageResult.toUpsert.length > 0) {
|
|
12710
|
+
if (tx.upsertFulltextBatch) {
|
|
12711
|
+
await tx.upsertFulltextBatch({
|
|
12712
|
+
graphId: ctx.graphId,
|
|
12713
|
+
nodeKind: kind,
|
|
12714
|
+
rows: pageResult.toUpsert
|
|
12715
|
+
});
|
|
12716
|
+
} else if (tx.upsertFulltext) {
|
|
12717
|
+
for (const item of pageResult.toUpsert) {
|
|
12718
|
+
await tx.upsertFulltext({
|
|
12719
|
+
graphId: ctx.graphId,
|
|
12720
|
+
nodeKind: kind,
|
|
12721
|
+
nodeId: item.nodeId,
|
|
12722
|
+
content: item.content,
|
|
12723
|
+
language: item.language
|
|
12724
|
+
});
|
|
12725
|
+
}
|
|
12726
|
+
}
|
|
12727
|
+
}
|
|
12728
|
+
if (pageResult.toDelete.length > 0) {
|
|
12729
|
+
if (tx.deleteFulltextBatch) {
|
|
12730
|
+
await tx.deleteFulltextBatch({
|
|
12731
|
+
graphId: ctx.graphId,
|
|
12732
|
+
nodeKind: kind,
|
|
12733
|
+
nodeIds: pageResult.toDelete
|
|
12734
|
+
});
|
|
12735
|
+
} else if (tx.deleteFulltext) {
|
|
12736
|
+
for (const nodeId of pageResult.toDelete) {
|
|
12737
|
+
await tx.deleteFulltext({
|
|
12738
|
+
graphId: ctx.graphId,
|
|
12739
|
+
nodeKind: kind,
|
|
12740
|
+
nodeId
|
|
12741
|
+
});
|
|
12742
|
+
}
|
|
12743
|
+
}
|
|
12744
|
+
}
|
|
12745
|
+
});
|
|
12746
|
+
processed += rows.length;
|
|
12747
|
+
upserted += pageResult.toUpsert.length;
|
|
12748
|
+
cleared += pageResult.toDelete.length;
|
|
12749
|
+
const lastRow = rows.at(-1);
|
|
12750
|
+
if (!lastRow) break;
|
|
12751
|
+
cursor = lastRow.id;
|
|
12752
|
+
if (rows.length < pageSize) break;
|
|
12753
|
+
}
|
|
12754
|
+
}
|
|
12755
|
+
return {
|
|
12756
|
+
kinds: rebuiltKinds,
|
|
12757
|
+
processed,
|
|
12758
|
+
upserted,
|
|
12759
|
+
cleared,
|
|
12760
|
+
skipped,
|
|
12761
|
+
skippedIds,
|
|
12762
|
+
skippedTruncated: skipped > skippedIds.length
|
|
12763
|
+
};
|
|
12764
|
+
}
|
|
12765
|
+
function processPage(schema, rows) {
|
|
12766
|
+
const toUpsert = [];
|
|
12767
|
+
const toDelete = [];
|
|
12768
|
+
const skippedIds = [];
|
|
12769
|
+
let skipped = 0;
|
|
12770
|
+
for (const row of rows) {
|
|
12771
|
+
if (row.deleted_at !== void 0) {
|
|
12772
|
+
toDelete.push(row.id);
|
|
12773
|
+
continue;
|
|
12774
|
+
}
|
|
12775
|
+
let parsed;
|
|
12776
|
+
try {
|
|
12777
|
+
parsed = JSON.parse(row.props);
|
|
12778
|
+
} catch {
|
|
12779
|
+
skipped += 1;
|
|
12780
|
+
skippedIds.push(row.id);
|
|
12781
|
+
continue;
|
|
12782
|
+
}
|
|
12783
|
+
if (!isPropsObject(parsed)) {
|
|
12784
|
+
skipped += 1;
|
|
12785
|
+
skippedIds.push(row.id);
|
|
12786
|
+
continue;
|
|
12787
|
+
}
|
|
12788
|
+
const computed = computeFulltextContent(schema, parsed);
|
|
12789
|
+
if (computed === void 0) {
|
|
12790
|
+
toDelete.push(row.id);
|
|
12791
|
+
} else {
|
|
12792
|
+
toUpsert.push({
|
|
12793
|
+
nodeId: row.id,
|
|
12794
|
+
content: computed.content,
|
|
12795
|
+
language: computed.language
|
|
12796
|
+
});
|
|
12797
|
+
}
|
|
12798
|
+
}
|
|
12799
|
+
return { toUpsert, toDelete, skipped, skippedIds };
|
|
12800
|
+
}
|
|
12801
|
+
|
|
12802
|
+
// src/store/search.ts
|
|
12803
|
+
async function executeFulltextSearch(ctx, nodeKind, options) {
|
|
12804
|
+
const { backend, graphId } = ctx;
|
|
12805
|
+
if (!backend.fulltextSearch) {
|
|
12806
|
+
throw new chunkO5PST6TB_cjs.ConfigurationError("Backend does not support fulltext search", {
|
|
12807
|
+
backend: backend.dialect,
|
|
12808
|
+
capability: "fulltext"
|
|
12809
|
+
});
|
|
12810
|
+
}
|
|
12811
|
+
if (!Number.isInteger(options.limit) || options.limit <= 0) {
|
|
12812
|
+
throw new RangeError(
|
|
12813
|
+
`fulltextSearch.limit must be a positive integer, got: ${options.limit}`
|
|
12814
|
+
);
|
|
12815
|
+
}
|
|
12816
|
+
validateFulltextCallOptions(backend, {
|
|
12817
|
+
mode: options.mode,
|
|
12818
|
+
includeSnippets: options.includeSnippets,
|
|
12819
|
+
language: options.language
|
|
12820
|
+
});
|
|
12821
|
+
const params = {
|
|
12822
|
+
graphId,
|
|
12823
|
+
nodeKind,
|
|
12824
|
+
query: options.query,
|
|
12825
|
+
limit: options.limit,
|
|
12826
|
+
...options.mode ? { mode: options.mode } : {},
|
|
12827
|
+
...options.language ? { language: options.language } : {},
|
|
12828
|
+
...options.minScore === void 0 ? {} : { minScore: options.minScore },
|
|
12829
|
+
...options.includeSnippets === void 0 ? {} : { includeSnippets: options.includeSnippets }
|
|
12830
|
+
};
|
|
12831
|
+
const rows = await backend.fulltextSearch(params);
|
|
12832
|
+
if (rows.length === 0) return [];
|
|
12833
|
+
const ids = rows.map((r) => r.nodeId);
|
|
12834
|
+
const nodeMap = await fetchNodesByIds(backend, graphId, nodeKind, ids);
|
|
12835
|
+
const hits = [];
|
|
12836
|
+
let rank = 1;
|
|
12837
|
+
for (const row of rows) {
|
|
12838
|
+
const node = nodeMap.get(row.nodeId);
|
|
12839
|
+
if (!node) continue;
|
|
12840
|
+
hits.push({
|
|
12841
|
+
node,
|
|
12842
|
+
score: row.score,
|
|
12843
|
+
rank,
|
|
12844
|
+
...row.snippet === void 0 ? {} : { snippet: row.snippet }
|
|
12845
|
+
});
|
|
12846
|
+
rank += 1;
|
|
12847
|
+
}
|
|
12848
|
+
return hits;
|
|
12849
|
+
}
|
|
12850
|
+
async function executeHybridSearch(ctx, nodeKind, options) {
|
|
12851
|
+
const { backend, graphId } = ctx;
|
|
12852
|
+
if (!backend.vectorSearch) {
|
|
12853
|
+
throw new chunkO5PST6TB_cjs.ConfigurationError("Backend does not support vector search", {
|
|
12854
|
+
backend: backend.dialect,
|
|
12855
|
+
capability: "vector"
|
|
12856
|
+
});
|
|
12857
|
+
}
|
|
12858
|
+
if (!backend.fulltextSearch) {
|
|
12859
|
+
throw new chunkO5PST6TB_cjs.ConfigurationError("Backend does not support fulltext search", {
|
|
12860
|
+
backend: backend.dialect,
|
|
12861
|
+
capability: "fulltext"
|
|
12862
|
+
});
|
|
12863
|
+
}
|
|
12864
|
+
if (!Number.isInteger(options.limit) || options.limit <= 0) {
|
|
12865
|
+
throw new RangeError(
|
|
12866
|
+
`hybridSearch.limit must be a positive integer, got: ${options.limit}`
|
|
12867
|
+
);
|
|
12868
|
+
}
|
|
12869
|
+
if (options.fusion !== void 0) {
|
|
12870
|
+
validateHybridFusionOptions(options.fusion);
|
|
12871
|
+
}
|
|
12872
|
+
validateFulltextCallOptions(backend, {
|
|
12873
|
+
mode: options.fulltext.mode,
|
|
12874
|
+
includeSnippets: options.fulltext.includeSnippets,
|
|
12875
|
+
language: options.fulltext.language
|
|
12876
|
+
});
|
|
12877
|
+
const fusionK = options.fusion?.k ?? DEFAULT_RRF_K;
|
|
12878
|
+
const vectorWeight = options.fusion?.weights?.vector ?? DEFAULT_RRF_WEIGHT;
|
|
12879
|
+
const fulltextWeight = options.fusion?.weights?.fulltext ?? DEFAULT_RRF_WEIGHT;
|
|
12880
|
+
const overFetchMultiplier = 4;
|
|
12881
|
+
const vectorK = options.vector.k ?? options.limit * overFetchMultiplier;
|
|
12882
|
+
const fulltextK = options.fulltext.k ?? options.limit * overFetchMultiplier;
|
|
12883
|
+
const vectorPromise = backend.vectorSearch({
|
|
12884
|
+
graphId,
|
|
12885
|
+
nodeKind,
|
|
12886
|
+
fieldPath: options.vector.fieldPath,
|
|
12887
|
+
queryEmbedding: options.vector.queryEmbedding,
|
|
12888
|
+
metric: options.vector.metric ?? "cosine",
|
|
12889
|
+
limit: vectorK,
|
|
12890
|
+
...options.vector.minScore === void 0 ? {} : { minScore: options.vector.minScore }
|
|
12891
|
+
});
|
|
12892
|
+
const fulltextPromise = backend.fulltextSearch({
|
|
12893
|
+
graphId,
|
|
12894
|
+
nodeKind,
|
|
12895
|
+
query: options.fulltext.query,
|
|
12896
|
+
limit: fulltextK,
|
|
12897
|
+
...options.fulltext.mode ? { mode: options.fulltext.mode } : {},
|
|
12898
|
+
...options.fulltext.language ? { language: options.fulltext.language } : {},
|
|
12899
|
+
...options.fulltext.minScore === void 0 ? {} : { minScore: options.fulltext.minScore },
|
|
12900
|
+
...options.fulltext.includeSnippets === void 0 ? {} : { includeSnippets: options.fulltext.includeSnippets }
|
|
12901
|
+
});
|
|
12902
|
+
const [vectorRows, fulltextRows] = await Promise.all([
|
|
12903
|
+
vectorPromise,
|
|
12904
|
+
fulltextPromise
|
|
12905
|
+
]);
|
|
12906
|
+
const fused = /* @__PURE__ */ new Map();
|
|
12907
|
+
for (const [index, row] of vectorRows.entries()) {
|
|
12908
|
+
const rank2 = index + 1;
|
|
12909
|
+
const contribution = vectorWeight / (fusionK + rank2);
|
|
12910
|
+
const entry = fused.get(row.nodeId) ?? {
|
|
12911
|
+
nodeId: row.nodeId,
|
|
12912
|
+
fusedScore: 0
|
|
12913
|
+
};
|
|
12914
|
+
entry.fusedScore += contribution;
|
|
12915
|
+
entry.vectorRank = rank2;
|
|
12916
|
+
entry.vectorScore = row.score;
|
|
12917
|
+
fused.set(row.nodeId, entry);
|
|
12918
|
+
}
|
|
12919
|
+
for (const [index, row] of fulltextRows.entries()) {
|
|
12920
|
+
const rank2 = index + 1;
|
|
12921
|
+
const contribution = fulltextWeight / (fusionK + rank2);
|
|
12922
|
+
const entry = fused.get(row.nodeId) ?? {
|
|
12923
|
+
nodeId: row.nodeId,
|
|
12924
|
+
fusedScore: 0
|
|
12925
|
+
};
|
|
12926
|
+
entry.fusedScore += contribution;
|
|
12927
|
+
entry.fulltextRank = rank2;
|
|
12928
|
+
entry.fulltextScore = row.score;
|
|
12929
|
+
if (row.snippet !== void 0) {
|
|
12930
|
+
entry.fulltextSnippet = row.snippet;
|
|
12931
|
+
}
|
|
12932
|
+
fused.set(row.nodeId, entry);
|
|
12933
|
+
}
|
|
12934
|
+
const ranked = [...fused.values()].toSorted(
|
|
12935
|
+
(a, b) => b.fusedScore - a.fusedScore || a.nodeId.localeCompare(b.nodeId)
|
|
12936
|
+
).slice(0, options.limit);
|
|
12937
|
+
const ids = ranked.map((entry) => entry.nodeId);
|
|
12938
|
+
const nodeMap = await fetchNodesByIds(backend, graphId, nodeKind, ids);
|
|
12939
|
+
const hits = [];
|
|
12940
|
+
let rank = 1;
|
|
12941
|
+
for (const entry of ranked) {
|
|
12942
|
+
const node = nodeMap.get(entry.nodeId);
|
|
12943
|
+
if (!node) continue;
|
|
12944
|
+
const typedNode = node;
|
|
12945
|
+
const hit = {
|
|
12946
|
+
node: typedNode,
|
|
12947
|
+
score: entry.fusedScore,
|
|
12948
|
+
rank,
|
|
12949
|
+
...entry.vectorRank !== void 0 && entry.vectorScore !== void 0 ? {
|
|
12950
|
+
vector: {
|
|
12951
|
+
node: typedNode,
|
|
12952
|
+
score: entry.vectorScore,
|
|
12953
|
+
rank: entry.vectorRank
|
|
12954
|
+
}
|
|
12955
|
+
} : {},
|
|
12956
|
+
...entry.fulltextRank !== void 0 && entry.fulltextScore !== void 0 ? {
|
|
12957
|
+
fulltext: {
|
|
12958
|
+
node: typedNode,
|
|
12959
|
+
score: entry.fulltextScore,
|
|
12960
|
+
rank: entry.fulltextRank,
|
|
12961
|
+
...entry.fulltextSnippet === void 0 ? {} : { snippet: entry.fulltextSnippet }
|
|
12962
|
+
}
|
|
12963
|
+
} : {}
|
|
12964
|
+
};
|
|
12965
|
+
hits.push(hit);
|
|
12966
|
+
rank += 1;
|
|
12967
|
+
}
|
|
12968
|
+
return hits;
|
|
12969
|
+
}
|
|
12970
|
+
function validateFulltextCallOptions(backend, options) {
|
|
12971
|
+
const strategy = backend.fulltextStrategy;
|
|
12972
|
+
const capabilities = backend.capabilities.fulltext;
|
|
12973
|
+
if (options.mode !== void 0) {
|
|
12974
|
+
validateFulltextMode(backend, strategy, capabilities, options.mode);
|
|
12975
|
+
}
|
|
12976
|
+
if (options.includeSnippets === true) {
|
|
12977
|
+
validateFulltextSnippets(backend, strategy, capabilities);
|
|
12978
|
+
}
|
|
12979
|
+
if (options.language !== void 0) {
|
|
12980
|
+
validateFulltextLanguageOption(
|
|
12981
|
+
backend,
|
|
12982
|
+
strategy,
|
|
12983
|
+
capabilities,
|
|
12984
|
+
options.language
|
|
12985
|
+
);
|
|
12986
|
+
}
|
|
12987
|
+
}
|
|
12988
|
+
function validateFulltextMode(backend, strategy, capabilities, mode) {
|
|
12989
|
+
if (strategy !== void 0) {
|
|
12990
|
+
if (strategy.supportedModes.includes(mode)) return;
|
|
12991
|
+
throw new chunkO5PST6TB_cjs.ConfigurationError(
|
|
12992
|
+
`Backend "${backend.dialect}" fulltext strategy "${strategy.name}" does not support mode "${mode}". Supported modes: ${strategy.supportedModes.join(", ")}`,
|
|
12993
|
+
{
|
|
12994
|
+
backend: backend.dialect,
|
|
12995
|
+
strategy: strategy.name,
|
|
12996
|
+
capability: `fulltext.modes.${mode}`
|
|
12997
|
+
}
|
|
12998
|
+
);
|
|
12999
|
+
}
|
|
13000
|
+
if (mode === "phrase" && capabilities && !capabilities.phraseQueries) {
|
|
13001
|
+
throw new chunkO5PST6TB_cjs.ConfigurationError(
|
|
13002
|
+
`Backend "${backend.dialect}" does not support phrase queries`,
|
|
13003
|
+
{ backend: backend.dialect, capability: "fulltext.phraseQueries" }
|
|
13004
|
+
);
|
|
13005
|
+
}
|
|
13006
|
+
}
|
|
13007
|
+
function validateFulltextSnippets(backend, strategy, capabilities) {
|
|
13008
|
+
const supportsSnippets = strategy === void 0 ? capabilities?.highlighting !== false : strategy.supportsSnippets;
|
|
13009
|
+
if (supportsSnippets) return;
|
|
13010
|
+
throw new chunkO5PST6TB_cjs.ConfigurationError(
|
|
13011
|
+
`Backend "${backend.dialect}" does not support snippet highlighting; drop includeSnippets or switch to a backend where highlighting is supported`,
|
|
13012
|
+
{ backend: backend.dialect, capability: "fulltext.highlighting" }
|
|
13013
|
+
);
|
|
13014
|
+
}
|
|
13015
|
+
function validateFulltextLanguageOption(backend, strategy, capabilities, language) {
|
|
13016
|
+
if (strategy !== void 0 && !strategy.supportsLanguageOverride) {
|
|
13017
|
+
throw new chunkO5PST6TB_cjs.ConfigurationError(
|
|
13018
|
+
`Backend "${backend.dialect}" fulltext strategy "${strategy.name}" does not honor a per-query \`language\` override (its tokenizer is fixed at table-create time). Drop the option, or pick a strategy that advertises \`supportsLanguageOverride: true\`.`,
|
|
13019
|
+
{
|
|
13020
|
+
backend: backend.dialect,
|
|
13021
|
+
strategy: strategy.name,
|
|
13022
|
+
capability: "fulltext.languageOverride"
|
|
13023
|
+
}
|
|
13024
|
+
);
|
|
13025
|
+
}
|
|
13026
|
+
if (capabilities !== void 0) {
|
|
13027
|
+
warnIfLanguageNotAdvertised(backend.dialect, capabilities, language);
|
|
13028
|
+
}
|
|
13029
|
+
}
|
|
13030
|
+
function warnIfLanguageNotAdvertised(dialect, capabilities, language) {
|
|
13031
|
+
if (capabilities.languages.length === 0) return;
|
|
13032
|
+
if (capabilities.languages.includes(language)) return;
|
|
13033
|
+
if (typeof console === "undefined" || typeof console.warn !== "function") {
|
|
13034
|
+
return;
|
|
13035
|
+
}
|
|
13036
|
+
console.warn(
|
|
13037
|
+
`[typegraph] fulltext language "${language}" is not in the advertised list for dialect "${dialect}" (${capabilities.languages.join(", ")}). If your backend has this language installed this warning is safe to ignore.`
|
|
13038
|
+
);
|
|
13039
|
+
}
|
|
13040
|
+
async function fetchNodesByIds(backend, graphId, nodeKind, ids) {
|
|
13041
|
+
if (ids.length === 0) return /* @__PURE__ */ new Map();
|
|
13042
|
+
const map = /* @__PURE__ */ new Map();
|
|
13043
|
+
if (backend.getNodes) {
|
|
13044
|
+
const rows = await backend.getNodes(graphId, nodeKind, ids);
|
|
13045
|
+
for (const row of rows) {
|
|
13046
|
+
if (row.deleted_at !== void 0) continue;
|
|
13047
|
+
map.set(row.id, rowToNode(row));
|
|
13048
|
+
}
|
|
13049
|
+
return map;
|
|
13050
|
+
}
|
|
13051
|
+
for (const id of ids) {
|
|
13052
|
+
const row = await backend.getNode(graphId, nodeKind, id);
|
|
13053
|
+
if (row && row.deleted_at === void 0) map.set(row.id, rowToNode(row));
|
|
13054
|
+
}
|
|
13055
|
+
return map;
|
|
13056
|
+
}
|
|
13057
|
+
|
|
13058
|
+
// src/store/search-facade.ts
|
|
13059
|
+
var StoreSearch = class {
|
|
13060
|
+
#context;
|
|
13061
|
+
constructor(context) {
|
|
13062
|
+
this.#context = context;
|
|
13063
|
+
}
|
|
13064
|
+
/**
|
|
13065
|
+
* Runs a fulltext search against nodes of the given kind.
|
|
13066
|
+
*
|
|
13067
|
+
* Requires fields on the node schema declared with `searchable()`.
|
|
13068
|
+
* The search hits the backend's fulltext index (tsvector + GIN on
|
|
13069
|
+
* Postgres, FTS5 on SQLite — or whatever strategy the backend is
|
|
13070
|
+
* configured with) and resolves the matching node IDs back to typed
|
|
13071
|
+
* `Node` objects.
|
|
13072
|
+
*/
|
|
13073
|
+
async fulltext(nodeKind, options) {
|
|
13074
|
+
return executeFulltextSearch(
|
|
13075
|
+
{ graphId: this.#context.graphId, backend: this.#context.backend },
|
|
13076
|
+
nodeKind,
|
|
13077
|
+
options
|
|
13078
|
+
);
|
|
13079
|
+
}
|
|
13080
|
+
/**
|
|
13081
|
+
* Runs a vector + fulltext hybrid search and fuses the results with
|
|
13082
|
+
* Reciprocal Rank Fusion.
|
|
13083
|
+
*
|
|
13084
|
+
* RRF is rank-based, so it composes well across heterogeneous score
|
|
13085
|
+
* scales (cosine similarity vs ts_rank_cd vs FTS5 BM25). Default
|
|
13086
|
+
* over-fetch is 4× `limit` from each source — tune via `vector.k` /
|
|
13087
|
+
* `fulltext.k` for higher-recall corpora.
|
|
13088
|
+
*/
|
|
13089
|
+
async hybrid(nodeKind, options) {
|
|
13090
|
+
return executeHybridSearch(
|
|
13091
|
+
{ graphId: this.#context.graphId, backend: this.#context.backend },
|
|
13092
|
+
nodeKind,
|
|
13093
|
+
options
|
|
13094
|
+
);
|
|
13095
|
+
}
|
|
13096
|
+
/**
|
|
13097
|
+
* Rebuilds the fulltext index from existing node data.
|
|
13098
|
+
*
|
|
13099
|
+
* Use when:
|
|
13100
|
+
* - A node kind gained a `searchable()` field after data was already
|
|
13101
|
+
* written and existing rows were never indexed.
|
|
13102
|
+
* - The fulltext table was dropped / truncated.
|
|
13103
|
+
* - `language` was changed on a `searchable()` field.
|
|
13104
|
+
*
|
|
13105
|
+
* Iterates nodes with keyset pagination (stable under shared
|
|
13106
|
+
* timestamps and light concurrent writes), transacts per page, skips
|
|
13107
|
+
* kinds with no searchable fields, and cleans up stale rows for
|
|
13108
|
+
* soft-deleted nodes. Corrupt or non-object `props` are counted in
|
|
13109
|
+
* `skipped` with their node IDs surfaced via `skippedIds` so operators
|
|
13110
|
+
* can investigate. Concurrent hard-deletes between page fetches can
|
|
13111
|
+
* be missed by a single pass — run during a maintenance window for
|
|
13112
|
+
* full consistency.
|
|
13113
|
+
*/
|
|
13114
|
+
async rebuildFulltext(nodeKind, options = {}) {
|
|
13115
|
+
return rebuildFulltextIndex(
|
|
13116
|
+
{
|
|
13117
|
+
graphId: this.#context.graphId,
|
|
13118
|
+
backend: this.#context.backend,
|
|
13119
|
+
registry: this.#context.registry
|
|
13120
|
+
},
|
|
13121
|
+
nodeKind,
|
|
13122
|
+
options
|
|
13123
|
+
);
|
|
13124
|
+
}
|
|
13125
|
+
};
|
|
13126
|
+
|
|
11680
13127
|
// src/store/store.ts
|
|
11681
13128
|
var Store = class {
|
|
11682
13129
|
#graph;
|
|
@@ -11687,6 +13134,8 @@ var Store = class {
|
|
|
11687
13134
|
#defaultTraversalExpansion;
|
|
11688
13135
|
#nodeCollections;
|
|
11689
13136
|
#edgeCollections;
|
|
13137
|
+
#algorithms;
|
|
13138
|
+
#search;
|
|
11690
13139
|
constructor(graph, backend, options) {
|
|
11691
13140
|
this.#graph = graph;
|
|
11692
13141
|
this.#backend = backend;
|
|
@@ -11768,6 +13217,38 @@ var Store = class {
|
|
|
11768
13217
|
}
|
|
11769
13218
|
return this.#edgeCollections;
|
|
11770
13219
|
}
|
|
13220
|
+
// === Graph Algorithms ===
|
|
13221
|
+
/**
|
|
13222
|
+
* Tier 1 graph algorithms: shortest path, reachability, neighborhoods, and
|
|
13223
|
+
* degree centrality.
|
|
13224
|
+
*
|
|
13225
|
+
* Each call compiles to a single recursive-CTE query against the backend.
|
|
13226
|
+
* The facade is built lazily on first access and cached for the lifetime
|
|
13227
|
+
* of the store.
|
|
13228
|
+
*
|
|
13229
|
+
* @example
|
|
13230
|
+
* ```typescript
|
|
13231
|
+
* const path = await store.algorithms.shortestPath(alice, bob, {
|
|
13232
|
+
* edges: ["knows"],
|
|
13233
|
+
* });
|
|
13234
|
+
*
|
|
13235
|
+
* const friends = await store.algorithms.neighbors(alice, {
|
|
13236
|
+
* edges: ["knows"],
|
|
13237
|
+
* depth: 2,
|
|
13238
|
+
* });
|
|
13239
|
+
* ```
|
|
13240
|
+
*/
|
|
13241
|
+
get algorithms() {
|
|
13242
|
+
if (this.#algorithms === void 0) {
|
|
13243
|
+
this.#algorithms = createGraphAlgorithms({
|
|
13244
|
+
graphId: this.graphId,
|
|
13245
|
+
backend: this.#backend,
|
|
13246
|
+
schema: this.#schema,
|
|
13247
|
+
defaultTemporalMode: this.#graph.defaults.temporalMode
|
|
13248
|
+
});
|
|
13249
|
+
}
|
|
13250
|
+
return this.#algorithms;
|
|
13251
|
+
}
|
|
11771
13252
|
// === Dynamic Collection Access ===
|
|
11772
13253
|
/**
|
|
11773
13254
|
* Returns the node collection for the given kind, or undefined if the kind
|
|
@@ -11903,6 +13384,44 @@ var Store = class {
|
|
|
11903
13384
|
query() {
|
|
11904
13385
|
return this.#createQueryForBackend(this.#backend);
|
|
11905
13386
|
}
|
|
13387
|
+
// === Search ===
|
|
13388
|
+
/**
|
|
13389
|
+
* Search-related operations (fulltext, hybrid, and rebuild).
|
|
13390
|
+
*
|
|
13391
|
+
* All search methods live under this facade to keep the top-level
|
|
13392
|
+
* Store API focused on CRUD + graph traversal. The facade is
|
|
13393
|
+
* lazy-initialized and cached for the lifetime of the store.
|
|
13394
|
+
*
|
|
13395
|
+
* @example
|
|
13396
|
+
* ```typescript
|
|
13397
|
+
* // Fulltext
|
|
13398
|
+
* const hits = await store.search.fulltext("Document", {
|
|
13399
|
+
* query: "climate warming",
|
|
13400
|
+
* limit: 10,
|
|
13401
|
+
* includeSnippets: true,
|
|
13402
|
+
* });
|
|
13403
|
+
*
|
|
13404
|
+
* // Hybrid (vector + fulltext fused with RRF)
|
|
13405
|
+
* const ranked = await store.search.hybrid("Document", {
|
|
13406
|
+
* limit: 10,
|
|
13407
|
+
* vector: { fieldPath: "embedding", queryEmbedding: vec },
|
|
13408
|
+
* fulltext: { query: "climate warming" },
|
|
13409
|
+
* });
|
|
13410
|
+
*
|
|
13411
|
+
* // Rebuild index after schema change
|
|
13412
|
+
* const stats = await store.search.rebuildFulltext();
|
|
13413
|
+
* ```
|
|
13414
|
+
*/
|
|
13415
|
+
get search() {
|
|
13416
|
+
if (this.#search === void 0) {
|
|
13417
|
+
this.#search = new StoreSearch({
|
|
13418
|
+
graphId: this.graphId,
|
|
13419
|
+
backend: this.#backend,
|
|
13420
|
+
registry: this.#registry
|
|
13421
|
+
});
|
|
13422
|
+
}
|
|
13423
|
+
return this.#search;
|
|
13424
|
+
}
|
|
11906
13425
|
// === Batch Query Execution ===
|
|
11907
13426
|
/**
|
|
11908
13427
|
* Executes multiple queries over a single connection with snapshot consistency.
|
|
@@ -11976,7 +13495,7 @@ var Store = class {
|
|
|
11976
13495
|
graphId: this.graphId,
|
|
11977
13496
|
rootId,
|
|
11978
13497
|
backend: this.#backend,
|
|
11979
|
-
dialect:
|
|
13498
|
+
dialect: chunkFQCQM3KG_cjs.getDialect(this.#backend.dialect),
|
|
11980
13499
|
schema: this.#schema,
|
|
11981
13500
|
options
|
|
11982
13501
|
});
|
|
@@ -12046,6 +13565,8 @@ var Store = class {
|
|
|
12046
13565
|
await (this.#backend.capabilities.transactions ? this.#backend.transaction(async (tx) => doClear(tx)) : doClear(this.#backend));
|
|
12047
13566
|
this.#nodeCollections = void 0;
|
|
12048
13567
|
this.#edgeCollections = void 0;
|
|
13568
|
+
this.#algorithms = void 0;
|
|
13569
|
+
this.#search = void 0;
|
|
12049
13570
|
}
|
|
12050
13571
|
// === Lifecycle ===
|
|
12051
13572
|
/**
|
|
@@ -12115,7 +13636,7 @@ var Store = class {
|
|
|
12115
13636
|
// === Internal: Temporal Filtering ===
|
|
12116
13637
|
#matchesTemporalMode(row, options) {
|
|
12117
13638
|
const mode = options?.temporalMode ?? this.#graph.defaults.temporalMode;
|
|
12118
|
-
const asOf = options?.asOf ??
|
|
13639
|
+
const asOf = options?.asOf ?? chunkWRWVWKQL_cjs.nowIso();
|
|
12119
13640
|
switch (mode) {
|
|
12120
13641
|
case "current":
|
|
12121
13642
|
case "asOf": {
|
|
@@ -12148,41 +13669,41 @@ function createStore(graph, backend, options) {
|
|
|
12148
13669
|
}
|
|
12149
13670
|
async function createStoreWithSchema(graph, backend, options) {
|
|
12150
13671
|
const store = createStore(graph, backend, options);
|
|
12151
|
-
const result = await
|
|
13672
|
+
const result = await chunkHRNQ3DVJ_cjs.ensureSchema(backend, graph, options);
|
|
12152
13673
|
return [store, result];
|
|
12153
13674
|
}
|
|
12154
13675
|
|
|
12155
13676
|
Object.defineProperty(exports, "isMetaEdge", {
|
|
12156
13677
|
enumerable: true,
|
|
12157
|
-
get: function () { return
|
|
13678
|
+
get: function () { return chunkHRNQ3DVJ_cjs.isMetaEdge; }
|
|
12158
13679
|
});
|
|
12159
13680
|
Object.defineProperty(exports, "defineGraph", {
|
|
12160
13681
|
enumerable: true,
|
|
12161
|
-
get: function () { return
|
|
13682
|
+
get: function () { return chunkWRWVWKQL_cjs.defineGraph; }
|
|
12162
13683
|
});
|
|
12163
13684
|
Object.defineProperty(exports, "getEdgeKinds", {
|
|
12164
13685
|
enumerable: true,
|
|
12165
|
-
get: function () { return
|
|
13686
|
+
get: function () { return chunkWRWVWKQL_cjs.getEdgeKinds; }
|
|
12166
13687
|
});
|
|
12167
13688
|
Object.defineProperty(exports, "getNodeKinds", {
|
|
12168
13689
|
enumerable: true,
|
|
12169
|
-
get: function () { return
|
|
13690
|
+
get: function () { return chunkWRWVWKQL_cjs.getNodeKinds; }
|
|
12170
13691
|
});
|
|
12171
13692
|
Object.defineProperty(exports, "isEdgeType", {
|
|
12172
13693
|
enumerable: true,
|
|
12173
|
-
get: function () { return
|
|
13694
|
+
get: function () { return chunkWRWVWKQL_cjs.isEdgeType; }
|
|
12174
13695
|
});
|
|
12175
13696
|
Object.defineProperty(exports, "isEdgeTypeWithEndpoints", {
|
|
12176
13697
|
enumerable: true,
|
|
12177
|
-
get: function () { return
|
|
13698
|
+
get: function () { return chunkWRWVWKQL_cjs.isEdgeTypeWithEndpoints; }
|
|
12178
13699
|
});
|
|
12179
13700
|
Object.defineProperty(exports, "isGraphDef", {
|
|
12180
13701
|
enumerable: true,
|
|
12181
|
-
get: function () { return
|
|
13702
|
+
get: function () { return chunkWRWVWKQL_cjs.isGraphDef; }
|
|
12182
13703
|
});
|
|
12183
13704
|
Object.defineProperty(exports, "isNodeType", {
|
|
12184
13705
|
enumerable: true,
|
|
12185
|
-
get: function () { return
|
|
13706
|
+
get: function () { return chunkWRWVWKQL_cjs.isNodeType; }
|
|
12186
13707
|
});
|
|
12187
13708
|
Object.defineProperty(exports, "BackendDisposedError", {
|
|
12188
13709
|
enumerable: true,
|
|
@@ -12284,37 +13805,61 @@ Object.defineProperty(exports, "isUserRecoverable", {
|
|
|
12284
13805
|
enumerable: true,
|
|
12285
13806
|
get: function () { return chunkO5PST6TB_cjs.isUserRecoverable; }
|
|
12286
13807
|
});
|
|
13808
|
+
Object.defineProperty(exports, "fts5Strategy", {
|
|
13809
|
+
enumerable: true,
|
|
13810
|
+
get: function () { return chunkFQCQM3KG_cjs.fts5Strategy; }
|
|
13811
|
+
});
|
|
13812
|
+
Object.defineProperty(exports, "tsvectorStrategy", {
|
|
13813
|
+
enumerable: true,
|
|
13814
|
+
get: function () { return chunkFQCQM3KG_cjs.tsvectorStrategy; }
|
|
13815
|
+
});
|
|
13816
|
+
Object.defineProperty(exports, "DEFAULT_SEARCHABLE_LANGUAGE", {
|
|
13817
|
+
enumerable: true,
|
|
13818
|
+
get: function () { return chunkUVZHTBZ5_cjs.DEFAULT_SEARCHABLE_LANGUAGE; }
|
|
13819
|
+
});
|
|
12287
13820
|
Object.defineProperty(exports, "MAX_JSON_POINTER_DEPTH", {
|
|
12288
13821
|
enumerable: true,
|
|
12289
|
-
get: function () { return
|
|
13822
|
+
get: function () { return chunkUVZHTBZ5_cjs.MAX_JSON_POINTER_DEPTH; }
|
|
12290
13823
|
});
|
|
12291
13824
|
Object.defineProperty(exports, "embedding", {
|
|
12292
13825
|
enumerable: true,
|
|
12293
|
-
get: function () { return
|
|
13826
|
+
get: function () { return chunkUVZHTBZ5_cjs.embedding; }
|
|
12294
13827
|
});
|
|
12295
13828
|
Object.defineProperty(exports, "getEmbeddingDimensions", {
|
|
12296
13829
|
enumerable: true,
|
|
12297
|
-
get: function () { return
|
|
13830
|
+
get: function () { return chunkUVZHTBZ5_cjs.getEmbeddingDimensions; }
|
|
13831
|
+
});
|
|
13832
|
+
Object.defineProperty(exports, "getSearchableMetadata", {
|
|
13833
|
+
enumerable: true,
|
|
13834
|
+
get: function () { return chunkUVZHTBZ5_cjs.getSearchableMetadata; }
|
|
12298
13835
|
});
|
|
12299
13836
|
Object.defineProperty(exports, "isEmbeddingSchema", {
|
|
12300
13837
|
enumerable: true,
|
|
12301
|
-
get: function () { return
|
|
13838
|
+
get: function () { return chunkUVZHTBZ5_cjs.isEmbeddingSchema; }
|
|
13839
|
+
});
|
|
13840
|
+
Object.defineProperty(exports, "isSearchableSchema", {
|
|
13841
|
+
enumerable: true,
|
|
13842
|
+
get: function () { return chunkUVZHTBZ5_cjs.isSearchableSchema; }
|
|
12302
13843
|
});
|
|
12303
13844
|
Object.defineProperty(exports, "joinJsonPointers", {
|
|
12304
13845
|
enumerable: true,
|
|
12305
|
-
get: function () { return
|
|
13846
|
+
get: function () { return chunkUVZHTBZ5_cjs.joinJsonPointers; }
|
|
12306
13847
|
});
|
|
12307
13848
|
Object.defineProperty(exports, "jsonPointer", {
|
|
12308
13849
|
enumerable: true,
|
|
12309
|
-
get: function () { return
|
|
13850
|
+
get: function () { return chunkUVZHTBZ5_cjs.jsonPointer; }
|
|
12310
13851
|
});
|
|
12311
13852
|
Object.defineProperty(exports, "normalizeJsonPointer", {
|
|
12312
13853
|
enumerable: true,
|
|
12313
|
-
get: function () { return
|
|
13854
|
+
get: function () { return chunkUVZHTBZ5_cjs.normalizeJsonPointer; }
|
|
12314
13855
|
});
|
|
12315
13856
|
Object.defineProperty(exports, "parseJsonPointer", {
|
|
12316
13857
|
enumerable: true,
|
|
12317
|
-
get: function () { return
|
|
13858
|
+
get: function () { return chunkUVZHTBZ5_cjs.parseJsonPointer; }
|
|
13859
|
+
});
|
|
13860
|
+
Object.defineProperty(exports, "searchable", {
|
|
13861
|
+
enumerable: true,
|
|
13862
|
+
get: function () { return chunkUVZHTBZ5_cjs.searchable; }
|
|
12318
13863
|
});
|
|
12319
13864
|
exports.DEFAULT_SQL_SCHEMA = DEFAULT_SQL_SCHEMA;
|
|
12320
13865
|
exports.ExecutableAggregateQuery = ExecutableAggregateQuery;
|
|
@@ -12323,6 +13868,7 @@ exports.MAX_EXPLICIT_RECURSIVE_DEPTH = MAX_EXPLICIT_RECURSIVE_DEPTH;
|
|
|
12323
13868
|
exports.MAX_RECURSIVE_DEPTH = MAX_RECURSIVE_DEPTH;
|
|
12324
13869
|
exports.PreparedQuery = PreparedQuery;
|
|
12325
13870
|
exports.QueryBuilder = QueryBuilder;
|
|
13871
|
+
exports.StoreSearch = StoreSearch;
|
|
12326
13872
|
exports.UnionableQuery = UnionableQuery;
|
|
12327
13873
|
exports.avg = avg;
|
|
12328
13874
|
exports.broader = broader;
|