speedkey 0.1.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.
@@ -0,0 +1,627 @@
1
+ "use strict";
2
+ /**
3
+ * Copyright Valkey GLIDE Project Contributors - SPDX Identifier: Apache-2.0
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.GlideFt = void 0;
7
+ const __1 = require("..");
8
+ /** Module for Vector Search commands. */
9
+ class GlideFt {
10
+ /**
11
+ * Creates an index and initiates a backfill of that index.
12
+ *
13
+ * @param client - The client to execute the command.
14
+ * @param indexName - The index name for the index to be created.
15
+ * @param schema - The fields of the index schema, specifying the fields and their types.
16
+ * @param options - (Optional) Options for the `FT.CREATE` command. See {@link FtCreateOptions}.
17
+ * @returns If the index is successfully created, returns "OK".
18
+ *
19
+ * @example
20
+ * ```typescript
21
+ * // Example usage of FT.CREATE to create a 6-dimensional JSON index using the HNSW algorithm
22
+ * await GlideFt.create(client, "json_idx1", [{
23
+ * type: "VECTOR",
24
+ * name: "$.vec",
25
+ * alias: "VEC",
26
+ * attributes: {
27
+ * algorithm: "HNSW",
28
+ * type: "FLOAT32",
29
+ * dimension: 6,
30
+ * distanceMetric: "L2",
31
+ * numberOfEdges: 32,
32
+ * },
33
+ * }], {
34
+ * dataType: "JSON",
35
+ * prefixes: ["json:"]
36
+ * });
37
+ * ```
38
+ */
39
+ static async create(client, indexName, schema, options) {
40
+ const args = ["FT.CREATE", indexName];
41
+ if (options) {
42
+ if ("dataType" in options) {
43
+ args.push("ON", options.dataType);
44
+ }
45
+ if ("prefixes" in options && options.prefixes) {
46
+ args.push("PREFIX", options.prefixes.length.toString(), ...options.prefixes);
47
+ }
48
+ }
49
+ args.push("SCHEMA");
50
+ schema.forEach((f) => {
51
+ args.push(f.name);
52
+ if (f.alias) {
53
+ args.push("AS", f.alias);
54
+ }
55
+ args.push(f.type);
56
+ switch (f.type) {
57
+ case "TAG": {
58
+ if (f.separator) {
59
+ args.push("SEPARATOR", f.separator);
60
+ }
61
+ if (f.caseSensitive) {
62
+ args.push("CASESENSITIVE");
63
+ }
64
+ break;
65
+ }
66
+ case "VECTOR": {
67
+ if (f.attributes) {
68
+ args.push(f.attributes.algorithm);
69
+ const attributes = [];
70
+ // all VectorFieldAttributes attributes
71
+ if (f.attributes.dimensions) {
72
+ attributes.push("DIM", f.attributes.dimensions.toString());
73
+ }
74
+ if (f.attributes.distanceMetric) {
75
+ attributes.push("DISTANCE_METRIC", f.attributes.distanceMetric.toString());
76
+ }
77
+ if (f.attributes.type) {
78
+ attributes.push("TYPE", f.attributes.type.toString());
79
+ }
80
+ else {
81
+ attributes.push("TYPE", "FLOAT32");
82
+ }
83
+ if (f.attributes.initialCap) {
84
+ attributes.push("INITIAL_CAP", f.attributes.initialCap.toString());
85
+ }
86
+ // VectorFieldAttributesHnsw attributes
87
+ if ("m" in f.attributes && f.attributes.m) {
88
+ attributes.push("M", f.attributes.m.toString());
89
+ }
90
+ if ("efContruction" in f.attributes &&
91
+ f.attributes.efContruction) {
92
+ attributes.push("EF_CONSTRUCTION", f.attributes.efContruction.toString());
93
+ }
94
+ if ("efRuntime" in f.attributes &&
95
+ f.attributes.efRuntime) {
96
+ attributes.push("EF_RUNTIME", f.attributes.efRuntime.toString());
97
+ }
98
+ args.push(attributes.length.toString(), ...attributes);
99
+ }
100
+ break;
101
+ }
102
+ default:
103
+ // no-op
104
+ }
105
+ });
106
+ return _handleCustomCommand(client, args, {
107
+ decoder: __1.Decoder.String,
108
+ });
109
+ }
110
+ /**
111
+ * Deletes an index and associated content. Indexed document keys are unaffected.
112
+ *
113
+ * @param client - The client to execute the command.
114
+ * @param indexName - The index name.
115
+ * @returns "OK"
116
+ *
117
+ * @example
118
+ * ```typescript
119
+ * // Example usage of FT.DROPINDEX to drop an index
120
+ * await GlideFt.dropindex(client, "json_idx1"); // "OK"
121
+ * ```
122
+ */
123
+ static async dropindex(client, indexName) {
124
+ const args = ["FT.DROPINDEX", indexName];
125
+ return _handleCustomCommand(client, args, {
126
+ decoder: __1.Decoder.String,
127
+ });
128
+ }
129
+ /**
130
+ * Lists all indexes.
131
+ *
132
+ * @param client - The client to execute the command.
133
+ * @param options - (Optional) See {@link DecoderOption}.
134
+ * @returns An array of index names.
135
+ *
136
+ * @example
137
+ * ```typescript
138
+ * console.log(await GlideFt.list(client)); // Output: ["index1", "index2"]
139
+ * ```
140
+ */
141
+ static async list(client, options) {
142
+ return _handleCustomCommand(client, ["FT._LIST"], options);
143
+ }
144
+ /**
145
+ * Runs a search query on an index, and perform aggregate transformations on the results.
146
+ *
147
+ * @param client - The client to execute the command.
148
+ * @param indexName - The index name.
149
+ * @param query - The text query to search.
150
+ * @param options - Additional parameters for the command - see {@link FtAggregateOptions} and {@link DecoderOption}.
151
+ * @returns Results of the last stage of the pipeline.
152
+ *
153
+ * @example
154
+ * ```typescript
155
+ * const options: FtAggregateOptions = {
156
+ * loadFields: ["__key"],
157
+ * clauses: [
158
+ * {
159
+ * type: "GROUPBY",
160
+ * properties: ["@condition"],
161
+ * reducers: [
162
+ * {
163
+ * function: "TOLIST",
164
+ * args: ["__key"],
165
+ * name: "bicycles",
166
+ * },
167
+ * ],
168
+ * },
169
+ * ],
170
+ * };
171
+ * const result = await GlideFt.aggregate(client, "myIndex", "*", options);
172
+ * console.log(result); // Output:
173
+ * // [
174
+ * // [
175
+ * // {
176
+ * // key: "condition",
177
+ * // value: "refurbished"
178
+ * // },
179
+ * // {
180
+ * // key: "bicycles",
181
+ * // value: [ "bicycle:9" ]
182
+ * // }
183
+ * // ],
184
+ * // [
185
+ * // {
186
+ * // key: "condition",
187
+ * // value: "used"
188
+ * // },
189
+ * // {
190
+ * // key: "bicycles",
191
+ * // value: [ "bicycle:1", "bicycle:2", "bicycle:3" ]
192
+ * // }
193
+ * // ],
194
+ * // [
195
+ * // {
196
+ * // key: "condition",
197
+ * // value: "new"
198
+ * // },
199
+ * // {
200
+ * // key: "bicycles",
201
+ * // value: [ "bicycle:0", "bicycle:5" ]
202
+ * // }
203
+ * // ]
204
+ * // ]
205
+ * ```
206
+ */
207
+ static async aggregate(client, indexName, query, options) {
208
+ const args = [
209
+ "FT.AGGREGATE",
210
+ indexName,
211
+ query,
212
+ ..._addFtAggregateOptions(options),
213
+ ];
214
+ return _handleCustomCommand(client, args, options);
215
+ }
216
+ /**
217
+ * Returns information about a given index.
218
+ *
219
+ * @param client - The client to execute the command.
220
+ * @param indexName - The index name.
221
+ * @param options - (Optional) See {@link DecoderOption}.
222
+ * @returns Nested maps with info about the index. See example for more details.
223
+ *
224
+ * @example
225
+ * ```typescript
226
+ * const info = await GlideFt.info(client, "myIndex");
227
+ * console.log(info); // Output:
228
+ * // {
229
+ * // index_name: 'myIndex',
230
+ * // index_status: 'AVAILABLE',
231
+ * // key_type: 'JSON',
232
+ * // creation_timestamp: 1728348101728771,
233
+ * // key_prefixes: [ 'json:' ],
234
+ * // num_indexed_vectors: 0,
235
+ * // space_usage: 653471,
236
+ * // num_docs: 0,
237
+ * // vector_space_usage: 653471,
238
+ * // index_degradation_percentage: 0,
239
+ * // fulltext_space_usage: 0,
240
+ * // current_lag: 0,
241
+ * // fields: [
242
+ * // {
243
+ * // identifier: '$.vec',
244
+ * // type: 'VECTOR',
245
+ * // field_name: 'VEC',
246
+ * // option: '',
247
+ * // vector_params: {
248
+ * // data_type: 'FLOAT32',
249
+ * // initial_capacity: 1000,
250
+ * // current_capacity: 1000,
251
+ * // distance_metric: 'L2',
252
+ * // dimension: 6,
253
+ * // block_size: 1024,
254
+ * // algorithm: 'FLAT'
255
+ * // }
256
+ * // },
257
+ * // {
258
+ * // identifier: 'name',
259
+ * // type: 'TEXT',
260
+ * // field_name: 'name',
261
+ * // option: ''
262
+ * // },
263
+ * // ]
264
+ * // }
265
+ * ```
266
+ */
267
+ static async info(client, indexName, options) {
268
+ const args = ["FT.INFO", indexName];
269
+ return _handleCustomCommand(client, args, options).then(__1.convertGlideRecordToRecord);
270
+ }
271
+ /**
272
+ * Parse a query and return information about how that query was parsed.
273
+ *
274
+ * @param client - The client to execute the command.
275
+ * @param indexName - The index name.
276
+ * @param query - The text query to search. It is the same as the query passed as
277
+ * an argument to {@link search | FT.SEARCH} or {@link aggregate | FT.AGGREGATE}.
278
+ * @param options - (Optional) See {@link DecoderOption}.
279
+ * @returns A query execution plan.
280
+ *
281
+ * @example
282
+ * ```typescript
283
+ * const result = GlideFt.explain(client, "myIndex", "@price:[0 10]");
284
+ * console.log(result); // Output: "Field {\n\tprice\n\t0\n\t10\n}"
285
+ * ```
286
+ */
287
+ static explain(client, indexName, query, options) {
288
+ const args = ["FT.EXPLAIN", indexName, query];
289
+ return _handleCustomCommand(client, args, options);
290
+ }
291
+ /**
292
+ * Parse a query and return information about how that query was parsed.
293
+ * Same as {@link explain | FT.EXPLAIN}, except that the results are
294
+ * displayed in a different format.
295
+ *
296
+ * @param client - The client to execute the command.
297
+ * @param indexName - The index name.
298
+ * @param query - The text query to search. It is the same as the query passed as
299
+ * an argument to {@link search | FT.SEARCH} or {@link aggregate | FT.AGGREGATE}.
300
+ * @param options - (Optional) See {@link DecoderOption}.
301
+ * @returns A query execution plan.
302
+ *
303
+ * @example
304
+ * ```typescript
305
+ * const result = GlideFt.explaincli(client, "myIndex", "@price:[0 10]");
306
+ * console.log(result); // Output: ["Field {", "price", "0", "10", "}"]
307
+ * ```
308
+ */
309
+ static explaincli(client, indexName, query, options) {
310
+ const args = ["FT.EXPLAINCLI", indexName, query];
311
+ return _handleCustomCommand(client, args, options);
312
+ }
313
+ /**
314
+ * Uses the provided query expression to locate keys within an index. Once located, the count
315
+ * and/or content of indexed fields within those keys can be returned.
316
+ *
317
+ * @param client - The client to execute the command.
318
+ * @param indexName - The index name to search into.
319
+ * @param query - The text query to search.
320
+ * @param options - (Optional) See {@link FtSearchOptions} and {@link DecoderOption}.
321
+ * @returns A two-element array, where the first element is the number of documents in the result set, and the
322
+ * second element has the format: `GlideRecord<GlideRecord<GlideString>>`:
323
+ * a mapping between document names and a map of their attributes.
324
+ *
325
+ * If `count` or `limit` with values `{offset: 0, count: 0}` is
326
+ * set, the command returns array with only one element: the number of documents.
327
+ *
328
+ * @example
329
+ * ```typescript
330
+ * //
331
+ * const vector = Buffer.alloc(24);
332
+ * const result = await GlideFt.search(client, "json_idx1", "*=>[KNN 2 @VEC $query_vec]", {params: [{key: "query_vec", value: vector}]});
333
+ * console.log(result); // Output:
334
+ * // [
335
+ * // 2,
336
+ * // [
337
+ * // {
338
+ * // key: "json:2",
339
+ * // value: [
340
+ * // {
341
+ * // key: "$",
342
+ * // value: '{"vec":[1.1,1.2,1.3,1.4,1.5,1.6]}',
343
+ * // },
344
+ * // {
345
+ * // key: "__VEC_score",
346
+ * // value: "11.1100006104",
347
+ * // },
348
+ * // ],
349
+ * // },
350
+ * // {
351
+ * // key: "json:0",
352
+ * // value: [
353
+ * // {
354
+ * // key: "$",
355
+ * // value: '{"vec":[1,2,3,4,5,6]}',
356
+ * // },
357
+ * // {
358
+ * // key: "__VEC_score",
359
+ * // value: "91",
360
+ * // },
361
+ * // ],
362
+ * // },
363
+ * // ],
364
+ * // ]
365
+ * ```
366
+ */
367
+ static async search(client, indexName, query, options) {
368
+ const args = [
369
+ "FT.SEARCH",
370
+ indexName,
371
+ query,
372
+ ..._addFtSearchOptions(options),
373
+ ];
374
+ return _handleCustomCommand(client, args, options);
375
+ }
376
+ /**
377
+ * Runs a search query and collects performance profiling information.
378
+ *
379
+ * @param client - The client to execute the command.
380
+ * @param indexName - The index name.
381
+ * @param query - The text query to search.
382
+ * @param options - (Optional) See {@link FtSearchOptions} and {@link DecoderOption}. Additionally:
383
+ * - `limited` (Optional) - Either provide a full verbose output or some brief version.
384
+ *
385
+ * @returns A two-element array. The first element contains results of the search query being profiled, the
386
+ * second element stores profiling information.
387
+ *
388
+ * @example
389
+ * ```typescript
390
+ * // Example of running profile on a search query
391
+ * const vector = Buffer.alloc(24);
392
+ * const result = await GlideFt.profileSearch(client, "json_idx1", "*=>[KNN 2 @VEC $query_vec]", {params: [{key: "query_vec", value: vector}]});
393
+ * console.log(result); // Output:
394
+ * // result[0] contains `FT.SEARCH` response with the given query
395
+ * // result[1] contains profiling data as a `Record<string, number>`
396
+ * ```
397
+ */
398
+ static async profileSearch(client, indexName, query, options) {
399
+ const args = ["FT.PROFILE", indexName, "SEARCH"];
400
+ if (options?.limited) {
401
+ args.push("LIMITED");
402
+ }
403
+ args.push("QUERY", query);
404
+ if (options) {
405
+ args.push(..._addFtSearchOptions(options));
406
+ }
407
+ return _handleCustomCommand(client, args, options).then((v) => [v[0], (0, __1.convertGlideRecordToRecord)(v[1])]);
408
+ }
409
+ /**
410
+ * Runs an aggregate query and collects performance profiling information.
411
+ *
412
+ * @param client - The client to execute the command.
413
+ * @param indexName - The index name.
414
+ * @param query - The text query to search.
415
+ * @param options - (Optional) See {@link FtAggregateOptions} and {@link DecoderOption}. Additionally:
416
+ * - `limited` (Optional) - Either provide a full verbose output or some brief version.
417
+ *
418
+ * @returns A two-element array. The first element contains results of the aggregate query being profiled, the
419
+ * second element stores profiling information.
420
+ *
421
+ * @example
422
+ * ```typescript
423
+ * // Example of running profile on an aggregate query
424
+ * const options: FtAggregateOptions = {
425
+ * loadFields: ["__key"],
426
+ * clauses: [
427
+ * {
428
+ * type: "GROUPBY",
429
+ * properties: ["@condition"],
430
+ * reducers: [
431
+ * {
432
+ * function: "TOLIST",
433
+ * args: ["__key"],
434
+ * name: "bicycles",
435
+ * },
436
+ * ],
437
+ * },
438
+ * ],
439
+ * };
440
+ * const result = await GlideFt.profileAggregate(client, "myIndex", "*", options);
441
+ * console.log(result); // Output:
442
+ * // result[0] contains `FT.AGGREGATE` response with the given query
443
+ * // result[1] contains profiling data as a `Record<string, number>`
444
+ * ```
445
+ */
446
+ static async profileAggregate(client, indexName, query, options) {
447
+ const args = ["FT.PROFILE", indexName, "AGGREGATE"];
448
+ if (options?.limited) {
449
+ args.push("LIMITED");
450
+ }
451
+ args.push("QUERY", query);
452
+ if (options) {
453
+ args.push(..._addFtAggregateOptions(options));
454
+ }
455
+ return _handleCustomCommand(client, args, options).then((v) => [v[0], (0, __1.convertGlideRecordToRecord)(v[1])]);
456
+ }
457
+ /**
458
+ * Adds an alias for an index. The new alias name can be used anywhere that an index name is required.
459
+ *
460
+ * @param client - The client to execute the command.
461
+ * @param indexName - The alias to be added to the index.
462
+ * @param alias - The index name for which the alias has to be added.
463
+ * @returns `"OK"`
464
+ *
465
+ * @example
466
+ * ```typescript
467
+ * // Example usage of FT.ALIASADD to add an alias for an index.
468
+ * await GlideFt.aliasadd(client, "index", "alias"); // "OK"
469
+ * ```
470
+ */
471
+ static async aliasadd(client, indexName, alias) {
472
+ const args = ["FT.ALIASADD", alias, indexName];
473
+ return _handleCustomCommand(client, args, {
474
+ decoder: __1.Decoder.String,
475
+ });
476
+ }
477
+ /**
478
+ * Deletes an existing alias for an index.
479
+ *
480
+ * @param client - The client to execute the command.
481
+ * @param alias - The existing alias to be deleted for an index.
482
+ * @returns `"OK"`
483
+ *
484
+ * @example
485
+ * ```typescript
486
+ * // Example usage of FT.ALIASDEL to delete an existing alias.
487
+ * await GlideFt.aliasdel(client, "alias"); // "OK"
488
+ * ```
489
+ */
490
+ static async aliasdel(client, alias) {
491
+ const args = ["FT.ALIASDEL", alias];
492
+ return _handleCustomCommand(client, args, {
493
+ decoder: __1.Decoder.String,
494
+ });
495
+ }
496
+ /**
497
+ * Updates an existing alias to point to a different physical index. This command only affects future references to the alias.
498
+ *
499
+ * @param client - The client to execute the command.
500
+ * @param alias - The alias name. This alias will now be pointed to a different index.
501
+ * @param indexName - The index name for which an existing alias has to updated.
502
+ * @returns `"OK"`
503
+ *
504
+ * @example
505
+ * ```typescript
506
+ * // Example usage of FT.ALIASUPDATE to update an alias to point to a different index.
507
+ * await GlideFt.aliasupdate(client, "newAlias", "index"); // "OK"
508
+ * ```
509
+ */
510
+ static async aliasupdate(client, alias, indexName) {
511
+ const args = ["FT.ALIASUPDATE", alias, indexName];
512
+ return _handleCustomCommand(client, args, {
513
+ decoder: __1.Decoder.String,
514
+ });
515
+ }
516
+ /**
517
+ * List the index aliases.
518
+ *
519
+ * @param client - The client to execute the command.
520
+ * @param options - (Optional) See {@link DecoderOption}.
521
+ * @returns A map of index aliases for indices being aliased.
522
+ *
523
+ * @example
524
+ * ```typescript
525
+ * // Example usage of FT._ALIASLIST to query index aliases
526
+ * const result = await GlideFt.aliaslist(client);
527
+ * console.log(result); // Output:
528
+ * //[{"key": "alias1", "value": "index1"}, {"key": "alias2", "value": "index2"}]
529
+ * ```
530
+ */
531
+ static async aliaslist(client, options) {
532
+ const args = ["FT._ALIASLIST"];
533
+ return _handleCustomCommand(client, args, options);
534
+ }
535
+ }
536
+ exports.GlideFt = GlideFt;
537
+ /**
538
+ * @internal
539
+ */
540
+ function _addFtAggregateOptions(options) {
541
+ if (!options)
542
+ return [];
543
+ const args = [];
544
+ if (options.loadAll)
545
+ args.push("LOAD", "*");
546
+ else if (options.loadFields)
547
+ args.push("LOAD", options.loadFields.length.toString(), ...options.loadFields);
548
+ if (options.timeout)
549
+ args.push("TIMEOUT", options.timeout.toString());
550
+ if (options.params) {
551
+ args.push("PARAMS", (options.params.length * 2).toString(), ...options.params.flatMap((param) => [param.key, param.value]));
552
+ }
553
+ if (options.clauses) {
554
+ for (const clause of options.clauses) {
555
+ switch (clause.type) {
556
+ case "LIMIT":
557
+ args.push(clause.type, clause.offset.toString(), clause.count.toString());
558
+ break;
559
+ case "FILTER":
560
+ args.push(clause.type, clause.expression);
561
+ break;
562
+ case "GROUPBY":
563
+ args.push(clause.type, clause.properties.length.toString(), ...clause.properties);
564
+ for (const reducer of clause.reducers) {
565
+ args.push("REDUCE", reducer.function, reducer.args.length.toString(), ...reducer.args);
566
+ if (reducer.name)
567
+ args.push("AS", reducer.name);
568
+ }
569
+ break;
570
+ case "SORTBY":
571
+ args.push(clause.type, (clause.properties.length * 2).toString());
572
+ for (const property of clause.properties)
573
+ args.push(property.property, property.order);
574
+ if (clause.max)
575
+ args.push("MAX", clause.max.toString());
576
+ break;
577
+ case "APPLY":
578
+ args.push(clause.type, clause.expression, "AS", clause.name);
579
+ break;
580
+ default:
581
+ throw new Error("Unknown clause type in FtAggregateOptions");
582
+ }
583
+ }
584
+ }
585
+ return args;
586
+ }
587
+ /**
588
+ * @internal
589
+ */
590
+ function _addFtSearchOptions(options) {
591
+ if (!options)
592
+ return [];
593
+ const args = [];
594
+ // RETURN
595
+ if (options.returnFields) {
596
+ const returnFields = [];
597
+ options.returnFields.forEach((returnField) => returnField.alias
598
+ ? returnFields.push(returnField.fieldIdentifier, "AS", returnField.alias)
599
+ : returnFields.push(returnField.fieldIdentifier));
600
+ args.push("RETURN", returnFields.length.toString(), ...returnFields);
601
+ }
602
+ // TIMEOUT
603
+ if (options.timeout) {
604
+ args.push("TIMEOUT", options.timeout.toString());
605
+ }
606
+ // PARAMS
607
+ if (options.params) {
608
+ args.push("PARAMS", (options.params.length * 2).toString(), ...options.params.flatMap((param) => [param.key, param.value]));
609
+ }
610
+ // LIMIT
611
+ if (options.limit) {
612
+ args.push("LIMIT", options.limit.offset.toString(), options.limit.count.toString());
613
+ }
614
+ // COUNT
615
+ if (options.count) {
616
+ args.push("COUNT");
617
+ }
618
+ return args;
619
+ }
620
+ /**
621
+ * @internal
622
+ */
623
+ async function _handleCustomCommand(client, args, decoderOption = {}) {
624
+ return client instanceof __1.GlideClient
625
+ ? client.customCommand(args, decoderOption)
626
+ : client.customCommand(args, decoderOption);
627
+ }