Skip to content

Commit 5f4331c

Browse files
authored
feat(commands): ZINTER, ZINTERSTORE, ZUNION, ZUNIONSTORE: add a new aggregator: COUNT (#3243)
1 parent f6e57ce commit 5f4331c

9 files changed

Lines changed: 694 additions & 6 deletions

File tree

packages/client/lib/commands/ZINTER.spec.ts

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,15 @@ describe('ZINTER', () => {
5252
['ZINTER', '1', 'key', 'AGGREGATE', 'SUM']
5353
);
5454
});
55+
56+
it('with AGGREGATE COUNT', () => {
57+
assert.deepEqual(
58+
parseArgs(ZINTER, 'key', {
59+
AGGREGATE: 'COUNT'
60+
}),
61+
['ZINTER', '1', 'key', 'AGGREGATE', 'COUNT']
62+
);
63+
});
5564
});
5665

5766
testUtils.testAll('zInter', async client => {
@@ -63,4 +72,78 @@ describe('ZINTER', () => {
6372
client: GLOBAL.SERVERS.OPEN,
6473
cluster: GLOBAL.CLUSTERS.OPEN
6574
});
75+
76+
testUtils.testAll('zInter with AGGREGATE COUNT', async client => {
77+
const keys = [
78+
'{tag}zinter-count-1',
79+
'{tag}zinter-count-2',
80+
'{tag}zinter-count-3'
81+
];
82+
83+
await Promise.all([
84+
client.zAdd(keys[0], [
85+
{ value: 'common1', score: 1 },
86+
{ value: 'common2', score: 2 },
87+
{ value: 'only1', score: 3 }
88+
]),
89+
client.zAdd(keys[1], [
90+
{ value: 'common1', score: 4 },
91+
{ value: 'common2', score: 5 },
92+
{ value: 'only2', score: 6 }
93+
]),
94+
client.zAdd(keys[2], [
95+
{ value: 'common1', score: 7 },
96+
{ value: 'common2', score: 8 },
97+
{ value: 'only3', score: 9 }
98+
])
99+
]);
100+
101+
assert.deepEqual(
102+
await client.zInter(keys, {
103+
AGGREGATE: 'COUNT'
104+
}),
105+
['common1', 'common2']
106+
);
107+
}, {
108+
client: { ...GLOBAL.SERVERS.OPEN, minimumDockerVersion: [8, 8] },
109+
cluster: { ...GLOBAL.CLUSTERS.OPEN, minimumDockerVersion: [8, 8] }
110+
});
111+
112+
testUtils.testAll('zInter with AGGREGATE SUM, MIN, MAX', async client => {
113+
const keys = [
114+
'{tag}zinter-other-1',
115+
'{tag}zinter-other-2',
116+
'{tag}zinter-other-3'
117+
];
118+
119+
await Promise.all([
120+
client.zAdd(keys[0], [
121+
{ value: 'common1', score: 1 },
122+
{ value: 'common2', score: 10 },
123+
{ value: 'only1', score: 3 }
124+
]),
125+
client.zAdd(keys[1], [
126+
{ value: 'common1', score: 5 },
127+
{ value: 'common2', score: 2 },
128+
{ value: 'only2', score: 6 }
129+
]),
130+
client.zAdd(keys[2], [
131+
{ value: 'common1', score: 7 },
132+
{ value: 'common2', score: 4 },
133+
{ value: 'only3', score: 9 }
134+
])
135+
]);
136+
137+
const aggregators = ['SUM', 'MIN', 'MAX'] as const;
138+
for (const aggregate of aggregators) {
139+
const result = await client.zInter(keys, {
140+
AGGREGATE: aggregate
141+
});
142+
143+
assert.deepEqual(result.sort(), ['common1', 'common2']);
144+
}
145+
}, {
146+
client: GLOBAL.SERVERS.OPEN,
147+
cluster: GLOBAL.CLUSTERS.OPEN
148+
});
66149
});

packages/client/lib/commands/ZINTER.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ export type ZInterKeys<T> = T | [T, ...Array<T>];
1212
export type ZInterKeysType = ZInterKeys<RedisArgument> | ZInterKeys<ZInterKeyAndWeight>;
1313

1414
export interface ZInterOptions {
15-
AGGREGATE?: 'SUM' | 'MIN' | 'MAX';
15+
/** `COUNT` added in 8.8 */
16+
AGGREGATE?: 'SUM' | 'MIN' | 'MAX' | 'COUNT';
1617
}
1718

1819
export function parseZInterArguments(

packages/client/lib/commands/ZINTERSTORE.spec.ts

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,15 @@ describe('ZINTERSTORE', () => {
5050
['ZINTERSTORE', 'destination', '1', 'source', 'AGGREGATE', 'SUM']
5151
);
5252
});
53+
54+
it('with AGGREGATE COUNT', () => {
55+
assert.deepEqual(
56+
parseArgs(ZINTERSTORE, 'destination', 'source', {
57+
AGGREGATE: 'COUNT'
58+
}),
59+
['ZINTERSTORE', 'destination', '1', 'source', 'AGGREGATE', 'COUNT']
60+
);
61+
});
5362
});
5463

5564
testUtils.testAll('zInterStore', async client => {
@@ -61,4 +70,112 @@ describe('ZINTERSTORE', () => {
6170
client: GLOBAL.SERVERS.OPEN,
6271
cluster: GLOBAL.CLUSTERS.OPEN
6372
});
73+
74+
testUtils.testAll('zInterStore with AGGREGATE COUNT', async client => {
75+
const keys = [
76+
'{tag}zinterstore-count-1',
77+
'{tag}zinterstore-count-2',
78+
'{tag}zinterstore-count-3'
79+
];
80+
const destination = '{tag}zinterstore-count-destination';
81+
82+
await Promise.all([
83+
client.zAdd(keys[0], [
84+
{ value: 'common1', score: 1 },
85+
{ value: 'common2', score: 2 },
86+
{ value: 'only1', score: 3 }
87+
]),
88+
client.zAdd(keys[1], [
89+
{ value: 'common1', score: 4 },
90+
{ value: 'common2', score: 5 },
91+
{ value: 'only2', score: 6 }
92+
]),
93+
client.zAdd(keys[2], [
94+
{ value: 'common1', score: 7 },
95+
{ value: 'common2', score: 8 },
96+
{ value: 'only3', score: 9 }
97+
])
98+
]);
99+
100+
assert.equal(
101+
await client.zInterStore(destination, keys, {
102+
AGGREGATE: 'COUNT'
103+
}),
104+
2
105+
);
106+
107+
const result = await client.zRangeWithScores(destination, 0, -1);
108+
assert.deepEqual(result, [
109+
{ value: 'common1', score: 3 },
110+
{ value: 'common2', score: 3 }
111+
]);
112+
113+
const scores = result.map(item => item.score);
114+
assert.equal(Math.min(...scores), 3);
115+
assert.equal(Math.max(...scores), 3);
116+
}, {
117+
client: { ...GLOBAL.SERVERS.OPEN, minimumDockerVersion: [8, 8] },
118+
cluster: { ...GLOBAL.CLUSTERS.OPEN, minimumDockerVersion: [8, 8] }
119+
});
120+
121+
testUtils.testAll('zInterStore with AGGREGATE SUM, MIN, MAX', async client => {
122+
const keys = [
123+
'{tag}zinterstore-other-1',
124+
'{tag}zinterstore-other-2',
125+
'{tag}zinterstore-other-3'
126+
];
127+
128+
await Promise.all([
129+
client.zAdd(keys[0], [
130+
{ value: 'common1', score: 1 },
131+
{ value: 'common2', score: 10 },
132+
{ value: 'only1', score: 3 }
133+
]),
134+
client.zAdd(keys[1], [
135+
{ value: 'common1', score: 5 },
136+
{ value: 'common2', score: 2 },
137+
{ value: 'only2', score: 6 }
138+
]),
139+
client.zAdd(keys[2], [
140+
{ value: 'common1', score: 7 },
141+
{ value: 'common2', score: 4 },
142+
{ value: 'only3', score: 9 }
143+
])
144+
]);
145+
146+
const expectedByAggregate = {
147+
SUM: {
148+
common1: 13,
149+
common2: 16
150+
},
151+
MIN: {
152+
common1: 1,
153+
common2: 2
154+
},
155+
MAX: {
156+
common1: 7,
157+
common2: 10
158+
}
159+
};
160+
161+
const aggregators = ['SUM', 'MIN', 'MAX'] as const;
162+
for (const aggregate of aggregators) {
163+
const destination = `{tag}zinterstore-other-destination-${aggregate}`;
164+
assert.equal(
165+
await client.zInterStore(destination, keys, {
166+
AGGREGATE: aggregate
167+
}),
168+
2
169+
);
170+
171+
const result = await client.zRangeWithScores(destination, 0, -1);
172+
assert.deepEqual(
173+
Object.fromEntries(result.map(({ value, score }) => [value.toString(), score])),
174+
expectedByAggregate[aggregate]
175+
);
176+
}
177+
}, {
178+
client: GLOBAL.SERVERS.OPEN,
179+
cluster: GLOBAL.CLUSTERS.OPEN
180+
});
64181
});

packages/client/lib/commands/ZINTER_WITHSCORES.spec.ts

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,15 @@ describe('ZINTER WITHSCORES', () => {
5252
['ZINTER', '1', 'key', 'AGGREGATE', 'SUM', 'WITHSCORES']
5353
);
5454
});
55+
56+
it('with AGGREGATE COUNT', () => {
57+
assert.deepEqual(
58+
parseArgs(ZINTER_WITHSCORES, 'key', {
59+
AGGREGATE: 'COUNT'
60+
}),
61+
['ZINTER', '1', 'key', 'AGGREGATE', 'COUNT', 'WITHSCORES']
62+
);
63+
});
5564
});
5665

5766
testUtils.testAll('zInterWithScores', async client => {
@@ -63,4 +72,102 @@ describe('ZINTER WITHSCORES', () => {
6372
client: GLOBAL.SERVERS.OPEN,
6473
cluster: GLOBAL.CLUSTERS.OPEN
6574
});
75+
76+
testUtils.testAll('zInterWithScores with AGGREGATE COUNT', async client => {
77+
const keys = [
78+
'{tag}zinterws-count-1',
79+
'{tag}zinterws-count-2',
80+
'{tag}zinterws-count-3'
81+
];
82+
83+
await Promise.all([
84+
client.zAdd(keys[0], [
85+
{ value: 'common1', score: 1 },
86+
{ value: 'common2', score: 2 },
87+
{ value: 'only1', score: 3 }
88+
]),
89+
client.zAdd(keys[1], [
90+
{ value: 'common1', score: 4 },
91+
{ value: 'common2', score: 5 },
92+
{ value: 'only2', score: 6 }
93+
]),
94+
client.zAdd(keys[2], [
95+
{ value: 'common1', score: 7 },
96+
{ value: 'common2', score: 8 },
97+
{ value: 'only3', score: 9 }
98+
])
99+
]);
100+
101+
const result = await client.zInterWithScores(keys, {
102+
AGGREGATE: 'COUNT'
103+
});
104+
105+
assert.deepEqual(result, [
106+
{ value: 'common1', score: 3 },
107+
{ value: 'common2', score: 3 }
108+
]);
109+
110+
const scores = result.map(item => item.score);
111+
assert.equal(Math.min(...scores), 3);
112+
assert.equal(Math.max(...scores), 3);
113+
}, {
114+
client: { ...GLOBAL.SERVERS.OPEN, minimumDockerVersion: [8, 8] },
115+
cluster: { ...GLOBAL.CLUSTERS.OPEN, minimumDockerVersion: [8, 8] }
116+
});
117+
118+
testUtils.testAll('zInterWithScores with AGGREGATE SUM, MIN, MAX', async client => {
119+
const keys = [
120+
'{tag}zinterws-other-1',
121+
'{tag}zinterws-other-2',
122+
'{tag}zinterws-other-3'
123+
];
124+
125+
await Promise.all([
126+
client.zAdd(keys[0], [
127+
{ value: 'common1', score: 1 },
128+
{ value: 'common2', score: 10 },
129+
{ value: 'only1', score: 3 }
130+
]),
131+
client.zAdd(keys[1], [
132+
{ value: 'common1', score: 5 },
133+
{ value: 'common2', score: 2 },
134+
{ value: 'only2', score: 6 }
135+
]),
136+
client.zAdd(keys[2], [
137+
{ value: 'common1', score: 7 },
138+
{ value: 'common2', score: 4 },
139+
{ value: 'only3', score: 9 }
140+
])
141+
]);
142+
143+
const expectedByAggregate = {
144+
SUM: {
145+
common1: 13,
146+
common2: 16
147+
},
148+
MIN: {
149+
common1: 1,
150+
common2: 2
151+
},
152+
MAX: {
153+
common1: 7,
154+
common2: 10
155+
}
156+
};
157+
158+
const aggregators = ['SUM', 'MIN', 'MAX'] as const;
159+
for (const aggregate of aggregators) {
160+
const result = await client.zInterWithScores(keys, {
161+
AGGREGATE: aggregate
162+
});
163+
164+
assert.deepEqual(
165+
Object.fromEntries(result.map(({ value, score }) => [value.toString(), score])),
166+
expectedByAggregate[aggregate]
167+
);
168+
}
169+
}, {
170+
client: GLOBAL.SERVERS.OPEN,
171+
cluster: GLOBAL.CLUSTERS.OPEN
172+
});
66173
});

0 commit comments

Comments
 (0)