From a23f93519f2ef3378e3246a8a05d0d3a312af519 Mon Sep 17 00:00:00 2001 From: Pavel Angelov Date: Fri, 11 Jul 2025 13:46:57 +0300 Subject: [PATCH 1/4] add implementation --- .../generateFtCreateCommand.spec.ts | 96 +++++++++++++++++++ .../generateFtCreateCommand.ts | 89 +++++++++++++++++ .../hooks/useFtCreateCommand/index.ts | 2 + .../hooks/useFtCreateCommand/types.ts | 50 ++++++++++ .../useFtCreateCommand.spec.ts | 25 +++++ .../useFtCreateCommand/useFtCreateCommand.ts | 25 +++++ 6 files changed, 287 insertions(+) create mode 100644 redisinsight/ui/src/services/hooks/useFtCreateCommand/generateFtCreateCommand.spec.ts create mode 100644 redisinsight/ui/src/services/hooks/useFtCreateCommand/generateFtCreateCommand.ts create mode 100644 redisinsight/ui/src/services/hooks/useFtCreateCommand/index.ts create mode 100644 redisinsight/ui/src/services/hooks/useFtCreateCommand/types.ts create mode 100644 redisinsight/ui/src/services/hooks/useFtCreateCommand/useFtCreateCommand.spec.ts create mode 100644 redisinsight/ui/src/services/hooks/useFtCreateCommand/useFtCreateCommand.ts diff --git a/redisinsight/ui/src/services/hooks/useFtCreateCommand/generateFtCreateCommand.spec.ts b/redisinsight/ui/src/services/hooks/useFtCreateCommand/generateFtCreateCommand.spec.ts new file mode 100644 index 0000000000..7064045e91 --- /dev/null +++ b/redisinsight/ui/src/services/hooks/useFtCreateCommand/generateFtCreateCommand.spec.ts @@ -0,0 +1,96 @@ +import { generateFtCreateCommand } from './generateFtCreateCommand' + +describe('generateFtCreateCommand', () => { + it('should generate basic FT.CREATE for HASH with TEXT and NUMERIC fields', () => { + const result = generateFtCreateCommand({ + indexName: 'idx:products', + dataType: 'HASH', + prefixes: ['products:'], + schema: [ + { name: 'name', type: 'TEXT', sortable: true }, + { name: 'price', type: 'NUMERIC' }, + ], + }) + + expect(result).toBe( + [ + 'FT.CREATE idx:products', + 'ON HASH', + 'PREFIX 1 "products:"', + 'SCHEMA', + '"name" TEXT SORTABLE', + '"price" NUMERIC', + ].join('\n'), + ) + }) + + it('should support JSON paths with AS aliasing', () => { + const result = generateFtCreateCommand({ + indexName: 'idx:users', + dataType: 'JSON', + schema: [ + { name: 'fullName', type: 'TEXT', as: 'name', nostem: true }, + { name: 'age', type: 'NUMERIC', sortable: true }, + ], + }) + + expect(result).toBe( + [ + 'FT.CREATE idx:users', + 'ON JSON', + 'SCHEMA', + '$.fullName AS name TEXT NOSTEM', + '$.age NUMERIC SORTABLE', + ].join('\n'), + ) + }) + + it('should include VECTOR fields with parameters', () => { + const result = generateFtCreateCommand({ + indexName: 'idx:embeddings', + dataType: 'HASH', + prefixes: ['vec:'], + schema: [ + { + name: 'embedding', + type: 'VECTOR', + algorithm: 'FLAT', + vectorType: 'FLOAT32', + dim: 768, + distance: 'L2', + initialCap: 100, + blockSize: 200, + }, + ], + }) + + expect(result).toContain('VECTOR "FLAT"') + expect(result).toContain('"TYPE" FLOAT32') + expect(result).toContain('"DIM" 768') + expect(result).toContain('"DISTANCE_METRIC" "L2"') + expect(result).toContain('"INITIAL_CAP" 100') + expect(result).toContain('"BLOCK_SIZE" 200') + }) + + it('should handle empty stopwords', () => { + const result = generateFtCreateCommand({ + indexName: 'idx:noStop', + dataType: 'HASH', + schema: [{ name: 'text', type: 'TEXT' }], + stopwords: [], + }) + + expect(result).toContain('STOPWORDS 0') + }) + + it('should include quoted stopwords', () => { + const result = generateFtCreateCommand({ + indexName: 'idx:stopwords', + dataType: 'HASH', + schema: [{ name: 'text', type: 'TEXT' }], + stopwords: ['the', 'and', 'but'], + }) + + expect(result).toContain('STOPWORDS 3 "the" "and" "but"') + }) +}) diff --git a/redisinsight/ui/src/services/hooks/useFtCreateCommand/generateFtCreateCommand.ts b/redisinsight/ui/src/services/hooks/useFtCreateCommand/generateFtCreateCommand.ts new file mode 100644 index 0000000000..a70850dbfe --- /dev/null +++ b/redisinsight/ui/src/services/hooks/useFtCreateCommand/generateFtCreateCommand.ts @@ -0,0 +1,89 @@ +import { CreateIndexOptions, SchemaField } from './types' + +function formatVectorField( + field: Extract, +): string[] { + const parts = [ + `VECTOR "${field.algorithm}"`, + `"TYPE" ${field.vectorType}`, + `"DIM" ${field.dim}`, + `"DISTANCE_METRIC" "${field.distance}"`, + ] + + if (field.initialCap !== undefined) + parts.push(`"INITIAL_CAP" ${field.initialCap}`) + if (field.blockSize !== undefined) + parts.push(`"BLOCK_SIZE" ${field.blockSize}`) + if (field.m !== undefined) parts.push(`"M" ${field.m}`) + if (field.efConstruction !== undefined) + parts.push(`"EF_CONSTRUCTION" ${field.efConstruction}`) + + return parts +} + +function formatSchemaField( + field: SchemaField, + dataType: 'HASH' | 'JSON', +): string { + const parts: string[] = [] + + const fieldRef = dataType === 'JSON' ? `$.${field.name}` : `"${field.name}"` + parts.push(fieldRef) + + if (field.as) parts.push(`AS ${field.as}`) + + switch (field.type) { + case 'TEXT': + parts.push('TEXT') + if (field.nostem) parts.push('NOSTEM') + if (field.unf) parts.push('UNF') + if (field.sortable) parts.push('SORTABLE') + break + case 'NUMERIC': + parts.push('NUMERIC') + if (field.sortable) parts.push('SORTABLE') + break + case 'TAG': + parts.push('TAG') + if (field.separator) parts.push(`SEPARATOR "${field.separator}"`) + if (field.sortable) parts.push('SORTABLE') + break + case 'VECTOR': { + const aliasPart = field.as ? ` AS ${field.as}` : '' + const vectorPart = formatVectorField(field).join(' ') + return `${fieldRef}${aliasPart} ${vectorPart}` + } + default: + throw new Error(`Unsupported field type: ${(field as any).type}`) + } + + return parts.join(' ') +} + +export function generateFtCreateCommand(options: CreateIndexOptions): string { + const { indexName, dataType, prefixes = [], schema, stopwords } = options + + const lines: string[] = [`FT.CREATE ${indexName}`, `ON ${dataType}`] + + if (prefixes.length > 0) { + const quotedPrefixes = prefixes.map((p) => `"${p}"`).join(' ') + lines.push(`PREFIX ${prefixes.length} ${quotedPrefixes}`) + } + + lines.push('SCHEMA') + + schema.forEach((field) => { + lines.push(formatSchemaField(field, dataType)) + }) + + if (stopwords) { + if (stopwords.length === 0) { + lines.push('STOPWORDS 0') + } else { + const quotedStopwords = stopwords.map((sw) => `"${sw}"`).join(' ') + lines.push(`STOPWORDS ${stopwords.length} ${quotedStopwords}`) + } + } + + return lines.join('\n') +} diff --git a/redisinsight/ui/src/services/hooks/useFtCreateCommand/index.ts b/redisinsight/ui/src/services/hooks/useFtCreateCommand/index.ts new file mode 100644 index 0000000000..4f35b8c19b --- /dev/null +++ b/redisinsight/ui/src/services/hooks/useFtCreateCommand/index.ts @@ -0,0 +1,2 @@ +export { useFtCreateCommand } from './useFtCreateCommand' +export type { CreateIndexOptions, SchemaField } from './types' diff --git a/redisinsight/ui/src/services/hooks/useFtCreateCommand/types.ts b/redisinsight/ui/src/services/hooks/useFtCreateCommand/types.ts new file mode 100644 index 0000000000..5a61fe0afb --- /dev/null +++ b/redisinsight/ui/src/services/hooks/useFtCreateCommand/types.ts @@ -0,0 +1,50 @@ +export type DataType = 'HASH' | 'JSON' + +export type VectorAlgorithm = 'FLAT' | 'HNSW' +export type VectorType = 'FLOAT32' | 'FLOAT64' +export type VectorDistance = 'L2' | 'IP' | 'COSINE' + +export type BaseField = { + name: string + as?: string +} + +export type TextField = BaseField & { + type: 'TEXT' + nostem?: boolean + unf?: boolean + sortable?: boolean +} + +export type NumericField = BaseField & { + type: 'NUMERIC' + sortable?: boolean +} + +export type TagField = BaseField & { + type: 'TAG' + separator?: string + sortable?: boolean +} + +export type VectorField = BaseField & { + type: 'VECTOR' + algorithm: VectorAlgorithm + vectorType: VectorType + dim: number + distance: VectorDistance + initialCap?: number + blockSize?: number + m?: number + efConstruction?: number +} + +export type SchemaField = TextField | NumericField | TagField | VectorField + +export type CreateIndexOptions = { + indexName: string + dataType: DataType + prefixes?: string[] + schema: SchemaField[] + stopwords?: string[] // optional +} diff --git a/redisinsight/ui/src/services/hooks/useFtCreateCommand/useFtCreateCommand.spec.ts b/redisinsight/ui/src/services/hooks/useFtCreateCommand/useFtCreateCommand.spec.ts new file mode 100644 index 0000000000..294bcb9260 --- /dev/null +++ b/redisinsight/ui/src/services/hooks/useFtCreateCommand/useFtCreateCommand.spec.ts @@ -0,0 +1,25 @@ +import { renderHook } from '@testing-library/react' +import { useFtCreateCommand } from './useFtCreateCommand' + +describe('useFtCreateCommand (hardcoded version)', () => { + it('returns the expected hardcoded FT.CREATE command', () => { + const { result } = renderHook(() => useFtCreateCommand()) + + expect(result.current).toBe(`FT.CREATE idx:bikes_vss + ON HASH + PREFIX 1 "bikes:" + SCHEMA + "model" TEXT NOSTEM SORTABLE + "brand" TEXT NOSTEM SORTABLE + "price" NUMERIC SORTABLE + "type" TAG + "material" TAG + "weight" NUMERIC SORTABLE + "description_embeddings" VECTOR "FLAT" 10 + "TYPE" FLOAT32 + "DIM" 768 + "DISTANCE_METRIC" "L2" + "INITIAL_CAP" 111 + "BLOCK_SIZE" 111`) + }) +}) diff --git a/redisinsight/ui/src/services/hooks/useFtCreateCommand/useFtCreateCommand.ts b/redisinsight/ui/src/services/hooks/useFtCreateCommand/useFtCreateCommand.ts new file mode 100644 index 0000000000..b64f22a730 --- /dev/null +++ b/redisinsight/ui/src/services/hooks/useFtCreateCommand/useFtCreateCommand.ts @@ -0,0 +1,25 @@ +// TODO: Since v1 would use predefined data, return a hardcoded command +// instead of generating it dynamically. Below is the intended implementation. + +// export function useFtCreateCommand(options: CreateIndexOptions): string { +// return useMemo(() => generateFtCreateCommand(options), [options]) +// } + +export function useFtCreateCommand(): string { + return `FT.CREATE idx:bikes_vss + ON HASH + PREFIX 1 "bikes:" + SCHEMA + "model" TEXT NOSTEM SORTABLE + "brand" TEXT NOSTEM SORTABLE + "price" NUMERIC SORTABLE + "type" TAG + "material" TAG + "weight" NUMERIC SORTABLE + "description_embeddings" VECTOR "FLAT" 10 + "TYPE" FLOAT32 + "DIM" 768 + "DISTANCE_METRIC" "L2" + "INITIAL_CAP" 111 + "BLOCK_SIZE" 111` +} From 84f5fce2cea051af7f3ebfd694d2c098b1dae5f2 Mon Sep 17 00:00:00 2001 From: Pavel Angelov Date: Tue, 22 Jul 2025 15:45:24 +0300 Subject: [PATCH 2/4] remove actual implementation --- .../generateFtCreateCommand.spec.ts | 96 ------------------- .../generateFtCreateCommand.ts | 89 ----------------- .../hooks/useFtCreateCommand/index.ts | 2 - .../hooks/useFtCreateCommand/types.ts | 50 ---------- .../useFtCreateCommand.spec.ts | 25 ----- .../useFtCreateCommand/useFtCreateCommand.ts | 25 ----- 6 files changed, 287 deletions(-) delete mode 100644 redisinsight/ui/src/services/hooks/useFtCreateCommand/generateFtCreateCommand.spec.ts delete mode 100644 redisinsight/ui/src/services/hooks/useFtCreateCommand/generateFtCreateCommand.ts delete mode 100644 redisinsight/ui/src/services/hooks/useFtCreateCommand/index.ts delete mode 100644 redisinsight/ui/src/services/hooks/useFtCreateCommand/types.ts delete mode 100644 redisinsight/ui/src/services/hooks/useFtCreateCommand/useFtCreateCommand.spec.ts delete mode 100644 redisinsight/ui/src/services/hooks/useFtCreateCommand/useFtCreateCommand.ts diff --git a/redisinsight/ui/src/services/hooks/useFtCreateCommand/generateFtCreateCommand.spec.ts b/redisinsight/ui/src/services/hooks/useFtCreateCommand/generateFtCreateCommand.spec.ts deleted file mode 100644 index 7064045e91..0000000000 --- a/redisinsight/ui/src/services/hooks/useFtCreateCommand/generateFtCreateCommand.spec.ts +++ /dev/null @@ -1,96 +0,0 @@ -import { generateFtCreateCommand } from './generateFtCreateCommand' - -describe('generateFtCreateCommand', () => { - it('should generate basic FT.CREATE for HASH with TEXT and NUMERIC fields', () => { - const result = generateFtCreateCommand({ - indexName: 'idx:products', - dataType: 'HASH', - prefixes: ['products:'], - schema: [ - { name: 'name', type: 'TEXT', sortable: true }, - { name: 'price', type: 'NUMERIC' }, - ], - }) - - expect(result).toBe( - [ - 'FT.CREATE idx:products', - 'ON HASH', - 'PREFIX 1 "products:"', - 'SCHEMA', - '"name" TEXT SORTABLE', - '"price" NUMERIC', - ].join('\n'), - ) - }) - - it('should support JSON paths with AS aliasing', () => { - const result = generateFtCreateCommand({ - indexName: 'idx:users', - dataType: 'JSON', - schema: [ - { name: 'fullName', type: 'TEXT', as: 'name', nostem: true }, - { name: 'age', type: 'NUMERIC', sortable: true }, - ], - }) - - expect(result).toBe( - [ - 'FT.CREATE idx:users', - 'ON JSON', - 'SCHEMA', - '$.fullName AS name TEXT NOSTEM', - '$.age NUMERIC SORTABLE', - ].join('\n'), - ) - }) - - it('should include VECTOR fields with parameters', () => { - const result = generateFtCreateCommand({ - indexName: 'idx:embeddings', - dataType: 'HASH', - prefixes: ['vec:'], - schema: [ - { - name: 'embedding', - type: 'VECTOR', - algorithm: 'FLAT', - vectorType: 'FLOAT32', - dim: 768, - distance: 'L2', - initialCap: 100, - blockSize: 200, - }, - ], - }) - - expect(result).toContain('VECTOR "FLAT"') - expect(result).toContain('"TYPE" FLOAT32') - expect(result).toContain('"DIM" 768') - expect(result).toContain('"DISTANCE_METRIC" "L2"') - expect(result).toContain('"INITIAL_CAP" 100') - expect(result).toContain('"BLOCK_SIZE" 200') - }) - - it('should handle empty stopwords', () => { - const result = generateFtCreateCommand({ - indexName: 'idx:noStop', - dataType: 'HASH', - schema: [{ name: 'text', type: 'TEXT' }], - stopwords: [], - }) - - expect(result).toContain('STOPWORDS 0') - }) - - it('should include quoted stopwords', () => { - const result = generateFtCreateCommand({ - indexName: 'idx:stopwords', - dataType: 'HASH', - schema: [{ name: 'text', type: 'TEXT' }], - stopwords: ['the', 'and', 'but'], - }) - - expect(result).toContain('STOPWORDS 3 "the" "and" "but"') - }) -}) diff --git a/redisinsight/ui/src/services/hooks/useFtCreateCommand/generateFtCreateCommand.ts b/redisinsight/ui/src/services/hooks/useFtCreateCommand/generateFtCreateCommand.ts deleted file mode 100644 index a70850dbfe..0000000000 --- a/redisinsight/ui/src/services/hooks/useFtCreateCommand/generateFtCreateCommand.ts +++ /dev/null @@ -1,89 +0,0 @@ -import { CreateIndexOptions, SchemaField } from './types' - -function formatVectorField( - field: Extract, -): string[] { - const parts = [ - `VECTOR "${field.algorithm}"`, - `"TYPE" ${field.vectorType}`, - `"DIM" ${field.dim}`, - `"DISTANCE_METRIC" "${field.distance}"`, - ] - - if (field.initialCap !== undefined) - parts.push(`"INITIAL_CAP" ${field.initialCap}`) - if (field.blockSize !== undefined) - parts.push(`"BLOCK_SIZE" ${field.blockSize}`) - if (field.m !== undefined) parts.push(`"M" ${field.m}`) - if (field.efConstruction !== undefined) - parts.push(`"EF_CONSTRUCTION" ${field.efConstruction}`) - - return parts -} - -function formatSchemaField( - field: SchemaField, - dataType: 'HASH' | 'JSON', -): string { - const parts: string[] = [] - - const fieldRef = dataType === 'JSON' ? `$.${field.name}` : `"${field.name}"` - parts.push(fieldRef) - - if (field.as) parts.push(`AS ${field.as}`) - - switch (field.type) { - case 'TEXT': - parts.push('TEXT') - if (field.nostem) parts.push('NOSTEM') - if (field.unf) parts.push('UNF') - if (field.sortable) parts.push('SORTABLE') - break - case 'NUMERIC': - parts.push('NUMERIC') - if (field.sortable) parts.push('SORTABLE') - break - case 'TAG': - parts.push('TAG') - if (field.separator) parts.push(`SEPARATOR "${field.separator}"`) - if (field.sortable) parts.push('SORTABLE') - break - case 'VECTOR': { - const aliasPart = field.as ? ` AS ${field.as}` : '' - const vectorPart = formatVectorField(field).join(' ') - return `${fieldRef}${aliasPart} ${vectorPart}` - } - default: - throw new Error(`Unsupported field type: ${(field as any).type}`) - } - - return parts.join(' ') -} - -export function generateFtCreateCommand(options: CreateIndexOptions): string { - const { indexName, dataType, prefixes = [], schema, stopwords } = options - - const lines: string[] = [`FT.CREATE ${indexName}`, `ON ${dataType}`] - - if (prefixes.length > 0) { - const quotedPrefixes = prefixes.map((p) => `"${p}"`).join(' ') - lines.push(`PREFIX ${prefixes.length} ${quotedPrefixes}`) - } - - lines.push('SCHEMA') - - schema.forEach((field) => { - lines.push(formatSchemaField(field, dataType)) - }) - - if (stopwords) { - if (stopwords.length === 0) { - lines.push('STOPWORDS 0') - } else { - const quotedStopwords = stopwords.map((sw) => `"${sw}"`).join(' ') - lines.push(`STOPWORDS ${stopwords.length} ${quotedStopwords}`) - } - } - - return lines.join('\n') -} diff --git a/redisinsight/ui/src/services/hooks/useFtCreateCommand/index.ts b/redisinsight/ui/src/services/hooks/useFtCreateCommand/index.ts deleted file mode 100644 index 4f35b8c19b..0000000000 --- a/redisinsight/ui/src/services/hooks/useFtCreateCommand/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { useFtCreateCommand } from './useFtCreateCommand' -export type { CreateIndexOptions, SchemaField } from './types' diff --git a/redisinsight/ui/src/services/hooks/useFtCreateCommand/types.ts b/redisinsight/ui/src/services/hooks/useFtCreateCommand/types.ts deleted file mode 100644 index 5a61fe0afb..0000000000 --- a/redisinsight/ui/src/services/hooks/useFtCreateCommand/types.ts +++ /dev/null @@ -1,50 +0,0 @@ -export type DataType = 'HASH' | 'JSON' - -export type VectorAlgorithm = 'FLAT' | 'HNSW' -export type VectorType = 'FLOAT32' | 'FLOAT64' -export type VectorDistance = 'L2' | 'IP' | 'COSINE' - -export type BaseField = { - name: string - as?: string -} - -export type TextField = BaseField & { - type: 'TEXT' - nostem?: boolean - unf?: boolean - sortable?: boolean -} - -export type NumericField = BaseField & { - type: 'NUMERIC' - sortable?: boolean -} - -export type TagField = BaseField & { - type: 'TAG' - separator?: string - sortable?: boolean -} - -export type VectorField = BaseField & { - type: 'VECTOR' - algorithm: VectorAlgorithm - vectorType: VectorType - dim: number - distance: VectorDistance - initialCap?: number - blockSize?: number - m?: number - efConstruction?: number -} - -export type SchemaField = TextField | NumericField | TagField | VectorField - -export type CreateIndexOptions = { - indexName: string - dataType: DataType - prefixes?: string[] - schema: SchemaField[] - stopwords?: string[] // optional -} diff --git a/redisinsight/ui/src/services/hooks/useFtCreateCommand/useFtCreateCommand.spec.ts b/redisinsight/ui/src/services/hooks/useFtCreateCommand/useFtCreateCommand.spec.ts deleted file mode 100644 index 294bcb9260..0000000000 --- a/redisinsight/ui/src/services/hooks/useFtCreateCommand/useFtCreateCommand.spec.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { renderHook } from '@testing-library/react' -import { useFtCreateCommand } from './useFtCreateCommand' - -describe('useFtCreateCommand (hardcoded version)', () => { - it('returns the expected hardcoded FT.CREATE command', () => { - const { result } = renderHook(() => useFtCreateCommand()) - - expect(result.current).toBe(`FT.CREATE idx:bikes_vss - ON HASH - PREFIX 1 "bikes:" - SCHEMA - "model" TEXT NOSTEM SORTABLE - "brand" TEXT NOSTEM SORTABLE - "price" NUMERIC SORTABLE - "type" TAG - "material" TAG - "weight" NUMERIC SORTABLE - "description_embeddings" VECTOR "FLAT" 10 - "TYPE" FLOAT32 - "DIM" 768 - "DISTANCE_METRIC" "L2" - "INITIAL_CAP" 111 - "BLOCK_SIZE" 111`) - }) -}) diff --git a/redisinsight/ui/src/services/hooks/useFtCreateCommand/useFtCreateCommand.ts b/redisinsight/ui/src/services/hooks/useFtCreateCommand/useFtCreateCommand.ts deleted file mode 100644 index b64f22a730..0000000000 --- a/redisinsight/ui/src/services/hooks/useFtCreateCommand/useFtCreateCommand.ts +++ /dev/null @@ -1,25 +0,0 @@ -// TODO: Since v1 would use predefined data, return a hardcoded command -// instead of generating it dynamically. Below is the intended implementation. - -// export function useFtCreateCommand(options: CreateIndexOptions): string { -// return useMemo(() => generateFtCreateCommand(options), [options]) -// } - -export function useFtCreateCommand(): string { - return `FT.CREATE idx:bikes_vss - ON HASH - PREFIX 1 "bikes:" - SCHEMA - "model" TEXT NOSTEM SORTABLE - "brand" TEXT NOSTEM SORTABLE - "price" NUMERIC SORTABLE - "type" TAG - "material" TAG - "weight" NUMERIC SORTABLE - "description_embeddings" VECTOR "FLAT" 10 - "TYPE" FLOAT32 - "DIM" 768 - "DISTANCE_METRIC" "L2" - "INITIAL_CAP" 111 - "BLOCK_SIZE" 111` -} From 285615dc5ff47fb45ca3afa9a5e21e90586be9fb Mon Sep 17 00:00:00 2001 From: Pavel Angelov Date: Tue, 22 Jul 2025 15:45:39 +0300 Subject: [PATCH 3/4] add the needed implementation (hardcoded) --- .../index/generateFtCreateCommand.spec.ts | 24 +++++++++++++++++++ .../utils/index/generateFtCreateCommand.ts | 19 +++++++++++++++ 2 files changed, 43 insertions(+) create mode 100644 redisinsight/ui/src/utils/index/generateFtCreateCommand.spec.ts create mode 100644 redisinsight/ui/src/utils/index/generateFtCreateCommand.ts diff --git a/redisinsight/ui/src/utils/index/generateFtCreateCommand.spec.ts b/redisinsight/ui/src/utils/index/generateFtCreateCommand.spec.ts new file mode 100644 index 0000000000..9771d00b2a --- /dev/null +++ b/redisinsight/ui/src/utils/index/generateFtCreateCommand.spec.ts @@ -0,0 +1,24 @@ +import { generateFtCreateCommand } from './generateFtCreateCommand' + +describe('generateFtCreateCommand', () => { + it('returns the expected hardcoded FT.CREATE command', () => { + const result = generateFtCreateCommand() + + expect(result).toBe(`FT.CREATE idx:bikes_vss + ON HASH + PREFIX 1 "bikes:" + SCHEMA + "model" TEXT NOSTEM SORTABLE + "brand" TEXT NOSTEM SORTABLE + "price" NUMERIC SORTABLE + "type" TAG + "material" TAG + "weight" NUMERIC SORTABLE + "description_embeddings" VECTOR "FLAT" 10 + "TYPE" FLOAT32 + "DIM" 768 + "DISTANCE_METRIC" "L2" + "INITIAL_CAP" 111 + "BLOCK_SIZE" 111`) + }) +}) diff --git a/redisinsight/ui/src/utils/index/generateFtCreateCommand.ts b/redisinsight/ui/src/utils/index/generateFtCreateCommand.ts new file mode 100644 index 0000000000..7bf28a3c7c --- /dev/null +++ b/redisinsight/ui/src/utils/index/generateFtCreateCommand.ts @@ -0,0 +1,19 @@ +// TODO: Since v1 would use predefined data, return a hardcoded command +// instead of generating it dynamically. Below is the intended implementation. + +export const generateFtCreateCommand = (): string => `FT.CREATE idx:bikes_vss + ON HASH + PREFIX 1 "bikes:" + SCHEMA + "model" TEXT NOSTEM SORTABLE + "brand" TEXT NOSTEM SORTABLE + "price" NUMERIC SORTABLE + "type" TAG + "material" TAG + "weight" NUMERIC SORTABLE + "description_embeddings" VECTOR "FLAT" 10 + "TYPE" FLOAT32 + "DIM" 768 + "DISTANCE_METRIC" "L2" + "INITIAL_CAP" 111 + "BLOCK_SIZE" 111` From bae687baf6653d7cbed8e67cdb965437fd0a16f4 Mon Sep 17 00:00:00 2001 From: Pavel Angelov Date: Tue, 22 Jul 2025 15:46:25 +0300 Subject: [PATCH 4/4] remvoe a leftover --- redisinsight/ui/src/utils/index/generateFtCreateCommand.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/redisinsight/ui/src/utils/index/generateFtCreateCommand.ts b/redisinsight/ui/src/utils/index/generateFtCreateCommand.ts index 7bf28a3c7c..84c4a313ee 100644 --- a/redisinsight/ui/src/utils/index/generateFtCreateCommand.ts +++ b/redisinsight/ui/src/utils/index/generateFtCreateCommand.ts @@ -1,5 +1,5 @@ // TODO: Since v1 would use predefined data, return a hardcoded command -// instead of generating it dynamically. Below is the intended implementation. +// instead of generating it dynamically. export const generateFtCreateCommand = (): string => `FT.CREATE idx:bikes_vss ON HASH