Skip to content

Commit

Permalink
Node: add SDIFFSTORE command (valkey-io#1931)
Browse files Browse the repository at this point in the history
Signed-off-by: aaron-congo <[email protected]>
  • Loading branch information
aaron-congo authored Jul 15, 2024
1 parent 84b0ee2 commit c5d2fd5
Show file tree
Hide file tree
Showing 7 changed files with 120 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#### Changes
* Node: Added SDIFFSTORE command ([#1931](https://github.com/valkey-io/valkey-glide/pull/1931))
* Node: Added SINTERSTORE command ([#1929](https://github.com/valkey-io/valkey-glide/pull/1929))
* Node: Added SUNION command ([#1919](https://github.com/valkey-io/valkey-glide/pull/1919))
* Node: Added SDIFF command ([#1924](https://github.com/valkey-io/valkey-glide/pull/1924))
Expand Down
23 changes: 23 additions & 0 deletions node/src/BaseClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ import {
createSAdd,
createSCard,
createSDiff,
createSDiffStore,
createSInter,
createSInterStore,
createSIsMember,
Expand Down Expand Up @@ -1363,6 +1364,28 @@ export class BaseClient {
);
}

/**
* Stores the difference between the first set and all the successive sets in `keys` into a new set at `destination`.
*
* See https://valkey.io/commands/sdiffstore/ for more details.
*
* @remarks When in cluster mode, `destination` and all `keys` must map to the same hash slot.
* @param destination - The key of the destination set.
* @param keys - The keys of the sets to diff.
* @returns The number of elements in the resulting set.
*
* @example
* ```typescript
* await client.sadd("set1", ["member1", "member2"]);
* await client.sadd("set2", ["member1"]);
* const result = await client.sdiffstore("set3", ["set1", "set2"]);
* console.log(result); // Output: 1 - One member was stored in "set3", and that member is the diff between "set1" and "set2".
* ```
*/
public sdiffstore(destination: string, keys: string[]): Promise<number> {
return this.createWritePromise(createSDiffStore(destination, keys));
}

/**
* Gets the union of all the given sets.
*
Expand Down
10 changes: 10 additions & 0 deletions node/src/Commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -631,6 +631,16 @@ export function createSDiff(keys: string[]): command_request.Command {
return createCommand(RequestType.SDiff, keys);
}

/**
* @internal
*/
export function createSDiffStore(
destination: string,
keys: string[],
): command_request.Command {
return createCommand(RequestType.SDiffStore, [destination].concat(keys));
}

/**
* @internal
*/
Expand Down
15 changes: 15 additions & 0 deletions node/src/Transaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ import {
createSAdd,
createSCard,
createSDiff,
createSDiffStore,
createSInter,
createSInterStore,
createSIsMember,
Expand Down Expand Up @@ -763,6 +764,20 @@ export class BaseTransaction<T extends BaseTransaction<T>> {
return this.addAndReturn(createSDiff(keys), true);
}

/**
* Stores the difference between the first set and all the successive sets in `keys` into a new set at `destination`.
*
* See https://valkey.io/commands/sdiffstore/ for more details.
*
* @param destination - The key of the destination set.
* @param keys - The keys of the sets to diff.
*
* Command Response - The number of elements in the resulting set.
*/
public sdiffstore(destination: string, keys: string[]): T {
return this.addAndReturn(createSDiffStore(destination, keys));
}

/**
* Gets the union of all the given sets.
*
Expand Down
1 change: 1 addition & 0 deletions node/tests/RedisClusterClient.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,7 @@ describe("GlideClusterClient", () => {
client.sunion(["abc", "zxy", "lkn"]),
client.pfcount(["abc", "zxy", "lkn"]),
client.sdiff(["abc", "zxy", "lkn"]),
client.sdiffstore("abc", ["zxy", "lkn"]),
// TODO all rest multi-key commands except ones tested below
];

Expand Down
68 changes: 68 additions & 0 deletions node/tests/SharedTests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1288,6 +1288,74 @@ export function runBaseTests<Context>(config: {
config.timeout,
);

it.each([ProtocolVersion.RESP2, ProtocolVersion.RESP3])(
`sdiffstore test_%p`,
async (protocol) => {
await runTest(async (client: BaseClient) => {
const key1 = `{key}-1-${uuidv4()}`;
const key2 = `{key}-2-${uuidv4()}`;
const key3 = `{key}-3-${uuidv4()}`;
const stringKey = `{key}-4-${uuidv4()}`;
const nonExistingKey = `{key}-5-${uuidv4()}`;
const member1_list = ["a", "b", "c"];
const member2_list = ["c", "d", "e"];

expect(await client.sadd(key1, member1_list)).toEqual(3);
expect(await client.sadd(key2, member2_list)).toEqual(3);

// store diff in new key
expect(await client.sdiffstore(key3, [key1, key2])).toEqual(2);
checkSimple(await client.smembers(key3)).toEqual(
new Set(["a", "b"]),
);

// overwrite existing set
expect(await client.sdiffstore(key3, [key2, key1])).toEqual(2);
checkSimple(await client.smembers(key3)).toEqual(
new Set(["d", "e"]),
);

// overwrite one of the source sets
expect(await client.sdiffstore(key3, [key2, key3])).toEqual(1);
checkSimple(await client.smembers(key3)).toEqual(
new Set(["c"]),
);

// diff between non-empty set and empty set
expect(
await client.sdiffstore(key3, [key1, nonExistingKey]),
).toEqual(3);
checkSimple(await client.smembers(key3)).toEqual(
new Set(["a", "b", "c"]),
);

// diff between empty set and non-empty set
expect(
await client.sdiffstore(key3, [nonExistingKey, key1]),
).toEqual(0);
checkSimple(await client.smembers(key3)).toEqual(new Set());

// invalid argument - key list must not be empty
await expect(client.sdiffstore(key3, [])).rejects.toThrow();

// source key exists, but it is not a set
checkSimple(await client.set(stringKey, "foo")).toEqual("OK");
await expect(
client.sdiffstore(key3, [stringKey]),
).rejects.toThrow();

// overwrite a key holding a non-set value
expect(
await client.sdiffstore(stringKey, [key1, key2]),
).toEqual(2);
checkSimple(await client.smembers(stringKey)).toEqual(
new Set(["a", "b"]),
);
}, protocol);
},
config.timeout,
);

it.each([ProtocolVersion.RESP2, ProtocolVersion.RESP3])(
`sunion test_%p`,
async (protocol) => {
Expand Down
2 changes: 2 additions & 0 deletions node/tests/TestUtilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -396,6 +396,8 @@ export async function transactionTest(
args.push(2);
baseTransaction.sdiff([key7, key7]);
args.push(new Set());
baseTransaction.sdiffstore(key7, [key7]);
args.push(2);
baseTransaction.srem(key7, ["foo"]);
args.push(1);
baseTransaction.scard(key7);
Expand Down

0 comments on commit c5d2fd5

Please sign in to comment.