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

Spike: returning a record replacement with glide strings. #418

2 changes: 2 additions & 0 deletions node/npm/glide/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ function initialize() {
GlideClient,
GlideClusterClient,
GlideClientConfiguration,
GlideRecord,
GlideString,
FunctionListOptions,
FunctionListResponse,
Expand Down Expand Up @@ -202,6 +203,7 @@ function initialize() {
Decoder,
DecoderOption,
GeoAddOptions,
GlideRecord,
GlideString,
CoordOrigin,
MemberOrigin,
Expand Down
13 changes: 9 additions & 4 deletions node/rust-client/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -195,13 +195,18 @@ fn redis_value_to_js(val: Value, js_env: Env, string_decoder: bool) -> Result<Js
Ok(js_array_view.into_unknown())
}
Value::Map(map) => {
let mut obj = js_env.create_object()?;
for (key, value) in map {
// Convert map to array of key-value pairs instead of a `Record`` (object),
Yury-Fridlyand marked this conversation as resolved.
Show resolved Hide resolved
// because `Record` does not support `GlideString` as a key.
let mut js_array = js_env.create_array_with_length(map.len())?;
for (idx, (key, value)) in (0_u32..).zip(map.into_iter()) {
let mut obj = js_env.create_object()?;
let field_name = String::from_owned_redis_value(key).map_err(to_js_error)?;
Yury-Fridlyand marked this conversation as resolved.
Show resolved Hide resolved
let value = redis_value_to_js(value, js_env, string_decoder)?;
obj.set_named_property(&field_name, value)?;
obj.set_named_property("key", field_name)?;
obj.set_named_property("value", value)?;
js_array.set_element(idx, obj)?;
}
Ok(obj.into_unknown())
Ok(js_array.into_unknown())
}
Value::Double(float) => js_env.create_double(float).map(|val| val.into_unknown()),
Value::Boolean(bool) => js_env.get_boolean(bool).map(|val| val.into_unknown()),
Expand Down
40 changes: 29 additions & 11 deletions node/src/BaseClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,9 @@ export type DecoderOption = {
decoder?: Decoder;
};

/** A replacement for `Record<GlideString, T>` - array of key-value pairs. */
export type GlideRecord<T> = { key: GlideString; value: T }[];
Yury-Fridlyand marked this conversation as resolved.
Show resolved Hide resolved

/**
* Our purpose in creating PointerResponse type is to mark when response is of number/long pointer response type.
* Consequently, when the response is returned, we can check whether it is instanceof the PointerResponse type and pass it to the Rust core function with the proper parameters.
Expand Down Expand Up @@ -1210,7 +1213,8 @@ export class BaseClient {
);
}

/** Retrieve the values of multiple keys.
/**
* Retrieves the values of multiple keys.
Yury-Fridlyand marked this conversation as resolved.
Show resolved Hide resolved
*
* @see {@link https://valkey.io/commands/mget/|valkey.io} for details.
* @remarks When in cluster mode, the command may route to multiple nodes when `keys` map to different hash slots.
Expand All @@ -1219,7 +1223,7 @@ export class BaseClient {
* @param decoder - (Optional) {@link Decoder} type which defines how to handle the response.
* If not set, the {@link BaseClientConfiguration.defaultDecoder|default decoder} will be used.
* @returns A list of values corresponding to the provided keys. If a key is not found,
* its corresponding value in the list will be null.
* its corresponding value in the list will be `null`.
*
* @example
* ```typescript
Expand All @@ -1237,13 +1241,14 @@ export class BaseClient {
return this.createWritePromise(createMGet(keys), { decoder: decoder });
}

/** Set multiple keys to multiple values in a single operation.
/**
* Sets multiple keys to multiple values in a single operation.
*
* @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.
* @returns always "OK".
* @returns Always `"OK"`.
*
* @example
* ```typescript
Expand All @@ -1252,7 +1257,7 @@ export class BaseClient {
* console.log(result); // Output: 'OK'
* ```
*/
public async mset(keyValueMap: Record<string, string>): Promise<"OK"> {
public async mset(keyValueMap: GlideRecord<GlideString>): Promise<"OK"> {
return this.createWritePromise(createMSet(keyValueMap));
}

Expand All @@ -1275,7 +1280,9 @@ export class BaseClient {
* console.log(result2); // Output: `false`
* ```
*/
public async msetnx(keyValueMap: Record<string, string>): Promise<boolean> {
public async msetnx(
keyValueMap: GlideRecord<GlideString>,
): Promise<boolean> {
return this.createWritePromise(createMSetNX(keyValueMap));
}

Expand Down Expand Up @@ -1781,23 +1788,34 @@ export class BaseClient {
return this.createWritePromise(createHExists(key, field));
}

/** Returns all fields and values of the hash stored at `key`.
/**
* Returns all fields and values of the hash stored at `key`.
*
* @see {@link https://valkey.io/commands/hgetall/|valkey.io} for details.
*
* @param key - The key of the hash.
* @returns a list of fields and their values stored in the hash. Every field name in the list is followed by its value.
* @returns A list of fields and their values stored in the hash.
* If `key` does not exist, it returns an empty list.
*
* @example
* ```typescript
* // Example usage of the hgetall method
* const result = await client.hgetall("my_hash");
* console.log(result); // Output: {"field1": "value1", "field2": "value2"}
* console.log(result); // Output:
* // [
* // {"field": "field1", "value": "value1"},
* // {"field", "field2", "value": "value2"}
* // ]
* ```
*/
public async hgetall(key: string): Promise<Record<string, string>> {
return this.createWritePromise(createHGetAll(key));
public async hgetall(
key: string,
): Promise<{ field: GlideString; value: GlideString }[]> {
return this.createWritePromise(createHGetAll(key)).then((res) =>
(res as GlideRecord<GlideString>).map((r) => {
return { field: r.key, value: r.value };
}),
);
}

/** Increments the number stored at `field` in the hash stored at `key` by increment.
Expand Down
13 changes: 8 additions & 5 deletions node/src/Commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { createLeakedStringVec, MAX_REQUEST_ARGS_LEN } from "glide-rs";
import Long from "long";

/* eslint-disable-next-line @typescript-eslint/no-unused-vars */
import { BaseClient } from "src/BaseClient";
import { BaseClient, GlideRecord } from "src/BaseClient";
/* eslint-disable-next-line @typescript-eslint/no-unused-vars */
import { GlideClient } from "src/GlideClient";
/* eslint-disable-next-line @typescript-eslint/no-unused-vars */
Expand Down Expand Up @@ -328,20 +328,23 @@ export function createMGet(keys: GlideString[]): command_request.Command {
* @internal
*/
export function createMSet(
keyValueMap: Record<string, string>,
keyValueMap: GlideRecord<GlideString>,
): command_request.Command {
return createCommand(RequestType.MSet, Object.entries(keyValueMap).flat());
return createCommand(
RequestType.MSet,
keyValueMap.map((r) => [r.key, r.value]).flat(),
);
}

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

Expand Down
20 changes: 13 additions & 7 deletions node/src/Transaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
*/

import {
BaseClient, // eslint-disable-line @typescript-eslint/no-unused-vars
BaseClient,
GlideRecord, // eslint-disable-line @typescript-eslint/no-unused-vars
GlideString,
ReadFrom, // eslint-disable-line @typescript-eslint/no-unused-vars
} from "./BaseClient";
Expand Down Expand Up @@ -502,26 +503,30 @@ export class BaseTransaction<T extends BaseTransaction<T>> {
return this.addAndReturn(createConfigResetStat());
}

/** Retrieve the values of multiple keys.
/**
* Retrieves the values of multiple keys.
*
* @see {@link https://valkey.io/commands/mget/|valkey.io} for details.
*
* @param keys - A list of keys to retrieve values for.
*
* Command Response - A list of values corresponding to the provided keys. If a key is not found,
* its corresponding value in the list will be null.
* its corresponding value in the list will be `null`.
*/
public mget(keys: GlideString[]): T {
return this.addAndReturn(createMGet(keys));
}

/** Set multiple keys to multiple values in a single atomic operation.
/**
* Sets 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.
*
* Command Response - always "OK".
* Command Response - always `"OK"`.
*/
public mset(keyValueMap: Record<string, string>): T {
public mset(keyValueMap: GlideRecord<GlideString>): T {
return this.addAndReturn(createMSet(keyValueMap));
}

Expand All @@ -532,9 +537,10 @@ 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.
*
* Command Response - `true` if all keys were set. `false` if no key was set.
*/
public msetnx(keyValueMap: Record<string, string>): T {
public msetnx(keyValueMap: GlideRecord<GlideString>): T {
return this.addAndReturn(createMSetNX(keyValueMap));
}

Expand Down
9 changes: 9 additions & 0 deletions node/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,15 @@
] /* Specify a set of bundled library declaration files that describe the target runtime environment. */,
"outDir": "./build-ts" /* Specify an output folder for all emitted files.*/
},
"ts-node": {
"transpileOnly": true,
"compilerOptions": {
"module": "CommonJS",
"target": "ES2018",
"esModuleInterop": true
},
"esm": true
Yury-Fridlyand marked this conversation as resolved.
Show resolved Hide resolved
},
Yury-Fridlyand marked this conversation as resolved.
Show resolved Hide resolved
"compileOnSave": false,
"include": ["./*.ts", "src/*.ts", "src/*.js"],
"exclude": ["node_modules", "build-ts"]
Expand Down
Loading