Skip to content

Commit 681d6ee

Browse files
committedDec 13, 2021
Merge branch 'master' of github.com:redis/node-redis
2 parents 7565ae3 + 01e66e7 commit 681d6ee

19 files changed

+581
-272
lines changed
 

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

+1-1
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 { AggregateGroupByReducers, AggregateSteps, transformArguments } from './AGGREGATE';
4-
import { SchemaFieldTypes } from './CREATE';
4+
import { SchemaFieldTypes } from '.';
55

66
describe('AGGREGATE', () => {
77
describe('transformArguments', () => {

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

+17-9
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { RedisCommandArguments } from '@node-redis/client/dist/lib/commands';
22
import { pushVerdictArgument, transformReplyTuples, TuplesObject } from '@node-redis/client/dist/lib/commands/generic-transformers';
3-
import { PropertyName, pushArgumentsWithLength, pushSortByArguments, SortByProperty } from '.';
3+
import { AggregateReply, PropertyName, pushArgumentsWithLength, pushSortByArguments, SortByProperty } from '.';
44

55
export enum AggregateSteps {
66
GROUPBY = 'GROUPBY',
@@ -118,14 +118,27 @@ type LoadField = PropertyName | {
118118
AS?: string;
119119
}
120120

121-
interface AggregateOptions {
121+
export interface AggregateOptions {
122122
VERBATIM?: true;
123123
LOAD?: LoadField | Array<LoadField>;
124124
STEPS?: Array<GroupByStep | SortStep | ApplyStep | LimitStep | FilterStep>;
125125
}
126126

127-
export function transformArguments(index: string, query: string, options?: AggregateOptions): RedisCommandArguments {
127+
export function transformArguments(
128+
index: string,
129+
query: string,
130+
options?: AggregateOptions
131+
): RedisCommandArguments {
132+
128133
const args = ['FT.AGGREGATE', index, query];
134+
pushAggregatehOptions(args, options);
135+
return args;
136+
}
137+
138+
export function pushAggregatehOptions(
139+
args: RedisCommandArguments,
140+
options?: AggregateOptions
141+
): RedisCommandArguments {
129142

130143
if (options?.VERBATIM) {
131144
args.push('VERBATIM');
@@ -258,16 +271,11 @@ function pushGroupByReducer(args: RedisCommandArguments, reducer: GroupByReducer
258271
}
259272
}
260273

261-
type AggregateRawReply = [
274+
export type AggregateRawReply = [
262275
total: number,
263276
...results: Array<Array<string>>
264277
];
265278

266-
interface AggregateReply {
267-
total: number;
268-
results: Array<TuplesObject>;
269-
}
270-
271279
export function transformReply(rawReply: AggregateRawReply): AggregateReply {
272280
const results: Array<TuplesObject> = [];
273281
for (let i = 1; i < rawReply.length; i++) {
+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { strict as assert } from 'assert';
2+
import testUtils, { GLOBAL } from '../test-utils';
3+
import { transformArguments } from './ALTER';
4+
import { SchemaFieldTypes } from '.';
5+
6+
describe('ALTER', () => {
7+
describe('transformArguments', () => {
8+
it('with NOINDEX', () => {
9+
assert.deepEqual(
10+
transformArguments('index', {
11+
field: {
12+
type: SchemaFieldTypes.TEXT,
13+
NOINDEX: true,
14+
SORTABLE: 'UNF',
15+
AS: 'text'
16+
}
17+
}),
18+
['FT.ALTER', 'index', 'SCHEMA', 'ADD', 'field', 'AS', 'text', 'TEXT', 'SORTABLE', 'UNF', 'NOINDEX']
19+
);
20+
});
21+
});
22+
23+
testUtils.testWithClient('client.ft.create', async client => {
24+
await Promise.all([
25+
client.ft.create('index', {
26+
title: SchemaFieldTypes.TEXT
27+
}),
28+
]);
29+
30+
assert.equal(
31+
await client.ft.alter('index', {
32+
body: SchemaFieldTypes.TEXT
33+
}),
34+
'OK'
35+
);
36+
}, GLOBAL.SERVERS.OPEN);
37+
});

‎packages/search/lib/commands/ALTER.ts

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { CreateSchema, pushSchema } from '.';
2+
3+
export function transformArguments(index: string, schema: CreateSchema): Array<string> {
4+
const args = ['FT.ALTER', index, 'SCHEMA', 'ADD'];
5+
pushSchema(args, schema);
6+
7+
return args;
8+
}
9+
10+
export declare function transformReply(): 'OK';

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

+2-2
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';
3-
import { SchemaFieldTypes, SchemaTextFieldPhonetics, transformArguments } from './CREATE';
4-
import { RedisSearchLanguages } from '.';
3+
import { transformArguments } from './CREATE';
4+
import { SchemaFieldTypes, SchemaTextFieldPhonetics, RedisSearchLanguages } from '.';
55

66
describe('CREATE', () => {
77
describe('transformArguments', () => {

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

+2-105
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,5 @@
11
import { pushOptionalVerdictArgument } from '@node-redis/client/dist/lib/commands/generic-transformers';
2-
import { RedisSearchLanguages, PropertyName } from '.';
3-
4-
export enum SchemaFieldTypes {
5-
TEXT = 'TEXT',
6-
NUMERIC = 'NUMERIC',
7-
GEO = 'GEO',
8-
TAG = 'TAG'
9-
}
10-
11-
type CreateSchemaField<T extends SchemaFieldTypes, E = Record<string, never>> = T | ({
12-
type: T;
13-
AS?: string;
14-
SORTABLE?: true | 'UNF';
15-
NOINDEX?: true;
16-
} & E);
17-
18-
export enum SchemaTextFieldPhonetics {
19-
DM_EN = 'dm:en',
20-
DM_FR = 'dm:fr',
21-
FM_PT = 'dm:pt',
22-
DM_ES = 'dm:es'
23-
}
24-
25-
type CreateSchemaTextField = CreateSchemaField<SchemaFieldTypes.TEXT, {
26-
NOSTEM?: true;
27-
WEIGHT?: number;
28-
PHONETIC?: SchemaTextFieldPhonetics;
29-
}>;
30-
31-
type CreateSchemaNumericField = CreateSchemaField<SchemaFieldTypes.NUMERIC>;
32-
33-
type CreateSchemaGeoField = CreateSchemaField<SchemaFieldTypes.GEO>;
34-
35-
type CreateSchemaTagField = CreateSchemaField<SchemaFieldTypes.TAG, {
36-
SEPERATOR?: string;
37-
CASESENSITIVE?: true;
38-
}>;
39-
40-
interface CreateSchema {
41-
[field: string]:
42-
CreateSchemaTextField |
43-
CreateSchemaNumericField |
44-
CreateSchemaGeoField |
45-
CreateSchemaTagField
46-
}
2+
import { RedisSearchLanguages, PropertyName, CreateSchema, pushSchema } from '.';
473

484
interface CreateOptions {
495
ON?: 'HASH' | 'JSON';
@@ -126,67 +82,8 @@ export function transformArguments(index: string, schema: CreateSchema, options?
12682
}
12783

12884
pushOptionalVerdictArgument(args, 'STOPWORDS', options?.STOPWORDS);
129-
13085
args.push('SCHEMA');
131-
132-
for (const [field, fieldOptions] of Object.entries(schema)) {
133-
args.push(field);
134-
135-
if (typeof fieldOptions === 'string') {
136-
args.push(fieldOptions);
137-
continue;
138-
}
139-
140-
if (fieldOptions.AS) {
141-
args.push('AS', fieldOptions.AS);
142-
}
143-
144-
args.push(fieldOptions.type);
145-
146-
switch (fieldOptions.type) {
147-
case 'TEXT':
148-
if (fieldOptions.NOSTEM) {
149-
args.push('NOSTEM');
150-
}
151-
152-
if (fieldOptions.WEIGHT) {
153-
args.push('WEIGHT', fieldOptions.WEIGHT.toString());
154-
}
155-
156-
if (fieldOptions.PHONETIC) {
157-
args.push('PHONETIC', fieldOptions.PHONETIC);
158-
}
159-
160-
break;
161-
162-
// case 'NUMERIC':
163-
// case 'GEO':
164-
// break;
165-
166-
case 'TAG':
167-
if (fieldOptions.SEPERATOR) {
168-
args.push('SEPERATOR', fieldOptions.SEPERATOR);
169-
}
170-
171-
if (fieldOptions.CASESENSITIVE) {
172-
args.push('CASESENSITIVE');
173-
}
174-
175-
break;
176-
}
177-
178-
if (fieldOptions.SORTABLE) {
179-
args.push('SORTABLE');
180-
181-
if (fieldOptions.SORTABLE === 'UNF') {
182-
args.push('UNF');
183-
}
184-
}
185-
186-
if (fieldOptions.NOINDEX) {
187-
args.push('NOINDEX');
188-
}
189-
}
86+
pushSchema(args, schema);
19087

19188
return args;
19289
}

‎packages/search/lib/commands/DROPINDEX.spec.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { strict as assert } from 'assert';
22
import testUtils, { GLOBAL } from '../test-utils';
3-
import { SchemaFieldTypes } from './CREATE';
3+
import { SchemaFieldTypes } from '.';
44
import { transformArguments } from './DROPINDEX';
55

66
describe('DROPINDEX', () => {

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

-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { strict as assert } from 'assert';
22
import testUtils, { GLOBAL } from '../test-utils';
3-
import { SchemaFieldTypes } from './CREATE';
43
import { transformArguments } from './INFO';
54

65
describe('INFO', () => {

‎packages/search/lib/commands/PROFILE.ts

-26
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import { strict as assert } from 'assert';
2+
import testUtils, { GLOBAL } from '../test-utils';
3+
import { SchemaFieldTypes } from '.';
4+
import { transformArguments } from './PROFILE_AGGREGATE';
5+
import { AggregateSteps } from './AGGREGATE';
6+
7+
describe('PROFILE AGGREGATE', () => {
8+
describe('transformArguments', () => {
9+
it('without options', () => {
10+
assert.deepEqual(
11+
transformArguments('index', 'query'),
12+
['FT.PROFILE', 'index', 'AGGREGATE', 'QUERY', 'query']
13+
);
14+
});
15+
16+
it('with options', () => {
17+
assert.deepEqual(
18+
transformArguments('index', 'query', {
19+
LIMITED: true,
20+
VERBATIM: true,
21+
STEPS: [{
22+
type: AggregateSteps.SORTBY,
23+
BY: '@by'
24+
}]
25+
}),
26+
['FT.PROFILE', 'index', 'AGGREGATE', 'LIMITED', 'QUERY', 'query',
27+
'VERBATIM', 'SORTBY', '1', '@by']
28+
);
29+
});
30+
});
31+
32+
testUtils.testWithClient('client.ft.search', async client => {
33+
await Promise.all([
34+
client.ft.create('index', {
35+
field: SchemaFieldTypes.NUMERIC
36+
}),
37+
client.hSet('1', 'field', '1'),
38+
client.hSet('2', 'field', '2')
39+
]);
40+
41+
const res = await client.ft.profileAggregate('index', '*');
42+
assert.ok(typeof res.profile.iteratorsProfile.counter === 'number');
43+
assert.ok(typeof res.profile.parsingTime === 'string');
44+
assert.ok(res.results.total == 1);
45+
}, GLOBAL.SERVERS.OPEN);
46+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { pushAggregatehOptions, AggregateOptions, transformReply as transformAggregateReply, AggregateRawReply } from './AGGREGATE';
2+
import { ProfileOptions, ProfileRawReply, ProfileReply, transformProfile } from '.';
3+
4+
export const IS_READ_ONLY = true;
5+
6+
export function transformArguments(
7+
index: string,
8+
query: string,
9+
options?: ProfileOptions & AggregateOptions
10+
): Array<string> {
11+
const args = ['FT.PROFILE', index, 'AGGREGATE'];
12+
13+
if (options?.LIMITED) {
14+
args.push('LIMITED');
15+
}
16+
17+
args.push('QUERY', query);
18+
pushAggregatehOptions(args, options)
19+
return args;
20+
}
21+
22+
type ProfileAggeregateRawReply = ProfileRawReply<AggregateRawReply>;
23+
24+
export function transformReply(reply: ProfileAggeregateRawReply): ProfileReply {
25+
return {
26+
results: transformAggregateReply(reply[0]),
27+
profile: transformProfile(reply[1])
28+
};
29+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { strict as assert } from 'assert';
2+
import testUtils, { GLOBAL } from '../test-utils';
3+
import { SchemaFieldTypes } from '.';
4+
import { transformArguments } from './PROFILE_SEARCH';
5+
6+
describe('PROFILE SEARCH', () => {
7+
describe('transformArguments', () => {
8+
it('without options', () => {
9+
assert.deepEqual(
10+
transformArguments('index', 'query'),
11+
['FT.PROFILE', 'index', 'SEARCH', 'QUERY', 'query']
12+
);
13+
});
14+
15+
it('with options', () => {
16+
assert.deepEqual(
17+
transformArguments('index', 'query', {
18+
LIMITED: true,
19+
VERBATIM: true,
20+
INKEYS: 'key'
21+
}),
22+
['FT.PROFILE', 'index', 'SEARCH', 'LIMITED', 'QUERY', 'query',
23+
'VERBATIM', 'INKEYS', '1', 'key']
24+
);
25+
});
26+
});
27+
28+
testUtils.testWithClient('client.ft.search', async client => {
29+
await Promise.all([
30+
client.ft.create('index', {
31+
field: SchemaFieldTypes.NUMERIC
32+
}),
33+
client.hSet('1', 'field', '1')
34+
]);
35+
36+
const res = await client.ft.profileSearch('index', '*');
37+
assert.ok(typeof res.profile.iteratorsProfile.counter === 'number');
38+
assert.ok(typeof res.profile.parsingTime === 'string');
39+
assert.ok(res.results.total == 1);
40+
}, GLOBAL.SERVERS.OPEN);
41+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { SearchOptions, SearchRawReply, transformReply as transformSearchReply } from './SEARCH';
2+
import { pushSearchOptions, ProfileOptions, ProfileRawReply, ProfileReply, transformProfile } from '.';
3+
4+
export const IS_READ_ONLY = true;
5+
6+
export function transformArguments(
7+
index: string,
8+
query: string,
9+
options?: ProfileOptions & SearchOptions
10+
): Array<string> {
11+
const args = ['FT.PROFILE', index, 'SEARCH'];
12+
13+
if (options?.LIMITED) {
14+
args.push('LIMITED');
15+
}
16+
17+
args.push('QUERY', query);
18+
pushSearchOptions(args, options)
19+
return args;
20+
}
21+
22+
type ProfileSearchRawReply = ProfileRawReply<SearchRawReply>;
23+
24+
export function transformReply(reply: ProfileSearchRawReply): ProfileReply {
25+
return {
26+
results: transformSearchReply(reply[0]),
27+
profile: transformProfile(reply[1])
28+
};
29+
}

‎packages/search/lib/commands/SEARCH.spec.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { strict as assert } from 'assert';
2-
import { RedisSearchLanguages } from '.';
2+
import { RedisSearchLanguages, SchemaFieldTypes } from '.';
33
import testUtils, { GLOBAL } from '../test-utils';
4-
import { SchemaFieldTypes } from './CREATE';
54
import { transformArguments } from './SEARCH';
65

76
describe('SEARCH', () => {

‎packages/search/lib/commands/SEARCH.ts

+6-119
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import { RedisCommandArguments } from '@node-redis/client/dist/lib/commands';
2-
import { pushOptionalVerdictArgument, pushVerdictArgument, transformReplyTuples } from '@node-redis/client/dist/lib/commands/generic-transformers';
3-
import { RedisSearchLanguages, PropertyName, pushSortByProperty, SortByProperty } from '.';
2+
import { transformReplyTuples } from '@node-redis/client/dist/lib/commands/generic-transformers';
3+
import { pushSearchOptions, RedisSearchLanguages, PropertyName, SortByProperty, SearchReply } from '.';
44

55
export const FIRST_KEY_INDEX = 1;
66

77
export const IS_READ_ONLY = true;
88

9-
interface SearchOptions {
9+
export interface SearchOptions {
1010
// NOCONTENT?: true; TODO
1111
VERBATIM?: true;
1212
NOSTOPWORDS?: true;
@@ -62,126 +62,13 @@ export function transformArguments(
6262
options?: SearchOptions
6363
): RedisCommandArguments {
6464
const args: RedisCommandArguments = ['FT.SEARCH', index, query];
65-
66-
if (options?.VERBATIM) {
67-
args.push('VERBATIM');
68-
}
69-
70-
if (options?.NOSTOPWORDS) {
71-
args.push('NOSTOPWORDS');
72-
}
73-
74-
// if (options?.WITHSCORES) {
75-
// args.push('WITHSCORES');
76-
// }
77-
78-
// if (options?.WITHPAYLOADS) {
79-
// args.push('WITHPAYLOADS');
80-
// }
81-
82-
pushOptionalVerdictArgument(args, 'INKEYS', options?.INKEYS);
83-
pushOptionalVerdictArgument(args, 'INFIELDS', options?.INFIELDS);
84-
pushOptionalVerdictArgument(args, 'RETURN', options?.RETURN);
85-
86-
if (options?.SUMMARIZE) {
87-
args.push('SUMMARIZE');
88-
89-
if (typeof options.SUMMARIZE === 'object') {
90-
if (options.SUMMARIZE.FIELDS) {
91-
args.push('FIELDS');
92-
pushVerdictArgument(args, options.SUMMARIZE.FIELDS);
93-
}
94-
95-
if (options.SUMMARIZE.FRAGS) {
96-
args.push('FRAGS', options.SUMMARIZE.FRAGS.toString());
97-
}
98-
99-
if (options.SUMMARIZE.LEN) {
100-
args.push('LEN', options.SUMMARIZE.LEN.toString());
101-
}
102-
103-
if (options.SUMMARIZE.SEPARATOR) {
104-
args.push('SEPARATOR', options.SUMMARIZE.SEPARATOR);
105-
}
106-
}
107-
}
108-
109-
if (options?.HIGHLIGHT) {
110-
args.push('HIGHLIGHT');
111-
112-
if (typeof options.HIGHLIGHT === 'object') {
113-
if (options.HIGHLIGHT.FIELDS) {
114-
args.push('FIELDS');
115-
pushVerdictArgument(args, options.HIGHLIGHT.FIELDS);
116-
}
117-
118-
if (options.HIGHLIGHT.TAGS) {
119-
args.push('TAGS', options.HIGHLIGHT.TAGS.open, options.HIGHLIGHT.TAGS.close);
120-
}
121-
}
122-
}
123-
124-
if (options?.SLOP) {
125-
args.push('SLOP', options.SLOP.toString());
126-
}
127-
128-
if (options?.INORDER) {
129-
args.push('INORDER');
130-
}
131-
132-
if (options?.LANGUAGE) {
133-
args.push('LANGUAGE', options.LANGUAGE);
134-
}
135-
136-
if (options?.EXPANDER) {
137-
args.push('EXPANDER', options.EXPANDER);
138-
}
139-
140-
if (options?.SCORER) {
141-
args.push('SCORER', options.SCORER);
142-
}
143-
144-
// if (options?.EXPLAINSCORE) {
145-
// args.push('EXPLAINSCORE');
146-
// }
147-
148-
// if (options?.PAYLOAD) {
149-
// args.push('PAYLOAD', options.PAYLOAD);
150-
// }
151-
152-
if (options?.SORTBY) {
153-
args.push('SORTBY');
154-
pushSortByProperty(args, options.SORTBY);
155-
}
156-
157-
// if (options?.MSORTBY) {
158-
// pushSortByArguments(args, 'MSORTBY', options.MSORTBY);
159-
// }
160-
161-
if (options?.LIMIT) {
162-
args.push(
163-
'LIMIT',
164-
options.LIMIT.from.toString(),
165-
options.LIMIT.size.toString()
166-
);
167-
}
168-
65+
pushSearchOptions(args, options);
16966
return args;
17067
}
17168

172-
interface SearchDocumentValue {
173-
[key: string]: string | number | null | Array<SearchDocumentValue> | SearchDocumentValue;
174-
}
175-
176-
interface SearchReply {
177-
total: number;
178-
documents: Array<{
179-
id: string;
180-
value: SearchDocumentValue;
181-
}>;
182-
}
69+
export type SearchRawReply = Array<any>;
18370

184-
export function transformReply(reply: Array<any>): SearchReply {
71+
export function transformReply(reply: SearchRawReply): SearchReply {
18572
const documents = [];
18673
for (let i = 1; i < reply.length; i += 2) {
18774
const tuples = reply[i + 1];

‎packages/search/lib/commands/SPELLCHECK.spec.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { strict as assert } from 'assert';
22
import testUtils, { GLOBAL } from '../test-utils';
3-
import { SchemaFieldTypes } from './CREATE';
3+
import { SchemaFieldTypes } from '.';
44
import { transformArguments } from './SPELLCHECK';
55

66
describe('SPELLCHECK', () => {

‎packages/search/lib/commands/TAGVALS.spec.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { strict as assert } from 'assert';
22
import testUtils, { GLOBAL } from '../test-utils';
3-
import { SchemaFieldTypes } from './CREATE';
3+
import { SchemaFieldTypes } from '.';
44
import { transformArguments } from './TAGVALS';
55

66
describe('TAGVALS', () => {

‎packages/search/lib/commands/index.ts

+356-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import * as _LIST from './_LIST';
2+
import * as ALTER from './ALTER';
23
import * as AGGREGATE from './AGGREGATE';
34
import * as ALIASADD from './ALIASADD';
45
import * as ALIASDEL from './ALIASDEL';
@@ -13,7 +14,8 @@ import * as DROPINDEX from './DROPINDEX';
1314
import * as EXPLAIN from './EXPLAIN';
1415
import * as EXPLAINCLI from './EXPLAINCLI';
1516
import * as INFO from './INFO';
16-
// import * as PROFILE from './PROFILE';
17+
import * as PROFILESEARCH from './PROFILE_SEARCH';
18+
import * as PROFILEAGGREGATE from './PROFILE_AGGREGATE';
1719
import * as SEARCH from './SEARCH';
1820
import * as SPELLCHECK from './SPELLCHECK';
1921
import * as SUGADD from './SUGADD';
@@ -27,10 +29,15 @@ import * as SYNDUMP from './SYNDUMP';
2729
import * as SYNUPDATE from './SYNUPDATE';
2830
import * as TAGVALS from './TAGVALS';
2931
import { RedisCommandArguments } from '@node-redis/client/dist/lib/commands';
32+
import { pushOptionalVerdictArgument, pushVerdictArgument, TuplesObject } from '@node-redis/client/dist/lib/commands/generic-transformers';
33+
import internal = require('stream');
34+
import { SearchOptions } from './SEARCH';
3035

3136
export default {
3237
_LIST,
3338
_list: _LIST,
39+
ALTER,
40+
alter: ALTER,
3441
AGGREGATE,
3542
aggregate: AGGREGATE,
3643
ALIASADD,
@@ -59,8 +66,10 @@ export default {
5966
explainCli: EXPLAINCLI,
6067
INFO,
6168
info: INFO,
62-
// PROFILE,
63-
// profile: PROFILE,
69+
PROFILESEARCH,
70+
profileSearch: PROFILESEARCH,
71+
PROFILEAGGREGATE,
72+
profileAggregate: PROFILEAGGREGATE,
6473
SEARCH,
6574
search: SEARCH,
6675
SPELLCHECK,
@@ -159,3 +168,347 @@ export function pushArgumentsWithLength(args: RedisCommandArguments, fn: (args:
159168
args[lengthIndex] = (args.length - lengthIndex - 1).toString();
160169
return args;
161170
}
171+
172+
export enum SchemaFieldTypes {
173+
TEXT = 'TEXT',
174+
NUMERIC = 'NUMERIC',
175+
GEO = 'GEO',
176+
TAG = 'TAG'
177+
}
178+
179+
type CreateSchemaField<T extends SchemaFieldTypes, E = Record<string, never>> = T | ({
180+
type: T;
181+
AS?: string;
182+
SORTABLE?: true | 'UNF';
183+
NOINDEX?: true;
184+
} & E);
185+
186+
export enum SchemaTextFieldPhonetics {
187+
DM_EN = 'dm:en',
188+
DM_FR = 'dm:fr',
189+
FM_PT = 'dm:pt',
190+
DM_ES = 'dm:es'
191+
}
192+
193+
type CreateSchemaTextField = CreateSchemaField<SchemaFieldTypes.TEXT, {
194+
NOSTEM?: true;
195+
WEIGHT?: number;
196+
PHONETIC?: SchemaTextFieldPhonetics;
197+
}>;
198+
199+
type CreateSchemaNumericField = CreateSchemaField<SchemaFieldTypes.NUMERIC>;
200+
201+
type CreateSchemaGeoField = CreateSchemaField<SchemaFieldTypes.GEO>;
202+
203+
type CreateSchemaTagField = CreateSchemaField<SchemaFieldTypes.TAG, {
204+
SEPERATOR?: string;
205+
CASESENSITIVE?: true;
206+
}>;
207+
208+
export interface CreateSchema {
209+
[field: string]:
210+
CreateSchemaTextField |
211+
CreateSchemaNumericField |
212+
CreateSchemaGeoField |
213+
CreateSchemaTagField
214+
}
215+
216+
export function pushSchema(args: RedisCommandArguments, schema: CreateSchema) {
217+
for (const [field, fieldOptions] of Object.entries(schema)) {
218+
args.push(field);
219+
220+
if (typeof fieldOptions === 'string') {
221+
args.push(fieldOptions);
222+
continue;
223+
}
224+
225+
if (fieldOptions.AS) {
226+
args.push('AS', fieldOptions.AS);
227+
}
228+
229+
args.push(fieldOptions.type);
230+
231+
switch (fieldOptions.type) {
232+
case 'TEXT':
233+
if (fieldOptions.NOSTEM) {
234+
args.push('NOSTEM');
235+
}
236+
237+
if (fieldOptions.WEIGHT) {
238+
args.push('WEIGHT', fieldOptions.WEIGHT.toString());
239+
}
240+
241+
if (fieldOptions.PHONETIC) {
242+
args.push('PHONETIC', fieldOptions.PHONETIC);
243+
}
244+
245+
break;
246+
247+
// case 'NUMERIC':
248+
// case 'GEO':
249+
// break;
250+
251+
case 'TAG':
252+
if (fieldOptions.SEPERATOR) {
253+
args.push('SEPERATOR', fieldOptions.SEPERATOR);
254+
}
255+
256+
if (fieldOptions.CASESENSITIVE) {
257+
args.push('CASESENSITIVE');
258+
}
259+
260+
break;
261+
}
262+
263+
if (fieldOptions.SORTABLE) {
264+
args.push('SORTABLE');
265+
266+
if (fieldOptions.SORTABLE === 'UNF') {
267+
args.push('UNF');
268+
}
269+
}
270+
271+
if (fieldOptions.NOINDEX) {
272+
args.push('NOINDEX');
273+
}
274+
}
275+
}
276+
277+
export function pushSearchOptions(
278+
args: RedisCommandArguments,
279+
options?: SearchOptions
280+
): RedisCommandArguments {
281+
282+
if (options?.VERBATIM) {
283+
args.push('VERBATIM');
284+
}
285+
286+
if (options?.NOSTOPWORDS) {
287+
args.push('NOSTOPWORDS');
288+
}
289+
290+
// if (options?.WITHSCORES) {
291+
// args.push('WITHSCORES');
292+
// }
293+
294+
// if (options?.WITHPAYLOADS) {
295+
// args.push('WITHPAYLOADS');
296+
// }
297+
298+
pushOptionalVerdictArgument(args, 'INKEYS', options?.INKEYS);
299+
pushOptionalVerdictArgument(args, 'INFIELDS', options?.INFIELDS);
300+
pushOptionalVerdictArgument(args, 'RETURN', options?.RETURN);
301+
302+
if (options?.SUMMARIZE) {
303+
args.push('SUMMARIZE');
304+
305+
if (typeof options.SUMMARIZE === 'object') {
306+
if (options.SUMMARIZE.FIELDS) {
307+
args.push('FIELDS');
308+
pushVerdictArgument(args, options.SUMMARIZE.FIELDS);
309+
}
310+
311+
if (options.SUMMARIZE.FRAGS) {
312+
args.push('FRAGS', options.SUMMARIZE.FRAGS.toString());
313+
}
314+
315+
if (options.SUMMARIZE.LEN) {
316+
args.push('LEN', options.SUMMARIZE.LEN.toString());
317+
}
318+
319+
if (options.SUMMARIZE.SEPARATOR) {
320+
args.push('SEPARATOR', options.SUMMARIZE.SEPARATOR);
321+
}
322+
}
323+
}
324+
325+
if (options?.HIGHLIGHT) {
326+
args.push('HIGHLIGHT');
327+
328+
if (typeof options.HIGHLIGHT === 'object') {
329+
if (options.HIGHLIGHT.FIELDS) {
330+
args.push('FIELDS');
331+
pushVerdictArgument(args, options.HIGHLIGHT.FIELDS);
332+
}
333+
334+
if (options.HIGHLIGHT.TAGS) {
335+
args.push('TAGS', options.HIGHLIGHT.TAGS.open, options.HIGHLIGHT.TAGS.close);
336+
}
337+
}
338+
}
339+
340+
if (options?.SLOP) {
341+
args.push('SLOP', options.SLOP.toString());
342+
}
343+
344+
if (options?.INORDER) {
345+
args.push('INORDER');
346+
}
347+
348+
if (options?.LANGUAGE) {
349+
args.push('LANGUAGE', options.LANGUAGE);
350+
}
351+
352+
if (options?.EXPANDER) {
353+
args.push('EXPANDER', options.EXPANDER);
354+
}
355+
356+
if (options?.SCORER) {
357+
args.push('SCORER', options.SCORER);
358+
}
359+
360+
// if (options?.EXPLAINSCORE) {
361+
// args.push('EXPLAINSCORE');
362+
// }
363+
364+
// if (options?.PAYLOAD) {
365+
// args.push('PAYLOAD', options.PAYLOAD);
366+
// }
367+
368+
if (options?.SORTBY) {
369+
args.push('SORTBY');
370+
pushSortByProperty(args, options.SORTBY);
371+
}
372+
373+
// if (options?.MSORTBY) {
374+
// pushSortByArguments(args, 'MSORTBY', options.MSORTBY);
375+
// }
376+
377+
if (options?.LIMIT) {
378+
args.push(
379+
'LIMIT',
380+
options.LIMIT.from.toString(),
381+
options.LIMIT.size.toString()
382+
);
383+
}
384+
385+
return args;
386+
}
387+
388+
interface SearchDocumentValue {
389+
[key: string]: string | number | null | Array<SearchDocumentValue> | SearchDocumentValue;
390+
}
391+
392+
export interface SearchReply {
393+
total: number;
394+
documents: Array<{
395+
id: string;
396+
value: SearchDocumentValue;
397+
}>;
398+
}
399+
400+
export interface AggregateReply {
401+
total: number;
402+
results: Array<TuplesObject>;
403+
}
404+
405+
export interface ProfileOptions {
406+
LIMITED?: true;
407+
}
408+
409+
export type ProfileRawReply<T> = [
410+
results: T,
411+
profile: [
412+
_: string,
413+
TotalProfileTime: string,
414+
_: string,
415+
ParsingTime: string,
416+
_: string,
417+
PipelineCreationTime: string,
418+
_: string,
419+
IteratorsProfile: Array<any>
420+
]
421+
];
422+
423+
export interface ProfileReply {
424+
results: SearchReply | AggregateReply,
425+
profile: ProfileData
426+
}
427+
428+
interface ChildIterator {
429+
type?: string,
430+
counter?: number,
431+
term?: string,
432+
size?: number,
433+
time?: string,
434+
childIterators?: Array<ChildIterator>
435+
}
436+
437+
interface IteratorsProfile {
438+
type?: string,
439+
counter?: number,
440+
queryType?: string,
441+
time?: string,
442+
childIterators?: Array<ChildIterator>
443+
}
444+
445+
interface ProfileData {
446+
totalProfileTime: string,
447+
parsingTime: string,
448+
pipelineCreationTime: string,
449+
iteratorsProfile: IteratorsProfile
450+
}
451+
452+
export function transformProfile(reply: Array<any>): ProfileData{
453+
return {
454+
totalProfileTime: reply[0][1],
455+
parsingTime: reply[1][1],
456+
pipelineCreationTime: reply[2][1],
457+
iteratorsProfile: transformIterators(reply[3][1])
458+
};
459+
}
460+
461+
function transformIterators(IteratorsProfile: Array<any>): IteratorsProfile {
462+
var res: IteratorsProfile = {};
463+
for (let i = 0; i < IteratorsProfile.length; i += 2) {
464+
const value = IteratorsProfile[i+1];
465+
switch (IteratorsProfile[i]) {
466+
case 'Type':
467+
res.type = value;
468+
break;
469+
case 'Counter':
470+
res.counter = value;
471+
break;
472+
case 'Time':
473+
res.time = value;
474+
break;
475+
case 'Query type':
476+
res.queryType = value;
477+
break;
478+
case 'Child iterators':
479+
res.childIterators = value.map(transformChildIterators);
480+
break;
481+
}
482+
}
483+
484+
return res;
485+
}
486+
487+
function transformChildIterators(IteratorsProfile: Array<any>): ChildIterator {
488+
var res: ChildIterator = {};
489+
for (let i = 1; i < IteratorsProfile.length; i += 2) {
490+
const value = IteratorsProfile[i+1];
491+
switch (IteratorsProfile[i]) {
492+
case 'Type':
493+
res.type = value;
494+
break;
495+
case 'Counter':
496+
res.counter = value;
497+
break;
498+
case 'Time':
499+
res.time = value;
500+
break;
501+
case 'Size':
502+
res.size = value;
503+
break;
504+
case 'Term':
505+
res.term = value;
506+
break;
507+
case 'Child iterators':
508+
res.childIterators = value.map(transformChildIterators);
509+
break;
510+
}
511+
}
512+
513+
return res;
514+
}

‎packages/search/lib/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
export { default } from './commands';
22

3-
export { SchemaFieldTypes, SchemaTextFieldPhonetics } from './commands/CREATE';
3+
export { SchemaFieldTypes, SchemaTextFieldPhonetics } from './commands';
44
export { AggregateSteps, AggregateGroupByReducers } from './commands/AGGREGATE';

0 commit comments

Comments
 (0)
Please sign in to comment.