Skip to content

Commit 4683e96

Browse files
AvitalFineRedisAvital-Fineleibale
authored
Support Vector Similarity (#1785)
* ft.alter * support paramas * remove only and skip * merge * fix imports * add Vector field * update version * push attributes * typo * test * version check * remove .only * remove unued import * add support for DIALECT * clean code Co-authored-by: Avital-Fine <[email protected]> Co-authored-by: leibale <[email protected]>
1 parent 33a3f3f commit 4683e96

16 files changed

+632
-305
lines changed

package-lock.json

Lines changed: 90 additions & 90 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/search/lib/commands/AGGREGATE.spec.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -434,6 +434,26 @@ describe('AGGREGATE', () => {
434434
);
435435
});
436436
});
437+
438+
it('with PARAMS', () => {
439+
assert.deepEqual(
440+
transformArguments('index', '*', {
441+
PARAMS: {
442+
param: 'value'
443+
}
444+
}),
445+
['FT.AGGREGATE', 'index', '*', 'PARAMS', '2', 'param', 'value']
446+
);
447+
});
448+
449+
it('with DIALECT', () => {
450+
assert.deepEqual(
451+
transformArguments('index', '*', {
452+
DIALECT: 1
453+
}),
454+
['FT.AGGREGATE', 'index', '*', 'DIALECT', '1']
455+
);
456+
});
437457
});
438458

439459
testUtils.testWithClient('client.ft.aggregate', async client => {

packages/search/lib/commands/AGGREGATE.ts

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { RedisCommandArgument, RedisCommandArguments } from '@node-redis/client/dist/lib/commands';
22
import { pushVerdictArgument, transformTuplesReply } from '@node-redis/client/dist/lib/commands/generic-transformers';
3-
import { PropertyName, pushArgumentsWithLength, pushSortByArguments, SortByProperty } from '.';
3+
import { Params, PropertyName, pushArgumentsWithLength, pushParamsArgs, pushSortByArguments, SortByProperty } from '.';
44

55
export enum AggregateSteps {
66
GROUPBY = 'GROUPBY',
@@ -122,24 +122,25 @@ export interface AggregateOptions {
122122
VERBATIM?: true;
123123
LOAD?: LoadField | Array<LoadField>;
124124
STEPS?: Array<GroupByStep | SortStep | ApplyStep | LimitStep | FilterStep>;
125+
PARAMS?: Params;
126+
DIALECT?: number;
125127
}
126128

127129
export function transformArguments(
128130
index: string,
129131
query: string,
130132
options?: AggregateOptions
131133
): RedisCommandArguments {
132-
133-
const args = ['FT.AGGREGATE', index, query];
134-
pushAggregatehOptions(args, options);
135-
return args;
134+
return pushAggregatehOptions(
135+
['FT.AGGREGATE', index, query],
136+
options
137+
);
136138
}
137139

138140
export function pushAggregatehOptions(
139141
args: RedisCommandArguments,
140142
options?: AggregateOptions
141143
): RedisCommandArguments {
142-
143144
if (options?.VERBATIM) {
144145
args.push('VERBATIM');
145146
}
@@ -202,6 +203,12 @@ export function pushAggregatehOptions(
202203
}
203204
}
204205

206+
pushParamsArgs(args, options?.PARAMS);
207+
208+
if (options?.DIALECT) {
209+
args.push('DIALECT', options.DIALECT.toString());
210+
}
211+
205212
return args;
206213
}
207214

@@ -257,7 +264,6 @@ function pushGroupByReducer(args: RedisCommandArguments, reducer: GroupByReducer
257264
}
258265
}
259266
});
260-
261267
break;
262268
}
263269

packages/search/lib/commands/CREATE.spec.ts

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { strict as assert } from 'assert';
22
import testUtils, { GLOBAL } from '../test-utils';
33
import { transformArguments } from './CREATE';
4-
import { SchemaFieldTypes, SchemaTextFieldPhonetics, RedisSearchLanguages } from '.';
4+
import { SchemaFieldTypes, SchemaTextFieldPhonetics, RedisSearchLanguages, VectorAlgorithms } from '.';
55

66
describe('CREATE', () => {
77
describe('transformArguments', () => {
@@ -126,6 +126,52 @@ describe('CREATE', () => {
126126
});
127127
});
128128

129+
describe('VECTOR', () => {
130+
it('Flat algorithm', () => {
131+
assert.deepEqual(
132+
transformArguments('index', {
133+
field: {
134+
type: SchemaFieldTypes.VECTOR,
135+
ALGORITHM: VectorAlgorithms.FLAT,
136+
TYPE: 'FLOAT32',
137+
DIM: 2,
138+
DISTANCE_METRIC: 'L2',
139+
INITIAL_CAP: 1000000,
140+
BLOCK_SIZE: 1000
141+
}
142+
}),
143+
[
144+
'FT.CREATE', 'index', 'SCHEMA', 'field', 'VECTOR', 'FLAT', '10', 'TYPE',
145+
'FLOAT32', 'DIM', '2', 'DISTANCE_METRIC', 'L2', 'INITIAL_CAP', '1000000',
146+
'BLOCK_SIZE', '1000'
147+
]
148+
);
149+
});
150+
151+
it('HNSW algorithm', () => {
152+
assert.deepEqual(
153+
transformArguments('index', {
154+
field: {
155+
type: SchemaFieldTypes.VECTOR,
156+
ALGORITHM: VectorAlgorithms.HNSW,
157+
TYPE: 'FLOAT32',
158+
DIM: 2,
159+
DISTANCE_METRIC: 'L2',
160+
INITIAL_CAP: 1000000,
161+
M: 40,
162+
EF_CONSTRUCTION: 250,
163+
EF_RUNTIME: 20
164+
}
165+
}),
166+
[
167+
'FT.CREATE', 'index', 'SCHEMA', 'field', 'VECTOR', 'HNSW', '14', 'TYPE',
168+
'FLOAT32', 'DIM', '2', 'DISTANCE_METRIC', 'L2', 'INITIAL_CAP', '1000000',
169+
'M', '40', 'EF_CONSTRUCTION', '250', 'EF_RUNTIME', '20'
170+
]
171+
);
172+
});
173+
});
174+
129175
describe('with generic options', () => {
130176
it('with AS', () => {
131177
assert.deepEqual(

packages/search/lib/commands/EXPLAIN.spec.ts

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,32 @@ import { strict as assert } from 'assert';
22
import { transformArguments } from './EXPLAIN';
33

44
describe('EXPLAIN', () => {
5-
it('transformArguments', () => {
6-
assert.deepEqual(
7-
transformArguments('index', '*'),
8-
['FT.EXPLAIN', 'index', '*']
9-
);
5+
describe('transformArguments', () => {
6+
it('simple', () => {
7+
assert.deepEqual(
8+
transformArguments('index', '*'),
9+
['FT.EXPLAIN', 'index', '*']
10+
);
11+
});
12+
13+
it('with PARAMS', () => {
14+
assert.deepEqual(
15+
transformArguments('index', '*', {
16+
PARAMS: {
17+
param: 'value'
18+
}
19+
}),
20+
['FT.EXPLAIN', 'index', '*', 'PARAMS', '2', 'param', 'value']
21+
);
22+
});
23+
24+
it('with DIALECT', () => {
25+
assert.deepEqual(
26+
transformArguments('index', '*', {
27+
DIALECT: 1
28+
}),
29+
['FT.EXPLAIN', 'index', '*', 'DIALECT', '1']
30+
);
31+
});
1032
});
1133
});
Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,26 @@
1+
import { Params, pushParamsArgs } from ".";
2+
13
export const IS_READ_ONLY = true;
24

3-
export function transformArguments(index: string, query: string): Array<string> {
4-
return ['FT.EXPLAIN', index, query];
5+
interface ExplainOptions {
6+
PARAMS?: Params;
7+
DIALECT?: number;
8+
}
9+
10+
export function transformArguments(
11+
index: string,
12+
query: string,
13+
options?: ExplainOptions
14+
): Array<string> {
15+
const args = ['FT.EXPLAIN', index, query];
16+
17+
pushParamsArgs(args, options?.PARAMS);
18+
19+
if (options?.DIALECT) {
20+
args.push('DIALECT', options.DIALECT.toString());
21+
}
22+
23+
return args;
524
}
625

726
export declare function transformReply(): string;

packages/search/lib/commands/INFO.spec.ts

Lines changed: 42 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -15,32 +15,56 @@ describe('INFO', () => {
1515
await client.ft.create('index', {
1616
field: SchemaFieldTypes.TEXT
1717
});
18-
1918
assert.deepEqual(
2019
await client.ft.info('index'),
2120
{
2221
indexName: 'index',
2322
indexOptions: [],
24-
indexDefinition: {
25-
defaultScore: '1',
26-
keyType: 'HASH',
27-
prefixes: ['']
28-
},
29-
attributes: [[
30-
'identifier',
31-
'field',
32-
'attribute',
33-
'field',
34-
'type',
35-
'TEXT',
36-
'WEIGHT',
37-
'1'
38-
]],
23+
indexDefinition: Object.create(null, {
24+
default_score: {
25+
value: '1',
26+
configurable: true,
27+
enumerable: true
28+
},
29+
key_type: {
30+
value: 'HASH',
31+
configurable: true,
32+
enumerable: true
33+
},
34+
prefixes: {
35+
value: [''],
36+
configurable: true,
37+
enumerable: true
38+
}
39+
}),
40+
attributes: [Object.create(null, {
41+
identifier: {
42+
value: 'field',
43+
configurable: true,
44+
enumerable: true
45+
},
46+
attribute: {
47+
value: 'field',
48+
configurable: true,
49+
enumerable: true
50+
},
51+
type: {
52+
value: 'TEXT',
53+
configurable: true,
54+
enumerable: true
55+
},
56+
WEIGHT: {
57+
value: '1',
58+
configurable: true,
59+
enumerable: true
60+
}
61+
})],
3962
numDocs: '0',
4063
maxDocId: '0',
4164
numTerms: '0',
4265
numRecords: '0',
4366
invertedSzMb: '0',
67+
vectorIndexSzMb: '0',
4468
totalInvertedIndexBlocks: '0',
4569
offsetVectorsSzMb: '0',
4670
docTableSizeMb: '0',
@@ -67,7 +91,8 @@ describe('INFO', () => {
6791
globalTotal: 0,
6892
indexCapacity: 128,
6993
idnexTotal: 0
70-
}
94+
},
95+
stopWords: undefined
7196
}
7297
);
7398
}, GLOBAL.SERVERS.OPEN);

0 commit comments

Comments
 (0)