From 0e7c95edc16e1bc53f3e267badc87a881b1c790e Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Sun, 2 Jun 2024 08:04:14 +0000 Subject: [PATCH 1/7] add zinterstore command node --- node/src/Commands.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/node/src/Commands.ts b/node/src/Commands.ts index 0853bafb5a..babdf2810c 100644 --- a/node/src/Commands.ts +++ b/node/src/Commands.ts @@ -827,6 +827,16 @@ export function createZAdd( return createCommand(RequestType.ZAdd, args); } +export function createZInterstore( + destination: string, + key: string, +): redis_request.Command { + const args = createZInterstoreArgs(); + return createCommand(RequestType.ZInterStore, args); +} + +function createZInterstoreArgs(); + /** * @internal */ From af61c595a0fc48092ea5bc24228146a33eba9b11 Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Mon, 3 Jun 2024 10:36:25 +0000 Subject: [PATCH 2/7] Node: added zinterstore command --- node/src/BaseClient.ts | 25 ++++++++ node/src/Commands.ts | 37 +++++++++++- node/src/Transaction.ts | 25 ++++++++ node/tests/SharedTests.ts | 116 ++++++++++++++++++++++++++++++++++++ node/tests/TestUtilities.ts | 8 +++ 5 files changed, 208 insertions(+), 3 deletions(-) diff --git a/node/src/BaseClient.ts b/node/src/BaseClient.ts index 4f0b0aee47..51cbaef8d8 100644 --- a/node/src/BaseClient.ts +++ b/node/src/BaseClient.ts @@ -12,6 +12,7 @@ import * as net from "net"; import { Buffer, BufferWriter, Reader, Writer } from "protobufjs"; import { ExpireOptions, + KeyWeight, RangeByIndex, RangeByLex, RangeByScore, @@ -81,6 +82,7 @@ import { createZAdd, createZCard, createZCount, + createZInterstore, createZPopMax, createZPopMin, createZRange, @@ -1881,6 +1883,29 @@ export class BaseClient { ); } + /** + * Computes the intersection of sorted sets given by the specified `keys` and stores the result in `destination`. + * If `destination` already exists, it is overwritten. Otherwise, a new sorted set will be created. + * When in cluster mode, `destination` and all keys in `keys` must map to the same hash slot. + * See https://redis.io/commands/zinterstore for more details. + * + * @param destination - The key of the destination sorted set. + * @param keys - The keys of the sorted sets with possible formats: + * string[] - for keys only. + * KeyWeight[] - for weighted keys with score multipliers. + * @param aggregationType - Specifies the aggregation strategy to apply when combining the scores of elements. + * @returns The number of elements in the resulting sorted set stored at `destination`. + */ + public zinterstore( + destination: string, + keys: (string | KeyWeight)[], + aggregationType?: "SUM" | "MIN" | "MAX", + ): Promise { + return this.createWritePromise( + createZInterstore(destination, keys, aggregationType), + ); + } + /** Returns the length of the string value stored at `key`. * See https://redis.io/commands/strlen/ for more details. * diff --git a/node/src/Commands.ts b/node/src/Commands.ts index babdf2810c..b1a3808f0f 100644 --- a/node/src/Commands.ts +++ b/node/src/Commands.ts @@ -827,16 +827,47 @@ export function createZAdd( return createCommand(RequestType.ZAdd, args); } +export type KeyWeight = [string, number]; + +/** + * @internal + */ export function createZInterstore( destination: string, - key: string, + keys: (string | KeyWeight)[], + aggregationType?: "SUM" | "MIN" | "MAX", ): redis_request.Command { - const args = createZInterstoreArgs(); + const args = createZInterstoreArgs(destination, keys, aggregationType); return createCommand(RequestType.ZInterStore, args); } -function createZInterstoreArgs(); +function createZInterstoreArgs( + destination: string, + keys: (string | KeyWeight)[], + aggregationType?: "SUM" | "MIN" | "MAX", +): string[] { + const args: string[] = [destination, `${keys.length}`]; + const keyWeightPairs = keys.map((key) => + typeof key === "string" ? [key, 1] : key, + ); + for (const [key, weight] of keyWeightPairs) { + args.push(`${key}`); + } + // keyWeightPairs.forEach((key, weight) => { + // args.push(`${key}`); + // }); + const weights = keyWeightPairs.map(([_, weight]) => weight); + if (weights.some((weight) => weight !== 1)) { + args.push("WEIGHTS", ...(weights as unknown as string)); + } + + if (aggregationType) { + args.push("AGGREGATE", aggregationType); + } + + return args; +} /** * @internal */ diff --git a/node/src/Transaction.ts b/node/src/Transaction.ts index 8b46534aac..a844b311f5 100644 --- a/node/src/Transaction.ts +++ b/node/src/Transaction.ts @@ -5,6 +5,7 @@ import { ExpireOptions, InfoOptions, + KeyWeight, RangeByIndex, RangeByLex, RangeByScore, @@ -86,6 +87,7 @@ import { createZAdd, createZCard, createZCount, + createZInterstore, createZPopMax, createZPopMin, createZRange, @@ -1035,6 +1037,29 @@ export class BaseTransaction> { ); } + /** + * Computes the intersection of sorted sets given by the specified `keys` and stores the result in `destination`. + * If `destination` already exists, it is overwritten. Otherwise, a new sorted set will be created. + * When in cluster mode, `destination` and all keys in `keys` must map to the same hash slot. + * See https://redis.io/commands/zinterstore for more details. + * + * @param destination - The key of the destination sorted set. + * @param keys - The keys of the sorted sets with possible formats: + * string[] - for keys only. + * KeyWeight[] - for weighted keys with score multipliers. + * @param aggregationType - Specifies the aggregation strategy to apply when combining the scores of elements. + * Command Response - The number of elements in the resulting sorted set stored at `destination`. + */ + public zinterstore( + destination: string, + keys: (string | KeyWeight)[], + aggregationType?: "SUM" | "MIN" | "MAX", + ): T { + return this.addAndReturn( + createZInterstore(destination, keys, aggregationType), + ); + } + /** Returns the string representation of the type of the value stored at `key`. * See https://redis.io/commands/type/ for more details. * diff --git a/node/tests/SharedTests.ts b/node/tests/SharedTests.ts index efd7059a67..1ed90ae48f 100644 --- a/node/tests/SharedTests.ts +++ b/node/tests/SharedTests.ts @@ -1878,6 +1878,122 @@ export function runBaseTests(config: { config.timeout, ); + it.each([ProtocolVersion.RESP2, ProtocolVersion.RESP3])( + `zinterstore test_%p`, + async (protocol) => { + await runTest(async (client: BaseClient) => { + const key1 = "{testKey}:1-" + uuidv4(); + const key2 = "{testKey}:2-" + uuidv4(); + const key3 = "{testKey}:3-" + uuidv4(); + const range = { + start: 0, + stop: -1, + }; + const membersScores1 = { one: 1.0, two: 2.0 }; + const membersScores2 = { one: 1.5, two: 2.5, three: 3.5 }; + + expect(await client.zadd(key1, membersScores1)).toEqual(2); + expect(await client.zadd(key2, membersScores2)).toEqual(3); + + expect(await client.zinterstore(key3, [key1, key2])).toEqual(2); + const zinterstoreMap = await client.zrangeWithScores( + key3, + range, + ); + const expectedMap = { + one: 2.5, + two: 4.5, + }; + expect(compareMaps(zinterstoreMap, expectedMap)).toBe(true); + + // Intersection results are aggregated by the MAX score of elements + expect( + await client.zinterstore(key3, [key1, key2], "MAX"), + ).toEqual(2); + const zinterstoreMapMax = await client.zrangeWithScores( + key3, + range, + ); + const expectedMapMax = { + one: 1.5, + two: 2.5, + }; + expect(compareMaps(zinterstoreMapMax, expectedMapMax)).toBe( + true, + ); + + // Intersection results are aggregated by the MIN score of elements + expect( + await client.zinterstore(key3, [key1, key2], "MIN"), + ).toEqual(2); + const zinterstoreMapMin = await client.zrangeWithScores( + key3, + range, + ); + const expectedMapMin = { + one: 1.0, + two: 2.0, + }; + expect(compareMaps(zinterstoreMapMin, expectedMapMin)).toBe( + true, + ); + + // Intersection results are aggregated by the SUM score of elements + expect( + await client.zinterstore(key3, [key1, key2], "SUM"), + ).toEqual(2); + const zinterstoreMapSum = await client.zrangeWithScores( + key3, + range, + ); + const expectedMapSum = { + one: 2.5, + two: 4.5, + }; + expect(compareMaps(zinterstoreMapSum, expectedMapSum)).toBe( + true, + ); + + // Scores are multiplied by 2.0 for key1 and key2 during aggregation. + expect( + await client.zinterstore( + key3, + [ + [key1, 2.0], + [key2, 2.0], + ], + "SUM", + ), + ).toEqual(2); + const zinterstoreMapMultiplied = await client.zrangeWithScores( + key3, + range, + ); + const expectedMapMultiplied = { + one: 5.0, + two: 9.0, + }; + expect( + compareMaps( + zinterstoreMapMultiplied, + expectedMapMultiplied, + ), + ).toBe(true); + + expect( + await client.zinterstore(key3, [ + key1, + "{testKey}-non_existing_key", + ]), + ).toEqual(0); + + // Empty list check + await expect(client.zinterstore("{xyz}", [])).rejects.toThrow(); + }, protocol); + }, + config.timeout, + ); + it.each([ProtocolVersion.RESP2, ProtocolVersion.RESP3])( `type test_%p`, async (protocol) => { diff --git a/node/tests/TestUtilities.ts b/node/tests/TestUtilities.ts index b6fdcd9890..14a23a6bc2 100644 --- a/node/tests/TestUtilities.ts +++ b/node/tests/TestUtilities.ts @@ -229,6 +229,8 @@ export async function transactionTest( const key9 = "{key}" + uuidv4(); const key10 = "{key}" + uuidv4(); const key11 = "{key}" + uuidv4(); // hyper log log + const key12 = "{key}" + uuidv4(); + const key13 = "{key}" + uuidv4(); const field = uuidv4(); const value = uuidv4(); const args: ReturnType[] = []; @@ -347,6 +349,12 @@ export async function transactionTest( args.push(["member2", "member3", "member4", "member5"]); baseTransaction.zrangeWithScores(key8, { start: 0, stop: -1 }); args.push({ member2: 3, member3: 3.5, member4: 4, member5: 5 }); + baseTransaction.zadd(key12, { one: 1, two: 2 }); + args.push(2); + baseTransaction.zadd(key13, { one: 1, two: 2, tree: 3.5 }); + args.push(3); + baseTransaction.zinterstore(key12, [key12, key13]); + args.push(2); baseTransaction.zcount(key8, { value: 2 }, "positiveInfinity"); args.push(4); baseTransaction.zpopmin(key8); From ff292075656eb4ab0b91602077aadcf7ce3f1ac5 Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Mon, 3 Jun 2024 10:36:25 +0000 Subject: [PATCH 3/7] Node: added zinterstore command --- CHANGELOG.md | 1 + node/src/BaseClient.ts | 3 ++- node/src/Commands.ts | 20 ++++++++++---------- node/src/Transaction.ts | 3 ++- node/tests/SharedTests.ts | 22 +++++++++++----------- 5 files changed, 26 insertions(+), 23 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c4d6749293..3e6c4ab7ea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,5 @@ #### Changes +* Node: Added ZINTERSTORE command ([#1513](https://github.com/aws/glide-for-redis/pull/1513)) * Python: Added OBJECT ENCODING command ([#1471](https://github.com/aws/glide-for-redis/pull/1471)) * Python: Added OBJECT FREQ command ([#1472](https://github.com/aws/glide-for-redis/pull/1472)) * Python: Added OBJECT IDLETIME command ([#1474](https://github.com/aws/glide-for-redis/pull/1474)) diff --git a/node/src/BaseClient.ts b/node/src/BaseClient.ts index 51cbaef8d8..751b7e900d 100644 --- a/node/src/BaseClient.ts +++ b/node/src/BaseClient.ts @@ -11,6 +11,7 @@ import { import * as net from "net"; import { Buffer, BufferWriter, Reader, Writer } from "protobufjs"; import { + AggregationType, ExpireOptions, KeyWeight, RangeByIndex, @@ -1899,7 +1900,7 @@ export class BaseClient { public zinterstore( destination: string, keys: (string | KeyWeight)[], - aggregationType?: "SUM" | "MIN" | "MAX", + aggregationType?: AggregationType, ): Promise { return this.createWritePromise( createZInterstore(destination, keys, aggregationType), diff --git a/node/src/Commands.ts b/node/src/Commands.ts index b1a3808f0f..76f2b84a53 100644 --- a/node/src/Commands.ts +++ b/node/src/Commands.ts @@ -828,6 +828,7 @@ export function createZAdd( } export type KeyWeight = [string, number]; +export type AggregationType = "SUM" | "MIN" | "MAX"; /** * @internal @@ -835,7 +836,7 @@ export type KeyWeight = [string, number]; export function createZInterstore( destination: string, keys: (string | KeyWeight)[], - aggregationType?: "SUM" | "MIN" | "MAX", + aggregationType?: AggregationType, ): redis_request.Command { const args = createZInterstoreArgs(destination, keys, aggregationType); return createCommand(RequestType.ZInterStore, args); @@ -844,22 +845,21 @@ export function createZInterstore( function createZInterstoreArgs( destination: string, keys: (string | KeyWeight)[], - aggregationType?: "SUM" | "MIN" | "MAX", + aggregationType?: AggregationType, ): string[] { - const args: string[] = [destination, `${keys.length}`]; + const args: string[] = [destination, keys.length.toString()]; const keyWeightPairs = keys.map((key) => typeof key === "string" ? [key, 1] : key, ); + for (const [key, weight] of keyWeightPairs) { - args.push(`${key}`); + args.push(key.toString()); } - // keyWeightPairs.forEach((key, weight) => { - // args.push(`${key}`); - // }); - const weights = keyWeightPairs.map(([_, weight]) => weight); - if (weights.some((weight) => weight !== 1)) { - args.push("WEIGHTS", ...(weights as unknown as string)); + const weights = keyWeightPairs.map(([, weight]) => weight.toString()); + + if (weights.some((weight) => weight !== "1")) { + args.push("WEIGHTS", ...weights); } if (aggregationType) { diff --git a/node/src/Transaction.ts b/node/src/Transaction.ts index a844b311f5..ece2890e7d 100644 --- a/node/src/Transaction.ts +++ b/node/src/Transaction.ts @@ -3,6 +3,7 @@ */ import { + AggregationType, ExpireOptions, InfoOptions, KeyWeight, @@ -1053,7 +1054,7 @@ export class BaseTransaction> { public zinterstore( destination: string, keys: (string | KeyWeight)[], - aggregationType?: "SUM" | "MIN" | "MAX", + aggregationType?: AggregationType, ): T { return this.addAndReturn( createZInterstore(destination, keys, aggregationType), diff --git a/node/tests/SharedTests.ts b/node/tests/SharedTests.ts index 1ed90ae48f..361706c3be 100644 --- a/node/tests/SharedTests.ts +++ b/node/tests/SharedTests.ts @@ -1890,7 +1890,7 @@ export function runBaseTests(config: { stop: -1, }; const membersScores1 = { one: 1.0, two: 2.0 }; - const membersScores2 = { one: 1.5, two: 2.5, three: 3.5 }; + const membersScores2 = { one: 2.0, two: 3.0, three: 4.0 }; expect(await client.zadd(key1, membersScores1)).toEqual(2); expect(await client.zadd(key2, membersScores2)).toEqual(3); @@ -1901,8 +1901,8 @@ export function runBaseTests(config: { range, ); const expectedMap = { - one: 2.5, - two: 4.5, + one: 3, + two: 5, }; expect(compareMaps(zinterstoreMap, expectedMap)).toBe(true); @@ -1915,8 +1915,8 @@ export function runBaseTests(config: { range, ); const expectedMapMax = { - one: 1.5, - two: 2.5, + one: 2, + two: 3, }; expect(compareMaps(zinterstoreMapMax, expectedMapMax)).toBe( true, @@ -1931,8 +1931,8 @@ export function runBaseTests(config: { range, ); const expectedMapMin = { - one: 1.0, - two: 2.0, + one: 1, + two: 2, }; expect(compareMaps(zinterstoreMapMin, expectedMapMin)).toBe( true, @@ -1947,8 +1947,8 @@ export function runBaseTests(config: { range, ); const expectedMapSum = { - one: 2.5, - two: 4.5, + one: 3, + two: 5, }; expect(compareMaps(zinterstoreMapSum, expectedMapSum)).toBe( true, @@ -1970,8 +1970,8 @@ export function runBaseTests(config: { range, ); const expectedMapMultiplied = { - one: 5.0, - two: 9.0, + one: 6, + two: 10, }; expect( compareMaps( From 0e963c903dd32fdd6647b118e645e979d4a95980 Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Wed, 5 Jun 2024 14:19:49 +0000 Subject: [PATCH 4/7] Node: add ZINTERSTORE command --- node/src/BaseClient.ts | 13 ++++++++++++- node/src/Commands.ts | 18 +++++++++++++----- node/src/Transaction.ts | 2 +- node/tests/RedisClusterClient.test.ts | 1 + 4 files changed, 27 insertions(+), 7 deletions(-) diff --git a/node/src/BaseClient.ts b/node/src/BaseClient.ts index 751b7e900d..1b07bdd591 100644 --- a/node/src/BaseClient.ts +++ b/node/src/BaseClient.ts @@ -1896,10 +1896,21 @@ export class BaseClient { * KeyWeight[] - for weighted keys with score multipliers. * @param aggregationType - Specifies the aggregation strategy to apply when combining the scores of elements. * @returns The number of elements in the resulting sorted set stored at `destination`. + * + * @example + * ```typescript + * // Example usage of zinterstore command with an existing key + * await client.zadd("key1", {"member1": 10.5, "member2": 8.2}) + * await client.zadd("key2", {"member1": 9.5}) + * await client.zinterstore("my_sorted_set", ["key1", "key2"]) // Output: 1 - Indicates that the sorted set "my_sorted_set" contains one element. + * await client.zrange_withscores("my_sorted_set", RangeByIndex(0, -1)) // Output: {'member1': 20} - "member1" is now stored in "my_sorted_set" with score of 20. + * await client.zinterstore("my_sorted_set", ["key1", "key2"] , AggregationType.MAX ) // Output: 1 - Indicates that the sorted set "my_sorted_set" contains one element, and it's score is the maximum score between the sets. + * await client.zrange_withscores("my_sorted_set", RangeByIndex(0, -1)) // Output: {'member1': 10.5} - "member1" is now stored in "my_sorted_set" with score of 10.5. + * ``` */ public zinterstore( destination: string, - keys: (string | KeyWeight)[], + keys: string[] | KeyWeight[], aggregationType?: AggregationType, ): Promise { return this.createWritePromise( diff --git a/node/src/Commands.ts b/node/src/Commands.ts index 76f2b84a53..04ebd52af6 100644 --- a/node/src/Commands.ts +++ b/node/src/Commands.ts @@ -827,7 +827,13 @@ export function createZAdd( return createCommand(RequestType.ZAdd, args); } +/** + * `KeyWeight` - pair of variables represents a weighted key for the `ZINTERSTORE` and `ZUNIONSTORE` sorted sets commands. + */ export type KeyWeight = [string, number]; +/** + * `AggregationType` - representing aggregation types for `ZINTERSTORE` and `ZUNIONSTORE` sorted set commands. + */ export type AggregationType = "SUM" | "MIN" | "MAX"; /** @@ -835,16 +841,16 @@ export type AggregationType = "SUM" | "MIN" | "MAX"; */ export function createZInterstore( destination: string, - keys: (string | KeyWeight)[], + keys: string[] | KeyWeight[], aggregationType?: AggregationType, ): redis_request.Command { - const args = createZInterstoreArgs(destination, keys, aggregationType); + const args = createZCmdStoreArgs(destination, keys, aggregationType); return createCommand(RequestType.ZInterStore, args); } -function createZInterstoreArgs( +function createZCmdStoreArgs( destination: string, - keys: (string | KeyWeight)[], + keys: string[] | KeyWeight[], aggregationType?: AggregationType, ): string[] { const args: string[] = [destination, keys.length.toString()]; @@ -853,9 +859,10 @@ function createZInterstoreArgs( typeof key === "string" ? [key, 1] : key, ); - for (const [key, weight] of keyWeightPairs) { + for (const [key] of keyWeightPairs) { args.push(key.toString()); } + const weights = keyWeightPairs.map(([, weight]) => weight.toString()); if (weights.some((weight) => weight !== "1")) { @@ -868,6 +875,7 @@ function createZInterstoreArgs( return args; } + /** * @internal */ diff --git a/node/src/Transaction.ts b/node/src/Transaction.ts index ece2890e7d..73f643738b 100644 --- a/node/src/Transaction.ts +++ b/node/src/Transaction.ts @@ -1053,7 +1053,7 @@ export class BaseTransaction> { */ public zinterstore( destination: string, - keys: (string | KeyWeight)[], + keys: string[] | KeyWeight[], aggregationType?: AggregationType, ): T { return this.addAndReturn( diff --git a/node/tests/RedisClusterClient.test.ts b/node/tests/RedisClusterClient.test.ts index 4ab7c49e1c..cbe5254b44 100644 --- a/node/tests/RedisClusterClient.test.ts +++ b/node/tests/RedisClusterClient.test.ts @@ -288,6 +288,7 @@ describe("RedisClusterClient", () => { client.smove("abc", "zxy", "value"), client.renamenx("abc", "zxy"), client.sinter(["abc", "zxy", "lkn"]), + client.zinterstore("abc", ["zxy", "lkn"]), // TODO all rest multi-key commands except ones tested below ]; From 8ace09ba8522007f8e20755729e183708d84326e Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Thu, 6 Jun 2024 18:07:04 +0000 Subject: [PATCH 5/7] split test to functions and fix doc --- node/src/BaseClient.ts | 5 +- node/src/Commands.ts | 18 +-- node/src/Transaction.ts | 4 +- node/tests/SharedTests.ts | 229 +++++++++++++++++++++----------------- 4 files changed, 137 insertions(+), 119 deletions(-) diff --git a/node/src/BaseClient.ts b/node/src/BaseClient.ts index 1b07bdd591..8d3d6dd2ed 100644 --- a/node/src/BaseClient.ts +++ b/node/src/BaseClient.ts @@ -1887,14 +1887,17 @@ export class BaseClient { /** * Computes the intersection of sorted sets given by the specified `keys` and stores the result in `destination`. * If `destination` already exists, it is overwritten. Otherwise, a new sorted set will be created. + * To get the result directly, see `zinter_withscores`. + * * When in cluster mode, `destination` and all keys in `keys` must map to the same hash slot. + * * See https://redis.io/commands/zinterstore for more details. * * @param destination - The key of the destination sorted set. * @param keys - The keys of the sorted sets with possible formats: * string[] - for keys only. * KeyWeight[] - for weighted keys with score multipliers. - * @param aggregationType - Specifies the aggregation strategy to apply when combining the scores of elements. + * @param aggregationType - Specifies the aggregation strategy to apply when combining the scores of elements. See `AggregationType`. * @returns The number of elements in the resulting sorted set stored at `destination`. * * @example diff --git a/node/src/Commands.ts b/node/src/Commands.ts index 04ebd52af6..1fe7c40921 100644 --- a/node/src/Commands.ts +++ b/node/src/Commands.ts @@ -855,24 +855,18 @@ function createZCmdStoreArgs( ): string[] { const args: string[] = [destination, keys.length.toString()]; - const keyWeightPairs = keys.map((key) => - typeof key === "string" ? [key, 1] : key, - ); - - for (const [key] of keyWeightPairs) { - args.push(key.toString()); - } - - const weights = keyWeightPairs.map(([, weight]) => weight.toString()); - - if (weights.some((weight) => weight !== "1")) { + if (typeof keys[0] === "string") { + args.push(...(keys as string[])); + } else { + const weightsKeys = keys.map(([key, _]) => key); + args.push(...(weightsKeys as string[])); + const weights = keys.map(([, weight]) => weight.toString()); args.push("WEIGHTS", ...weights); } if (aggregationType) { args.push("AGGREGATE", aggregationType); } - return args; } diff --git a/node/src/Transaction.ts b/node/src/Transaction.ts index 73f643738b..83d64b9fc8 100644 --- a/node/src/Transaction.ts +++ b/node/src/Transaction.ts @@ -1041,14 +1041,16 @@ export class BaseTransaction> { /** * Computes the intersection of sorted sets given by the specified `keys` and stores the result in `destination`. * If `destination` already exists, it is overwritten. Otherwise, a new sorted set will be created. + * * When in cluster mode, `destination` and all keys in `keys` must map to the same hash slot. + * * See https://redis.io/commands/zinterstore for more details. * * @param destination - The key of the destination sorted set. * @param keys - The keys of the sorted sets with possible formats: * string[] - for keys only. * KeyWeight[] - for weighted keys with score multipliers. - * @param aggregationType - Specifies the aggregation strategy to apply when combining the scores of elements. + * @param aggregationType - Specifies the aggregation strategy to apply when combining the scores of elements. See `AggregationType`. * Command Response - The number of elements in the resulting sorted set stored at `destination`. */ public zinterstore( diff --git a/node/tests/SharedTests.ts b/node/tests/SharedTests.ts index 361706c3be..b9c7fcedd1 100644 --- a/node/tests/SharedTests.ts +++ b/node/tests/SharedTests.ts @@ -1878,117 +1878,136 @@ export function runBaseTests(config: { config.timeout, ); - it.each([ProtocolVersion.RESP2, ProtocolVersion.RESP3])( - `zinterstore test_%p`, - async (protocol) => { - await runTest(async (client: BaseClient) => { - const key1 = "{testKey}:1-" + uuidv4(); - const key2 = "{testKey}:2-" + uuidv4(); - const key3 = "{testKey}:3-" + uuidv4(); - const range = { - start: 0, - stop: -1, - }; - const membersScores1 = { one: 1.0, two: 2.0 }; - const membersScores2 = { one: 2.0, two: 3.0, three: 4.0 }; - - expect(await client.zadd(key1, membersScores1)).toEqual(2); - expect(await client.zadd(key2, membersScores2)).toEqual(3); - - expect(await client.zinterstore(key3, [key1, key2])).toEqual(2); - const zinterstoreMap = await client.zrangeWithScores( - key3, - range, - ); - const expectedMap = { - one: 3, - two: 5, - }; - expect(compareMaps(zinterstoreMap, expectedMap)).toBe(true); + // Zinterstore command tests + async function zinterstoreWithAggregation(client: BaseClient) { + const key1 = "{testKey}:1-" + uuidv4(); + const key2 = "{testKey}:2-" + uuidv4(); + const key3 = "{testKey}:3-" + uuidv4(); + const range = { + start: 0, + stop: -1, + }; + + const membersScores1 = { one: 1.0, two: 2.0 }; + const membersScores2 = { one: 2.0, two: 3.0, three: 4.0 }; + + expect(await client.zadd(key1, membersScores1)).toEqual(2); + expect(await client.zadd(key2, membersScores2)).toEqual(3); + + // Intersection results are aggregated by the MAX score of elements + expect(await client.zinterstore(key3, [key1, key2], "MAX")).toEqual(2); + const zinterstoreMapMax = await client.zrangeWithScores(key3, range); + const expectedMapMax = { + one: 2, + two: 3, + }; + expect(compareMaps(zinterstoreMapMax, expectedMapMax)).toBe(true); + + // Intersection results are aggregated by the MIN score of elements + expect(await client.zinterstore(key3, [key1, key2], "MIN")).toEqual(2); + const zinterstoreMapMin = await client.zrangeWithScores(key3, range); + const expectedMapMin = { + one: 1, + two: 2, + }; + expect(compareMaps(zinterstoreMapMin, expectedMapMin)).toBe(true); + + // Intersection results are aggregated by the SUM score of elements + expect(await client.zinterstore(key3, [key1, key2], "SUM")).toEqual(2); + const zinterstoreMapSum = await client.zrangeWithScores(key3, range); + const expectedMapSum = { + one: 3, + two: 5, + }; + expect(compareMaps(zinterstoreMapSum, expectedMapSum)).toBe(true); + } - // Intersection results are aggregated by the MAX score of elements - expect( - await client.zinterstore(key3, [key1, key2], "MAX"), - ).toEqual(2); - const zinterstoreMapMax = await client.zrangeWithScores( - key3, - range, - ); - const expectedMapMax = { - one: 2, - two: 3, - }; - expect(compareMaps(zinterstoreMapMax, expectedMapMax)).toBe( - true, - ); + async function zinterstoreBasicTest(client: BaseClient) { + const key1 = "{testKey}:1-" + uuidv4(); + const key2 = "{testKey}:2-" + uuidv4(); + const key3 = "{testKey}:3-" + uuidv4(); + const range = { + start: 0, + stop: -1, + }; + + const membersScores1 = { one: 1.0, two: 2.0 }; + const membersScores2 = { one: 2.0, two: 3.0, three: 4.0 }; + + expect(await client.zadd(key1, membersScores1)).toEqual(2); + expect(await client.zadd(key2, membersScores2)).toEqual(3); + + expect(await client.zinterstore(key3, [key1, key2])).toEqual(2); + const zinterstoreMap = await client.zrangeWithScores(key3, range); + const expectedMap = { + one: 3, + two: 5, + }; + expect(compareMaps(zinterstoreMap, expectedMap)).toBe(true); + } - // Intersection results are aggregated by the MIN score of elements - expect( - await client.zinterstore(key3, [key1, key2], "MIN"), - ).toEqual(2); - const zinterstoreMapMin = await client.zrangeWithScores( - key3, - range, - ); - const expectedMapMin = { - one: 1, - two: 2, - }; - expect(compareMaps(zinterstoreMapMin, expectedMapMin)).toBe( - true, - ); + async function zinterstoreWithWeightsAndAggregation(client: BaseClient) { + const key1 = "{testKey}:1-" + uuidv4(); + const key2 = "{testKey}:2-" + uuidv4(); + const key3 = "{testKey}:3-" + uuidv4(); + const range = { + start: 0, + stop: -1, + }; + const membersScores1 = { one: 1.0, two: 2.0 }; + const membersScores2 = { one: 2.0, two: 3.0, three: 4.0 }; + + expect(await client.zadd(key1, membersScores1)).toEqual(2); + expect(await client.zadd(key2, membersScores2)).toEqual(3); + + // Scores are multiplied by 2.0 for key1 and key2 during aggregation. + expect( + await client.zinterstore( + key3, + [ + [key1, 2.0], + [key2, 2.0], + ], + "SUM", + ), + ).toEqual(2); + const zinterstoreMapMultiplied = await client.zrangeWithScores( + key3, + range, + ); + const expectedMapMultiplied = { + one: 6, + two: 10, + }; + expect( + compareMaps(zinterstoreMapMultiplied, expectedMapMultiplied), + ).toBe(true); + } - // Intersection results are aggregated by the SUM score of elements - expect( - await client.zinterstore(key3, [key1, key2], "SUM"), - ).toEqual(2); - const zinterstoreMapSum = await client.zrangeWithScores( - key3, - range, - ); - const expectedMapSum = { - one: 3, - two: 5, - }; - expect(compareMaps(zinterstoreMapSum, expectedMapSum)).toBe( - true, - ); + async function zinterstoreEmptyCases(client: BaseClient) { + const key1 = "{testKey}:1-" + uuidv4(); + const key2 = "{testKey}:2-" + uuidv4(); - // Scores are multiplied by 2.0 for key1 and key2 during aggregation. - expect( - await client.zinterstore( - key3, - [ - [key1, 2.0], - [key2, 2.0], - ], - "SUM", - ), - ).toEqual(2); - const zinterstoreMapMultiplied = await client.zrangeWithScores( - key3, - range, - ); - const expectedMapMultiplied = { - one: 6, - two: 10, - }; - expect( - compareMaps( - zinterstoreMapMultiplied, - expectedMapMultiplied, - ), - ).toBe(true); + // Non existing key + expect( + await client.zinterstore(key2, [ + key1, + "{testKey}-non_existing_key", + ]), + ).toEqual(0); - expect( - await client.zinterstore(key3, [ - key1, - "{testKey}-non_existing_key", - ]), - ).toEqual(0); + // Empty list check + await expect(client.zinterstore("{xyz}", [])).rejects.toThrow(); + } - // Empty list check - await expect(client.zinterstore("{xyz}", [])).rejects.toThrow(); + it.each([ProtocolVersion.RESP2, ProtocolVersion.RESP3])( + `zinterstore test_%p`, + async (protocol) => { + await runTest(async (client: BaseClient) => { + await zinterstoreBasicTest; + await zinterstoreWithAggregation; + await zinterstoreWithWeightsAndAggregation; + await zinterstoreEmptyCases; }, protocol); }, config.timeout, From ca70834724af8ad141e92fe34bec89c8d475ae6b Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Thu, 6 Jun 2024 18:09:25 +0000 Subject: [PATCH 6/7] change links to valkey --- node/src/BaseClient.ts | 2 +- node/src/Transaction.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/node/src/BaseClient.ts b/node/src/BaseClient.ts index 8d3d6dd2ed..91692a7ea6 100644 --- a/node/src/BaseClient.ts +++ b/node/src/BaseClient.ts @@ -1891,7 +1891,7 @@ export class BaseClient { * * When in cluster mode, `destination` and all keys in `keys` must map to the same hash slot. * - * See https://redis.io/commands/zinterstore for more details. + * See https://valkey.io/commands/zinterstore/ for more details. * * @param destination - The key of the destination sorted set. * @param keys - The keys of the sorted sets with possible formats: diff --git a/node/src/Transaction.ts b/node/src/Transaction.ts index 83d64b9fc8..747ce65c22 100644 --- a/node/src/Transaction.ts +++ b/node/src/Transaction.ts @@ -1044,7 +1044,7 @@ export class BaseTransaction> { * * When in cluster mode, `destination` and all keys in `keys` must map to the same hash slot. * - * See https://redis.io/commands/zinterstore for more details. + * See https://valkey.io/commands/zinterstore/ for more details. * * @param destination - The key of the destination sorted set. * @param keys - The keys of the sorted sets with possible formats: From d4cef6d5c8ab6030660c4f6aceded0f8a8ef9b68 Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Sat, 8 Jun 2024 14:36:15 +0000 Subject: [PATCH 7/7] fix lint errors --- node/src/Commands.ts | 3 ++- node/tests/SharedTests.ts | 8 ++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/node/src/Commands.ts b/node/src/Commands.ts index 1fe7c40921..df5f3304cc 100644 --- a/node/src/Commands.ts +++ b/node/src/Commands.ts @@ -858,7 +858,7 @@ function createZCmdStoreArgs( if (typeof keys[0] === "string") { args.push(...(keys as string[])); } else { - const weightsKeys = keys.map(([key, _]) => key); + const weightsKeys = keys.map(([key]) => key); args.push(...(weightsKeys as string[])); const weights = keys.map(([, weight]) => weight.toString()); args.push("WEIGHTS", ...weights); @@ -867,6 +867,7 @@ function createZCmdStoreArgs( if (aggregationType) { args.push("AGGREGATE", aggregationType); } + return args; } diff --git a/node/tests/SharedTests.ts b/node/tests/SharedTests.ts index b9c7fcedd1..190fe033bd 100644 --- a/node/tests/SharedTests.ts +++ b/node/tests/SharedTests.ts @@ -2004,10 +2004,10 @@ export function runBaseTests(config: { `zinterstore test_%p`, async (protocol) => { await runTest(async (client: BaseClient) => { - await zinterstoreBasicTest; - await zinterstoreWithAggregation; - await zinterstoreWithWeightsAndAggregation; - await zinterstoreEmptyCases; + await zinterstoreBasicTest(client); + await zinterstoreWithAggregation(client); + await zinterstoreWithWeightsAndAggregation(client); + await zinterstoreEmptyCases(client); }, protocol); }, config.timeout,