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: Convert classes to types #2005

Merged
merged 3 commits into from
Jul 25, 2024
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
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@
* Node: Added ZMPOP command ([#1994](https://github.com/valkey-io/valkey-glide/pull/1994))
* Node: Added ZINCRBY command ([#2009](https://github.com/valkey-io/valkey-glide/pull/2009))

#### Breaking Changes
* Node: (Refactor) Convert classes to types ([#2005](https://github.com/valkey-io/valkey-glide/pull/2005))
GumpacG marked this conversation as resolved.
Show resolved Hide resolved

#### Fixes
* Java: Add overloads for XADD to allow duplicate entry keys ([#1970](https://github.com/valkey-io/valkey-glide/pull/1970))
* Node: Fix ZADD bug where command could not be called with only the `changed` optional parameter ([#1995](https://github.com/valkey-io/valkey-glide/pull/1995))
Expand Down
2 changes: 2 additions & 0 deletions node/npm/glide/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ function initialize() {
SetOptions,
ZaddOptions,
ScoreBoundry,
UpdateOptions,
RangeByIndex,
RangeByScore,
RangeByLex,
Expand Down Expand Up @@ -157,6 +158,7 @@ function initialize() {
SetOptions,
ZaddOptions,
ScoreBoundry,
UpdateOptions,
RangeByIndex,
RangeByScore,
RangeByLex,
Expand Down
30 changes: 17 additions & 13 deletions node/src/BaseClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,15 @@ import { Buffer, BufferWriter, Reader, Writer } from "protobufjs";
import {
AggregationType,
BitmapIndexType,
BitOffsetOptions,
BitwiseOperation,
ExpireOptions,
GeoAddOptions,
GeospatialData,
GeoUnit,
InsertPosition,
KeyWeight,
LPosOptions,
ListDirection,
RangeByIndex,
RangeByLex,
Expand Down Expand Up @@ -140,10 +144,6 @@ import {
createZRevRankWithScore,
createZScore,
} from "./Commands";
import { BitOffsetOptions } from "./commands/BitOffsetOptions";
import { GeoAddOptions } from "./commands/geospatial/GeoAddOptions";
import { GeospatialData } from "./commands/geospatial/GeospatialData";
import { LPosOptions } from "./commands/LPosOptions";
import {
ClosingError,
ConfigurationError,
Expand Down Expand Up @@ -2388,7 +2388,8 @@ export class BaseClient {
* @example
* ```typescript
* // Example usage of the zadd method to update scores in an existing sorted set
* const result = await client.zadd("existing_sorted_set", { member1: 15.0, member2: 5.5 }, { conditionalChange: "onlyIfExists", changed: true });
* const options = { conditionalChange: ConditionalChange.ONLY_IF_EXISTS, changed: true };
* const result = await client.zadd("existing_sorted_set", { member1: 15.0, member2: 5.5 }, options);
* console.log(result); // Output: 2 - Updates the scores of two existing members in the sorted set "existing_sorted_set."
* ```
*/
Expand Down Expand Up @@ -3567,8 +3568,8 @@ export class BaseClient {
* @example
* ```typescript
* await client.rpush("myList", ["a", "b", "c", "d", "e", "e"]);
* console.log(await client.lpos("myList", "e", new LPosOptions({ rank: 2 }))); // Output: 5 - the second occurrence of "e" is at index 5.
* console.log(await client.lpos("myList", "e", new LPosOptions({ count: 3 }))); // Output: [ 4, 5 ] - indices for the occurrences of "e" in list "myList".
* console.log(await client.lpos("myList", "e", { rank: 2 })); // Output: 5 - the second occurrence of "e" is at index 5.
* console.log(await client.lpos("myList", "e", { count: 3 })); // Output: [ 4, 5 ] - indices for the occurrences of "e" in list "myList".
* ```
*/
public lpos(
Expand All @@ -3594,9 +3595,9 @@ export class BaseClient {
* @example
* ```typescript
* console.log(await client.bitcount("my_key1")); // Output: 2 - The string stored at "my_key1" contains 2 set bits.
* console.log(await client.bitcount("my_key2", OffsetOptions(1, 3))); // Output: 2 - The second to fourth bytes of the string stored at "my_key2" contain 2 set bits.
* console.log(await client.bitcount("my_key3", OffsetOptions(1, 1, BitmapIndexType.BIT))); // Output: 1 - Indicates that the second bit of the string stored at "my_key3" is set.
* console.log(await client.bitcount("my_key3", OffsetOptions(-1, -1, BitmapIndexType.BIT))); // Output: 1 - Indicates that the last bit of the string stored at "my_key3" is set.
* console.log(await client.bitcount("my_key2", { start: 1, end: 3 })); // Output: 2 - The second to fourth bytes of the string stored at "my_key2" contain 2 set bits.
* console.log(await client.bitcount("my_key3", { start: 1, end: 1, indexType: BitmapIndexType.BIT })); // Output: 1 - Indicates that the second bit of the string stored at "my_key3" is set.
* console.log(await client.bitcount("my_key3", { start: -1, end: -1, indexType: BitmapIndexType.BIT })); // Output: 1 - Indicates that the last bit of the string stored at "my_key3" is set.
* ```
*/
public bitcount(key: string, options?: BitOffsetOptions): Promise<number> {
Expand All @@ -3619,8 +3620,11 @@ export class BaseClient {
*
* @example
* ```typescript
* const options = new GeoAddOptions({updateMode: ConditionalChange.ONLY_IF_EXISTS, changed: true});
* const num = await client.geoadd("mySortedSet", new Map([["Palermo", new GeospatialData(13.361389, 38.115556)]]), options);
* const options = {updateMode: ConditionalChange.ONLY_IF_EXISTS, changed: true};
* const membersToCoordinates = new Map<string, GeospatialData>([
* ["Palermo", { longitude: 13.361389, latitude: 38.115556 }],
* ]);
* const num = await client.geoadd("mySortedSet", membersToCoordinates, options);
* console.log(num); // Output: 1 - Indicates that the position of an existing member in the sorted set "mySortedSet" has been updated.
* ```
*/
Expand Down Expand Up @@ -3648,7 +3652,7 @@ export class BaseClient {
*
* @example
* ```typescript
* const data = new Map([["Palermo", new GeospatialData(13.361389, 38.115556)], ["Catania", new GeospatialData(15.087269, 37.502669)]]);
* const data = new Map([["Palermo", { longitude: 13.361389, latitude: 38.115556 }], ["Catania", { longitude: 15.087269, latitude: 37.502669 }]]);
* await client.geoadd("mySortedSet", data);
* const result = await client.geopos("mySortedSet", ["Palermo", "Catania", "NonExisting"]);
* // When added via GEOADD, the geospatial coordinates are converted into a 52 bit geohash, so the coordinates
Expand Down
186 changes: 157 additions & 29 deletions node/src/Commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@

import { createLeakedStringVec, MAX_REQUEST_ARGS_LEN } from "glide-rs";
import Long from "long";
import { FlushMode } from "./commands/FlushMode";
import { LPosOptions } from "./commands/LPosOptions";

/* eslint-disable-next-line @typescript-eslint/no-unused-vars */
import { BaseClient } 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 */
import { GlideClusterClient } from "src/GlideClusterClient";
import { command_request } from "./ProtobufMessage";
import { BitOffsetOptions } from "./commands/BitOffsetOptions";
import { GeoAddOptions } from "./commands/geospatial/GeoAddOptions";
import { GeospatialData } from "./commands/geospatial/GeospatialData";

import RequestType = command_request.RequestType;

Expand Down Expand Up @@ -976,21 +977,25 @@ export function createTTL(key: string): command_request.Command {
return createCommand(RequestType.TTL, [key]);
}

/**
* Options for updating elements of a sorted set key.
GumpacG marked this conversation as resolved.
Show resolved Hide resolved
*/
export enum UpdateByScore {
/** Only update existing elements if the new score is less than the current score. */
LESS_THAN = "LT",
/** Only update existing elements if the new score is greater than the current score. */
GREATER_THAN = "GT",
}

export type ZAddOptions = {
/**
* `onlyIfDoesNotExist` - Only add new elements. Don't update already existing
* elements. Equivalent to `NX` in the Redis API. `onlyIfExists` - Only update
* elements that already exist. Don't add new elements. Equivalent to `XX` in
* the Redis API.
* Options for handling existing members.
*/
conditionalChange?: "onlyIfExists" | "onlyIfDoesNotExist";
conditionalChange?: ConditionalChange;
/**
* `scoreLessThanCurrent` - Only update existing elements if the new score is
* less than the current score. Equivalent to `LT` in the Redis API.
* `scoreGreaterThanCurrent` - Only update existing elements if the new score
* is greater than the current score. Equivalent to `GT` in the Redis API.
* Options for updating scores.
*/
updateOptions?: "scoreLessThanCurrent" | "scoreGreaterThanCurrent";
updateOptions?: UpdateByScore;
/**
* Modify the return value from the number of new elements added, to the total number of elements changed.
*/
Expand All @@ -1009,22 +1014,22 @@ export function createZAdd(
let args = [key];

if (options) {
if (options.conditionalChange === "onlyIfExists") {
args.push("XX");
} else if (options.conditionalChange === "onlyIfDoesNotExist") {
if (options.updateOptions) {
if (options.conditionalChange) {
if (
options.conditionalChange ===
ConditionalChange.ONLY_IF_DOES_NOT_EXIST &&
options.updateOptions
) {
throw new Error(
`The GT, LT, and NX options are mutually exclusive. Cannot choose both ${options.updateOptions} and NX.`,
);
}

args.push("NX");
args.push(options.conditionalChange);
}

if (options.updateOptions === "scoreLessThanCurrent") {
args.push("LT");
} else if (options.updateOptions === "scoreGreaterThanCurrent") {
args.push("GT");
if (options.updateOptions) {
args.push(options.updateOptions);
}

if (options.changed) {
Expand Down Expand Up @@ -1694,6 +1699,27 @@ export function createFunctionLoad(
return createCommand(RequestType.FunctionLoad, args);
}

/**
* Represents offsets specifying a string interval to analyze in the {@link BaseClient.bitcount|bitcount} command. The offsets are
* zero-based indexes, with `0` being the first index of the string, `1` being the next index and so on.
* The offsets can also be negative numbers indicating offsets starting at the end of the string, with `-1` being
* the last index of the string, `-2` being the penultimate, and so on.
*
* See https://valkey.io/commands/bitcount/ for more details.
*/
export type BitOffsetOptions = {
/** The starting offset index. */
start: number;
/** The ending offset index. */
end: number;
/**
* The index offset type. This option can only be specified if you are using server version 7.0.0 or above.
* Could be either {@link BitmapIndexType.BYTE} or {@link BitmapIndexType.BIT}.
* If no index type is provided, the indexes will be assumed to be byte indexes.
*/
indexType?: BitmapIndexType;
};

/**
* @internal
*/
Expand All @@ -1702,7 +1728,13 @@ export function createBitCount(
options?: BitOffsetOptions,
): command_request.Command {
const args = [key];
if (options) args.push(...options.toArgs());

if (options) {
args.push(options.start.toString());
args.push(options.end.toString());
if (options.indexType) args.push(options.indexType);
}

return createCommand(RequestType.BitCount, args);
}

Expand Down Expand Up @@ -1747,6 +1779,24 @@ export function createBitPos(
return createCommand(RequestType.BitPos, args);
}

/**
* Defines flushing mode for {@link GlideClient.flushall}, {@link GlideClusterClient.flushall},
* {@link GlideClient.functionFlush}, {@link GlideClusterClient.functionFlush},
* {@link GlideClient.flushdb} and {@link GlideClusterClient.flushdb} commands.
*
GumpacG marked this conversation as resolved.
Show resolved Hide resolved
* See https://valkey.io/commands/flushall/ and https://valkey.io/commands/flushdb/ for details.
*/
export enum FlushMode {
/**
* Flushes synchronously.
*
* since Valkey version 6.2.0.
*/
SYNC = "SYNC",
/** Flushes asynchronously. */
ASYNC = "ASYNC",
}

export type StreamReadOptions = {
/**
* If set, the read request will block for the set amount of milliseconds or
Expand Down Expand Up @@ -1932,6 +1982,20 @@ export function createFlushDB(mode?: FlushMode): command_request.Command {
}
}

/**
* Optional arguments to LPOS command.
*
* See https://valkey.io/commands/lpos/ for more details.
*/
export type LPosOptions = {
/** The rank of the match to return. */
rank?: number;
/** The specific number of matching indices from a list. */
count?: number;
/** The maximum number of comparisons to make between the element and the items in the list. */
maxLength?: number;
};

/**
* @internal
*/
Expand All @@ -1940,10 +2004,23 @@ export function createLPos(
element: string,
options?: LPosOptions,
): command_request.Command {
let args: string[] = [key, element];
const args: string[] = [key, element];

if (options) {
args = args.concat(options.toArgs());
if (options.rank !== undefined) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can't you just use if (options.rank) without checking explicitly for undefined? Same for the other usages here.

Copy link
Collaborator

Choose a reason for hiding this comment

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

+1

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

We can do that with non-number types however, we can't do that with number types because it can be 0 which would be treated as false and behaves differently.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Be careful. The string value "" is also considered falsey - not relevant here, but could be relevant in certain circumstances.
https://www.freecodecamp.org/news/falsy-values-in-javascript/

args.push("RANK");
args.push(options.rank.toString());
}

if (options.count !== undefined) {
args.push("COUNT");
args.push(options.count.toString());
}

if (options.maxLength !== undefined) {
args.push("MAXLEN");
args.push(options.maxLength.toString());
}
}

return createCommand(RequestType.LPos, args);
Expand All @@ -1956,6 +2033,48 @@ export function createDBSize(): command_request.Command {
return createCommand(RequestType.DBSize, []);
}

/**
* An optional condition to the {@link BaseClient.geoadd} command.
*/
export enum ConditionalChange {
/**
* Only update elements that already exist. Don't add new elements. Equivalent to `XX` in the Valkey API.
*/
ONLY_IF_EXISTS = "XX",

/**
* Only add new elements. Don't update already existing elements. Equivalent to `NX` in the Valkey API.
*/
ONLY_IF_DOES_NOT_EXIST = "NX",
}

/**
* Represents a geographic position defined by longitude and latitude.
* The exact limits, as specified by `EPSG:900913 / EPSG:3785 / OSGEO:41001` are the
* following:
*
* Valid longitudes are from `-180` to `180` degrees.
* Valid latitudes are from `-85.05112878` to `85.05112878` degrees.
*/
export type GeospatialData = {
/** The longitude coordinate. */
longitude: number;
/** The latitude coordinate. */
latitude: number;
};

/**
* Optional arguments for the GeoAdd command.
*
* See https://valkey.io/commands/geoadd/ for more details.
*/
export type GeoAddOptions = {
/** Options for handling existing members. See {@link ConditionalChange}. */
updateMode?: ConditionalChange;
/** If `true`, returns the count of changed elements instead of new elements added. */
changed?: boolean;
};

/**
* @internal
*/
Expand All @@ -1967,11 +2086,20 @@ export function createGeoAdd(
let args: string[] = [key];

if (options) {
args = args.concat(options.toArgs());
if (options.updateMode) {
args.push(options.updateMode);
}

if (options.changed) {
args.push("CH");
Copy link
Collaborator

Choose a reason for hiding this comment

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

do we have a place to put string constants?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Not currently, I think if we want to do this, it should be a part of a different PR as we may want to convert other types with string constants to enums. Would potentially be big enough on it's own.

}
}

membersToGeospatialData.forEach((coord, member) => {
args = args.concat(coord.toArgs());
args = args.concat([
coord.longitude.toString(),
coord.latitude.toString(),
]);
args.push(member);
});
return createCommand(RequestType.GeoAdd, args);
Expand Down
Loading
Loading