Skip to content

Commit a3720ae

Browse files
committed
refactor!(search): simplify PROFILE commands to return raw response
- BREAKING CHANGE: FT.PROFILE now returns raw response, letting users implement their own parsing
1 parent a9333b2 commit a3720ae

File tree

6 files changed

+215
-203
lines changed

6 files changed

+215
-203
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
"./packages/*"
66
],
77
"scripts": {
8+
"test-single": "TS_NODE_PROJECT='./packages/test-utils/tsconfig.json' mocha --require ts-node/register/transpile-only ",
89
"test": "npm run test -ws --if-present",
910
"build": "tsc --build",
1011
"documentation": "typedoc --out ./documentation",

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

Lines changed: 49 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -31,19 +31,53 @@ describe('PROFILE AGGREGATE', () => {
3131
});
3232
});
3333

34-
testUtils.testWithClient('client.ft.search', async client => {
35-
await Promise.all([
36-
client.ft.create('index', {
37-
field: SCHEMA_FIELD_TYPE.NUMERIC
38-
}),
39-
client.hSet('1', 'field', '1'),
40-
client.hSet('2', 'field', '2')
41-
]);
42-
43-
const res = await client.ft.profileAggregate('index', '*');
44-
assert.deepEqual('None', res.profile.warning);
45-
assert.ok(typeof res.profile.iteratorsProfile.counter === 'number');
46-
assert.ok(typeof res.profile.parsingTime === 'string');
47-
assert.ok(res.results.total == 1);
48-
}, GLOBAL.SERVERS.OPEN);
34+
testUtils.testWithClient('client.ft.search', async client => {
35+
await Promise.all([
36+
client.ft.create('index', {
37+
field: SCHEMA_FIELD_TYPE.NUMERIC
38+
}),
39+
client.hSet('1', 'field', '1'),
40+
client.hSet('2', 'field', '2')
41+
]);
42+
43+
44+
const normalizeObject = obj => JSON.parse(JSON.stringify(obj));
45+
const res = await client.ft.profileAggregate('index', '*');
46+
47+
const normalizedRes = normalizeObject(res);
48+
assert.equal(normalizedRes.results.total, 1);
49+
50+
assert.ok(normalizedRes.profile[0] === 'Shards');
51+
assert.ok(Array.isArray(normalizedRes.profile[1]));
52+
assert.ok(normalizedRes.profile[2] === 'Coordinator');
53+
assert.ok(Array.isArray(normalizedRes.profile[3]));
54+
55+
const shardProfile = normalizedRes.profile[1][0];
56+
assert.ok(shardProfile.includes('Total profile time'));
57+
assert.ok(shardProfile.includes('Parsing time'));
58+
assert.ok(shardProfile.includes('Pipeline creation time'));
59+
assert.ok(shardProfile.includes('Warning'));
60+
assert.ok(shardProfile.includes('Iterators profile'));
61+
62+
}, GLOBAL.SERVERS.OPEN);
63+
64+
testUtils.testWithClientIfVersionWithinRange([[8], 'LATEST'], '[RESP3] client.ft.search', async client => {
65+
await Promise.all([
66+
client.ft.create('index', {
67+
field: SCHEMA_FIELD_TYPE.NUMERIC
68+
}),
69+
client.hSet('1', 'field', '1'),
70+
client.hSet('2', 'field', '2')
71+
]);
72+
73+
74+
const normalizeObject = obj => JSON.parse(JSON.stringify(obj));
75+
const res = await client.ft.profileAggregate('index', '*');
76+
77+
const normalizedRes = normalizeObject(res);
78+
assert.equal(normalizedRes.Results.total_results, 1);
79+
assert.ok(normalizedRes.Profile.Shards);
80+
81+
}, GLOBAL.SERVERS.OPEN_3)
82+
4983
});
Lines changed: 29 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,35 @@
11
import { CommandParser } from '@redis/client/dist/lib/client/parser';
2-
import { Command, ReplyUnion } from "@redis/client/dist/lib/RESP/types";
3-
import AGGREGATE, { AggregateRawReply, FtAggregateOptions, parseAggregateOptions } from "./AGGREGATE";
4-
import { ProfileOptions, ProfileRawReply, ProfileReply, transformProfile } from "./PROFILE_SEARCH";
2+
import { Command, ReplyUnion, UnwrapReply } from '@redis/client/dist/lib/RESP/types';
3+
import AGGREGATE, { AggregateRawReply, FtAggregateOptions, parseAggregateOptions } from './AGGREGATE';
4+
import { ProfileOptions, ProfileRawReplyResp2, ProfileReplyResp2, } from './PROFILE_SEARCH';
55

66
export default {
77
NOT_KEYED_COMMAND: true,
8-
IS_READ_ONLY: true,
9-
parseCommand(
10-
parser: CommandParser,
11-
index: string,
12-
query: string,
13-
options?: ProfileOptions & FtAggregateOptions
14-
) {
15-
parser.push('FT.PROFILE', index, 'AGGREGATE');
16-
17-
if (options?.LIMITED) {
18-
parser.push('LIMITED');
19-
}
20-
21-
parser.push('QUERY', query);
8+
IS_READ_ONLY: true,
9+
parseCommand(
10+
parser: CommandParser,
11+
index: string,
12+
query: string,
13+
options?: ProfileOptions & FtAggregateOptions
14+
) {
15+
parser.push('FT.PROFILE', index, 'AGGREGATE');
2216

23-
parseAggregateOptions(parser, options)
24-
},
25-
transformReply: {
26-
2: (reply: ProfileAggeregateRawReply): ProfileReply => {
27-
return {
28-
results: AGGREGATE.transformReply[2](reply[0]),
29-
profile: transformProfile(reply[1])
30-
}
31-
},
32-
3: undefined as unknown as () => ReplyUnion
33-
},
34-
unstableResp3: true
35-
} as const satisfies Command;
17+
if (options?.LIMITED) {
18+
parser.push('LIMITED');
19+
}
3620

37-
type ProfileAggeregateRawReply = ProfileRawReply<AggregateRawReply>;
21+
parser.push('QUERY', query);
22+
23+
parseAggregateOptions(parser, options)
24+
},
25+
transformReply: {
26+
2: (reply: UnwrapReply<ProfileRawReplyResp2<AggregateRawReply>>): ProfileReplyResp2 => {
27+
return {
28+
results: AGGREGATE.transformReply[2](reply[0]),
29+
profile: reply[1]
30+
}
31+
},
32+
3: (reply: ReplyUnion): ReplyUnion => reply
33+
},
34+
unstableResp3: true
35+
} as const satisfies Command;

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

Lines changed: 100 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -6,39 +6,106 @@ import { parseArgs } from '@redis/client/lib/commands/generic-transformers';
66
import { DEFAULT_DIALECT } from '../dialect/default';
77

88
describe('PROFILE SEARCH', () => {
9-
describe('transformArguments', () => {
10-
it('without options', () => {
11-
assert.deepEqual(
12-
parseArgs(PROFILE_SEARCH, 'index', 'query'),
13-
['FT.PROFILE', 'index', 'SEARCH', 'QUERY', 'query', 'DIALECT', DEFAULT_DIALECT]
14-
);
15-
});
16-
17-
it('with options', () => {
18-
assert.deepEqual(
19-
parseArgs(PROFILE_SEARCH, 'index', 'query', {
20-
LIMITED: true,
21-
VERBATIM: true,
22-
INKEYS: 'key'
23-
}),
24-
['FT.PROFILE', 'index', 'SEARCH', 'LIMITED', 'QUERY', 'query',
25-
'VERBATIM', 'INKEYS', '1', 'key', 'DIALECT', DEFAULT_DIALECT]
26-
);
27-
});
9+
describe('transformArguments', () => {
10+
it('without options', () => {
11+
assert.deepEqual(
12+
parseArgs(PROFILE_SEARCH, 'index', 'query'),
13+
['FT.PROFILE', 'index', 'SEARCH', 'QUERY', 'query', 'DIALECT', DEFAULT_DIALECT]
14+
);
2815
});
2916

30-
testUtils.testWithClient('client.ft.search', async client => {
31-
await Promise.all([
32-
client.ft.create('index', {
33-
field: SCHEMA_FIELD_TYPE.NUMERIC
34-
}),
35-
client.hSet('1', 'field', '1')
36-
]);
37-
38-
const res = await client.ft.profileSearch('index', '*');
39-
assert.strictEqual('None', res.profile.warning);
40-
assert.ok(typeof res.profile.iteratorsProfile.counter === 'number');
41-
assert.ok(typeof res.profile.parsingTime === 'string');
42-
assert.ok(res.results.total == 1);
43-
}, GLOBAL.SERVERS.OPEN);
17+
it('with options', () => {
18+
assert.deepEqual(
19+
parseArgs(PROFILE_SEARCH, 'index', 'query', {
20+
LIMITED: true,
21+
VERBATIM: true,
22+
INKEYS: 'key'
23+
}),
24+
['FT.PROFILE', 'index', 'SEARCH', 'LIMITED', 'QUERY', 'query',
25+
'VERBATIM', 'INKEYS', '1', 'key', 'DIALECT', DEFAULT_DIALECT]
26+
);
27+
});
28+
});
29+
30+
testUtils.testWithClientIfVersionWithinRange([[8], 'LATEST'], 'client.ft.search', async client => {
31+
await Promise.all([
32+
client.ft.create('index', {
33+
field: SCHEMA_FIELD_TYPE.NUMERIC
34+
}),
35+
client.hSet('1', 'field', '1')
36+
]);
37+
38+
const normalizeObject = obj => JSON.parse(JSON.stringify(obj));
39+
40+
const res = await client.ft.profileSearch('index', '*');
41+
42+
const normalizedRes = normalizeObject(res);
43+
assert.equal(normalizedRes.results.total, 1);
44+
45+
assert.ok(normalizedRes.profile[0] === 'Shards');
46+
assert.ok(Array.isArray(normalizedRes.profile[1]));
47+
assert.ok(normalizedRes.profile[2] === 'Coordinator');
48+
assert.ok(Array.isArray(normalizedRes.profile[3]));
49+
50+
const shardProfile = normalizedRes.profile[1][0];
51+
assert.ok(shardProfile.includes('Total profile time'));
52+
assert.ok(shardProfile.includes('Parsing time'));
53+
assert.ok(shardProfile.includes('Pipeline creation time'));
54+
assert.ok(shardProfile.includes('Warning'));
55+
assert.ok(shardProfile.includes('Iterators profile'));
56+
;
57+
58+
}, GLOBAL.SERVERS.OPEN);
59+
60+
61+
testUtils.testWithClientIfVersionWithinRange([[8], 'LATEST'], '[RESP3] client.ft.search', async client => {
62+
await Promise.all([
63+
client.ft.create('index', {
64+
field: SCHEMA_FIELD_TYPE.NUMERIC
65+
}),
66+
client.hSet('1', 'field', '1')
67+
]);
68+
69+
const normalizeObject = obj => JSON.parse(JSON.stringify(obj));
70+
const res = await client.ft.profileSearch('index', '*');
71+
const normalizedRes = normalizeObject(res);
72+
assert.equal(normalizedRes.Results.total_results, 1);
73+
assert.ok(normalizedRes.Profile.Shards);
74+
75+
}, GLOBAL.SERVERS.OPEN_3)
76+
77+
78+
testUtils.testWithClientIfVersionWithinRange([[7, 2, 0], [7, 4, 0]], 'client.ft.search', async client => {
79+
await Promise.all([
80+
client.ft.create('index', {
81+
field: SCHEMA_FIELD_TYPE.NUMERIC
82+
}),
83+
client.hSet('1', 'field', '1')
84+
]);
85+
86+
const normalizeObject = obj => JSON.parse(JSON.stringify(obj));
87+
88+
const res = await client.ft.profileSearch('index', '*');
89+
90+
console.log('output for redis 7', JSON.stringify(res))
91+
92+
const normalizedRes = normalizeObject(res);
93+
assert.equal(normalizedRes.results.total, 1);
94+
95+
assert.ok(Array.isArray(normalizedRes.profile));
96+
assert.equal(normalizedRes.profile[0][0], 'Total profile time');
97+
assert.equal(normalizedRes.profile[1][0], 'Parsing time');
98+
assert.equal(normalizedRes.profile[2][0], 'Pipeline creation time');
99+
assert.equal(normalizedRes.profile[3][0], 'Warning');
100+
assert.equal(normalizedRes.profile[4][0], 'Iterators profile');
101+
assert.equal(normalizedRes.profile[5][0], 'Result processors profile');
102+
103+
const iteratorsProfile = normalizedRes.profile[4][1];
104+
assert.equal(iteratorsProfile[0], 'Type');
105+
assert.equal(iteratorsProfile[1], 'WILDCARD');
106+
assert.equal(iteratorsProfile[2], 'Time');
107+
assert.equal(iteratorsProfile[4], 'Counter');
108+
109+
}, GLOBAL.SERVERS.OPEN);
110+
44111
});

0 commit comments

Comments
 (0)