diff --git a/CHANGELOG.md b/CHANGELOG.md index 854f9271e0..e9a14f920d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -113,7 +113,7 @@ * 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)) * Node: Added ZRevRank command ([#1977](https://github.com/valkey-io/valkey-glide/pull/1977)) -* Node: Added GeoUnit command ([#TBD](https://github.com/valkey-io/valkey-glide/pull/TBD)) +* Node: Added GeoUnit command ([#1988](https://github.com/valkey-io/valkey-glide/pull/1988)) #### 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 3464809038..b26e8b2609 100644 --- a/node/src/BaseClient.ts +++ b/node/src/BaseClient.ts @@ -124,6 +124,8 @@ import { createZRevRank, createZRevRankWithScore, createZScore, + createGeoDist, + GeoUnit, } from "./Commands"; import { BitOffsetOptions } from "./commands/BitOffsetOptions"; import { LPosOptions } from "./commands/LPosOptions"; @@ -3416,6 +3418,35 @@ export class BaseClient { ); } + /** + * Returns the distance between `member1` and `member2<` saved in the geospatial index stored at `key`. + * + * See https://valkey.io/commands/geodist/ for more details. + * + * @param key - The key of the sorted set. + * @param member1 - The name of the first member. + * @param member2 - The name of the second member. + * @param geoUnit - The unit of distance measurement - see {@link GeoUnit}. + * @returns The distance between `member1` and `member2`. If one or both members do not exist, + * or if the key does not exist, returns `null`. + * + * @example + * ```typescript + * const result = await client.geodist("mySortedSet", "Place1", "Place2", GeoUnit.KILOMETERS); + * console.log(num); // Output: the distance between Place1 and Place2. + * ``` + */ + public geodist( + key: string, + member1: string, + member2: string, + geoUnit?: GeoUnit, + ): Promise { + return this.createWritePromise( + createGeoDist(key, member1, member2, geoUnit), + ); + } + /** * @internal */ diff --git a/node/src/Commands.ts b/node/src/Commands.ts index b2ebbfe702..9ded033013 100644 --- a/node/src/Commands.ts +++ b/node/src/Commands.ts @@ -1850,6 +1850,23 @@ export function createGeoAdd( return createCommand(RequestType.GeoAdd, args); } +/** + * Enumeration representing distance units options for the {@link geodist} command. + */ +export enum GeoUnit { + /** Represents distance in meters. */ + METERS = "m", + + /** Represents distance in kilometers. */ + KILOMETERS = "km", + + /** Represents distance in miles. */ + MILES = "mi", + + /** Represents distance in feet. */ + FEET = "ft", +} + /** * @internal */ @@ -1857,8 +1874,15 @@ export function createGeoDist( key: string, member1: string, member2: string, + geoUnit?: GeoUnit, ): command_request.Command { - return createCommand(RequestType.GeoAdd, [key, member1, member2]); + let args: string[] = [key, member1, member2]; + + if (geoUnit) { + args.push(geoUnit); + } + + return createCommand(RequestType.GeoDist, args); } /** diff --git a/node/src/Transaction.ts b/node/src/Transaction.ts index 02e859d840..9aa9f6b681 100644 --- a/node/src/Transaction.ts +++ b/node/src/Transaction.ts @@ -137,6 +137,8 @@ import { createZRevRank, createZRevRankWithScore, createZScore, + createGeoDist, + GeoUnit, } from "./Commands"; import { command_request } from "./ProtobufMessage"; import { BitOffsetOptions } from "./commands/BitOffsetOptions"; @@ -2001,6 +2003,28 @@ export class BaseTransaction> { createGeoAdd(key, membersToGeospatialData, options), ); } + + /** + * Returns the distance between `member1` and `member2<` saved in the geospatial index stored at `key`. + * + * See https://valkey.io/commands/geodist/ for more details. + * + * @param key - The key of the sorted set. + * @param member1 - The name of the first member. + * @param member2 - The name of the second member. + * @param geoUnit - The unit of distance measurement - see {@link GeoUnit}. + * + * Command Response - The distance between `member1` and `member2`. If one or both members do not exist, + * or if the key does not exist, returns `null`. + */ + public geodist( + key: string, + member1: string, + member2: string, + geoUnit?: GeoUnit, + ): T { + return this.addAndReturn(createGeoDist(key, member1, member2, geoUnit)); + } } /** diff --git a/node/src/commands/geospatial/GeoUnit.ts b/node/src/commands/geospatial/GeoUnit.ts deleted file mode 100644 index d7bf717692..0000000000 --- a/node/src/commands/geospatial/GeoUnit.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * Copyright Valkey GLIDE Project Contributors - SPDX Identifier: Apache-2.0 - */ - -/** - * Enumeration representing distance units options for the {@link geodist} command. -*/ -export enum GeoUnit { - /** Represents distance in meters. */ - METERS = "m", - - /** Represents distance in kilometers. */ - KILOMETERS = "km", - - /** Represents distance in miles. */ - MILES = "mi", - - /** Represents distance in feet. */ - FEET = "ft", -} diff --git a/node/tests/SharedTests.ts b/node/tests/SharedTests.ts index bab16def9a..909b472c05 100644 --- a/node/tests/SharedTests.ts +++ b/node/tests/SharedTests.ts @@ -16,6 +16,7 @@ import { RequestError, Script, parseInfoResponse, + GeoUnit, } from "../"; import { Client, @@ -4545,6 +4546,61 @@ export function runBaseTests(config: { }, config.timeout, ); + + it.each([ProtocolVersion.RESP2, ProtocolVersion.RESP3])( + `geodist test_%p`, + async (protocol) => { + await runTest(async (client: BaseClient) => { + const key1 = uuidv4(); + const key2 = uuidv4(); + const member1 = "Palermo"; + const member2 = "Catania"; + const nonExistingMember = "NonExisting"; + const expected = 166274.1516; + const expectedKM = 166.2742; + const delta = 1e-9; + + // adding the geo locations + const membersToCoordinates = new Map(); + membersToCoordinates.set( + member1, + new GeospatialData(13.361389, 38.115556), + ); + membersToCoordinates.set( + member2, + new GeospatialData(15.087269, 37.502669), + ); + expect(await client.geoadd(key1, membersToCoordinates)).toBe(2); + + // checking result with default metric + expect( + await client.geodist(key1, member1, member2), + ).toBeCloseTo(expected, delta); + + // checking result with metric specification of kilometers + expect( + await client.geodist( + key1, + member1, + member2, + GeoUnit.KILOMETERS, + ), + ).toBeCloseTo(expectedKM, delta); + + // null result when member index is missing + expect( + await client.geodist(key1, member1, nonExistingMember), + ).toBeNull(); + + // key exists but holds non-ZSET value + expect(await client.set(key2, "geodist")).toBe("OK"); + await expect( + client.geodist(key2, member1, member2), + ).rejects.toThrow(); + }, protocol); + }, + config.timeout, + ); } export function runCommonTests(config: { diff --git a/node/tests/TestUtilities.ts b/node/tests/TestUtilities.ts index bb292de4ee..e1e8a798f8 100644 --- a/node/tests/TestUtilities.ts +++ b/node/tests/TestUtilities.ts @@ -10,6 +10,7 @@ import { BaseClient, BaseClientConfiguration, ClusterTransaction, + GeoUnit, GlideClient, GlideClusterClient, InsertPosition, @@ -662,6 +663,10 @@ export async function transactionTest( ]), ); args.push(2); + baseTransaction.geodist(key18, "Palermo", "Catania"); + args.push(166274.1516); + baseTransaction.geodist(key18, "Palermo", "Catania", GeoUnit.KILOMETERS); + args.push(166.2742); const libName = "mylib1C" + uuidv4().replaceAll("-", ""); const funcName = "myfunc1c" + uuidv4().replaceAll("-", "");