From bad84c56e35383000f786b87519755f656fdd3f6 Mon Sep 17 00:00:00 2001 From: TJ Zhang Date: Thu, 18 Jul 2024 17:52:21 -0700 Subject: [PATCH] Node: Add GEOADD --- CHANGELOG.md | 1 + node/src/BaseClient.ts | 35 ++++++++++++ node/src/Commands.ts | 23 ++++++++ node/src/Transaction.ts | 29 ++++++++++ node/src/command-options/ConditionalChange.ts | 19 +++++++ .../geospatial/GeoAddOptions.ts | 55 +++++++++++++++++++ .../geospatial/GeospatialData.ts | 39 +++++++++++++ 7 files changed, 201 insertions(+) create mode 100644 node/src/command-options/ConditionalChange.ts create mode 100644 node/src/command-options/geospatial/GeoAddOptions.ts create mode 100644 node/src/command-options/geospatial/GeospatialData.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c4fb488db..45f5f19a31 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -103,6 +103,7 @@ * Python: Added transaction supports for DUMP, RESTORE, FUNCTION DUMP and FUNCTION RESTORE ([#1814](https://github.com/valkey-io/valkey-glide/pull/1814)) * Node: Added FlushAll command ([#1958](https://github.com/valkey-io/valkey-glide/pull/1958)) * Node: Added DBSize command ([#1932](https://github.com/valkey-io/valkey-glide/pull/1932)) +* Node: Added GeoAdd command ([#1980](https://github.com/valkey-io/valkey-glide/pull/1980)) #### Breaking Changes * Node: Update XREAD to return a Map of Map ([#1494](https://github.com/valkey-io/valkey-glide/pull/1494)) diff --git a/node/src/BaseClient.ts b/node/src/BaseClient.ts index 99ae4af26a..d30930a7ad 100644 --- a/node/src/BaseClient.ts +++ b/node/src/BaseClient.ts @@ -33,6 +33,7 @@ import { createExists, createExpire, createExpireAt, + createGeoAdd, createGet, createGetDel, createHDel, @@ -135,6 +136,9 @@ import { connection_request, response, } from "./ProtobufMessage"; +import { GeoAddOptions } from "./command-options/geospatial/GeoAddOptions"; +import { GeospatialData } from "./command-options/geospatial/GeospatialData"; +import { ConditionalChange } from "./command-options/ConditionalChange"; /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ type PromiseFunction = (value?: any) => void; @@ -3206,6 +3210,37 @@ export class BaseClient { return this.createWritePromise(createLPos(key, element, options)); } + /** + * Adds geospatial members with their positions to the specified sorted set stored at `key`.` + * If a member is already a part of the sorted set, its position is updated. + * + * See https://valkey.io/commands/geoadd/ for more details. + * + * @param key - The key of the sorted set. + * @param membersToGeospatialData - A mapping of member names to their corresponding positions - see + * {@link GeospatialData}. The command will report an error when the user attempts to index + * coordinates outside the specified ranges. + * @param options - The GeoAdd options - see {@link GeoAddOptions}. + * @returns The number of elements added to the sorted set. If `changed` is set to + * `true` in the options, returns the number of elements updated in the sorted set. + * + * @example + * ```typescript + * const options = new GeoAddOptions({updateMode: ConditionalChange.ONLY_IF_EXISTS, changed: true}); + * let num = await client.geoadd("mySortedSet", {"Palermo", new GeospatialData({13.361389, 38.115556})}, options); + * console.log(num); // Output: 1 - Indicates that the position of an existing member in the sorted set "mySortedSet" has been updated. + * ``` + */ + public geoadd( + key: string, + membersToGeospatialData: Map, + options?: GeoAddOptions, + ): Promise { + return this.createWritePromise( + createGeoAdd(key, membersToGeospatialData, options), + ); + } + /** * @internal */ diff --git a/node/src/Commands.ts b/node/src/Commands.ts index 0c61c11070..6aa8e75172 100644 --- a/node/src/Commands.ts +++ b/node/src/Commands.ts @@ -7,6 +7,8 @@ import Long from "long"; import { LPosOptions } from "./command-options/LPosOptions"; import { command_request } from "./ProtobufMessage"; +import { GeospatialData } from "./command-options/geospatial/GeospatialData"; +import { GeoAddOptions } from "./command-options/geospatial/GeoAddOptions"; import RequestType = command_request.RequestType; @@ -1752,3 +1754,24 @@ export function createLPos( export function createDBSize(): command_request.Command { return createCommand(RequestType.DBSize, []); } + +/** + * @internal + */ +export function createGeoAdd( + key: string, + membersToGeospatialData: Map, + options?: GeoAddOptions, +): command_request.Command { + let args: string[] = [key]; + membersToGeospatialData.forEach((coord, member) => { + args.concat(coord.toArgs()); + args.push(member); + }); + + if (options) { + args = args.concat(options.toArgs()); + } + + return createCommand(RequestType.GeoAdd, args); +} diff --git a/node/src/Transaction.ts b/node/src/Transaction.ts index 866163c636..ed2f90e154 100644 --- a/node/src/Transaction.ts +++ b/node/src/Transaction.ts @@ -127,9 +127,12 @@ import { createZRemRangeByRank, createZRemRangeByScore, createZScore, + createGeoAdd, createFunctionLoad, } from "./Commands"; import { command_request } from "./ProtobufMessage"; +import { GeoAddOptions } from "./command-options/geospatial/GeoAddOptions"; +import { GeospatialData } from "./command-options/geospatial/GeospatialData"; /** * Base class encompassing shared commands for both standalone and cluster mode implementations in a transaction. @@ -1797,6 +1800,32 @@ export class BaseTransaction> { public dbsize(): T { return this.addAndReturn(createDBSize()); } + + /** + * Adds geospatial members with their positions to the specified sorted set stored at `key`.` + * If a member is already a part of the sorted set, its position is updated. + * + * See https://valkey.io/commands/geoadd/ for more details. + * + * @param key - The key of the sorted set. + * @param membersToGeospatialData - A mapping of member names to their corresponding positions - see + * {@link GeospatialData}. The command will report an error when the user attempts to index + * coordinates outside the specified ranges. + * @param options - The GeoAdd options - see {@link GeoAddOptions}. + * + * Command Response - The number of elements added to the sorted set. If `changed` is set to + * `true` in the options, returns the number of elements updated in the sorted set. + * ``` + */ + public geoadd( + key: string, + membersToGeospatialData: Map, + options?: GeoAddOptions, + ): T { + return this.addAndReturn( + createGeoAdd(key, membersToGeospatialData, options), + ); + } } /** diff --git a/node/src/command-options/ConditionalChange.ts b/node/src/command-options/ConditionalChange.ts new file mode 100644 index 0000000000..36f12dc5ce --- /dev/null +++ b/node/src/command-options/ConditionalChange.ts @@ -0,0 +1,19 @@ +/** + * Copyright Valkey GLIDE Project Contributors - SPDX Identifier: Apache-2.0 + */ + +/** + * + * An optional condition to the 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", +} diff --git a/node/src/command-options/geospatial/GeoAddOptions.ts b/node/src/command-options/geospatial/GeoAddOptions.ts new file mode 100644 index 0000000000..64addb3cf0 --- /dev/null +++ b/node/src/command-options/geospatial/GeoAddOptions.ts @@ -0,0 +1,55 @@ +/** + * Copyright Valkey GLIDE Project Contributors - SPDX Identifier: Apache-2.0 + */ + +import { ConditionalChange } from "../ConditionalChange"; + +/** + * Optional arguments for the GeoAdd command. + * + * See https://valkey.io/commands/geoadd/ for more details. + */ +export class GeoAddOptions { + /** Redis API keyword use to modify the return value from the number of new elements added, to the total number of elements changed. */ + public static CHANGED_VALKEY_API = "CH"; + + private updateMode?: ConditionalChange; + + private changed?: boolean; + + /** + * + * default constructor for GeoAddOptions. + * + * @param updateMode - Options for handling existing members. See {@link ConditionalChange}. + * @param latitude - If `true`, returns the count of changed elements instead of new elements added. + */ + constructor(options: { + updateMode?: ConditionalChange; + changed?: boolean; + }) { + this.updateMode = options.updateMode; + this.changed = options.changed; + } + + /** + * + * Converts GeoAddOptions into a string[]. + * + * @returns string[] + */ + public toArgs(): string[] { + const args: string[] = []; + + if (this.updateMode !== undefined) { + args.push(this.updateMode.toString()); + } + + if (this.changed !== undefined) { + args.push(GeoAddOptions.CHANGED_VALKEY_API); + args.push(this.changed.toString()); + } + + return args; + } +} diff --git a/node/src/command-options/geospatial/GeospatialData.ts b/node/src/command-options/geospatial/GeospatialData.ts new file mode 100644 index 0000000000..545abc14c5 --- /dev/null +++ b/node/src/command-options/geospatial/GeospatialData.ts @@ -0,0 +1,39 @@ +/** + * Copyright Valkey GLIDE Project Contributors - SPDX Identifier: Apache-2.0 + */ + +/** + * 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 class GeospatialData { + private longitude: number; + + private latitude: number; + + /** + * + * default constructor for GeospatialData. + * + * @param longitude - The longitude coordinate. + * @param latitude - The latitude coordinate. + */ + constructor(coord: { longitude: number; latitude: number }) { + this.longitude = coord.longitude; + this.latitude = coord.latitude; + } + + /** + * + * Converts GeospatialData into a string[]. + * + * @returns string[] + */ + public toArgs(): string[] { + return [this.longitude.toString(), this.latitude.toString()]; + } +}