@wiscale/velesdb-sdk 1.2.0 → 1.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +145 -1
- package/dist/index.d.mts +515 -11
- package/dist/index.d.ts +515 -11
- package/dist/index.js +800 -24
- package/dist/index.mjs +797 -23
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -36,7 +36,9 @@ __export(index_exports, {
|
|
|
36
36
|
ValidationError: () => ValidationError,
|
|
37
37
|
VelesDB: () => VelesDB,
|
|
38
38
|
VelesDBError: () => VelesDBError,
|
|
39
|
-
|
|
39
|
+
VelesQLBuilder: () => VelesQLBuilder,
|
|
40
|
+
WasmBackend: () => WasmBackend,
|
|
41
|
+
velesql: () => velesql
|
|
40
42
|
});
|
|
41
43
|
module.exports = __toCommonJS(index_exports);
|
|
42
44
|
|
|
@@ -175,7 +177,7 @@ var WasmBackend = class {
|
|
|
175
177
|
throw new NotFoundError(`Collection '${collectionName}'`);
|
|
176
178
|
}
|
|
177
179
|
for (const doc of docs) {
|
|
178
|
-
const vectorLen =
|
|
180
|
+
const vectorLen = doc.vector.length;
|
|
179
181
|
if (vectorLen !== collection.config.dimension) {
|
|
180
182
|
throw new VelesDBError(
|
|
181
183
|
`Vector dimension mismatch for doc ${doc.id}: expected ${collection.config.dimension}, got ${vectorLen}`,
|
|
@@ -281,7 +283,7 @@ var WasmBackend = class {
|
|
|
281
283
|
"NOT_SUPPORTED"
|
|
282
284
|
);
|
|
283
285
|
}
|
|
284
|
-
async query(_queryString, _params) {
|
|
286
|
+
async query(_collection, _queryString, _params, _options) {
|
|
285
287
|
throw new VelesDBError(
|
|
286
288
|
"VelesQL queries are not supported in WASM backend. Use REST backend for query support.",
|
|
287
289
|
"NOT_SUPPORTED"
|
|
@@ -331,6 +333,60 @@ var WasmBackend = class {
|
|
|
331
333
|
}
|
|
332
334
|
return Math.abs(hash);
|
|
333
335
|
}
|
|
336
|
+
// ========================================================================
|
|
337
|
+
// Index Management (EPIC-009) - Stubs for WASM backend
|
|
338
|
+
// Note: Full implementation requires velesdb-wasm support
|
|
339
|
+
// ========================================================================
|
|
340
|
+
async createIndex(_collection, _options) {
|
|
341
|
+
this.ensureInitialized();
|
|
342
|
+
throw new Error(
|
|
343
|
+
"WasmBackend: createIndex is not yet supported. Index operations require the REST backend with velesdb-server."
|
|
344
|
+
);
|
|
345
|
+
}
|
|
346
|
+
async listIndexes(_collection) {
|
|
347
|
+
this.ensureInitialized();
|
|
348
|
+
return [];
|
|
349
|
+
}
|
|
350
|
+
async hasIndex(_collection, _label, _property) {
|
|
351
|
+
this.ensureInitialized();
|
|
352
|
+
return false;
|
|
353
|
+
}
|
|
354
|
+
async dropIndex(_collection, _label, _property) {
|
|
355
|
+
this.ensureInitialized();
|
|
356
|
+
return false;
|
|
357
|
+
}
|
|
358
|
+
// ========================================================================
|
|
359
|
+
// Knowledge Graph (EPIC-016 US-041) - Stubs for WASM backend
|
|
360
|
+
// Note: Graph operations require server-side EdgeStore
|
|
361
|
+
// ========================================================================
|
|
362
|
+
async addEdge(_collection, _edge) {
|
|
363
|
+
this.ensureInitialized();
|
|
364
|
+
throw new VelesDBError(
|
|
365
|
+
"Knowledge Graph operations are not supported in WASM backend. Use REST backend for graph features.",
|
|
366
|
+
"NOT_SUPPORTED"
|
|
367
|
+
);
|
|
368
|
+
}
|
|
369
|
+
async getEdges(_collection, _options) {
|
|
370
|
+
this.ensureInitialized();
|
|
371
|
+
throw new VelesDBError(
|
|
372
|
+
"Knowledge Graph operations are not supported in WASM backend. Use REST backend for graph features.",
|
|
373
|
+
"NOT_SUPPORTED"
|
|
374
|
+
);
|
|
375
|
+
}
|
|
376
|
+
async traverseGraph(_collection, _request) {
|
|
377
|
+
this.ensureInitialized();
|
|
378
|
+
throw new VelesDBError(
|
|
379
|
+
"Graph traversal is not supported in WASM backend. Use REST backend for graph features.",
|
|
380
|
+
"NOT_SUPPORTED"
|
|
381
|
+
);
|
|
382
|
+
}
|
|
383
|
+
async getNodeDegree(_collection, _nodeId) {
|
|
384
|
+
this.ensureInitialized();
|
|
385
|
+
throw new VelesDBError(
|
|
386
|
+
"Graph degree query is not supported in WASM backend. Use REST backend for graph features.",
|
|
387
|
+
"NOT_SUPPORTED"
|
|
388
|
+
);
|
|
389
|
+
}
|
|
334
390
|
};
|
|
335
391
|
|
|
336
392
|
// src/backends/rest.ts
|
|
@@ -366,6 +422,64 @@ var RestBackend = class {
|
|
|
366
422
|
throw new ConnectionError("REST backend not initialized");
|
|
367
423
|
}
|
|
368
424
|
}
|
|
425
|
+
mapStatusToErrorCode(status) {
|
|
426
|
+
switch (status) {
|
|
427
|
+
case 400:
|
|
428
|
+
return "BAD_REQUEST";
|
|
429
|
+
case 401:
|
|
430
|
+
return "UNAUTHORIZED";
|
|
431
|
+
case 403:
|
|
432
|
+
return "FORBIDDEN";
|
|
433
|
+
case 404:
|
|
434
|
+
return "NOT_FOUND";
|
|
435
|
+
case 409:
|
|
436
|
+
return "CONFLICT";
|
|
437
|
+
case 429:
|
|
438
|
+
return "RATE_LIMITED";
|
|
439
|
+
case 500:
|
|
440
|
+
return "INTERNAL_ERROR";
|
|
441
|
+
case 503:
|
|
442
|
+
return "SERVICE_UNAVAILABLE";
|
|
443
|
+
default:
|
|
444
|
+
return "UNKNOWN_ERROR";
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
extractErrorPayload(data) {
|
|
448
|
+
if (!data || typeof data !== "object") {
|
|
449
|
+
return {};
|
|
450
|
+
}
|
|
451
|
+
const payload = data;
|
|
452
|
+
const code = typeof payload.code === "string" ? payload.code : void 0;
|
|
453
|
+
const messageField = payload.message ?? payload.error;
|
|
454
|
+
const message = typeof messageField === "string" ? messageField : void 0;
|
|
455
|
+
return { code, message };
|
|
456
|
+
}
|
|
457
|
+
/**
|
|
458
|
+
* Parse node ID safely to handle u64 values above Number.MAX_SAFE_INTEGER.
|
|
459
|
+
* Returns bigint for large values, number for safe values.
|
|
460
|
+
*/
|
|
461
|
+
parseNodeId(value) {
|
|
462
|
+
if (value === null || value === void 0) {
|
|
463
|
+
return 0;
|
|
464
|
+
}
|
|
465
|
+
if (typeof value === "bigint") {
|
|
466
|
+
return value;
|
|
467
|
+
}
|
|
468
|
+
if (typeof value === "string") {
|
|
469
|
+
const num = Number(value);
|
|
470
|
+
if (num > Number.MAX_SAFE_INTEGER) {
|
|
471
|
+
return BigInt(value);
|
|
472
|
+
}
|
|
473
|
+
return num;
|
|
474
|
+
}
|
|
475
|
+
if (typeof value === "number") {
|
|
476
|
+
if (value > Number.MAX_SAFE_INTEGER) {
|
|
477
|
+
return value;
|
|
478
|
+
}
|
|
479
|
+
return value;
|
|
480
|
+
}
|
|
481
|
+
return 0;
|
|
482
|
+
}
|
|
369
483
|
async request(method, path, body) {
|
|
370
484
|
const url = `${this.baseUrl}${path}`;
|
|
371
485
|
const headers = {
|
|
@@ -384,12 +498,13 @@ var RestBackend = class {
|
|
|
384
498
|
signal: controller.signal
|
|
385
499
|
});
|
|
386
500
|
clearTimeout(timeoutId);
|
|
387
|
-
const data = await response.json();
|
|
501
|
+
const data = await response.json().catch(() => ({}));
|
|
388
502
|
if (!response.ok) {
|
|
503
|
+
const errorPayload = this.extractErrorPayload(data);
|
|
389
504
|
return {
|
|
390
505
|
error: {
|
|
391
|
-
code:
|
|
392
|
-
message:
|
|
506
|
+
code: errorPayload.code ?? this.mapStatusToErrorCode(response.status),
|
|
507
|
+
message: errorPayload.message ?? `HTTP ${response.status}`
|
|
393
508
|
}
|
|
394
509
|
};
|
|
395
510
|
}
|
|
@@ -456,11 +571,13 @@ var RestBackend = class {
|
|
|
456
571
|
const vector = doc.vector instanceof Float32Array ? Array.from(doc.vector) : doc.vector;
|
|
457
572
|
const response = await this.request(
|
|
458
573
|
"POST",
|
|
459
|
-
`/collections/${encodeURIComponent(collection)}/
|
|
574
|
+
`/collections/${encodeURIComponent(collection)}/points`,
|
|
460
575
|
{
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
576
|
+
points: [{
|
|
577
|
+
id: doc.id,
|
|
578
|
+
vector,
|
|
579
|
+
payload: doc.payload
|
|
580
|
+
}]
|
|
464
581
|
}
|
|
465
582
|
);
|
|
466
583
|
if (response.error) {
|
|
@@ -479,8 +596,8 @@ var RestBackend = class {
|
|
|
479
596
|
}));
|
|
480
597
|
const response = await this.request(
|
|
481
598
|
"POST",
|
|
482
|
-
`/collections/${encodeURIComponent(collection)}/
|
|
483
|
-
{ vectors }
|
|
599
|
+
`/collections/${encodeURIComponent(collection)}/points`,
|
|
600
|
+
{ points: vectors }
|
|
484
601
|
);
|
|
485
602
|
if (response.error) {
|
|
486
603
|
if (response.error.code === "NOT_FOUND") {
|
|
@@ -534,7 +651,7 @@ var RestBackend = class {
|
|
|
534
651
|
this.ensureInitialized();
|
|
535
652
|
const response = await this.request(
|
|
536
653
|
"DELETE",
|
|
537
|
-
`/collections/${encodeURIComponent(collection)}/
|
|
654
|
+
`/collections/${encodeURIComponent(collection)}/points/${encodeURIComponent(String(id))}`
|
|
538
655
|
);
|
|
539
656
|
if (response.error) {
|
|
540
657
|
if (response.error.code === "NOT_FOUND") {
|
|
@@ -548,7 +665,7 @@ var RestBackend = class {
|
|
|
548
665
|
this.ensureInitialized();
|
|
549
666
|
const response = await this.request(
|
|
550
667
|
"GET",
|
|
551
|
-
`/collections/${encodeURIComponent(collection)}/
|
|
668
|
+
`/collections/${encodeURIComponent(collection)}/points/${encodeURIComponent(String(id))}`
|
|
552
669
|
);
|
|
553
670
|
if (response.error) {
|
|
554
671
|
if (response.error.code === "NOT_FOUND") {
|
|
@@ -599,7 +716,7 @@ var RestBackend = class {
|
|
|
599
716
|
}
|
|
600
717
|
return response.data?.results ?? [];
|
|
601
718
|
}
|
|
602
|
-
async query(queryString, params) {
|
|
719
|
+
async query(collection, queryString, params, _options) {
|
|
603
720
|
this.ensureInitialized();
|
|
604
721
|
const response = await this.request(
|
|
605
722
|
"POST",
|
|
@@ -610,9 +727,32 @@ var RestBackend = class {
|
|
|
610
727
|
}
|
|
611
728
|
);
|
|
612
729
|
if (response.error) {
|
|
730
|
+
if (response.error.code === "NOT_FOUND") {
|
|
731
|
+
throw new NotFoundError(`Collection '${collection}'`);
|
|
732
|
+
}
|
|
613
733
|
throw new VelesDBError(response.error.message, response.error.code);
|
|
614
734
|
}
|
|
615
|
-
|
|
735
|
+
const rawData = response.data;
|
|
736
|
+
return {
|
|
737
|
+
results: (rawData?.results ?? []).map((r) => ({
|
|
738
|
+
// Server returns `id` (u64), map to nodeId with precision handling
|
|
739
|
+
nodeId: this.parseNodeId(r.id ?? r.node_id ?? r.nodeId),
|
|
740
|
+
// Server returns `score`, map to vectorScore (primary score for SELECT queries)
|
|
741
|
+
vectorScore: r.score ?? r.vector_score ?? r.vectorScore,
|
|
742
|
+
// graph_score not returned by SELECT queries, only by future MATCH queries
|
|
743
|
+
graphScore: r.graph_score ?? r.graphScore,
|
|
744
|
+
// Use score as fusedScore for compatibility
|
|
745
|
+
fusedScore: r.score ?? r.fused_score ?? r.fusedScore ?? 0,
|
|
746
|
+
// payload maps to bindings for compatibility
|
|
747
|
+
bindings: r.payload ?? r.bindings ?? {},
|
|
748
|
+
columnData: r.column_data ?? r.columnData
|
|
749
|
+
})),
|
|
750
|
+
stats: {
|
|
751
|
+
executionTimeMs: rawData?.timing_ms ?? 0,
|
|
752
|
+
strategy: "select",
|
|
753
|
+
scannedNodes: rawData?.rows_returned ?? 0
|
|
754
|
+
}
|
|
755
|
+
};
|
|
616
756
|
}
|
|
617
757
|
async multiQuerySearch(collection, vectors, options) {
|
|
618
758
|
this.ensureInitialized();
|
|
@@ -625,8 +765,8 @@ var RestBackend = class {
|
|
|
625
765
|
{
|
|
626
766
|
vectors: formattedVectors,
|
|
627
767
|
top_k: options?.k ?? 10,
|
|
628
|
-
|
|
629
|
-
|
|
768
|
+
strategy: options?.fusion ?? "rrf",
|
|
769
|
+
rrf_k: options?.fusionParams?.k ?? 60,
|
|
630
770
|
filter: options?.filter
|
|
631
771
|
}
|
|
632
772
|
);
|
|
@@ -668,6 +808,158 @@ var RestBackend = class {
|
|
|
668
808
|
async close() {
|
|
669
809
|
this._initialized = false;
|
|
670
810
|
}
|
|
811
|
+
// ========================================================================
|
|
812
|
+
// Index Management (EPIC-009)
|
|
813
|
+
// ========================================================================
|
|
814
|
+
async createIndex(collection, options) {
|
|
815
|
+
this.ensureInitialized();
|
|
816
|
+
const response = await this.request(
|
|
817
|
+
"POST",
|
|
818
|
+
`/collections/${encodeURIComponent(collection)}/indexes`,
|
|
819
|
+
{
|
|
820
|
+
label: options.label,
|
|
821
|
+
property: options.property,
|
|
822
|
+
index_type: options.indexType ?? "hash"
|
|
823
|
+
}
|
|
824
|
+
);
|
|
825
|
+
if (response.error) {
|
|
826
|
+
if (response.error.code === "NOT_FOUND") {
|
|
827
|
+
throw new NotFoundError(`Collection '${collection}'`);
|
|
828
|
+
}
|
|
829
|
+
throw new VelesDBError(response.error.message, response.error.code);
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
async listIndexes(collection) {
|
|
833
|
+
this.ensureInitialized();
|
|
834
|
+
const response = await this.request(
|
|
835
|
+
"GET",
|
|
836
|
+
`/collections/${encodeURIComponent(collection)}/indexes`
|
|
837
|
+
);
|
|
838
|
+
if (response.error) {
|
|
839
|
+
if (response.error.code === "NOT_FOUND") {
|
|
840
|
+
throw new NotFoundError(`Collection '${collection}'`);
|
|
841
|
+
}
|
|
842
|
+
throw new VelesDBError(response.error.message, response.error.code);
|
|
843
|
+
}
|
|
844
|
+
return (response.data?.indexes ?? []).map((idx) => ({
|
|
845
|
+
label: idx.label,
|
|
846
|
+
property: idx.property,
|
|
847
|
+
indexType: idx.index_type,
|
|
848
|
+
cardinality: idx.cardinality,
|
|
849
|
+
memoryBytes: idx.memory_bytes
|
|
850
|
+
}));
|
|
851
|
+
}
|
|
852
|
+
async hasIndex(collection, label, property) {
|
|
853
|
+
const indexes = await this.listIndexes(collection);
|
|
854
|
+
return indexes.some((idx) => idx.label === label && idx.property === property);
|
|
855
|
+
}
|
|
856
|
+
async dropIndex(collection, label, property) {
|
|
857
|
+
this.ensureInitialized();
|
|
858
|
+
const response = await this.request(
|
|
859
|
+
"DELETE",
|
|
860
|
+
`/collections/${encodeURIComponent(collection)}/indexes/${encodeURIComponent(label)}/${encodeURIComponent(property)}`
|
|
861
|
+
);
|
|
862
|
+
if (response.error) {
|
|
863
|
+
if (response.error.code === "NOT_FOUND") {
|
|
864
|
+
return false;
|
|
865
|
+
}
|
|
866
|
+
throw new VelesDBError(response.error.message, response.error.code);
|
|
867
|
+
}
|
|
868
|
+
return response.data?.dropped ?? true;
|
|
869
|
+
}
|
|
870
|
+
// ========================================================================
|
|
871
|
+
// Knowledge Graph (EPIC-016 US-041)
|
|
872
|
+
// ========================================================================
|
|
873
|
+
async addEdge(collection, edge) {
|
|
874
|
+
this.ensureInitialized();
|
|
875
|
+
const response = await this.request(
|
|
876
|
+
"POST",
|
|
877
|
+
`/collections/${encodeURIComponent(collection)}/graph/edges`,
|
|
878
|
+
{
|
|
879
|
+
id: edge.id,
|
|
880
|
+
source: edge.source,
|
|
881
|
+
target: edge.target,
|
|
882
|
+
label: edge.label,
|
|
883
|
+
properties: edge.properties ?? {}
|
|
884
|
+
}
|
|
885
|
+
);
|
|
886
|
+
if (response.error) {
|
|
887
|
+
if (response.error.code === "NOT_FOUND") {
|
|
888
|
+
throw new NotFoundError(`Collection '${collection}'`);
|
|
889
|
+
}
|
|
890
|
+
throw new VelesDBError(response.error.message, response.error.code);
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
async getEdges(collection, options) {
|
|
894
|
+
this.ensureInitialized();
|
|
895
|
+
const queryParams = options?.label ? `?label=${encodeURIComponent(options.label)}` : "";
|
|
896
|
+
const response = await this.request(
|
|
897
|
+
"GET",
|
|
898
|
+
`/collections/${encodeURIComponent(collection)}/graph/edges${queryParams}`
|
|
899
|
+
);
|
|
900
|
+
if (response.error) {
|
|
901
|
+
if (response.error.code === "NOT_FOUND") {
|
|
902
|
+
throw new NotFoundError(`Collection '${collection}'`);
|
|
903
|
+
}
|
|
904
|
+
throw new VelesDBError(response.error.message, response.error.code);
|
|
905
|
+
}
|
|
906
|
+
return response.data?.edges ?? [];
|
|
907
|
+
}
|
|
908
|
+
// ========================================================================
|
|
909
|
+
// Graph Traversal (EPIC-016 US-050)
|
|
910
|
+
// ========================================================================
|
|
911
|
+
async traverseGraph(collection, request) {
|
|
912
|
+
this.ensureInitialized();
|
|
913
|
+
const response = await this.request(
|
|
914
|
+
"POST",
|
|
915
|
+
`/collections/${encodeURIComponent(collection)}/graph/traverse`,
|
|
916
|
+
{
|
|
917
|
+
source: request.source,
|
|
918
|
+
strategy: request.strategy ?? "bfs",
|
|
919
|
+
max_depth: request.maxDepth ?? 3,
|
|
920
|
+
limit: request.limit ?? 100,
|
|
921
|
+
cursor: request.cursor,
|
|
922
|
+
rel_types: request.relTypes ?? []
|
|
923
|
+
}
|
|
924
|
+
);
|
|
925
|
+
if (response.error) {
|
|
926
|
+
if (response.error.code === "NOT_FOUND") {
|
|
927
|
+
throw new NotFoundError(`Collection '${collection}'`);
|
|
928
|
+
}
|
|
929
|
+
throw new VelesDBError(response.error.message, response.error.code);
|
|
930
|
+
}
|
|
931
|
+
const data = response.data;
|
|
932
|
+
return {
|
|
933
|
+
results: data.results.map((r) => ({
|
|
934
|
+
targetId: r.target_id,
|
|
935
|
+
depth: r.depth,
|
|
936
|
+
path: r.path
|
|
937
|
+
})),
|
|
938
|
+
nextCursor: data.next_cursor ?? void 0,
|
|
939
|
+
hasMore: data.has_more,
|
|
940
|
+
stats: {
|
|
941
|
+
visited: data.stats.visited,
|
|
942
|
+
depthReached: data.stats.depth_reached
|
|
943
|
+
}
|
|
944
|
+
};
|
|
945
|
+
}
|
|
946
|
+
async getNodeDegree(collection, nodeId) {
|
|
947
|
+
this.ensureInitialized();
|
|
948
|
+
const response = await this.request(
|
|
949
|
+
"GET",
|
|
950
|
+
`/collections/${encodeURIComponent(collection)}/graph/nodes/${nodeId}/degree`
|
|
951
|
+
);
|
|
952
|
+
if (response.error) {
|
|
953
|
+
if (response.error.code === "NOT_FOUND") {
|
|
954
|
+
throw new NotFoundError(`Collection '${collection}'`);
|
|
955
|
+
}
|
|
956
|
+
throw new VelesDBError(response.error.message, response.error.code);
|
|
957
|
+
}
|
|
958
|
+
return {
|
|
959
|
+
inDegree: response.data?.in_degree ?? 0,
|
|
960
|
+
outDegree: response.data?.out_degree ?? 0
|
|
961
|
+
};
|
|
962
|
+
}
|
|
671
963
|
};
|
|
672
964
|
|
|
673
965
|
// src/client.ts
|
|
@@ -921,18 +1213,36 @@ var VelesDB = class {
|
|
|
921
1213
|
return this.backend.hybridSearch(collection, vector, textQuery, options);
|
|
922
1214
|
}
|
|
923
1215
|
/**
|
|
924
|
-
* Execute a VelesQL query
|
|
1216
|
+
* Execute a VelesQL multi-model query (EPIC-031 US-011)
|
|
925
1217
|
*
|
|
1218
|
+
* Supports hybrid vector + graph queries with VelesQL syntax.
|
|
1219
|
+
*
|
|
1220
|
+
* @param collection - Collection name
|
|
926
1221
|
* @param queryString - VelesQL query string
|
|
927
|
-
* @param params -
|
|
928
|
-
* @
|
|
1222
|
+
* @param params - Query parameters (vectors, scalars)
|
|
1223
|
+
* @param options - Query options (timeout, streaming)
|
|
1224
|
+
* @returns Query response with results and execution stats
|
|
1225
|
+
*
|
|
1226
|
+
* @example
|
|
1227
|
+
* ```typescript
|
|
1228
|
+
* const response = await db.query('docs', `
|
|
1229
|
+
* MATCH (d:Doc) WHERE vector NEAR $q LIMIT 20
|
|
1230
|
+
* `, { q: queryVector });
|
|
1231
|
+
*
|
|
1232
|
+
* for (const r of response.results) {
|
|
1233
|
+
* console.log(`Node ${r.nodeId}: ${r.fusedScore}`);
|
|
1234
|
+
* }
|
|
1235
|
+
* ```
|
|
929
1236
|
*/
|
|
930
|
-
async query(queryString, params) {
|
|
1237
|
+
async query(collection, queryString, params, options) {
|
|
931
1238
|
this.ensureInitialized();
|
|
1239
|
+
if (!collection || typeof collection !== "string") {
|
|
1240
|
+
throw new ValidationError("Collection name must be a non-empty string");
|
|
1241
|
+
}
|
|
932
1242
|
if (!queryString || typeof queryString !== "string") {
|
|
933
1243
|
throw new ValidationError("Query string must be a non-empty string");
|
|
934
1244
|
}
|
|
935
|
-
return this.backend.query(queryString, params);
|
|
1245
|
+
return this.backend.query(collection, queryString, params, options);
|
|
936
1246
|
}
|
|
937
1247
|
/**
|
|
938
1248
|
* Multi-query fusion search combining results from multiple query vectors
|
|
@@ -1007,7 +1317,471 @@ var VelesDB = class {
|
|
|
1007
1317
|
get backendType() {
|
|
1008
1318
|
return this.config.backend;
|
|
1009
1319
|
}
|
|
1320
|
+
// ========================================================================
|
|
1321
|
+
// Index Management (EPIC-009)
|
|
1322
|
+
// ========================================================================
|
|
1323
|
+
/**
|
|
1324
|
+
* Create a property index for O(1) equality lookups or O(log n) range queries
|
|
1325
|
+
*
|
|
1326
|
+
* @param collection - Collection name
|
|
1327
|
+
* @param options - Index configuration (label, property, indexType)
|
|
1328
|
+
*
|
|
1329
|
+
* @example
|
|
1330
|
+
* ```typescript
|
|
1331
|
+
* // Create hash index for fast email lookups
|
|
1332
|
+
* await db.createIndex('users', { label: 'Person', property: 'email' });
|
|
1333
|
+
*
|
|
1334
|
+
* // Create range index for timestamp queries
|
|
1335
|
+
* await db.createIndex('events', { label: 'Event', property: 'timestamp', indexType: 'range' });
|
|
1336
|
+
* ```
|
|
1337
|
+
*/
|
|
1338
|
+
async createIndex(collection, options) {
|
|
1339
|
+
this.ensureInitialized();
|
|
1340
|
+
if (!options.label || !options.property) {
|
|
1341
|
+
throw new ValidationError("Index requires label and property");
|
|
1342
|
+
}
|
|
1343
|
+
await this.backend.createIndex(collection, options);
|
|
1344
|
+
}
|
|
1345
|
+
/**
|
|
1346
|
+
* List all indexes on a collection
|
|
1347
|
+
*
|
|
1348
|
+
* @param collection - Collection name
|
|
1349
|
+
* @returns Array of index information
|
|
1350
|
+
*/
|
|
1351
|
+
async listIndexes(collection) {
|
|
1352
|
+
this.ensureInitialized();
|
|
1353
|
+
return this.backend.listIndexes(collection);
|
|
1354
|
+
}
|
|
1355
|
+
/**
|
|
1356
|
+
* Check if an index exists
|
|
1357
|
+
*
|
|
1358
|
+
* @param collection - Collection name
|
|
1359
|
+
* @param label - Node label
|
|
1360
|
+
* @param property - Property name
|
|
1361
|
+
* @returns true if index exists
|
|
1362
|
+
*/
|
|
1363
|
+
async hasIndex(collection, label, property) {
|
|
1364
|
+
this.ensureInitialized();
|
|
1365
|
+
return this.backend.hasIndex(collection, label, property);
|
|
1366
|
+
}
|
|
1367
|
+
/**
|
|
1368
|
+
* Drop an index
|
|
1369
|
+
*
|
|
1370
|
+
* @param collection - Collection name
|
|
1371
|
+
* @param label - Node label
|
|
1372
|
+
* @param property - Property name
|
|
1373
|
+
* @returns true if index was dropped, false if it didn't exist
|
|
1374
|
+
*/
|
|
1375
|
+
async dropIndex(collection, label, property) {
|
|
1376
|
+
this.ensureInitialized();
|
|
1377
|
+
return this.backend.dropIndex(collection, label, property);
|
|
1378
|
+
}
|
|
1379
|
+
// ========================================================================
|
|
1380
|
+
// Knowledge Graph (EPIC-016 US-041)
|
|
1381
|
+
// ========================================================================
|
|
1382
|
+
/**
|
|
1383
|
+
* Add an edge to the collection's knowledge graph
|
|
1384
|
+
*
|
|
1385
|
+
* @param collection - Collection name
|
|
1386
|
+
* @param edge - Edge to add (id, source, target, label, properties)
|
|
1387
|
+
*
|
|
1388
|
+
* @example
|
|
1389
|
+
* ```typescript
|
|
1390
|
+
* await db.addEdge('social', {
|
|
1391
|
+
* id: 1,
|
|
1392
|
+
* source: 100,
|
|
1393
|
+
* target: 200,
|
|
1394
|
+
* label: 'FOLLOWS',
|
|
1395
|
+
* properties: { since: '2024-01-01' }
|
|
1396
|
+
* });
|
|
1397
|
+
* ```
|
|
1398
|
+
*/
|
|
1399
|
+
async addEdge(collection, edge) {
|
|
1400
|
+
this.ensureInitialized();
|
|
1401
|
+
if (!edge.label || typeof edge.label !== "string") {
|
|
1402
|
+
throw new ValidationError("Edge label is required and must be a string");
|
|
1403
|
+
}
|
|
1404
|
+
if (typeof edge.source !== "number" || typeof edge.target !== "number") {
|
|
1405
|
+
throw new ValidationError("Edge source and target must be numbers");
|
|
1406
|
+
}
|
|
1407
|
+
await this.backend.addEdge(collection, edge);
|
|
1408
|
+
}
|
|
1409
|
+
/**
|
|
1410
|
+
* Get edges from the collection's knowledge graph
|
|
1411
|
+
*
|
|
1412
|
+
* @param collection - Collection name
|
|
1413
|
+
* @param options - Query options (filter by label)
|
|
1414
|
+
* @returns Array of edges
|
|
1415
|
+
*
|
|
1416
|
+
* @example
|
|
1417
|
+
* ```typescript
|
|
1418
|
+
* // Get all edges with label "FOLLOWS"
|
|
1419
|
+
* const edges = await db.getEdges('social', { label: 'FOLLOWS' });
|
|
1420
|
+
* ```
|
|
1421
|
+
*/
|
|
1422
|
+
async getEdges(collection, options) {
|
|
1423
|
+
this.ensureInitialized();
|
|
1424
|
+
return this.backend.getEdges(collection, options);
|
|
1425
|
+
}
|
|
1426
|
+
// ========================================================================
|
|
1427
|
+
// Graph Traversal (EPIC-016 US-050)
|
|
1428
|
+
// ========================================================================
|
|
1429
|
+
/**
|
|
1430
|
+
* Traverse the graph using BFS or DFS from a source node
|
|
1431
|
+
*
|
|
1432
|
+
* @param collection - Collection name
|
|
1433
|
+
* @param request - Traversal request options
|
|
1434
|
+
* @returns Traversal response with results and stats
|
|
1435
|
+
*
|
|
1436
|
+
* @example
|
|
1437
|
+
* ```typescript
|
|
1438
|
+
* // BFS traversal from node 100
|
|
1439
|
+
* const result = await db.traverseGraph('social', {
|
|
1440
|
+
* source: 100,
|
|
1441
|
+
* strategy: 'bfs',
|
|
1442
|
+
* maxDepth: 3,
|
|
1443
|
+
* limit: 100,
|
|
1444
|
+
* relTypes: ['FOLLOWS', 'KNOWS']
|
|
1445
|
+
* });
|
|
1446
|
+
*
|
|
1447
|
+
* for (const node of result.results) {
|
|
1448
|
+
* console.log(`Reached node ${node.targetId} at depth ${node.depth}`);
|
|
1449
|
+
* }
|
|
1450
|
+
* ```
|
|
1451
|
+
*/
|
|
1452
|
+
async traverseGraph(collection, request) {
|
|
1453
|
+
this.ensureInitialized();
|
|
1454
|
+
if (typeof request.source !== "number") {
|
|
1455
|
+
throw new ValidationError("Source node ID must be a number");
|
|
1456
|
+
}
|
|
1457
|
+
if (request.strategy && !["bfs", "dfs"].includes(request.strategy)) {
|
|
1458
|
+
throw new ValidationError("Strategy must be 'bfs' or 'dfs'");
|
|
1459
|
+
}
|
|
1460
|
+
return this.backend.traverseGraph(collection, request);
|
|
1461
|
+
}
|
|
1462
|
+
/**
|
|
1463
|
+
* Get the in-degree and out-degree of a node
|
|
1464
|
+
*
|
|
1465
|
+
* @param collection - Collection name
|
|
1466
|
+
* @param nodeId - Node ID
|
|
1467
|
+
* @returns Degree response with inDegree and outDegree
|
|
1468
|
+
*
|
|
1469
|
+
* @example
|
|
1470
|
+
* ```typescript
|
|
1471
|
+
* const degree = await db.getNodeDegree('social', 100);
|
|
1472
|
+
* console.log(`In: ${degree.inDegree}, Out: ${degree.outDegree}`);
|
|
1473
|
+
* ```
|
|
1474
|
+
*/
|
|
1475
|
+
async getNodeDegree(collection, nodeId) {
|
|
1476
|
+
this.ensureInitialized();
|
|
1477
|
+
if (typeof nodeId !== "number") {
|
|
1478
|
+
throw new ValidationError("Node ID must be a number");
|
|
1479
|
+
}
|
|
1480
|
+
return this.backend.getNodeDegree(collection, nodeId);
|
|
1481
|
+
}
|
|
1482
|
+
};
|
|
1483
|
+
|
|
1484
|
+
// src/query-builder.ts
|
|
1485
|
+
var VelesQLBuilder = class _VelesQLBuilder {
|
|
1486
|
+
constructor(state) {
|
|
1487
|
+
this.state = {
|
|
1488
|
+
matchClauses: state?.matchClauses ?? [],
|
|
1489
|
+
whereClauses: state?.whereClauses ?? [],
|
|
1490
|
+
whereOperators: state?.whereOperators ?? [],
|
|
1491
|
+
params: state?.params ?? {},
|
|
1492
|
+
limitValue: state?.limitValue,
|
|
1493
|
+
offsetValue: state?.offsetValue,
|
|
1494
|
+
orderByClause: state?.orderByClause,
|
|
1495
|
+
returnClause: state?.returnClause,
|
|
1496
|
+
fusionOptions: state?.fusionOptions,
|
|
1497
|
+
currentNode: state?.currentNode,
|
|
1498
|
+
pendingRel: state?.pendingRel
|
|
1499
|
+
};
|
|
1500
|
+
}
|
|
1501
|
+
clone(updates) {
|
|
1502
|
+
return new _VelesQLBuilder({
|
|
1503
|
+
...this.state,
|
|
1504
|
+
matchClauses: [...this.state.matchClauses],
|
|
1505
|
+
whereClauses: [...this.state.whereClauses],
|
|
1506
|
+
whereOperators: [...this.state.whereOperators],
|
|
1507
|
+
params: { ...this.state.params },
|
|
1508
|
+
...updates
|
|
1509
|
+
});
|
|
1510
|
+
}
|
|
1511
|
+
/**
|
|
1512
|
+
* Start a MATCH clause with a node pattern
|
|
1513
|
+
*
|
|
1514
|
+
* @param alias - Node alias (e.g., 'n', 'person')
|
|
1515
|
+
* @param label - Optional node label(s)
|
|
1516
|
+
*/
|
|
1517
|
+
match(alias, label) {
|
|
1518
|
+
const labelStr = this.formatLabel(label);
|
|
1519
|
+
const nodePattern = `(${alias}${labelStr})`;
|
|
1520
|
+
return this.clone({
|
|
1521
|
+
matchClauses: [...this.state.matchClauses, nodePattern],
|
|
1522
|
+
currentNode: alias
|
|
1523
|
+
});
|
|
1524
|
+
}
|
|
1525
|
+
/**
|
|
1526
|
+
* Add a relationship pattern
|
|
1527
|
+
*
|
|
1528
|
+
* @param type - Relationship type (e.g., 'KNOWS', 'FOLLOWS')
|
|
1529
|
+
* @param alias - Optional relationship alias
|
|
1530
|
+
* @param options - Relationship options (direction, hops)
|
|
1531
|
+
*/
|
|
1532
|
+
rel(type, alias, options) {
|
|
1533
|
+
return this.clone({
|
|
1534
|
+
pendingRel: { type, alias, options }
|
|
1535
|
+
});
|
|
1536
|
+
}
|
|
1537
|
+
/**
|
|
1538
|
+
* Complete a relationship pattern with target node
|
|
1539
|
+
*
|
|
1540
|
+
* @param alias - Target node alias
|
|
1541
|
+
* @param label - Optional target node label(s)
|
|
1542
|
+
*/
|
|
1543
|
+
to(alias, label) {
|
|
1544
|
+
if (!this.state.pendingRel) {
|
|
1545
|
+
throw new Error("to() must be called after rel()");
|
|
1546
|
+
}
|
|
1547
|
+
const { type, alias: relAlias, options } = this.state.pendingRel;
|
|
1548
|
+
const direction = options?.direction ?? "outgoing";
|
|
1549
|
+
const labelStr = this.formatLabel(label);
|
|
1550
|
+
const relPattern = this.formatRelationship(type, relAlias, options);
|
|
1551
|
+
const targetNode = `(${alias}${labelStr})`;
|
|
1552
|
+
let fullPattern;
|
|
1553
|
+
switch (direction) {
|
|
1554
|
+
case "incoming":
|
|
1555
|
+
fullPattern = `<-${relPattern}-${targetNode}`;
|
|
1556
|
+
break;
|
|
1557
|
+
case "both":
|
|
1558
|
+
fullPattern = `-${relPattern}-${targetNode}`;
|
|
1559
|
+
break;
|
|
1560
|
+
default:
|
|
1561
|
+
fullPattern = `-${relPattern}->${targetNode}`;
|
|
1562
|
+
}
|
|
1563
|
+
const lastMatch = this.state.matchClauses[this.state.matchClauses.length - 1];
|
|
1564
|
+
const updatedMatch = lastMatch + fullPattern;
|
|
1565
|
+
const newMatchClauses = [...this.state.matchClauses.slice(0, -1), updatedMatch];
|
|
1566
|
+
return this.clone({
|
|
1567
|
+
matchClauses: newMatchClauses,
|
|
1568
|
+
currentNode: alias,
|
|
1569
|
+
pendingRel: void 0
|
|
1570
|
+
});
|
|
1571
|
+
}
|
|
1572
|
+
/**
|
|
1573
|
+
* Add a WHERE clause
|
|
1574
|
+
*
|
|
1575
|
+
* @param condition - WHERE condition
|
|
1576
|
+
* @param params - Optional parameters
|
|
1577
|
+
*/
|
|
1578
|
+
where(condition, params) {
|
|
1579
|
+
const newParams = params ? { ...this.state.params, ...params } : this.state.params;
|
|
1580
|
+
return this.clone({
|
|
1581
|
+
whereClauses: [...this.state.whereClauses, condition],
|
|
1582
|
+
whereOperators: [...this.state.whereOperators],
|
|
1583
|
+
params: newParams
|
|
1584
|
+
});
|
|
1585
|
+
}
|
|
1586
|
+
/**
|
|
1587
|
+
* Add an AND WHERE clause
|
|
1588
|
+
*
|
|
1589
|
+
* @param condition - WHERE condition
|
|
1590
|
+
* @param params - Optional parameters
|
|
1591
|
+
*/
|
|
1592
|
+
andWhere(condition, params) {
|
|
1593
|
+
const newParams = params ? { ...this.state.params, ...params } : this.state.params;
|
|
1594
|
+
return this.clone({
|
|
1595
|
+
whereClauses: [...this.state.whereClauses, condition],
|
|
1596
|
+
whereOperators: [...this.state.whereOperators, "AND"],
|
|
1597
|
+
params: newParams
|
|
1598
|
+
});
|
|
1599
|
+
}
|
|
1600
|
+
/**
|
|
1601
|
+
* Add an OR WHERE clause
|
|
1602
|
+
*
|
|
1603
|
+
* @param condition - WHERE condition
|
|
1604
|
+
* @param params - Optional parameters
|
|
1605
|
+
*/
|
|
1606
|
+
orWhere(condition, params) {
|
|
1607
|
+
const newParams = params ? { ...this.state.params, ...params } : this.state.params;
|
|
1608
|
+
return this.clone({
|
|
1609
|
+
whereClauses: [...this.state.whereClauses, condition],
|
|
1610
|
+
whereOperators: [...this.state.whereOperators, "OR"],
|
|
1611
|
+
params: newParams
|
|
1612
|
+
});
|
|
1613
|
+
}
|
|
1614
|
+
/**
|
|
1615
|
+
* Add a vector NEAR clause for similarity search
|
|
1616
|
+
*
|
|
1617
|
+
* @param paramName - Parameter name (e.g., '$query', '$embedding')
|
|
1618
|
+
* @param vector - Vector data
|
|
1619
|
+
* @param options - NEAR options (topK)
|
|
1620
|
+
*/
|
|
1621
|
+
nearVector(paramName, vector, options) {
|
|
1622
|
+
const cleanParamName = paramName.startsWith("$") ? paramName.slice(1) : paramName;
|
|
1623
|
+
const topKSuffix = options?.topK ? ` TOP ${options.topK}` : "";
|
|
1624
|
+
const condition = `vector NEAR $${cleanParamName}${topKSuffix}`;
|
|
1625
|
+
const newParams = { ...this.state.params, [cleanParamName]: vector };
|
|
1626
|
+
if (this.state.whereClauses.length === 0) {
|
|
1627
|
+
return this.clone({
|
|
1628
|
+
whereClauses: [condition],
|
|
1629
|
+
params: newParams
|
|
1630
|
+
});
|
|
1631
|
+
}
|
|
1632
|
+
return this.clone({
|
|
1633
|
+
whereClauses: [...this.state.whereClauses, condition],
|
|
1634
|
+
whereOperators: [...this.state.whereOperators, "AND"],
|
|
1635
|
+
params: newParams
|
|
1636
|
+
});
|
|
1637
|
+
}
|
|
1638
|
+
/**
|
|
1639
|
+
* Add LIMIT clause
|
|
1640
|
+
*
|
|
1641
|
+
* @param value - Maximum number of results
|
|
1642
|
+
*/
|
|
1643
|
+
limit(value) {
|
|
1644
|
+
if (value < 0) {
|
|
1645
|
+
throw new Error("LIMIT must be non-negative");
|
|
1646
|
+
}
|
|
1647
|
+
return this.clone({ limitValue: value });
|
|
1648
|
+
}
|
|
1649
|
+
/**
|
|
1650
|
+
* Add OFFSET clause
|
|
1651
|
+
*
|
|
1652
|
+
* @param value - Number of results to skip
|
|
1653
|
+
*/
|
|
1654
|
+
offset(value) {
|
|
1655
|
+
if (value < 0) {
|
|
1656
|
+
throw new Error("OFFSET must be non-negative");
|
|
1657
|
+
}
|
|
1658
|
+
return this.clone({ offsetValue: value });
|
|
1659
|
+
}
|
|
1660
|
+
/**
|
|
1661
|
+
* Add ORDER BY clause
|
|
1662
|
+
*
|
|
1663
|
+
* @param field - Field to order by
|
|
1664
|
+
* @param direction - Sort direction (ASC or DESC)
|
|
1665
|
+
*/
|
|
1666
|
+
orderBy(field, direction) {
|
|
1667
|
+
const orderClause = direction ? `${field} ${direction}` : field;
|
|
1668
|
+
return this.clone({ orderByClause: orderClause });
|
|
1669
|
+
}
|
|
1670
|
+
/**
|
|
1671
|
+
* Add RETURN clause with specific fields
|
|
1672
|
+
*
|
|
1673
|
+
* @param fields - Fields to return (array or object with aliases)
|
|
1674
|
+
*/
|
|
1675
|
+
return(fields) {
|
|
1676
|
+
let returnClause;
|
|
1677
|
+
if (Array.isArray(fields)) {
|
|
1678
|
+
returnClause = fields.join(", ");
|
|
1679
|
+
} else {
|
|
1680
|
+
returnClause = Object.entries(fields).map(([field, alias]) => `${field} AS ${alias}`).join(", ");
|
|
1681
|
+
}
|
|
1682
|
+
return this.clone({ returnClause });
|
|
1683
|
+
}
|
|
1684
|
+
/**
|
|
1685
|
+
* Add RETURN * clause
|
|
1686
|
+
*/
|
|
1687
|
+
returnAll() {
|
|
1688
|
+
return this.clone({ returnClause: "*" });
|
|
1689
|
+
}
|
|
1690
|
+
/**
|
|
1691
|
+
* Set fusion strategy for hybrid queries
|
|
1692
|
+
*
|
|
1693
|
+
* @param strategy - Fusion strategy
|
|
1694
|
+
* @param options - Fusion parameters
|
|
1695
|
+
*/
|
|
1696
|
+
fusion(strategy, options) {
|
|
1697
|
+
return this.clone({
|
|
1698
|
+
fusionOptions: {
|
|
1699
|
+
strategy,
|
|
1700
|
+
...options
|
|
1701
|
+
}
|
|
1702
|
+
});
|
|
1703
|
+
}
|
|
1704
|
+
/**
|
|
1705
|
+
* Get the fusion options
|
|
1706
|
+
*/
|
|
1707
|
+
getFusionOptions() {
|
|
1708
|
+
return this.state.fusionOptions;
|
|
1709
|
+
}
|
|
1710
|
+
/**
|
|
1711
|
+
* Get all parameters
|
|
1712
|
+
*/
|
|
1713
|
+
getParams() {
|
|
1714
|
+
return { ...this.state.params };
|
|
1715
|
+
}
|
|
1716
|
+
/**
|
|
1717
|
+
* Build the VelesQL query string
|
|
1718
|
+
*/
|
|
1719
|
+
toVelesQL() {
|
|
1720
|
+
if (this.state.matchClauses.length === 0) {
|
|
1721
|
+
throw new Error("Query must have at least one MATCH clause");
|
|
1722
|
+
}
|
|
1723
|
+
const parts = [];
|
|
1724
|
+
parts.push(`MATCH ${this.state.matchClauses.join(", ")}`);
|
|
1725
|
+
if (this.state.whereClauses.length > 0) {
|
|
1726
|
+
const whereStr = this.buildWhereClause();
|
|
1727
|
+
parts.push(`WHERE ${whereStr}`);
|
|
1728
|
+
}
|
|
1729
|
+
if (this.state.orderByClause) {
|
|
1730
|
+
parts.push(`ORDER BY ${this.state.orderByClause}`);
|
|
1731
|
+
}
|
|
1732
|
+
if (this.state.limitValue !== void 0) {
|
|
1733
|
+
parts.push(`LIMIT ${this.state.limitValue}`);
|
|
1734
|
+
}
|
|
1735
|
+
if (this.state.offsetValue !== void 0) {
|
|
1736
|
+
parts.push(`OFFSET ${this.state.offsetValue}`);
|
|
1737
|
+
}
|
|
1738
|
+
if (this.state.returnClause) {
|
|
1739
|
+
parts.push(`RETURN ${this.state.returnClause}`);
|
|
1740
|
+
}
|
|
1741
|
+
if (this.state.fusionOptions) {
|
|
1742
|
+
parts.push(`/* FUSION ${this.state.fusionOptions.strategy} */`);
|
|
1743
|
+
}
|
|
1744
|
+
return parts.join(" ");
|
|
1745
|
+
}
|
|
1746
|
+
formatLabel(label) {
|
|
1747
|
+
if (!label) return "";
|
|
1748
|
+
if (Array.isArray(label)) {
|
|
1749
|
+
return label.map((l) => `:${l}`).join("");
|
|
1750
|
+
}
|
|
1751
|
+
return `:${label}`;
|
|
1752
|
+
}
|
|
1753
|
+
formatRelationship(type, alias, options) {
|
|
1754
|
+
const aliasStr = alias ? alias : "";
|
|
1755
|
+
const hopsStr = this.formatHops(options);
|
|
1756
|
+
if (alias) {
|
|
1757
|
+
return `[${aliasStr}:${type}${hopsStr}]`;
|
|
1758
|
+
}
|
|
1759
|
+
return `[:${type}${hopsStr}]`;
|
|
1760
|
+
}
|
|
1761
|
+
formatHops(options) {
|
|
1762
|
+
if (!options?.minHops && !options?.maxHops) return "";
|
|
1763
|
+
const min = options.minHops ?? 1;
|
|
1764
|
+
const max = options.maxHops ?? "";
|
|
1765
|
+
return `*${min}..${max}`;
|
|
1766
|
+
}
|
|
1767
|
+
buildWhereClause() {
|
|
1768
|
+
if (this.state.whereClauses.length === 0) return "";
|
|
1769
|
+
const first = this.state.whereClauses[0];
|
|
1770
|
+
if (!first) return "";
|
|
1771
|
+
let result = first;
|
|
1772
|
+
for (let i = 1; i < this.state.whereClauses.length; i++) {
|
|
1773
|
+
const operator = this.state.whereOperators[i - 1] ?? "AND";
|
|
1774
|
+
const clause = this.state.whereClauses[i];
|
|
1775
|
+
if (clause) {
|
|
1776
|
+
result += ` ${operator} ${clause}`;
|
|
1777
|
+
}
|
|
1778
|
+
}
|
|
1779
|
+
return result;
|
|
1780
|
+
}
|
|
1010
1781
|
};
|
|
1782
|
+
function velesql() {
|
|
1783
|
+
return new VelesQLBuilder();
|
|
1784
|
+
}
|
|
1011
1785
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1012
1786
|
0 && (module.exports = {
|
|
1013
1787
|
ConnectionError,
|
|
@@ -1016,5 +1790,7 @@ var VelesDB = class {
|
|
|
1016
1790
|
ValidationError,
|
|
1017
1791
|
VelesDB,
|
|
1018
1792
|
VelesDBError,
|
|
1019
|
-
|
|
1793
|
+
VelesQLBuilder,
|
|
1794
|
+
WasmBackend,
|
|
1795
|
+
velesql
|
|
1020
1796
|
});
|