redis-vl 0.1.0-beta.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.
Files changed (87) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +212 -0
  3. package/dist/errors.d.ts +50 -0
  4. package/dist/errors.d.ts.map +1 -0
  5. package/dist/errors.js +87 -0
  6. package/dist/errors.js.map +1 -0
  7. package/dist/index.d.ts +21 -0
  8. package/dist/index.d.ts.map +1 -0
  9. package/dist/index.js +25 -0
  10. package/dist/index.js.map +1 -0
  11. package/dist/indexes/search-index.d.ts +368 -0
  12. package/dist/indexes/search-index.d.ts.map +1 -0
  13. package/dist/indexes/search-index.js +475 -0
  14. package/dist/indexes/search-index.js.map +1 -0
  15. package/dist/query/base.d.ts +51 -0
  16. package/dist/query/base.d.ts.map +1 -0
  17. package/dist/query/base.js +6 -0
  18. package/dist/query/base.js.map +1 -0
  19. package/dist/query/index.d.ts +7 -0
  20. package/dist/query/index.d.ts.map +1 -0
  21. package/dist/query/index.js +7 -0
  22. package/dist/query/index.js.map +1 -0
  23. package/dist/query/vector.d.ts +199 -0
  24. package/dist/query/vector.d.ts.map +1 -0
  25. package/dist/query/vector.js +198 -0
  26. package/dist/query/vector.js.map +1 -0
  27. package/dist/redis/index-info-parser.d.ts +25 -0
  28. package/dist/redis/index-info-parser.d.ts.map +1 -0
  29. package/dist/redis/index-info-parser.js +301 -0
  30. package/dist/redis/index-info-parser.js.map +1 -0
  31. package/dist/redis/schema-types.d.ts +81 -0
  32. package/dist/redis/schema-types.d.ts.map +1 -0
  33. package/dist/redis/schema-types.js +18 -0
  34. package/dist/redis/schema-types.js.map +1 -0
  35. package/dist/redis/utils.d.ts +7 -0
  36. package/dist/redis/utils.d.ts.map +1 -0
  37. package/dist/redis/utils.js +221 -0
  38. package/dist/redis/utils.js.map +1 -0
  39. package/dist/schema/fields.d.ts +163 -0
  40. package/dist/schema/fields.d.ts.map +1 -0
  41. package/dist/schema/fields.js +304 -0
  42. package/dist/schema/fields.js.map +1 -0
  43. package/dist/schema/index.d.ts +8 -0
  44. package/dist/schema/index.d.ts.map +1 -0
  45. package/dist/schema/index.js +8 -0
  46. package/dist/schema/index.js.map +1 -0
  47. package/dist/schema/schema.d.ts +247 -0
  48. package/dist/schema/schema.d.ts.map +1 -0
  49. package/dist/schema/schema.js +403 -0
  50. package/dist/schema/schema.js.map +1 -0
  51. package/dist/schema/types.d.ts +56 -0
  52. package/dist/schema/types.d.ts.map +1 -0
  53. package/dist/schema/types.js +62 -0
  54. package/dist/schema/types.js.map +1 -0
  55. package/dist/storage/base-storage.d.ts +107 -0
  56. package/dist/storage/base-storage.d.ts.map +1 -0
  57. package/dist/storage/base-storage.js +187 -0
  58. package/dist/storage/base-storage.js.map +1 -0
  59. package/dist/storage/hash-storage.d.ts +29 -0
  60. package/dist/storage/hash-storage.d.ts.map +1 -0
  61. package/dist/storage/hash-storage.js +122 -0
  62. package/dist/storage/hash-storage.js.map +1 -0
  63. package/dist/storage/index.d.ts +4 -0
  64. package/dist/storage/index.d.ts.map +1 -0
  65. package/dist/storage/index.js +4 -0
  66. package/dist/storage/index.js.map +1 -0
  67. package/dist/storage/json-storage.d.ts +19 -0
  68. package/dist/storage/json-storage.d.ts.map +1 -0
  69. package/dist/storage/json-storage.js +81 -0
  70. package/dist/storage/json-storage.js.map +1 -0
  71. package/dist/utils/distance.d.ts +92 -0
  72. package/dist/utils/distance.d.ts.map +1 -0
  73. package/dist/utils/distance.js +98 -0
  74. package/dist/utils/distance.js.map +1 -0
  75. package/dist/vectorizers/base-vectorizer.d.ts +88 -0
  76. package/dist/vectorizers/base-vectorizer.d.ts.map +1 -0
  77. package/dist/vectorizers/base-vectorizer.js +32 -0
  78. package/dist/vectorizers/base-vectorizer.js.map +1 -0
  79. package/dist/vectorizers/huggingface-vectorizer.d.ts +103 -0
  80. package/dist/vectorizers/huggingface-vectorizer.d.ts.map +1 -0
  81. package/dist/vectorizers/huggingface-vectorizer.js +142 -0
  82. package/dist/vectorizers/huggingface-vectorizer.js.map +1 -0
  83. package/dist/vectorizers/index.d.ts +10 -0
  84. package/dist/vectorizers/index.d.ts.map +1 -0
  85. package/dist/vectorizers/index.js +10 -0
  86. package/dist/vectorizers/index.js.map +1 -0
  87. package/package.json +96 -0
@@ -0,0 +1,247 @@
1
+ import { z } from 'zod';
2
+ import { StorageType } from './types.js';
3
+ import { BaseField, TextFieldAttrs, TagFieldAttrs, NumericFieldAttrs, GeoFieldAttrs, VectorFieldAttrs } from './fields.js';
4
+ /**
5
+ * Zod schema for IndexInfo validation.
6
+ * Validates index configuration including name, prefix, key separator, storage type, and stopwords.
7
+ */
8
+ export declare const IndexInfoSchema: z.ZodObject<{
9
+ name: z.ZodString;
10
+ prefix: z.ZodDefault<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>;
11
+ keySeparator: z.ZodDefault<z.ZodString>;
12
+ storageType: z.ZodDefault<z.ZodEnum<typeof StorageType>>;
13
+ stopwords: z.ZodOptional<z.ZodArray<z.ZodString>>;
14
+ }, z.core.$strip>;
15
+ /**
16
+ * TypeScript type inferred from IndexInfoSchema.
17
+ */
18
+ export type IndexInfoOptions = z.input<typeof IndexInfoSchema>;
19
+ /**
20
+ * IndexInfo includes the essential details regarding index settings,
21
+ * such as its name, prefix, key separator, storage type, and stopwords in Redis.
22
+ */
23
+ export declare class IndexInfo {
24
+ /** The unique name of the index */
25
+ readonly name: string;
26
+ /** The prefix(es) used for Redis keys associated with this index */
27
+ readonly prefix: string | string[];
28
+ /** The separator character used in designing Redis keys */
29
+ readonly keySeparator: string;
30
+ /** The storage type used in Redis (e.g., StorageType.HASH or StorageType.JSON) */
31
+ readonly storageType: StorageType;
32
+ /** Index-level stopwords configuration */
33
+ readonly stopwords?: string[];
34
+ constructor(options: IndexInfoOptions);
35
+ }
36
+ /**
37
+ * Zod schema for IndexSchema validation.
38
+ */
39
+ export declare const IndexSchemaOptionsSchema: z.ZodObject<{
40
+ index: z.ZodCustom<IndexInfo, IndexInfo>;
41
+ fields: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodCustom<BaseField, BaseField>>>;
42
+ version: z.ZodOptional<z.ZodLiteral<"0.1.0">>;
43
+ }, z.core.$strip>;
44
+ /**
45
+ * TypeScript type inferred from IndexSchemaOptionsSchema.
46
+ */
47
+ export type IndexSchemaOptions = z.input<typeof IndexSchemaOptionsSchema>;
48
+ /**
49
+ * Field input type for addField and addFields methods.
50
+ *
51
+ * The attrs property is type-safe based on the field type:
52
+ * - text: TextFieldAttrs
53
+ * - tag: TagFieldAttrs
54
+ * - numeric: NumericFieldAttrs
55
+ * - geo: GeoFieldAttrs
56
+ * - vector: VectorFieldAttrs (FLAT or HNSW)
57
+ */
58
+ export type FieldInput = {
59
+ name: string;
60
+ type: 'text';
61
+ attrs?: TextFieldAttrs;
62
+ path?: string;
63
+ } | {
64
+ name: string;
65
+ type: 'tag';
66
+ attrs?: TagFieldAttrs;
67
+ path?: string;
68
+ } | {
69
+ name: string;
70
+ type: 'numeric';
71
+ attrs?: NumericFieldAttrs;
72
+ path?: string;
73
+ } | {
74
+ name: string;
75
+ type: 'geo';
76
+ attrs?: GeoFieldAttrs;
77
+ path?: string;
78
+ } | {
79
+ name: string;
80
+ type: 'vector';
81
+ attrs?: VectorFieldAttrs;
82
+ path?: string;
83
+ } | {
84
+ name: string;
85
+ type: string;
86
+ attrs?: Record<string, unknown>;
87
+ path?: string;
88
+ };
89
+ /**
90
+ * A schema definition for a search index in Redis, used in RedisVL for
91
+ * configuring index settings and organizing vector and metadata fields.
92
+ */
93
+ export declare class IndexSchema {
94
+ /** Details of the basic index configurations */
95
+ readonly index: IndexInfo;
96
+ /** Fields associated with the search index and their properties */
97
+ fields: Record<string, BaseField>;
98
+ /** Version of the underlying index schema */
99
+ readonly version: '0.1.0';
100
+ constructor(options: IndexSchemaOptions);
101
+ /**
102
+ * A list of field names associated with the index schema.
103
+ *
104
+ * @returns A list of field names from the schema
105
+ */
106
+ get fieldNames(): string[];
107
+ /**
108
+ * Internal helper to create a field and set its path based on storage type.
109
+ * For JSON storage: auto-sets path to $.fieldname if not provided.
110
+ * For HASH storage: sets path to null (paths not used).
111
+ */
112
+ private makeField;
113
+ /**
114
+ * Adds a single field to the index schema.
115
+ *
116
+ * @param fieldInputs - Field definition object with name, type, and optional attributes
117
+ * @throws {Error} If a field with the same name already exists
118
+ *
119
+ * @example
120
+ * ```typescript
121
+ * schema.addField({ name: 'title', type: 'text' });
122
+ * schema.addField({
123
+ * name: 'embedding',
124
+ * type: 'vector',
125
+ * attrs: { dims: 1536, algorithm: 'hnsw' }
126
+ * });
127
+ * ```
128
+ */
129
+ addField(fieldInputs: FieldInput): void;
130
+ /**
131
+ * Adds multiple fields to the index schema.
132
+ *
133
+ * @param fields - Array of field definition objects
134
+ * @throws {Error} If any field has a duplicate name
135
+ *
136
+ * @example
137
+ * ```typescript
138
+ * schema.addFields([
139
+ * { name: 'title', type: 'text' },
140
+ * { name: 'category', type: 'tag' },
141
+ * { name: 'price', type: 'numeric' }
142
+ * ]);
143
+ * ```
144
+ */
145
+ addFields(fields: FieldInput[]): void;
146
+ /**
147
+ * Removes a field from the schema by name.
148
+ *
149
+ * @param fieldName - The name of the field to remove
150
+ *
151
+ * @example
152
+ * ```typescript
153
+ * schema.removeField('old_field');
154
+ * ```
155
+ */
156
+ removeField(fieldName: string): void;
157
+ /**
158
+ * Create an IndexSchema from a plain object.
159
+ * Accepts both snake_case (from YAML/JSON files) and camelCase (from TypeScript) property names.
160
+ *
161
+ * @param data - The index schema data
162
+ * @returns A new IndexSchema instance
163
+ *
164
+ * @example
165
+ * ```typescript
166
+ * // From YAML/JSON (snake_case)
167
+ * const schema = IndexSchema.fromObject({
168
+ * index: {
169
+ * name: 'docs-index',
170
+ * prefix: 'docs',
171
+ * storage_type: 'hash', // snake_case from YAML/JSON
172
+ * },
173
+ * fields: [
174
+ * { name: 'doc-id', type: 'tag' },
175
+ * { name: 'title', type: 'text' }
176
+ * ]
177
+ * });
178
+ * ```
179
+ */
180
+ static fromObject(data: {
181
+ index: IndexInfoOptions | IndexInfo | Record<string, unknown>;
182
+ fields?: FieldInput[] | Record<string, FieldInput>;
183
+ version?: '0.1.0';
184
+ }): IndexSchema;
185
+ /**
186
+ * Create an IndexSchema from a YAML file.
187
+ *
188
+ * @param filePath - The path to the YAML file
189
+ * @returns A new IndexSchema instance
190
+ *
191
+ * @throws Error if the file path is invalid
192
+ * @throws Error if the file does not exist
193
+ *
194
+ * @example
195
+ * ```typescript
196
+ * const schema = await IndexSchema.fromYAML('schema.yaml');
197
+ * console.log(schema.index.name);
198
+ * ```
199
+ */
200
+ static fromYAML(filePath: string): Promise<IndexSchema>;
201
+ /**
202
+ * Serialize the index schema to a plain object.
203
+ * Converts camelCase properties to snake_case for YAML/JSON compatibility.
204
+ *
205
+ * @returns The index schema as a plain object with snake_case keys
206
+ *
207
+ * @example
208
+ * ```typescript
209
+ * const schema = new IndexSchema({ index: indexInfo });
210
+ * const obj = schema.toObject();
211
+ * console.log(obj.index.name);
212
+ * console.log(obj.index.storage_type);
213
+ * ```
214
+ */
215
+ toObject(): {
216
+ index: {
217
+ name: string;
218
+ prefix: string | string[];
219
+ key_separator: string;
220
+ storage_type: string;
221
+ stopwords?: string[];
222
+ };
223
+ fields: Array<{
224
+ name: string;
225
+ type: string;
226
+ attrs?: Record<string, unknown>;
227
+ path?: string;
228
+ }>;
229
+ version: '0.1.0';
230
+ };
231
+ /**
232
+ * Write the index schema to a YAML file.
233
+ *
234
+ * @param filePath - The path to the YAML file
235
+ * @param overwrite - Whether to overwrite the file if it already exists (default: true)
236
+ *
237
+ * @throws Error if the file already exists and overwrite is false
238
+ *
239
+ * @example
240
+ * ```typescript
241
+ * const schema = new IndexSchema({ index: indexInfo });
242
+ * await schema.toYAML('schema.yaml');
243
+ * ```
244
+ */
245
+ toYAML(filePath: string, overwrite?: boolean): Promise<void>;
246
+ }
247
+ //# sourceMappingURL=schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/schema/schema.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,403 @@
1
+ import { z } from 'zod';
2
+ import * as yaml from 'js-yaml';
3
+ import { promises as fs } from 'fs';
4
+ import { resolve } from 'path';
5
+ import { StorageType } from './types.js';
6
+ import { BaseField, FieldFactory, } from './fields.js';
7
+ import { SchemaValidationError, RedisVLError } from '../errors.js';
8
+ /**
9
+ * Zod schema for IndexInfo validation.
10
+ * Validates index configuration including name, prefix, key separator, storage type, and stopwords.
11
+ */
12
+ export const IndexInfoSchema = z.object({
13
+ name: z
14
+ .string()
15
+ .min(1, 'IndexInfo name cannot be empty')
16
+ .meta({
17
+ title: 'Index Name',
18
+ description: 'The unique name of the index (required, non-empty)',
19
+ examples: ['user-index', 'product-search', 'document-embeddings'],
20
+ }),
21
+ prefix: z
22
+ .union([z.string(), z.array(z.string())])
23
+ .default('rvl')
24
+ .meta({
25
+ title: 'Key Prefix',
26
+ description: 'The prefix(es) used for Redis keys associated with this index. ' +
27
+ 'Can be a single string or array of strings. ' +
28
+ 'When multiple prefixes are provided, the first one is used for write operations, but the index searches across all prefixes.',
29
+ examples: ['user', ['user', 'person', 'customer'], 'product'],
30
+ }),
31
+ keySeparator: z
32
+ .string()
33
+ .default(':')
34
+ .meta({
35
+ title: 'Key Separator',
36
+ description: 'The separator character used between prefix and key in Redis keys',
37
+ examples: [':', '::', '-', '_'],
38
+ }),
39
+ storageType: z
40
+ .enum(StorageType)
41
+ .default(StorageType.HASH)
42
+ .meta({
43
+ title: 'Storage Type',
44
+ description: 'The storage type used in Redis. ' +
45
+ 'Options: "hash" (HASH storage) or "json" (JSON storage)',
46
+ examples: ['hash', 'json'],
47
+ }),
48
+ stopwords: z
49
+ .array(z.string())
50
+ .optional()
51
+ .meta({
52
+ title: 'Stopwords',
53
+ description: 'Index-level stopwords configuration for full-text search. ' +
54
+ 'undefined (default) uses Redis default stopwords, ' +
55
+ 'empty array [] disables stopwords (STOPWORDS 0), ' +
56
+ 'or provide a custom list',
57
+ examples: [[], ['the', 'a', 'an', 'and', 'or']],
58
+ }),
59
+ });
60
+ /**
61
+ * IndexInfo includes the essential details regarding index settings,
62
+ * such as its name, prefix, key separator, storage type, and stopwords in Redis.
63
+ */
64
+ export class IndexInfo {
65
+ /** The unique name of the index */
66
+ name;
67
+ /** The prefix(es) used for Redis keys associated with this index */
68
+ prefix;
69
+ /** The separator character used in designing Redis keys */
70
+ keySeparator;
71
+ /** The storage type used in Redis (e.g., StorageType.HASH or StorageType.JSON) */
72
+ storageType;
73
+ /** Index-level stopwords configuration */
74
+ stopwords;
75
+ constructor(options) {
76
+ // Validate and parse options using Zod
77
+ const validated = IndexInfoSchema.parse(options);
78
+ this.name = validated.name;
79
+ this.prefix = validated.prefix;
80
+ this.keySeparator = validated.keySeparator;
81
+ this.storageType = validated.storageType;
82
+ this.stopwords = validated.stopwords;
83
+ }
84
+ }
85
+ /**
86
+ * Zod schema for IndexSchema validation.
87
+ */
88
+ export const IndexSchemaOptionsSchema = z.object({
89
+ /** Details of the basic index configurations */
90
+ index: z.instanceof(IndexInfo),
91
+ /** Fields associated with the search index and their properties */
92
+ fields: z.record(z.string(), z.instanceof(BaseField)).optional(),
93
+ /** Version of the underlying index schema */
94
+ version: z.literal('0.1.0').optional(),
95
+ });
96
+ /**
97
+ * A schema definition for a search index in Redis, used in RedisVL for
98
+ * configuring index settings and organizing vector and metadata fields.
99
+ */
100
+ export class IndexSchema {
101
+ /** Details of the basic index configurations */
102
+ index;
103
+ /** Fields associated with the search index and their properties */
104
+ fields;
105
+ /** Version of the underlying index schema */
106
+ version;
107
+ constructor(options) {
108
+ // Validate options using Zod
109
+ const validated = IndexSchemaOptionsSchema.parse(options);
110
+ this.index = validated.index;
111
+ this.fields = validated.fields ?? {};
112
+ this.version = validated.version ?? '0.1.0';
113
+ }
114
+ /**
115
+ * A list of field names associated with the index schema.
116
+ *
117
+ * @returns A list of field names from the schema
118
+ */
119
+ get fieldNames() {
120
+ return Object.keys(this.fields);
121
+ }
122
+ /**
123
+ * Internal helper to create a field and set its path based on storage type.
124
+ * For JSON storage: auto-sets path to $.fieldname if not provided.
125
+ * For HASH storage: sets path to null (paths not used).
126
+ */
127
+ makeField(storageType, fieldInputs) {
128
+ // Extract field properties from inputs
129
+ const { name, type, attrs, path } = fieldInputs;
130
+ if (!name) {
131
+ throw new SchemaValidationError('Field name is required');
132
+ }
133
+ if (!type) {
134
+ throw new SchemaValidationError('Field type is required');
135
+ }
136
+ // Create field from inputs using FieldFactory
137
+ const field = FieldFactory.createField(type, name, attrs);
138
+ // Handle field path based on storage type
139
+ if (storageType === StorageType.JSON) {
140
+ // For JSON storage, set path appropriately
141
+ if (path) {
142
+ // Explicit path provided, use it
143
+ field.path = path;
144
+ }
145
+ else if (field.name.startsWith('$.')) {
146
+ // Name already is a JSON path, use it as-is
147
+ field.path = field.name;
148
+ }
149
+ else {
150
+ // Name is a simple field name, add $.prefix
151
+ field.path = `$.${field.name}`;
152
+ }
153
+ }
154
+ else {
155
+ // For HASH storage, path should always be null
156
+ if (path !== null && path !== undefined) {
157
+ // TODO: log a warning
158
+ }
159
+ field.path = null;
160
+ }
161
+ return field;
162
+ }
163
+ /**
164
+ * Adds a single field to the index schema.
165
+ *
166
+ * @param fieldInputs - Field definition object with name, type, and optional attributes
167
+ * @throws {Error} If a field with the same name already exists
168
+ *
169
+ * @example
170
+ * ```typescript
171
+ * schema.addField({ name: 'title', type: 'text' });
172
+ * schema.addField({
173
+ * name: 'embedding',
174
+ * type: 'vector',
175
+ * attrs: { dims: 1536, algorithm: 'hnsw' }
176
+ * });
177
+ * ```
178
+ */
179
+ addField(fieldInputs) {
180
+ // Create field with proper path handling
181
+ const field = this.makeField(this.index.storageType, fieldInputs);
182
+ // Check for duplicates
183
+ if (Object.hasOwn(this.fields, field.name)) {
184
+ throw new SchemaValidationError(`Duplicate field name: ${field.name}. Field names must be unique across all fields for this index.`);
185
+ }
186
+ // Add field to schema
187
+ this.fields[field.name] = field;
188
+ }
189
+ /**
190
+ * Adds multiple fields to the index schema.
191
+ *
192
+ * @param fields - Array of field definition objects
193
+ * @throws {Error} If any field has a duplicate name
194
+ *
195
+ * @example
196
+ * ```typescript
197
+ * schema.addFields([
198
+ * { name: 'title', type: 'text' },
199
+ * { name: 'category', type: 'tag' },
200
+ * { name: 'price', type: 'numeric' }
201
+ * ]);
202
+ * ```
203
+ */
204
+ addFields(fields) {
205
+ for (const fieldInput of fields) {
206
+ this.addField(fieldInput);
207
+ }
208
+ }
209
+ /**
210
+ * Removes a field from the schema by name.
211
+ *
212
+ * @param fieldName - The name of the field to remove
213
+ *
214
+ * @example
215
+ * ```typescript
216
+ * schema.removeField('old_field');
217
+ * ```
218
+ */
219
+ removeField(fieldName) {
220
+ if (!Object.hasOwn(this.fields, fieldName)) {
221
+ // TODO: log a warning
222
+ return;
223
+ }
224
+ delete this.fields[fieldName];
225
+ }
226
+ /**
227
+ * Create an IndexSchema from a plain object.
228
+ * Accepts both snake_case (from YAML/JSON files) and camelCase (from TypeScript) property names.
229
+ *
230
+ * @param data - The index schema data
231
+ * @returns A new IndexSchema instance
232
+ *
233
+ * @example
234
+ * ```typescript
235
+ * // From YAML/JSON (snake_case)
236
+ * const schema = IndexSchema.fromObject({
237
+ * index: {
238
+ * name: 'docs-index',
239
+ * prefix: 'docs',
240
+ * storage_type: 'hash', // snake_case from YAML/JSON
241
+ * },
242
+ * fields: [
243
+ * { name: 'doc-id', type: 'tag' },
244
+ * { name: 'title', type: 'text' }
245
+ * ]
246
+ * });
247
+ * ```
248
+ */
249
+ static fromObject(data) {
250
+ // 1. Create IndexInfo if it's a plain object
251
+ let indexInfo;
252
+ if (data.index instanceof IndexInfo) {
253
+ indexInfo = data.index;
254
+ }
255
+ else {
256
+ // Convert snake_case to camelCase for IndexInfo properties
257
+ const indexData = { ...data.index };
258
+ // Handle storage_type -> storageType conversion
259
+ if ('storage_type' in indexData && !('storageType' in indexData)) {
260
+ indexData.storageType = indexData.storage_type;
261
+ delete indexData.storage_type;
262
+ }
263
+ // Handle key_separator -> keySeparator conversion
264
+ if ('key_separator' in indexData && !('keySeparator' in indexData)) {
265
+ indexData.keySeparator = indexData.key_separator;
266
+ delete indexData.key_separator;
267
+ }
268
+ indexInfo = new IndexInfo(indexData);
269
+ }
270
+ // 2. Create empty schema
271
+ const schema = new IndexSchema({
272
+ index: indexInfo,
273
+ version: data.version,
274
+ });
275
+ // 3. Add fields if provided
276
+ if (data.fields) {
277
+ if (Array.isArray(data.fields)) {
278
+ // Handle array format
279
+ schema.addFields(data.fields);
280
+ }
281
+ else {
282
+ // Handle object format
283
+ for (const [key, fieldData] of Object.entries(data.fields)) {
284
+ // Validate field name matches key
285
+ if (fieldData.name !== key) {
286
+ throw new SchemaValidationError(`Field name mismatch: key is "${key}" but field.name is "${fieldData.name}"`);
287
+ }
288
+ schema.addField(fieldData);
289
+ }
290
+ }
291
+ }
292
+ return schema;
293
+ }
294
+ /**
295
+ * Create an IndexSchema from a YAML file.
296
+ *
297
+ * @param filePath - The path to the YAML file
298
+ * @returns A new IndexSchema instance
299
+ *
300
+ * @throws Error if the file path is invalid
301
+ * @throws Error if the file does not exist
302
+ *
303
+ * @example
304
+ * ```typescript
305
+ * const schema = await IndexSchema.fromYAML('schema.yaml');
306
+ * console.log(schema.index.name);
307
+ * ```
308
+ */
309
+ static async fromYAML(filePath) {
310
+ const resolvedPath = resolve(filePath);
311
+ // Check if file exists
312
+ try {
313
+ await fs.access(resolvedPath);
314
+ }
315
+ catch {
316
+ throw new RedisVLError(`Schema file ${filePath} does not exist`);
317
+ }
318
+ // Read and parse YAML file
319
+ const yamlContent = await fs.readFile(resolvedPath, 'utf-8');
320
+ const data = yaml.load(yamlContent);
321
+ // Use fromObject to create the schema
322
+ return IndexSchema.fromObject(data);
323
+ }
324
+ /**
325
+ * Serialize the index schema to a plain object.
326
+ * Converts camelCase properties to snake_case for YAML/JSON compatibility.
327
+ *
328
+ * @returns The index schema as a plain object with snake_case keys
329
+ *
330
+ * @example
331
+ * ```typescript
332
+ * const schema = new IndexSchema({ index: indexInfo });
333
+ * const obj = schema.toObject();
334
+ * console.log(obj.index.name);
335
+ * console.log(obj.index.storage_type);
336
+ * ```
337
+ */
338
+ toObject() {
339
+ // Serialize index info - convert camelCase to snake_case
340
+ const indexDict = {
341
+ name: this.index.name,
342
+ prefix: this.index.prefix,
343
+ key_separator: this.index.keySeparator, // camelCase -> snake_case
344
+ storage_type: this.index.storageType, // camelCase -> snake_case (enum value is already string)
345
+ ...(this.index.stopwords !== undefined && { stopwords: this.index.stopwords }),
346
+ };
347
+ // Serialize fields as array
348
+ const fieldsArray = Object.values(this.fields).map((field) => {
349
+ const fieldDict = {
350
+ name: field.name,
351
+ type: field.type,
352
+ };
353
+ if (Object.keys(field.attrs).length > 0) {
354
+ fieldDict.attrs = field.attrs;
355
+ }
356
+ if (field.path !== undefined && field.path !== null) {
357
+ fieldDict.path = field.path;
358
+ }
359
+ return fieldDict;
360
+ });
361
+ return {
362
+ index: indexDict,
363
+ fields: fieldsArray,
364
+ version: this.version,
365
+ };
366
+ }
367
+ /**
368
+ * Write the index schema to a YAML file.
369
+ *
370
+ * @param filePath - The path to the YAML file
371
+ * @param overwrite - Whether to overwrite the file if it already exists (default: true)
372
+ *
373
+ * @throws Error if the file already exists and overwrite is false
374
+ *
375
+ * @example
376
+ * ```typescript
377
+ * const schema = new IndexSchema({ index: indexInfo });
378
+ * await schema.toYAML('schema.yaml');
379
+ * ```
380
+ */
381
+ async toYAML(filePath, overwrite = true) {
382
+ const resolvedPath = resolve(filePath);
383
+ // Check if file exists
384
+ if (!overwrite) {
385
+ try {
386
+ await fs.access(resolvedPath);
387
+ throw new RedisVLError(`Schema file ${filePath} already exists.`);
388
+ }
389
+ catch (error) {
390
+ // File doesn't exist, which is what we want
391
+ if (error instanceof RedisVLError && error.message.includes('already exists')) {
392
+ throw error;
393
+ }
394
+ // Otherwise, file doesn't exist, continue
395
+ }
396
+ }
397
+ // Convert to object and write as YAML
398
+ const objData = this.toObject();
399
+ const yamlData = yaml.dump(objData, { sortKeys: false });
400
+ await fs.writeFile(resolvedPath, yamlData, 'utf-8');
401
+ }
402
+ }
403
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/schema/schema.ts"],"names":[],"mappings":"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"}