-
Notifications
You must be signed in to change notification settings - Fork 148
Node: add FT.CREATE command
#2501
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 25 commits
1a32394
938e262
05a4cc1
71c973e
d9a2909
a4a112d
74442a7
e9fc446
fde3b6c
7af79e1
9b550e2
41db72e
745da41
2b57f3b
d861df6
2b1ba26
c401d4f
e29be99
aeed5d2
351d32a
67437f8
7b5546e
8cc465b
6fdadc4
9297eb6
5362967
bea6cf9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -32,7 +32,8 @@ | |
| "compile-protobuf-files": "cd src && pbjs -t static-module -o ProtobufMessage.js ../../glide-core/src/protobuf/*.proto && pbts -o ProtobufMessage.d.ts ProtobufMessage.js", | ||
| "fix-protobuf-file": "replace 'this\\.encode\\(message, writer\\)\\.ldelim' 'this.encode(message, writer && writer.len ? writer.fork() : writer).ldelim' src/ProtobufMessage.js", | ||
| "test": "npm run build-test-utils && jest --verbose --runInBand --testPathIgnorePatterns='ServerModules'", | ||
| "test-modules": "npm run build-test-utils && jest --verbose --runInBand --testPathPattern='ServerModules'", | ||
| "test-minimum": "npm run build-test-utils && jest --verbose --runInBand --testNamePattern='^(.(?!(GlideJson|GlideFt|pubsub|kill)))*$'", | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. small nit: with a negative lookahead removing the modules/pubsub/kill tests, would it be better to name this something like base rather than minimum?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't like base. I originally called it 'fast'. The idea is that it skips the long tests, and could maybe be used for doing a sanity check on the client. |
||
| "test-modules": "npm run build-test-utils && jest --verbose --runInBand --testNamePattern='(GlideJson|GlideFt)'", | ||
acarbonetto marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| "build-test-utils": "cd ../utils && npm i && npm run build", | ||
| "lint:fix": "npm run install-linting && npx eslint -c ../eslint.config.mjs --fix && npm run prettier:format", | ||
| "lint": "npm run install-linting && npx eslint -c ../eslint.config.mjs && npm run prettier:check:ci", | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,176 @@ | ||
| /** | ||
| * Copyright Valkey GLIDE Project Contributors - SPDX Identifier: Apache-2.0 | ||
| */ | ||
|
|
||
| import { Decoder, DecoderOption, GlideString } from "../BaseClient"; | ||
| import { GlideClient } from "../GlideClient"; | ||
| import { GlideClusterClient } from "../GlideClusterClient"; | ||
| import { Field, FtCreateOptions } from "./GlideFtOptions"; | ||
|
|
||
| /** Module for Vector Search commands */ | ||
| export class GlideFt { | ||
| /** | ||
| * Creates an index and initiates a backfill of that index. | ||
| * | ||
| * @param client The client to execute the command. | ||
| * @param indexName The index name for the index to be created. | ||
| * @param schema The fields of the index schema, specifying the fields and their types. | ||
| * @param options Optional arguments for the `FT.CREATE` command. See {@link FtCreateOptions}. | ||
| * | ||
| * @returns If the index is successfully created, returns "OK". | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * // Example usage of FT.CREATE to create a 6-dimensional JSON index using the HNSW algorithm | ||
| * await GlideFt.create(client, "json_idx1", [{ | ||
| * type: "VECTOR", | ||
| * name: "$.vec", | ||
| * alias: "VEC", | ||
| * attributes: { | ||
| * algorithm: "HNSW", | ||
| * type: "FLOAT32", | ||
| * dimension: 6, | ||
| * distanceMetric: "L2", | ||
| * numberOfEdges: 32, | ||
| * }, | ||
| * }], { | ||
| * dataType: "JSON", | ||
| * prefixes: ["json:"] | ||
| * }); | ||
| * ``` | ||
| */ | ||
| static async create( | ||
| client: GlideClient | GlideClusterClient, | ||
| indexName: GlideString, | ||
| schema: Field[], | ||
| options?: FtCreateOptions, | ||
| ): Promise<"OK" | null> { | ||
acarbonetto marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| const args: GlideString[] = ["FT.CREATE", indexName]; | ||
|
|
||
| if (options) { | ||
| if ("dataType" in options) { | ||
| args.push("ON", options.dataType); | ||
| } | ||
|
|
||
| if ("prefixes" in options && options.prefixes) { | ||
| args.push( | ||
| "PREFIX", | ||
| options.prefixes.length.toString(), | ||
| ...options.prefixes, | ||
| ); | ||
| } | ||
| } | ||
|
|
||
| args.push("SCHEMA"); | ||
|
|
||
| schema.forEach((f) => { | ||
| args.push(f.name); | ||
|
|
||
| if (f.alias) { | ||
| args.push("AS", f.alias); | ||
| } | ||
|
|
||
| args.push(f.type); | ||
|
|
||
| switch (f.type) { | ||
| case "TAG": { | ||
| if (f.separator) { | ||
| args.push("SEPARATOR", f.separator); | ||
| } | ||
|
|
||
| if (f.caseSensitive) { | ||
| args.push("CASESENSITIVE"); | ||
| } | ||
|
|
||
| break; | ||
| } | ||
|
|
||
| case "VECTOR": { | ||
| if (f.attributes) { | ||
| args.push(f.attributes.algorithm); | ||
|
|
||
| const attributes: GlideString[] = []; | ||
|
|
||
| // all VectorFieldAttributes attributes | ||
| if (f.attributes.dimension) { | ||
| attributes.push( | ||
| "DIM", | ||
| f.attributes.dimension.toString(), | ||
| ); | ||
| } | ||
|
|
||
| if (f.attributes.distanceMetric) { | ||
| attributes.push( | ||
| "DISTANCE_METRIC", | ||
| f.attributes.distanceMetric.toString(), | ||
| ); | ||
| } | ||
|
|
||
| if (f.attributes.type) { | ||
| attributes.push( | ||
| "TYPE", | ||
| f.attributes.type.toString(), | ||
| ); | ||
| } | ||
|
|
||
| if (f.attributes.initialCap) { | ||
| attributes.push( | ||
| "INITIAL_CAP", | ||
| f.attributes.initialCap.toString(), | ||
| ); | ||
| } | ||
|
|
||
| // VectorFieldAttributesHnsw attributes | ||
| if ("m" in f.attributes && f.attributes.m) { | ||
| attributes.push("M", f.attributes.m.toString()); | ||
| } | ||
|
|
||
| if ( | ||
| "efContruction" in f.attributes && | ||
| f.attributes.efContruction | ||
| ) { | ||
| attributes.push( | ||
| "EF_CONSTRUCTION", | ||
| f.attributes.efContruction.toString(), | ||
| ); | ||
| } | ||
|
|
||
| if ( | ||
| "efRuntime" in f.attributes && | ||
| f.attributes.efRuntime | ||
| ) { | ||
| attributes.push( | ||
| "EF_RUNTIME", | ||
| f.attributes.efRuntime.toString(), | ||
| ); | ||
| } | ||
|
|
||
| args.push(attributes.length.toString(), ...attributes); | ||
| } | ||
|
|
||
| break; | ||
| } | ||
|
|
||
| default: | ||
| // no-op | ||
| } | ||
| }); | ||
|
|
||
| return _handleCustomCommand(client, args, { | ||
| decoder: Decoder.String, | ||
| }) as Promise<"OK" | null>; | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * @internal | ||
| */ | ||
| function _handleCustomCommand( | ||
| client: GlideClient | GlideClusterClient, | ||
| args: GlideString[], | ||
| decoderOption: DecoderOption, | ||
| ) { | ||
| return client instanceof GlideClient | ||
| ? (client as GlideClient).customCommand(args, decoderOption) | ||
| : (client as GlideClusterClient).customCommand(args, decoderOption); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,120 @@ | ||
| /** | ||
| * Copyright Valkey GLIDE Project Contributors - SPDX Identifier: Apache-2.0 | ||
| */ | ||
|
|
||
| import { GlideString } from "../BaseClient"; | ||
|
|
||
| interface BaseField { | ||
| /** The name of the field. */ | ||
| name: GlideString; | ||
| /** An alias for field. */ | ||
| alias?: GlideString; | ||
| } | ||
|
|
||
| /** | ||
| * Field contains any blob of data. | ||
| */ | ||
| export type TextField = BaseField & { | ||
| /** Field identifier */ | ||
| type: "TEXT"; | ||
| }; | ||
|
|
||
| /** | ||
| * Tag fields are similar to full-text fields, but they interpret the text as a simple list of | ||
| * tags delimited by a separator character. | ||
| * | ||
| * For HASH fields, separator default is a comma (`,`). For JSON fields, there is no default | ||
| * separator; you must declare one explicitly if needed. | ||
| */ | ||
| export type TagField = BaseField & { | ||
| /** Field identifier */ | ||
| type: "TAG"; | ||
| /** Specify how text in the attribute is split into individual tags. Must be a single character. */ | ||
| separator?: GlideString; | ||
| /** Preserve the original letter cases of tags. If set to False, characters are converted to lowercase by default. */ | ||
| caseSensitive?: boolean; | ||
| }; | ||
|
|
||
| /** | ||
| * Field contains a number. | ||
| */ | ||
| export type NumericField = BaseField & { | ||
| /** Field identifier */ | ||
| type: "NUMERIC"; | ||
| }; | ||
|
|
||
| /** | ||
| * Superclass for vector field implementations, contains common logic. | ||
| */ | ||
| export type VectorField = BaseField & { | ||
| /** Field identifier */ | ||
| type: "VECTOR"; | ||
Yury-Fridlyand marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| /** Additional attributes to be passed with the vector field after the algorithm name. */ | ||
| attributes: VectorFieldAttributesFlat | VectorFieldAttributesHnsw; | ||
| }; | ||
|
|
||
| /** | ||
| * Base class for defining vector field attributes to be used after the vector algorithm name. | ||
| */ | ||
| export interface VectorFieldAttributes { | ||
| /** Number of dimensions in the vector. Equivalent to DIM in the option. */ | ||
| dimension: number; | ||
| /** | ||
| * The distance metric used in vector type field. Can be one of [L2 | IP | COSINE]. | ||
| */ | ||
| distanceMetric: "L2" | "IP" | "COSINE"; | ||
Yury-Fridlyand marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| /** Vector type. The only supported type is FLOAT32. */ | ||
| type: "FLOAT32"; | ||
| /** | ||
| * Initial vector capacity in the index affecting memory allocation size of the index. Defaults to 1024. | ||
| */ | ||
| initialCap?: number; | ||
| } | ||
|
|
||
| /** | ||
| * Vector field that supports vector search by FLAT (brute force) algorithm. | ||
| * | ||
| * The algorithm is a brute force linear processing of each vector in the index, yielding exact | ||
| * answers within the bounds of the precision of the distance computations. | ||
| */ | ||
| export type VectorFieldAttributesFlat = VectorFieldAttributes & { | ||
| algorithm: "FLAT"; | ||
acarbonetto marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| }; | ||
|
|
||
| /** | ||
| * Vector field that supports vector search by HNSM (Hierarchical Navigable Small World) algorithm. | ||
| * | ||
| * The algorithm provides an approximation of the correct answer in exchange for substantially | ||
| * lower execution times. | ||
| */ | ||
| export type VectorFieldAttributesHnsw = VectorFieldAttributes & { | ||
| algorithm: "HNSW"; | ||
acarbonetto marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| /** | ||
| * Number of maximum allowed outgoing edges for each node in the graph in each layer. Default is 16, maximum is 512. | ||
| * Equivalent to the `m` attribute. | ||
| */ | ||
| numberOfEdges?: number; | ||
| /** | ||
| * Controls the number of vectors examined during index construction. Default value is 200, Maximum value is 4096. | ||
| * Equivalent to the `efContruction` attribute. | ||
| */ | ||
| vectorsExaminedOnConstruction?: number; | ||
| /** | ||
| * Controls the number of vectors examined during query operations. Default value is 10, Maximum value is 4096. | ||
| * Equivalent to the `efRuntime` attribute. | ||
| */ | ||
| vectorsExaminedOnRuntime?: number; | ||
| }; | ||
|
|
||
| export type Field = TextField | TagField | NumericField | VectorField; | ||
|
|
||
| /** | ||
| * Represents the input options to be used in the FT.CREATE command. | ||
| * All fields in this class are optional inputs for FT.CREATE. | ||
| */ | ||
| export interface FtCreateOptions { | ||
| /** The type of data to be indexed using FT.CREATE. */ | ||
| dataType: "JSON" | "HASH"; | ||
| /** The prefix of the key to be indexed. */ | ||
| prefixes?: GlideString[]; | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.