Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add SMOVE command for node #1476

Merged
merged 5 commits into from
May 30, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions node/src/BaseClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ import {
createSCard,
createSIsMember,
createSMembers,
createSMove,
createSPop,
createSRem,
createSet,
Expand Down Expand Up @@ -1224,6 +1225,33 @@ export class BaseClient {
);
}

/** Moves `member` from the set at `source` to the set at `destination`, removing it from the source set.
* Creates a new destination set if needed. The operation is atomic.
* See https://valkey.io/commands/smove for more details.
*
adarovadya marked this conversation as resolved.
Show resolved Hide resolved
* Note: When in cluster mode, `source` and `destination` must map to the same hash slot.
Yury-Fridlyand marked this conversation as resolved.
Show resolved Hide resolved
*
* @param source - The key of the set to remove the element from.
* @param destination - The key of the set to add the element to.
* @param member - The set element to move.
* @returns True on success, or False if the `source` set does not exist or the element is not a member of the source set.
Yury-Fridlyand marked this conversation as resolved.
Show resolved Hide resolved
*
* @example
* ```typescript
* const result = await client.smove("set1", "set2", "member1");
* console.log(result); // Output: True - "member1" was moved from "set1" to "set2".
Yury-Fridlyand marked this conversation as resolved.
Show resolved Hide resolved
* ```
*/
public smove(
source: string,
destination: string,
member: string,
): Promise<boolean> {
return this.createWritePromise(
createSMove(source, destination, member),
);
}

/** Returns the set cardinality (number of elements) of the set stored at `key`.
* See https://redis.io/commands/scard/ for details.
*
Expand Down
12 changes: 12 additions & 0 deletions node/src/Commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -549,6 +549,18 @@ export function createSMembers(key: string): redis_request.Command {
return createCommand(RequestType.SMembers, [key]);
}

/**
*
* @internal
*/
export function createSMove(
source: string,
destination: string,
member: string,
): redis_request.Command {
return createCommand(RequestType.SMove, [source, destination, member]);
}

/**
* @internal
*/
Expand Down
17 changes: 17 additions & 0 deletions node/src/Transaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ import {
createSCard,
createSIsMember,
createSMembers,
createSMove,
createSPop,
createSRem,
createSelect,
Expand Down Expand Up @@ -678,6 +679,22 @@ export class BaseTransaction<T extends BaseTransaction<T>> {
return this.addAndReturn(createSMembers(key), true);
}

/** Moves `member` from the set at `source` to the set at `destination`, removing it from the source set.
* Creates a new destination set if needed. The operation is atomic.
* See https://valkey.io/commands/smove for more details.
*
adarovadya marked this conversation as resolved.
Show resolved Hide resolved
* Note: When in cluster mode, `source` and `destination` must map to the same hash slot.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not applicable for transaction, please remove

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we decided to not add it in Transaction?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Transaction could be routed to a single node only.
It does not protect a user from making a mistake, but this notice is more applicable to exec function which submits a transaction, rather than to a commands in the transaction.
If we change that decision (it is OK), we need to add the notice to all multi-key commands in transaction in all clients.

*
* @param source - The key of the set to remove the element from.
* @param destination - The key of the set to add the element to.
* @param member - The set element to move.
*
* Command Response - True on success, or False if the `source` set does not exist or the element is not a member of the source set.
Yury-Fridlyand marked this conversation as resolved.
Show resolved Hide resolved
*/
public smove(source: string, destination: string, member: string): T {
return this.addAndReturn(createSMove(source, destination, member));
}

/** Returns the set cardinality (number of elements) of the set stored at `key`.
* See https://redis.io/commands/scard/ for details.
*
Expand Down
66 changes: 66 additions & 0 deletions node/tests/SharedTests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1046,6 +1046,72 @@ export function runBaseTests<Context>(config: {
config.timeout,
);

it.each([ProtocolVersion.RESP2, ProtocolVersion.RESP3])(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

`smove test_%p`,
async (protocol) => {
await runTest(async (client: BaseClient) => {
const key1 = "{key}" + uuidv4();
const key2 = "{key}" + uuidv4();
const key3 = "{key}" + uuidv4();
const string_key = "{key}" + uuidv4();
const non_existing_key = "{key}" + uuidv4();

expect(await client.sadd(key1, ["1", "2", "3"])).toEqual(3);
expect(await client.sadd(key2, ["2", "3"])).toEqual(2);

// move an element
expect(await client.smove(key1, key2, "1"));
expect(await client.smembers(key1)).toEqual(
new Set(["2", "3"]),
);
expect(await client.smembers(key2)).toEqual(
new Set(["1", "2", "3"]),
);

// moved element already exists in the destination set
expect(await client.smove(key2, key1, "2"));
expect(await client.smembers(key1)).toEqual(
new Set(["2", "3"]),
);
expect(await client.smembers(key2)).toEqual(
new Set(["1", "3"]),
);

// attempt to move from a non-existing key
expect(await client.smove(non_existing_key, key1, "4")).toEqual(
false,
);
expect(await client.smembers(key1)).toEqual(
new Set(["2", "3"]),
);

// move to a new set
expect(await client.smove(key1, key3, "2"));
expect(await client.smembers(key1)).toEqual(new Set(["3"]));
expect(await client.smembers(key3)).toEqual(new Set(["2"]));

// attempt to move a missing element
expect(await client.smove(key1, key3, "42")).toEqual(false);
expect(await client.smembers(key1)).toEqual(new Set(["3"]));
expect(await client.smembers(key3)).toEqual(new Set(["2"]));

// move missing element to missing key
expect(
await client.smove(key1, non_existing_key, "42"),
).toEqual(false);
expect(await client.smembers(key1)).toEqual(new Set(["3"]));
expect(await client.type(non_existing_key)).toEqual("none");

// key exists, but it is not a set
expect(await client.set(string_key, "value")).toEqual("OK");
await expect(
client.smove(string_key, key1, "_"),
).rejects.toThrow();
}, protocol);
},
config.timeout,
);

it.each([ProtocolVersion.RESP2, ProtocolVersion.RESP3])(
`srem, scard and smembers with non existing key_%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 @@ -300,6 +300,8 @@ export async function transactionTest(
args.push("bar");
baseTransaction.spopCount(key7, 2);
args.push(new Set());
baseTransaction.smove(key7, key7, "non_existing_member");
args.push(false);
baseTransaction.scard(key7);
args.push(0);
baseTransaction.zadd(key8, {
Expand Down
Loading