@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.mjs
CHANGED
|
@@ -133,7 +133,7 @@ var WasmBackend = class {
|
|
|
133
133
|
throw new NotFoundError(`Collection '${collectionName}'`);
|
|
134
134
|
}
|
|
135
135
|
for (const doc of docs) {
|
|
136
|
-
const vectorLen =
|
|
136
|
+
const vectorLen = doc.vector.length;
|
|
137
137
|
if (vectorLen !== collection.config.dimension) {
|
|
138
138
|
throw new VelesDBError(
|
|
139
139
|
`Vector dimension mismatch for doc ${doc.id}: expected ${collection.config.dimension}, got ${vectorLen}`,
|
|
@@ -239,7 +239,7 @@ var WasmBackend = class {
|
|
|
239
239
|
"NOT_SUPPORTED"
|
|
240
240
|
);
|
|
241
241
|
}
|
|
242
|
-
async query(_queryString, _params) {
|
|
242
|
+
async query(_collection, _queryString, _params, _options) {
|
|
243
243
|
throw new VelesDBError(
|
|
244
244
|
"VelesQL queries are not supported in WASM backend. Use REST backend for query support.",
|
|
245
245
|
"NOT_SUPPORTED"
|
|
@@ -289,6 +289,60 @@ var WasmBackend = class {
|
|
|
289
289
|
}
|
|
290
290
|
return Math.abs(hash);
|
|
291
291
|
}
|
|
292
|
+
// ========================================================================
|
|
293
|
+
// Index Management (EPIC-009) - Stubs for WASM backend
|
|
294
|
+
// Note: Full implementation requires velesdb-wasm support
|
|
295
|
+
// ========================================================================
|
|
296
|
+
async createIndex(_collection, _options) {
|
|
297
|
+
this.ensureInitialized();
|
|
298
|
+
throw new Error(
|
|
299
|
+
"WasmBackend: createIndex is not yet supported. Index operations require the REST backend with velesdb-server."
|
|
300
|
+
);
|
|
301
|
+
}
|
|
302
|
+
async listIndexes(_collection) {
|
|
303
|
+
this.ensureInitialized();
|
|
304
|
+
return [];
|
|
305
|
+
}
|
|
306
|
+
async hasIndex(_collection, _label, _property) {
|
|
307
|
+
this.ensureInitialized();
|
|
308
|
+
return false;
|
|
309
|
+
}
|
|
310
|
+
async dropIndex(_collection, _label, _property) {
|
|
311
|
+
this.ensureInitialized();
|
|
312
|
+
return false;
|
|
313
|
+
}
|
|
314
|
+
// ========================================================================
|
|
315
|
+
// Knowledge Graph (EPIC-016 US-041) - Stubs for WASM backend
|
|
316
|
+
// Note: Graph operations require server-side EdgeStore
|
|
317
|
+
// ========================================================================
|
|
318
|
+
async addEdge(_collection, _edge) {
|
|
319
|
+
this.ensureInitialized();
|
|
320
|
+
throw new VelesDBError(
|
|
321
|
+
"Knowledge Graph operations are not supported in WASM backend. Use REST backend for graph features.",
|
|
322
|
+
"NOT_SUPPORTED"
|
|
323
|
+
);
|
|
324
|
+
}
|
|
325
|
+
async getEdges(_collection, _options) {
|
|
326
|
+
this.ensureInitialized();
|
|
327
|
+
throw new VelesDBError(
|
|
328
|
+
"Knowledge Graph operations are not supported in WASM backend. Use REST backend for graph features.",
|
|
329
|
+
"NOT_SUPPORTED"
|
|
330
|
+
);
|
|
331
|
+
}
|
|
332
|
+
async traverseGraph(_collection, _request) {
|
|
333
|
+
this.ensureInitialized();
|
|
334
|
+
throw new VelesDBError(
|
|
335
|
+
"Graph traversal is not supported in WASM backend. Use REST backend for graph features.",
|
|
336
|
+
"NOT_SUPPORTED"
|
|
337
|
+
);
|
|
338
|
+
}
|
|
339
|
+
async getNodeDegree(_collection, _nodeId) {
|
|
340
|
+
this.ensureInitialized();
|
|
341
|
+
throw new VelesDBError(
|
|
342
|
+
"Graph degree query is not supported in WASM backend. Use REST backend for graph features.",
|
|
343
|
+
"NOT_SUPPORTED"
|
|
344
|
+
);
|
|
345
|
+
}
|
|
292
346
|
};
|
|
293
347
|
|
|
294
348
|
// src/backends/rest.ts
|
|
@@ -324,6 +378,64 @@ var RestBackend = class {
|
|
|
324
378
|
throw new ConnectionError("REST backend not initialized");
|
|
325
379
|
}
|
|
326
380
|
}
|
|
381
|
+
mapStatusToErrorCode(status) {
|
|
382
|
+
switch (status) {
|
|
383
|
+
case 400:
|
|
384
|
+
return "BAD_REQUEST";
|
|
385
|
+
case 401:
|
|
386
|
+
return "UNAUTHORIZED";
|
|
387
|
+
case 403:
|
|
388
|
+
return "FORBIDDEN";
|
|
389
|
+
case 404:
|
|
390
|
+
return "NOT_FOUND";
|
|
391
|
+
case 409:
|
|
392
|
+
return "CONFLICT";
|
|
393
|
+
case 429:
|
|
394
|
+
return "RATE_LIMITED";
|
|
395
|
+
case 500:
|
|
396
|
+
return "INTERNAL_ERROR";
|
|
397
|
+
case 503:
|
|
398
|
+
return "SERVICE_UNAVAILABLE";
|
|
399
|
+
default:
|
|
400
|
+
return "UNKNOWN_ERROR";
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
extractErrorPayload(data) {
|
|
404
|
+
if (!data || typeof data !== "object") {
|
|
405
|
+
return {};
|
|
406
|
+
}
|
|
407
|
+
const payload = data;
|
|
408
|
+
const code = typeof payload.code === "string" ? payload.code : void 0;
|
|
409
|
+
const messageField = payload.message ?? payload.error;
|
|
410
|
+
const message = typeof messageField === "string" ? messageField : void 0;
|
|
411
|
+
return { code, message };
|
|
412
|
+
}
|
|
413
|
+
/**
|
|
414
|
+
* Parse node ID safely to handle u64 values above Number.MAX_SAFE_INTEGER.
|
|
415
|
+
* Returns bigint for large values, number for safe values.
|
|
416
|
+
*/
|
|
417
|
+
parseNodeId(value) {
|
|
418
|
+
if (value === null || value === void 0) {
|
|
419
|
+
return 0;
|
|
420
|
+
}
|
|
421
|
+
if (typeof value === "bigint") {
|
|
422
|
+
return value;
|
|
423
|
+
}
|
|
424
|
+
if (typeof value === "string") {
|
|
425
|
+
const num = Number(value);
|
|
426
|
+
if (num > Number.MAX_SAFE_INTEGER) {
|
|
427
|
+
return BigInt(value);
|
|
428
|
+
}
|
|
429
|
+
return num;
|
|
430
|
+
}
|
|
431
|
+
if (typeof value === "number") {
|
|
432
|
+
if (value > Number.MAX_SAFE_INTEGER) {
|
|
433
|
+
return value;
|
|
434
|
+
}
|
|
435
|
+
return value;
|
|
436
|
+
}
|
|
437
|
+
return 0;
|
|
438
|
+
}
|
|
327
439
|
async request(method, path, body) {
|
|
328
440
|
const url = `${this.baseUrl}${path}`;
|
|
329
441
|
const headers = {
|
|
@@ -342,12 +454,13 @@ var RestBackend = class {
|
|
|
342
454
|
signal: controller.signal
|
|
343
455
|
});
|
|
344
456
|
clearTimeout(timeoutId);
|
|
345
|
-
const data = await response.json();
|
|
457
|
+
const data = await response.json().catch(() => ({}));
|
|
346
458
|
if (!response.ok) {
|
|
459
|
+
const errorPayload = this.extractErrorPayload(data);
|
|
347
460
|
return {
|
|
348
461
|
error: {
|
|
349
|
-
code:
|
|
350
|
-
message:
|
|
462
|
+
code: errorPayload.code ?? this.mapStatusToErrorCode(response.status),
|
|
463
|
+
message: errorPayload.message ?? `HTTP ${response.status}`
|
|
351
464
|
}
|
|
352
465
|
};
|
|
353
466
|
}
|
|
@@ -414,11 +527,13 @@ var RestBackend = class {
|
|
|
414
527
|
const vector = doc.vector instanceof Float32Array ? Array.from(doc.vector) : doc.vector;
|
|
415
528
|
const response = await this.request(
|
|
416
529
|
"POST",
|
|
417
|
-
`/collections/${encodeURIComponent(collection)}/
|
|
530
|
+
`/collections/${encodeURIComponent(collection)}/points`,
|
|
418
531
|
{
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
532
|
+
points: [{
|
|
533
|
+
id: doc.id,
|
|
534
|
+
vector,
|
|
535
|
+
payload: doc.payload
|
|
536
|
+
}]
|
|
422
537
|
}
|
|
423
538
|
);
|
|
424
539
|
if (response.error) {
|
|
@@ -437,8 +552,8 @@ var RestBackend = class {
|
|
|
437
552
|
}));
|
|
438
553
|
const response = await this.request(
|
|
439
554
|
"POST",
|
|
440
|
-
`/collections/${encodeURIComponent(collection)}/
|
|
441
|
-
{ vectors }
|
|
555
|
+
`/collections/${encodeURIComponent(collection)}/points`,
|
|
556
|
+
{ points: vectors }
|
|
442
557
|
);
|
|
443
558
|
if (response.error) {
|
|
444
559
|
if (response.error.code === "NOT_FOUND") {
|
|
@@ -492,7 +607,7 @@ var RestBackend = class {
|
|
|
492
607
|
this.ensureInitialized();
|
|
493
608
|
const response = await this.request(
|
|
494
609
|
"DELETE",
|
|
495
|
-
`/collections/${encodeURIComponent(collection)}/
|
|
610
|
+
`/collections/${encodeURIComponent(collection)}/points/${encodeURIComponent(String(id))}`
|
|
496
611
|
);
|
|
497
612
|
if (response.error) {
|
|
498
613
|
if (response.error.code === "NOT_FOUND") {
|
|
@@ -506,7 +621,7 @@ var RestBackend = class {
|
|
|
506
621
|
this.ensureInitialized();
|
|
507
622
|
const response = await this.request(
|
|
508
623
|
"GET",
|
|
509
|
-
`/collections/${encodeURIComponent(collection)}/
|
|
624
|
+
`/collections/${encodeURIComponent(collection)}/points/${encodeURIComponent(String(id))}`
|
|
510
625
|
);
|
|
511
626
|
if (response.error) {
|
|
512
627
|
if (response.error.code === "NOT_FOUND") {
|
|
@@ -557,7 +672,7 @@ var RestBackend = class {
|
|
|
557
672
|
}
|
|
558
673
|
return response.data?.results ?? [];
|
|
559
674
|
}
|
|
560
|
-
async query(queryString, params) {
|
|
675
|
+
async query(collection, queryString, params, _options) {
|
|
561
676
|
this.ensureInitialized();
|
|
562
677
|
const response = await this.request(
|
|
563
678
|
"POST",
|
|
@@ -568,9 +683,32 @@ var RestBackend = class {
|
|
|
568
683
|
}
|
|
569
684
|
);
|
|
570
685
|
if (response.error) {
|
|
686
|
+
if (response.error.code === "NOT_FOUND") {
|
|
687
|
+
throw new NotFoundError(`Collection '${collection}'`);
|
|
688
|
+
}
|
|
571
689
|
throw new VelesDBError(response.error.message, response.error.code);
|
|
572
690
|
}
|
|
573
|
-
|
|
691
|
+
const rawData = response.data;
|
|
692
|
+
return {
|
|
693
|
+
results: (rawData?.results ?? []).map((r) => ({
|
|
694
|
+
// Server returns `id` (u64), map to nodeId with precision handling
|
|
695
|
+
nodeId: this.parseNodeId(r.id ?? r.node_id ?? r.nodeId),
|
|
696
|
+
// Server returns `score`, map to vectorScore (primary score for SELECT queries)
|
|
697
|
+
vectorScore: r.score ?? r.vector_score ?? r.vectorScore,
|
|
698
|
+
// graph_score not returned by SELECT queries, only by future MATCH queries
|
|
699
|
+
graphScore: r.graph_score ?? r.graphScore,
|
|
700
|
+
// Use score as fusedScore for compatibility
|
|
701
|
+
fusedScore: r.score ?? r.fused_score ?? r.fusedScore ?? 0,
|
|
702
|
+
// payload maps to bindings for compatibility
|
|
703
|
+
bindings: r.payload ?? r.bindings ?? {},
|
|
704
|
+
columnData: r.column_data ?? r.columnData
|
|
705
|
+
})),
|
|
706
|
+
stats: {
|
|
707
|
+
executionTimeMs: rawData?.timing_ms ?? 0,
|
|
708
|
+
strategy: "select",
|
|
709
|
+
scannedNodes: rawData?.rows_returned ?? 0
|
|
710
|
+
}
|
|
711
|
+
};
|
|
574
712
|
}
|
|
575
713
|
async multiQuerySearch(collection, vectors, options) {
|
|
576
714
|
this.ensureInitialized();
|
|
@@ -583,8 +721,8 @@ var RestBackend = class {
|
|
|
583
721
|
{
|
|
584
722
|
vectors: formattedVectors,
|
|
585
723
|
top_k: options?.k ?? 10,
|
|
586
|
-
|
|
587
|
-
|
|
724
|
+
strategy: options?.fusion ?? "rrf",
|
|
725
|
+
rrf_k: options?.fusionParams?.k ?? 60,
|
|
588
726
|
filter: options?.filter
|
|
589
727
|
}
|
|
590
728
|
);
|
|
@@ -626,6 +764,158 @@ var RestBackend = class {
|
|
|
626
764
|
async close() {
|
|
627
765
|
this._initialized = false;
|
|
628
766
|
}
|
|
767
|
+
// ========================================================================
|
|
768
|
+
// Index Management (EPIC-009)
|
|
769
|
+
// ========================================================================
|
|
770
|
+
async createIndex(collection, options) {
|
|
771
|
+
this.ensureInitialized();
|
|
772
|
+
const response = await this.request(
|
|
773
|
+
"POST",
|
|
774
|
+
`/collections/${encodeURIComponent(collection)}/indexes`,
|
|
775
|
+
{
|
|
776
|
+
label: options.label,
|
|
777
|
+
property: options.property,
|
|
778
|
+
index_type: options.indexType ?? "hash"
|
|
779
|
+
}
|
|
780
|
+
);
|
|
781
|
+
if (response.error) {
|
|
782
|
+
if (response.error.code === "NOT_FOUND") {
|
|
783
|
+
throw new NotFoundError(`Collection '${collection}'`);
|
|
784
|
+
}
|
|
785
|
+
throw new VelesDBError(response.error.message, response.error.code);
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
async listIndexes(collection) {
|
|
789
|
+
this.ensureInitialized();
|
|
790
|
+
const response = await this.request(
|
|
791
|
+
"GET",
|
|
792
|
+
`/collections/${encodeURIComponent(collection)}/indexes`
|
|
793
|
+
);
|
|
794
|
+
if (response.error) {
|
|
795
|
+
if (response.error.code === "NOT_FOUND") {
|
|
796
|
+
throw new NotFoundError(`Collection '${collection}'`);
|
|
797
|
+
}
|
|
798
|
+
throw new VelesDBError(response.error.message, response.error.code);
|
|
799
|
+
}
|
|
800
|
+
return (response.data?.indexes ?? []).map((idx) => ({
|
|
801
|
+
label: idx.label,
|
|
802
|
+
property: idx.property,
|
|
803
|
+
indexType: idx.index_type,
|
|
804
|
+
cardinality: idx.cardinality,
|
|
805
|
+
memoryBytes: idx.memory_bytes
|
|
806
|
+
}));
|
|
807
|
+
}
|
|
808
|
+
async hasIndex(collection, label, property) {
|
|
809
|
+
const indexes = await this.listIndexes(collection);
|
|
810
|
+
return indexes.some((idx) => idx.label === label && idx.property === property);
|
|
811
|
+
}
|
|
812
|
+
async dropIndex(collection, label, property) {
|
|
813
|
+
this.ensureInitialized();
|
|
814
|
+
const response = await this.request(
|
|
815
|
+
"DELETE",
|
|
816
|
+
`/collections/${encodeURIComponent(collection)}/indexes/${encodeURIComponent(label)}/${encodeURIComponent(property)}`
|
|
817
|
+
);
|
|
818
|
+
if (response.error) {
|
|
819
|
+
if (response.error.code === "NOT_FOUND") {
|
|
820
|
+
return false;
|
|
821
|
+
}
|
|
822
|
+
throw new VelesDBError(response.error.message, response.error.code);
|
|
823
|
+
}
|
|
824
|
+
return response.data?.dropped ?? true;
|
|
825
|
+
}
|
|
826
|
+
// ========================================================================
|
|
827
|
+
// Knowledge Graph (EPIC-016 US-041)
|
|
828
|
+
// ========================================================================
|
|
829
|
+
async addEdge(collection, edge) {
|
|
830
|
+
this.ensureInitialized();
|
|
831
|
+
const response = await this.request(
|
|
832
|
+
"POST",
|
|
833
|
+
`/collections/${encodeURIComponent(collection)}/graph/edges`,
|
|
834
|
+
{
|
|
835
|
+
id: edge.id,
|
|
836
|
+
source: edge.source,
|
|
837
|
+
target: edge.target,
|
|
838
|
+
label: edge.label,
|
|
839
|
+
properties: edge.properties ?? {}
|
|
840
|
+
}
|
|
841
|
+
);
|
|
842
|
+
if (response.error) {
|
|
843
|
+
if (response.error.code === "NOT_FOUND") {
|
|
844
|
+
throw new NotFoundError(`Collection '${collection}'`);
|
|
845
|
+
}
|
|
846
|
+
throw new VelesDBError(response.error.message, response.error.code);
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
async getEdges(collection, options) {
|
|
850
|
+
this.ensureInitialized();
|
|
851
|
+
const queryParams = options?.label ? `?label=${encodeURIComponent(options.label)}` : "";
|
|
852
|
+
const response = await this.request(
|
|
853
|
+
"GET",
|
|
854
|
+
`/collections/${encodeURIComponent(collection)}/graph/edges${queryParams}`
|
|
855
|
+
);
|
|
856
|
+
if (response.error) {
|
|
857
|
+
if (response.error.code === "NOT_FOUND") {
|
|
858
|
+
throw new NotFoundError(`Collection '${collection}'`);
|
|
859
|
+
}
|
|
860
|
+
throw new VelesDBError(response.error.message, response.error.code);
|
|
861
|
+
}
|
|
862
|
+
return response.data?.edges ?? [];
|
|
863
|
+
}
|
|
864
|
+
// ========================================================================
|
|
865
|
+
// Graph Traversal (EPIC-016 US-050)
|
|
866
|
+
// ========================================================================
|
|
867
|
+
async traverseGraph(collection, request) {
|
|
868
|
+
this.ensureInitialized();
|
|
869
|
+
const response = await this.request(
|
|
870
|
+
"POST",
|
|
871
|
+
`/collections/${encodeURIComponent(collection)}/graph/traverse`,
|
|
872
|
+
{
|
|
873
|
+
source: request.source,
|
|
874
|
+
strategy: request.strategy ?? "bfs",
|
|
875
|
+
max_depth: request.maxDepth ?? 3,
|
|
876
|
+
limit: request.limit ?? 100,
|
|
877
|
+
cursor: request.cursor,
|
|
878
|
+
rel_types: request.relTypes ?? []
|
|
879
|
+
}
|
|
880
|
+
);
|
|
881
|
+
if (response.error) {
|
|
882
|
+
if (response.error.code === "NOT_FOUND") {
|
|
883
|
+
throw new NotFoundError(`Collection '${collection}'`);
|
|
884
|
+
}
|
|
885
|
+
throw new VelesDBError(response.error.message, response.error.code);
|
|
886
|
+
}
|
|
887
|
+
const data = response.data;
|
|
888
|
+
return {
|
|
889
|
+
results: data.results.map((r) => ({
|
|
890
|
+
targetId: r.target_id,
|
|
891
|
+
depth: r.depth,
|
|
892
|
+
path: r.path
|
|
893
|
+
})),
|
|
894
|
+
nextCursor: data.next_cursor ?? void 0,
|
|
895
|
+
hasMore: data.has_more,
|
|
896
|
+
stats: {
|
|
897
|
+
visited: data.stats.visited,
|
|
898
|
+
depthReached: data.stats.depth_reached
|
|
899
|
+
}
|
|
900
|
+
};
|
|
901
|
+
}
|
|
902
|
+
async getNodeDegree(collection, nodeId) {
|
|
903
|
+
this.ensureInitialized();
|
|
904
|
+
const response = await this.request(
|
|
905
|
+
"GET",
|
|
906
|
+
`/collections/${encodeURIComponent(collection)}/graph/nodes/${nodeId}/degree`
|
|
907
|
+
);
|
|
908
|
+
if (response.error) {
|
|
909
|
+
if (response.error.code === "NOT_FOUND") {
|
|
910
|
+
throw new NotFoundError(`Collection '${collection}'`);
|
|
911
|
+
}
|
|
912
|
+
throw new VelesDBError(response.error.message, response.error.code);
|
|
913
|
+
}
|
|
914
|
+
return {
|
|
915
|
+
inDegree: response.data?.in_degree ?? 0,
|
|
916
|
+
outDegree: response.data?.out_degree ?? 0
|
|
917
|
+
};
|
|
918
|
+
}
|
|
629
919
|
};
|
|
630
920
|
|
|
631
921
|
// src/client.ts
|
|
@@ -879,18 +1169,36 @@ var VelesDB = class {
|
|
|
879
1169
|
return this.backend.hybridSearch(collection, vector, textQuery, options);
|
|
880
1170
|
}
|
|
881
1171
|
/**
|
|
882
|
-
* Execute a VelesQL query
|
|
1172
|
+
* Execute a VelesQL multi-model query (EPIC-031 US-011)
|
|
883
1173
|
*
|
|
1174
|
+
* Supports hybrid vector + graph queries with VelesQL syntax.
|
|
1175
|
+
*
|
|
1176
|
+
* @param collection - Collection name
|
|
884
1177
|
* @param queryString - VelesQL query string
|
|
885
|
-
* @param params -
|
|
886
|
-
* @
|
|
1178
|
+
* @param params - Query parameters (vectors, scalars)
|
|
1179
|
+
* @param options - Query options (timeout, streaming)
|
|
1180
|
+
* @returns Query response with results and execution stats
|
|
1181
|
+
*
|
|
1182
|
+
* @example
|
|
1183
|
+
* ```typescript
|
|
1184
|
+
* const response = await db.query('docs', `
|
|
1185
|
+
* MATCH (d:Doc) WHERE vector NEAR $q LIMIT 20
|
|
1186
|
+
* `, { q: queryVector });
|
|
1187
|
+
*
|
|
1188
|
+
* for (const r of response.results) {
|
|
1189
|
+
* console.log(`Node ${r.nodeId}: ${r.fusedScore}`);
|
|
1190
|
+
* }
|
|
1191
|
+
* ```
|
|
887
1192
|
*/
|
|
888
|
-
async query(queryString, params) {
|
|
1193
|
+
async query(collection, queryString, params, options) {
|
|
889
1194
|
this.ensureInitialized();
|
|
1195
|
+
if (!collection || typeof collection !== "string") {
|
|
1196
|
+
throw new ValidationError("Collection name must be a non-empty string");
|
|
1197
|
+
}
|
|
890
1198
|
if (!queryString || typeof queryString !== "string") {
|
|
891
1199
|
throw new ValidationError("Query string must be a non-empty string");
|
|
892
1200
|
}
|
|
893
|
-
return this.backend.query(queryString, params);
|
|
1201
|
+
return this.backend.query(collection, queryString, params, options);
|
|
894
1202
|
}
|
|
895
1203
|
/**
|
|
896
1204
|
* Multi-query fusion search combining results from multiple query vectors
|
|
@@ -965,7 +1273,471 @@ var VelesDB = class {
|
|
|
965
1273
|
get backendType() {
|
|
966
1274
|
return this.config.backend;
|
|
967
1275
|
}
|
|
1276
|
+
// ========================================================================
|
|
1277
|
+
// Index Management (EPIC-009)
|
|
1278
|
+
// ========================================================================
|
|
1279
|
+
/**
|
|
1280
|
+
* Create a property index for O(1) equality lookups or O(log n) range queries
|
|
1281
|
+
*
|
|
1282
|
+
* @param collection - Collection name
|
|
1283
|
+
* @param options - Index configuration (label, property, indexType)
|
|
1284
|
+
*
|
|
1285
|
+
* @example
|
|
1286
|
+
* ```typescript
|
|
1287
|
+
* // Create hash index for fast email lookups
|
|
1288
|
+
* await db.createIndex('users', { label: 'Person', property: 'email' });
|
|
1289
|
+
*
|
|
1290
|
+
* // Create range index for timestamp queries
|
|
1291
|
+
* await db.createIndex('events', { label: 'Event', property: 'timestamp', indexType: 'range' });
|
|
1292
|
+
* ```
|
|
1293
|
+
*/
|
|
1294
|
+
async createIndex(collection, options) {
|
|
1295
|
+
this.ensureInitialized();
|
|
1296
|
+
if (!options.label || !options.property) {
|
|
1297
|
+
throw new ValidationError("Index requires label and property");
|
|
1298
|
+
}
|
|
1299
|
+
await this.backend.createIndex(collection, options);
|
|
1300
|
+
}
|
|
1301
|
+
/**
|
|
1302
|
+
* List all indexes on a collection
|
|
1303
|
+
*
|
|
1304
|
+
* @param collection - Collection name
|
|
1305
|
+
* @returns Array of index information
|
|
1306
|
+
*/
|
|
1307
|
+
async listIndexes(collection) {
|
|
1308
|
+
this.ensureInitialized();
|
|
1309
|
+
return this.backend.listIndexes(collection);
|
|
1310
|
+
}
|
|
1311
|
+
/**
|
|
1312
|
+
* Check if an index exists
|
|
1313
|
+
*
|
|
1314
|
+
* @param collection - Collection name
|
|
1315
|
+
* @param label - Node label
|
|
1316
|
+
* @param property - Property name
|
|
1317
|
+
* @returns true if index exists
|
|
1318
|
+
*/
|
|
1319
|
+
async hasIndex(collection, label, property) {
|
|
1320
|
+
this.ensureInitialized();
|
|
1321
|
+
return this.backend.hasIndex(collection, label, property);
|
|
1322
|
+
}
|
|
1323
|
+
/**
|
|
1324
|
+
* Drop an index
|
|
1325
|
+
*
|
|
1326
|
+
* @param collection - Collection name
|
|
1327
|
+
* @param label - Node label
|
|
1328
|
+
* @param property - Property name
|
|
1329
|
+
* @returns true if index was dropped, false if it didn't exist
|
|
1330
|
+
*/
|
|
1331
|
+
async dropIndex(collection, label, property) {
|
|
1332
|
+
this.ensureInitialized();
|
|
1333
|
+
return this.backend.dropIndex(collection, label, property);
|
|
1334
|
+
}
|
|
1335
|
+
// ========================================================================
|
|
1336
|
+
// Knowledge Graph (EPIC-016 US-041)
|
|
1337
|
+
// ========================================================================
|
|
1338
|
+
/**
|
|
1339
|
+
* Add an edge to the collection's knowledge graph
|
|
1340
|
+
*
|
|
1341
|
+
* @param collection - Collection name
|
|
1342
|
+
* @param edge - Edge to add (id, source, target, label, properties)
|
|
1343
|
+
*
|
|
1344
|
+
* @example
|
|
1345
|
+
* ```typescript
|
|
1346
|
+
* await db.addEdge('social', {
|
|
1347
|
+
* id: 1,
|
|
1348
|
+
* source: 100,
|
|
1349
|
+
* target: 200,
|
|
1350
|
+
* label: 'FOLLOWS',
|
|
1351
|
+
* properties: { since: '2024-01-01' }
|
|
1352
|
+
* });
|
|
1353
|
+
* ```
|
|
1354
|
+
*/
|
|
1355
|
+
async addEdge(collection, edge) {
|
|
1356
|
+
this.ensureInitialized();
|
|
1357
|
+
if (!edge.label || typeof edge.label !== "string") {
|
|
1358
|
+
throw new ValidationError("Edge label is required and must be a string");
|
|
1359
|
+
}
|
|
1360
|
+
if (typeof edge.source !== "number" || typeof edge.target !== "number") {
|
|
1361
|
+
throw new ValidationError("Edge source and target must be numbers");
|
|
1362
|
+
}
|
|
1363
|
+
await this.backend.addEdge(collection, edge);
|
|
1364
|
+
}
|
|
1365
|
+
/**
|
|
1366
|
+
* Get edges from the collection's knowledge graph
|
|
1367
|
+
*
|
|
1368
|
+
* @param collection - Collection name
|
|
1369
|
+
* @param options - Query options (filter by label)
|
|
1370
|
+
* @returns Array of edges
|
|
1371
|
+
*
|
|
1372
|
+
* @example
|
|
1373
|
+
* ```typescript
|
|
1374
|
+
* // Get all edges with label "FOLLOWS"
|
|
1375
|
+
* const edges = await db.getEdges('social', { label: 'FOLLOWS' });
|
|
1376
|
+
* ```
|
|
1377
|
+
*/
|
|
1378
|
+
async getEdges(collection, options) {
|
|
1379
|
+
this.ensureInitialized();
|
|
1380
|
+
return this.backend.getEdges(collection, options);
|
|
1381
|
+
}
|
|
1382
|
+
// ========================================================================
|
|
1383
|
+
// Graph Traversal (EPIC-016 US-050)
|
|
1384
|
+
// ========================================================================
|
|
1385
|
+
/**
|
|
1386
|
+
* Traverse the graph using BFS or DFS from a source node
|
|
1387
|
+
*
|
|
1388
|
+
* @param collection - Collection name
|
|
1389
|
+
* @param request - Traversal request options
|
|
1390
|
+
* @returns Traversal response with results and stats
|
|
1391
|
+
*
|
|
1392
|
+
* @example
|
|
1393
|
+
* ```typescript
|
|
1394
|
+
* // BFS traversal from node 100
|
|
1395
|
+
* const result = await db.traverseGraph('social', {
|
|
1396
|
+
* source: 100,
|
|
1397
|
+
* strategy: 'bfs',
|
|
1398
|
+
* maxDepth: 3,
|
|
1399
|
+
* limit: 100,
|
|
1400
|
+
* relTypes: ['FOLLOWS', 'KNOWS']
|
|
1401
|
+
* });
|
|
1402
|
+
*
|
|
1403
|
+
* for (const node of result.results) {
|
|
1404
|
+
* console.log(`Reached node ${node.targetId} at depth ${node.depth}`);
|
|
1405
|
+
* }
|
|
1406
|
+
* ```
|
|
1407
|
+
*/
|
|
1408
|
+
async traverseGraph(collection, request) {
|
|
1409
|
+
this.ensureInitialized();
|
|
1410
|
+
if (typeof request.source !== "number") {
|
|
1411
|
+
throw new ValidationError("Source node ID must be a number");
|
|
1412
|
+
}
|
|
1413
|
+
if (request.strategy && !["bfs", "dfs"].includes(request.strategy)) {
|
|
1414
|
+
throw new ValidationError("Strategy must be 'bfs' or 'dfs'");
|
|
1415
|
+
}
|
|
1416
|
+
return this.backend.traverseGraph(collection, request);
|
|
1417
|
+
}
|
|
1418
|
+
/**
|
|
1419
|
+
* Get the in-degree and out-degree of a node
|
|
1420
|
+
*
|
|
1421
|
+
* @param collection - Collection name
|
|
1422
|
+
* @param nodeId - Node ID
|
|
1423
|
+
* @returns Degree response with inDegree and outDegree
|
|
1424
|
+
*
|
|
1425
|
+
* @example
|
|
1426
|
+
* ```typescript
|
|
1427
|
+
* const degree = await db.getNodeDegree('social', 100);
|
|
1428
|
+
* console.log(`In: ${degree.inDegree}, Out: ${degree.outDegree}`);
|
|
1429
|
+
* ```
|
|
1430
|
+
*/
|
|
1431
|
+
async getNodeDegree(collection, nodeId) {
|
|
1432
|
+
this.ensureInitialized();
|
|
1433
|
+
if (typeof nodeId !== "number") {
|
|
1434
|
+
throw new ValidationError("Node ID must be a number");
|
|
1435
|
+
}
|
|
1436
|
+
return this.backend.getNodeDegree(collection, nodeId);
|
|
1437
|
+
}
|
|
1438
|
+
};
|
|
1439
|
+
|
|
1440
|
+
// src/query-builder.ts
|
|
1441
|
+
var VelesQLBuilder = class _VelesQLBuilder {
|
|
1442
|
+
constructor(state) {
|
|
1443
|
+
this.state = {
|
|
1444
|
+
matchClauses: state?.matchClauses ?? [],
|
|
1445
|
+
whereClauses: state?.whereClauses ?? [],
|
|
1446
|
+
whereOperators: state?.whereOperators ?? [],
|
|
1447
|
+
params: state?.params ?? {},
|
|
1448
|
+
limitValue: state?.limitValue,
|
|
1449
|
+
offsetValue: state?.offsetValue,
|
|
1450
|
+
orderByClause: state?.orderByClause,
|
|
1451
|
+
returnClause: state?.returnClause,
|
|
1452
|
+
fusionOptions: state?.fusionOptions,
|
|
1453
|
+
currentNode: state?.currentNode,
|
|
1454
|
+
pendingRel: state?.pendingRel
|
|
1455
|
+
};
|
|
1456
|
+
}
|
|
1457
|
+
clone(updates) {
|
|
1458
|
+
return new _VelesQLBuilder({
|
|
1459
|
+
...this.state,
|
|
1460
|
+
matchClauses: [...this.state.matchClauses],
|
|
1461
|
+
whereClauses: [...this.state.whereClauses],
|
|
1462
|
+
whereOperators: [...this.state.whereOperators],
|
|
1463
|
+
params: { ...this.state.params },
|
|
1464
|
+
...updates
|
|
1465
|
+
});
|
|
1466
|
+
}
|
|
1467
|
+
/**
|
|
1468
|
+
* Start a MATCH clause with a node pattern
|
|
1469
|
+
*
|
|
1470
|
+
* @param alias - Node alias (e.g., 'n', 'person')
|
|
1471
|
+
* @param label - Optional node label(s)
|
|
1472
|
+
*/
|
|
1473
|
+
match(alias, label) {
|
|
1474
|
+
const labelStr = this.formatLabel(label);
|
|
1475
|
+
const nodePattern = `(${alias}${labelStr})`;
|
|
1476
|
+
return this.clone({
|
|
1477
|
+
matchClauses: [...this.state.matchClauses, nodePattern],
|
|
1478
|
+
currentNode: alias
|
|
1479
|
+
});
|
|
1480
|
+
}
|
|
1481
|
+
/**
|
|
1482
|
+
* Add a relationship pattern
|
|
1483
|
+
*
|
|
1484
|
+
* @param type - Relationship type (e.g., 'KNOWS', 'FOLLOWS')
|
|
1485
|
+
* @param alias - Optional relationship alias
|
|
1486
|
+
* @param options - Relationship options (direction, hops)
|
|
1487
|
+
*/
|
|
1488
|
+
rel(type, alias, options) {
|
|
1489
|
+
return this.clone({
|
|
1490
|
+
pendingRel: { type, alias, options }
|
|
1491
|
+
});
|
|
1492
|
+
}
|
|
1493
|
+
/**
|
|
1494
|
+
* Complete a relationship pattern with target node
|
|
1495
|
+
*
|
|
1496
|
+
* @param alias - Target node alias
|
|
1497
|
+
* @param label - Optional target node label(s)
|
|
1498
|
+
*/
|
|
1499
|
+
to(alias, label) {
|
|
1500
|
+
if (!this.state.pendingRel) {
|
|
1501
|
+
throw new Error("to() must be called after rel()");
|
|
1502
|
+
}
|
|
1503
|
+
const { type, alias: relAlias, options } = this.state.pendingRel;
|
|
1504
|
+
const direction = options?.direction ?? "outgoing";
|
|
1505
|
+
const labelStr = this.formatLabel(label);
|
|
1506
|
+
const relPattern = this.formatRelationship(type, relAlias, options);
|
|
1507
|
+
const targetNode = `(${alias}${labelStr})`;
|
|
1508
|
+
let fullPattern;
|
|
1509
|
+
switch (direction) {
|
|
1510
|
+
case "incoming":
|
|
1511
|
+
fullPattern = `<-${relPattern}-${targetNode}`;
|
|
1512
|
+
break;
|
|
1513
|
+
case "both":
|
|
1514
|
+
fullPattern = `-${relPattern}-${targetNode}`;
|
|
1515
|
+
break;
|
|
1516
|
+
default:
|
|
1517
|
+
fullPattern = `-${relPattern}->${targetNode}`;
|
|
1518
|
+
}
|
|
1519
|
+
const lastMatch = this.state.matchClauses[this.state.matchClauses.length - 1];
|
|
1520
|
+
const updatedMatch = lastMatch + fullPattern;
|
|
1521
|
+
const newMatchClauses = [...this.state.matchClauses.slice(0, -1), updatedMatch];
|
|
1522
|
+
return this.clone({
|
|
1523
|
+
matchClauses: newMatchClauses,
|
|
1524
|
+
currentNode: alias,
|
|
1525
|
+
pendingRel: void 0
|
|
1526
|
+
});
|
|
1527
|
+
}
|
|
1528
|
+
/**
|
|
1529
|
+
* Add a WHERE clause
|
|
1530
|
+
*
|
|
1531
|
+
* @param condition - WHERE condition
|
|
1532
|
+
* @param params - Optional parameters
|
|
1533
|
+
*/
|
|
1534
|
+
where(condition, params) {
|
|
1535
|
+
const newParams = params ? { ...this.state.params, ...params } : this.state.params;
|
|
1536
|
+
return this.clone({
|
|
1537
|
+
whereClauses: [...this.state.whereClauses, condition],
|
|
1538
|
+
whereOperators: [...this.state.whereOperators],
|
|
1539
|
+
params: newParams
|
|
1540
|
+
});
|
|
1541
|
+
}
|
|
1542
|
+
/**
|
|
1543
|
+
* Add an AND WHERE clause
|
|
1544
|
+
*
|
|
1545
|
+
* @param condition - WHERE condition
|
|
1546
|
+
* @param params - Optional parameters
|
|
1547
|
+
*/
|
|
1548
|
+
andWhere(condition, params) {
|
|
1549
|
+
const newParams = params ? { ...this.state.params, ...params } : this.state.params;
|
|
1550
|
+
return this.clone({
|
|
1551
|
+
whereClauses: [...this.state.whereClauses, condition],
|
|
1552
|
+
whereOperators: [...this.state.whereOperators, "AND"],
|
|
1553
|
+
params: newParams
|
|
1554
|
+
});
|
|
1555
|
+
}
|
|
1556
|
+
/**
|
|
1557
|
+
* Add an OR WHERE clause
|
|
1558
|
+
*
|
|
1559
|
+
* @param condition - WHERE condition
|
|
1560
|
+
* @param params - Optional parameters
|
|
1561
|
+
*/
|
|
1562
|
+
orWhere(condition, params) {
|
|
1563
|
+
const newParams = params ? { ...this.state.params, ...params } : this.state.params;
|
|
1564
|
+
return this.clone({
|
|
1565
|
+
whereClauses: [...this.state.whereClauses, condition],
|
|
1566
|
+
whereOperators: [...this.state.whereOperators, "OR"],
|
|
1567
|
+
params: newParams
|
|
1568
|
+
});
|
|
1569
|
+
}
|
|
1570
|
+
/**
|
|
1571
|
+
* Add a vector NEAR clause for similarity search
|
|
1572
|
+
*
|
|
1573
|
+
* @param paramName - Parameter name (e.g., '$query', '$embedding')
|
|
1574
|
+
* @param vector - Vector data
|
|
1575
|
+
* @param options - NEAR options (topK)
|
|
1576
|
+
*/
|
|
1577
|
+
nearVector(paramName, vector, options) {
|
|
1578
|
+
const cleanParamName = paramName.startsWith("$") ? paramName.slice(1) : paramName;
|
|
1579
|
+
const topKSuffix = options?.topK ? ` TOP ${options.topK}` : "";
|
|
1580
|
+
const condition = `vector NEAR $${cleanParamName}${topKSuffix}`;
|
|
1581
|
+
const newParams = { ...this.state.params, [cleanParamName]: vector };
|
|
1582
|
+
if (this.state.whereClauses.length === 0) {
|
|
1583
|
+
return this.clone({
|
|
1584
|
+
whereClauses: [condition],
|
|
1585
|
+
params: newParams
|
|
1586
|
+
});
|
|
1587
|
+
}
|
|
1588
|
+
return this.clone({
|
|
1589
|
+
whereClauses: [...this.state.whereClauses, condition],
|
|
1590
|
+
whereOperators: [...this.state.whereOperators, "AND"],
|
|
1591
|
+
params: newParams
|
|
1592
|
+
});
|
|
1593
|
+
}
|
|
1594
|
+
/**
|
|
1595
|
+
* Add LIMIT clause
|
|
1596
|
+
*
|
|
1597
|
+
* @param value - Maximum number of results
|
|
1598
|
+
*/
|
|
1599
|
+
limit(value) {
|
|
1600
|
+
if (value < 0) {
|
|
1601
|
+
throw new Error("LIMIT must be non-negative");
|
|
1602
|
+
}
|
|
1603
|
+
return this.clone({ limitValue: value });
|
|
1604
|
+
}
|
|
1605
|
+
/**
|
|
1606
|
+
* Add OFFSET clause
|
|
1607
|
+
*
|
|
1608
|
+
* @param value - Number of results to skip
|
|
1609
|
+
*/
|
|
1610
|
+
offset(value) {
|
|
1611
|
+
if (value < 0) {
|
|
1612
|
+
throw new Error("OFFSET must be non-negative");
|
|
1613
|
+
}
|
|
1614
|
+
return this.clone({ offsetValue: value });
|
|
1615
|
+
}
|
|
1616
|
+
/**
|
|
1617
|
+
* Add ORDER BY clause
|
|
1618
|
+
*
|
|
1619
|
+
* @param field - Field to order by
|
|
1620
|
+
* @param direction - Sort direction (ASC or DESC)
|
|
1621
|
+
*/
|
|
1622
|
+
orderBy(field, direction) {
|
|
1623
|
+
const orderClause = direction ? `${field} ${direction}` : field;
|
|
1624
|
+
return this.clone({ orderByClause: orderClause });
|
|
1625
|
+
}
|
|
1626
|
+
/**
|
|
1627
|
+
* Add RETURN clause with specific fields
|
|
1628
|
+
*
|
|
1629
|
+
* @param fields - Fields to return (array or object with aliases)
|
|
1630
|
+
*/
|
|
1631
|
+
return(fields) {
|
|
1632
|
+
let returnClause;
|
|
1633
|
+
if (Array.isArray(fields)) {
|
|
1634
|
+
returnClause = fields.join(", ");
|
|
1635
|
+
} else {
|
|
1636
|
+
returnClause = Object.entries(fields).map(([field, alias]) => `${field} AS ${alias}`).join(", ");
|
|
1637
|
+
}
|
|
1638
|
+
return this.clone({ returnClause });
|
|
1639
|
+
}
|
|
1640
|
+
/**
|
|
1641
|
+
* Add RETURN * clause
|
|
1642
|
+
*/
|
|
1643
|
+
returnAll() {
|
|
1644
|
+
return this.clone({ returnClause: "*" });
|
|
1645
|
+
}
|
|
1646
|
+
/**
|
|
1647
|
+
* Set fusion strategy for hybrid queries
|
|
1648
|
+
*
|
|
1649
|
+
* @param strategy - Fusion strategy
|
|
1650
|
+
* @param options - Fusion parameters
|
|
1651
|
+
*/
|
|
1652
|
+
fusion(strategy, options) {
|
|
1653
|
+
return this.clone({
|
|
1654
|
+
fusionOptions: {
|
|
1655
|
+
strategy,
|
|
1656
|
+
...options
|
|
1657
|
+
}
|
|
1658
|
+
});
|
|
1659
|
+
}
|
|
1660
|
+
/**
|
|
1661
|
+
* Get the fusion options
|
|
1662
|
+
*/
|
|
1663
|
+
getFusionOptions() {
|
|
1664
|
+
return this.state.fusionOptions;
|
|
1665
|
+
}
|
|
1666
|
+
/**
|
|
1667
|
+
* Get all parameters
|
|
1668
|
+
*/
|
|
1669
|
+
getParams() {
|
|
1670
|
+
return { ...this.state.params };
|
|
1671
|
+
}
|
|
1672
|
+
/**
|
|
1673
|
+
* Build the VelesQL query string
|
|
1674
|
+
*/
|
|
1675
|
+
toVelesQL() {
|
|
1676
|
+
if (this.state.matchClauses.length === 0) {
|
|
1677
|
+
throw new Error("Query must have at least one MATCH clause");
|
|
1678
|
+
}
|
|
1679
|
+
const parts = [];
|
|
1680
|
+
parts.push(`MATCH ${this.state.matchClauses.join(", ")}`);
|
|
1681
|
+
if (this.state.whereClauses.length > 0) {
|
|
1682
|
+
const whereStr = this.buildWhereClause();
|
|
1683
|
+
parts.push(`WHERE ${whereStr}`);
|
|
1684
|
+
}
|
|
1685
|
+
if (this.state.orderByClause) {
|
|
1686
|
+
parts.push(`ORDER BY ${this.state.orderByClause}`);
|
|
1687
|
+
}
|
|
1688
|
+
if (this.state.limitValue !== void 0) {
|
|
1689
|
+
parts.push(`LIMIT ${this.state.limitValue}`);
|
|
1690
|
+
}
|
|
1691
|
+
if (this.state.offsetValue !== void 0) {
|
|
1692
|
+
parts.push(`OFFSET ${this.state.offsetValue}`);
|
|
1693
|
+
}
|
|
1694
|
+
if (this.state.returnClause) {
|
|
1695
|
+
parts.push(`RETURN ${this.state.returnClause}`);
|
|
1696
|
+
}
|
|
1697
|
+
if (this.state.fusionOptions) {
|
|
1698
|
+
parts.push(`/* FUSION ${this.state.fusionOptions.strategy} */`);
|
|
1699
|
+
}
|
|
1700
|
+
return parts.join(" ");
|
|
1701
|
+
}
|
|
1702
|
+
formatLabel(label) {
|
|
1703
|
+
if (!label) return "";
|
|
1704
|
+
if (Array.isArray(label)) {
|
|
1705
|
+
return label.map((l) => `:${l}`).join("");
|
|
1706
|
+
}
|
|
1707
|
+
return `:${label}`;
|
|
1708
|
+
}
|
|
1709
|
+
formatRelationship(type, alias, options) {
|
|
1710
|
+
const aliasStr = alias ? alias : "";
|
|
1711
|
+
const hopsStr = this.formatHops(options);
|
|
1712
|
+
if (alias) {
|
|
1713
|
+
return `[${aliasStr}:${type}${hopsStr}]`;
|
|
1714
|
+
}
|
|
1715
|
+
return `[:${type}${hopsStr}]`;
|
|
1716
|
+
}
|
|
1717
|
+
formatHops(options) {
|
|
1718
|
+
if (!options?.minHops && !options?.maxHops) return "";
|
|
1719
|
+
const min = options.minHops ?? 1;
|
|
1720
|
+
const max = options.maxHops ?? "";
|
|
1721
|
+
return `*${min}..${max}`;
|
|
1722
|
+
}
|
|
1723
|
+
buildWhereClause() {
|
|
1724
|
+
if (this.state.whereClauses.length === 0) return "";
|
|
1725
|
+
const first = this.state.whereClauses[0];
|
|
1726
|
+
if (!first) return "";
|
|
1727
|
+
let result = first;
|
|
1728
|
+
for (let i = 1; i < this.state.whereClauses.length; i++) {
|
|
1729
|
+
const operator = this.state.whereOperators[i - 1] ?? "AND";
|
|
1730
|
+
const clause = this.state.whereClauses[i];
|
|
1731
|
+
if (clause) {
|
|
1732
|
+
result += ` ${operator} ${clause}`;
|
|
1733
|
+
}
|
|
1734
|
+
}
|
|
1735
|
+
return result;
|
|
1736
|
+
}
|
|
968
1737
|
};
|
|
1738
|
+
function velesql() {
|
|
1739
|
+
return new VelesQLBuilder();
|
|
1740
|
+
}
|
|
969
1741
|
export {
|
|
970
1742
|
ConnectionError,
|
|
971
1743
|
NotFoundError,
|
|
@@ -973,5 +1745,7 @@ export {
|
|
|
973
1745
|
ValidationError,
|
|
974
1746
|
VelesDB,
|
|
975
1747
|
VelesDBError,
|
|
976
|
-
|
|
1748
|
+
VelesQLBuilder,
|
|
1749
|
+
WasmBackend,
|
|
1750
|
+
velesql
|
|
977
1751
|
};
|