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

Node: add binary support for mset, msetnx #2229

Merged
Merged
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#### Changes
* Node: Added binary variant to sorted set commands ([#2190](https://github.com/valkey-io/valkey-glide/pull/2190), [#2210](https://github.com/valkey-io/valkey-glide/pull/2210))
* Node: Added binary variant for MSET, MSETNX commands ([#2229](https://github.com/valkey-io/valkey-glide/pull/2229))
* Node: Added binary variant to HASH commands ([#2194](https://github.com/valkey-io/valkey-glide/pull/2194))
* Node: Added binary variant to server management commands ([#2179](https://github.com/valkey-io/valkey-glide/pull/2179))
* Node: Added/updated binary variant to connection management commands and WATCH/UNWATCH ([#2160](https://github.com/valkey-io/valkey-glide/pull/2160))
Expand Down
79 changes: 54 additions & 25 deletions node/src/BaseClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -319,26 +319,51 @@ export type SortedSetDataType = {
score: number;
}[];

/**
* This function converts an input from GlideRecord or Record types to GlideRecord.
*
* @param keysAndValues - key names and their values.
* @returns GlideRecord array containing keys and their values.
*/
export function convertGlideRecord(
keysAndValues: GlideRecord<GlideString> | Record<string, GlideString>,
): GlideRecord<GlideString> {
if (!Array.isArray(keysAndValues)) {
return Object.entries(keysAndValues).map((e) => {
return { key: e[0], value: e[1] };
});
}

return keysAndValues;
}

/**
* Data type which represents how data are returned from hashes or insterted there.
* Similar to `Record<GlideString, GlideString>` - see {@link GlideRecord}.
*/
export type HashDataType = {
/** The hash element name. */
field: GlideString;
/** The hash element value. */
value: GlideString;
}[];

/**
* This function converts an input from HashDataType or Record types to HashDataType.
*
* @param fieldsAndValues - field names and their values.
* @returns HashDataType array containing field names and their values.
*/
export function convertFieldsAndValuesForHset(
export function convertHashDataType(
fieldsAndValues: HashDataType | Record<string, GlideString>,
): HashDataType {
let finalFieldAndValues = [];

if (!Array.isArray(fieldsAndValues)) {
finalFieldAndValues = Object.entries(fieldsAndValues).map((e) => {
return Object.entries(fieldsAndValues).map((e) => {
return { field: e[0], value: e[1] };
});
} else {
finalFieldAndValues = fieldsAndValues;
}

return finalFieldAndValues;
return fieldsAndValues;
}

/**
Expand Down Expand Up @@ -381,17 +406,6 @@ class PointerResponse {
}
}

/**
* Data type which represents how data are returned from hashes or insterted there.
* Similar to `Record<GlideString, GlideString>` - see {@link GlideRecord}.
*/
export type HashDataType = {
/** The hash element name. */
field: GlideString;
/** The hash element value. */
value: GlideString;
}[];

/** Represents the credentials for connecting to a server. */
export type RedisCredentials = {
/**
Expand Down Expand Up @@ -1323,7 +1337,7 @@ export class BaseClient {
* @see {@link https://valkey.io/commands/mset/|valkey.io} for details.
* @remarks When in cluster mode, the command may route to multiple nodes when keys in `keyValueMap` map to different hash slots.
*
* @param keyValueMap - A key-value map consisting of keys and their respective values to set.
* @param keysAndValues - A list of key-value pairs to set.
* @returns always "OK".
*
* @example
Expand All @@ -1332,9 +1346,20 @@ export class BaseClient {
* const result = await client.mset({"key1": "value1", "key2": "value2"});
* console.log(result); // Output: 'OK'
* ```
*
* @example
* ```typescript
* // Example usage of mset method to set values for multiple keys (GlideRecords allow binary data in the key)
* const result = await client.mset([{key: "key1", value: "value1"}, {key: "key2", value: "value2"}]);
* console.log(result); // Output: 'OK'
* ```
*/
public async mset(keyValueMap: Record<string, string>): Promise<"OK"> {
return this.createWritePromise(createMSet(keyValueMap));
public async mset(
keysAndValues: Record<string, GlideString> | GlideRecord<GlideString>,
): Promise<"OK"> {
return this.createWritePromise(
createMSet(convertGlideRecord(keysAndValues)),
);
}

/**
Expand All @@ -1344,7 +1369,7 @@ export class BaseClient {
* @see {@link https://valkey.io/commands/msetnx/|valkey.io} for more details.
* @remarks When in cluster mode, all keys in `keyValueMap` must map to the same hash slot.
*
* @param keyValueMap - A key-value map consisting of keys and their respective values to set.
* @param keysAndValues - A list of key-value pairs to set.
* @returns `true` if all keys were set. `false` if no key was set.
*
* @example
Expand All @@ -1356,8 +1381,12 @@ export class BaseClient {
* console.log(result2); // Output: `false`
* ```
*/
public async msetnx(keyValueMap: Record<string, string>): Promise<boolean> {
return this.createWritePromise(createMSetNX(keyValueMap));
public async msetnx(
keysAndValues: Record<string, GlideString> | GlideRecord<GlideString>,
): Promise<boolean> {
return this.createWritePromise(
createMSetNX(convertGlideRecord(keysAndValues)),
);
}

/** Increments the number stored at `key` by one. If `key` does not exist, it is set to 0 before performing the operation.
Expand Down Expand Up @@ -1739,7 +1768,7 @@ export class BaseClient {
fieldsAndValues: HashDataType | Record<string, GlideString>,
): Promise<number> {
return this.createWritePromise(
createHSet(key, convertFieldsAndValuesForHset(fieldsAndValues)),
createHSet(key, convertHashDataType(fieldsAndValues)),
);
}

Expand Down
11 changes: 7 additions & 4 deletions node/src/Commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -332,20 +332,23 @@ export function createMGet(keys: GlideString[]): command_request.Command {
* @internal
*/
export function createMSet(
keyValueMap: Record<string, string>,
keysAndValues: GlideRecord<GlideString>,
): command_request.Command {
return createCommand(RequestType.MSet, Object.entries(keyValueMap).flat());
return createCommand(
RequestType.MSet,
keysAndValues.flatMap((e) => [e.key, e.value]),
);
}

/**
* @internal
*/
export function createMSetNX(
keyValueMap: Record<string, string>,
keysAndValues: GlideRecord<GlideString>,
): command_request.Command {
return createCommand(
RequestType.MSetNX,
Object.entries(keyValueMap).flat(),
keysAndValues.flatMap((e) => [e.key, e.value]),
);
}

Expand Down
25 changes: 16 additions & 9 deletions node/src/Transaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@

import {
BaseClient, // eslint-disable-line @typescript-eslint/no-unused-vars
GlideRecord,
GlideRecord, // eslint-disable-line @typescript-eslint/no-unused-vars
GlideString,
HashDataType,
convertGlideRecord,
convertHashDataType,
ReadFrom, // eslint-disable-line @typescript-eslint/no-unused-vars
SortedSetDataType, // eslint-disable-line @typescript-eslint/no-unused-vars
convertFieldsAndValuesForHset,
} from "./BaseClient";

import {
Expand Down Expand Up @@ -524,12 +525,14 @@ export class BaseTransaction<T extends BaseTransaction<T>> {
/** Set multiple keys to multiple values in a single atomic operation.
* @see {@link https://valkey.io/commands/mset/|valkey.io} for details.
*
* @param keyValueMap - A key-value map consisting of keys and their respective values to set.
* @param keysAndValues - A list of key-value pairs to set.
*
* Command Response - always "OK".
*/
public mset(keyValueMap: Record<string, string>): T {
return this.addAndReturn(createMSet(keyValueMap));
public mset(
keysAndValues: Record<string, GlideString> | GlideRecord<GlideString>,
): T {
return this.addAndReturn(createMSet(convertGlideRecord(keysAndValues)));
}

/**
Expand All @@ -538,11 +541,15 @@ export class BaseTransaction<T extends BaseTransaction<T>> {
*
* @see {@link https://valkey.io/commands/msetnx/|valkey.io} for details.
*
* @param keyValueMap - A key-value map consisting of keys and their respective values to set.
* @param keysAndValues - A list of key-value pairs to set.
* Command Response - `true` if all keys were set. `false` if no key was set.
*/
public msetnx(keyValueMap: Record<string, string>): T {
return this.addAndReturn(createMSetNX(keyValueMap));
public msetnx(
keysAndValues: Record<string, GlideString> | GlideRecord<GlideString>,
): T {
return this.addAndReturn(
createMSetNX(convertGlideRecord(keysAndValues)),
);
}

/** Increments the number stored at `key` by one. If `key` does not exist, it is set to 0 before performing the operation.
Expand Down Expand Up @@ -821,7 +828,7 @@ export class BaseTransaction<T extends BaseTransaction<T>> {
fieldsAndValues: HashDataType | Record<string, GlideString>,
): T {
return this.addAndReturn(
createHSet(key, convertFieldsAndValuesForHset(fieldsAndValues)),
createHSet(key, convertHashDataType(fieldsAndValues)),
);
}

Expand Down
38 changes: 24 additions & 14 deletions node/tests/SharedTests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
// represents a running server instance. See first 2 test cases as examples.

import { expect, it } from "@jest/globals";
import { HashDataType } from "src/BaseClient";
import { GlideRecord, HashDataType } from "src/BaseClient";
import { v4 as uuidv4 } from "uuid";
import {
BaseClientConfiguration,
Expand Down Expand Up @@ -371,23 +371,31 @@ export function runBaseTests(config: {
const key2 = uuidv4();
const key3 = uuidv4();
const value = uuidv4();
const keyValueList = {
[key1]: value,
[key2]: value,
[key3]: value,
};
const valueEncoded = Buffer.from(value);
const keyValueList = [
{ key: key1, value },
{ key: key2, value },
{ key: key3, value },
];

expect(await client.mset(keyValueList)).toEqual("OK");
expect(
await client.mget([key1, key2, "nonExistingKey", key3]),
).toEqual([value, value, null, value]);

//mget with binary buffers
expect(await client.mset(keyValueList)).toEqual("OK");
//mget & mset with binary buffers
const key1Encoded = Buffer.from(key1);
const key3Encoded = Buffer.from(key3);
const valueEncoded = Buffer.from(value);
const keyValueListEncoded: GlideRecord<GlideString> = [
{ key: key1Encoded, value: valueEncoded },
{ key: key2, value },
{ key: key3Encoded, value: valueEncoded },
];

expect(await client.mset(keyValueListEncoded)).toEqual("OK");
expect(
await client.mget(
[key1, key2, "nonExistingKey", key3],
[key1Encoded, key2, "nonExistingKey", key3Encoded],
Decoder.Bytes,
),
).toEqual([valueEncoded, valueEncoded, null, valueEncoded]);
Expand All @@ -409,10 +417,12 @@ export function runBaseTests(config: {
[key1]: value,
[key2]: value,
};
const keyValueMap2 = {
[key2]: value,
[key3]: value,
};
const key2Encoded = Buffer.from(key2);
const valueEncoded = Buffer.from(value);
const keyValueMap2 = [
{ key: key2Encoded, value: valueEncoded },
{ key: key3, value: valueEncoded },
];

expect(await client.msetnx(keyValueMap1)).toEqual(true);

Expand Down
Loading