Skip to content

Commit

Permalink
Node: Add GEOPOS command. (#1991)
Browse files Browse the repository at this point in the history
* Add `GEOPOS` command.

Signed-off-by: Yury-Fridlyand <yury.fridlyand@improving.com>
  • Loading branch information
Yury-Fridlyand authored Jul 23, 2024
1 parent 0438afe commit 52a7ed6
Show file tree
Hide file tree
Showing 6 changed files with 93 additions and 7 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#### Changes
* Node: Added GEOPOS command ([#1991](https://github.com/valkey-io/valkey-glide/pull/1991))
* Node: Added BITCOUNT command ([#1982](https://github.com/valkey-io/valkey-glide/pull/1982))
* Node: Added FLUSHDB command ([#1986](https://github.com/valkey-io/valkey-glide/pull/1986))
* Node: Added GETDEL command ([#1968](https://github.com/valkey-io/valkey-glide/pull/1968))
Expand Down
38 changes: 34 additions & 4 deletions node/src/BaseClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import {
createExpire,
createExpireAt,
createGeoAdd,
createGeoPos,
createGet,
createGetBit,
createGetDel,
Expand Down Expand Up @@ -84,7 +85,6 @@ import {
createSCard,
createSDiff,
createSDiffStore,
createSetBit,
createSInter,
createSInterCard,
createSInterStore,
Expand All @@ -97,6 +97,7 @@ import {
createSUnion,
createSUnionStore,
createSet,
createSetBit,
createStrlen,
createTTL,
createType,
Expand Down Expand Up @@ -128,6 +129,8 @@ import {
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,
Expand All @@ -146,8 +149,6 @@ import {
connection_request,
response,
} from "./ProtobufMessage";
import { GeospatialData } from "./commands/geospatial/GeospatialData";
import { GeoAddOptions } from "./commands/geospatial/GeoAddOptions";

/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
type PromiseFunction = (value?: any) => void;
Expand Down Expand Up @@ -3397,7 +3398,7 @@ export class BaseClient {
* @example
* ```typescript
* const options = new GeoAddOptions({updateMode: ConditionalChange.ONLY_IF_EXISTS, changed: true});
* const num = await client.geoadd("mySortedSet", {"Palermo", new GeospatialData(13.361389, 38.115556)}, options);
* const num = await client.geoadd("mySortedSet", new Map([["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.
* ```
*/
Expand All @@ -3411,6 +3412,35 @@ export class BaseClient {
);
}

/**
* Returns the positions (longitude, latitude) of all the specified `members` of the
* geospatial index represented by the sorted set at `key`.
*
* See https://valkey.io/commands/geopos for more details.
*
* @param key - The key of the sorted set.
* @param members - The members for which to get the positions.
* @returns A 2D `Array` which represents positions (longitude and latitude) corresponding to the
* given members. The order of the returned positions matches the order of the input members.
* If a member does not exist, its position will be `null`.
*
* @example
* ```typescript
* const data = new Map([["Palermo", new GeospatialData(13.361389, 38.115556)], ["Catania", new GeospatialData(15.087269, 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
* // returned might not be exactly the same as the input values
* console.log(result); // Output: [[13.36138933897018433, 38.11555639549629859], [15.08726745843887329, 37.50266842333162032], null]
* ```
*/
public geopos(
key: string,
members: string[],
): Promise<(number[] | null)[]> {
return this.createWritePromise(createGeoPos(key, members));
}

/**
* Pops a member-score pair from the first non-empty sorted set, with the given `keys`
* being checked in the order they are provided.
Expand Down
11 changes: 10 additions & 1 deletion node/src/Commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1854,10 +1854,19 @@ export function createGeoAdd(
args = args.concat(coord.toArgs());
args.push(member);
});

return createCommand(RequestType.GeoAdd, args);
}

/**
* @internal
*/
export function createGeoPos(
key: string,
members: string[],
): command_request.Command {
return createCommand(RequestType.GeoPos, [key].concat(members));
}

/**
* @internal
*/
Expand Down
20 changes: 19 additions & 1 deletion node/src/Transaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import {
createFunctionFlush,
createFunctionLoad,
createGeoAdd,
createGeoPos,
createGet,
createGetBit,
createGetDel,
Expand Down Expand Up @@ -126,6 +127,7 @@ import {
createZDiffWithScores,
createZInterCard,
createZInterstore,
createZMPop,
createZMScore,
createZPopMax,
createZPopMin,
Expand All @@ -137,7 +139,6 @@ import {
createZRemRangeByScore,
createZRevRank,
createZRevRankWithScore,
createZMPop,
createZScore,
} from "./Commands";
import { command_request } from "./ProtobufMessage";
Expand Down Expand Up @@ -1995,6 +1996,23 @@ export class BaseTransaction<T extends BaseTransaction<T>> {
);
}

/**
* Returns the positions (longitude, latitude) of all the specified `members` of the
* geospatial index represented by the sorted set at `key`.
*
* See https://valkey.io/commands/geopos for more details.
*
* @param key - The key of the sorted set.
* @param members - The members for which to get the positions.
*
* Command Response - A 2D `Array` which represents positions (longitude and latitude) corresponding to the
* given members. The order of the returned positions matches the order of the input members.
* If a member does not exist, its position will be `null`.
*/
public geopos(key: string, members: string[]): T {
return this.addAndReturn(createGeoPos(key, members));
}

/**
* Pops a member-score pair from the first non-empty sorted set, with the given `keys`
* being checked in the order they are provided.
Expand Down
25 changes: 24 additions & 1 deletion node/tests/SharedTests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4434,7 +4434,7 @@ export function runBaseTests<Context>(config: {
);

it.each([ProtocolVersion.RESP2, ProtocolVersion.RESP3])(
`geoadd test_%p`,
`geoadd geopos test_%p`,
async (protocol) => {
await runTest(async (client: BaseClient) => {
const key1 = uuidv4();
Expand All @@ -4452,6 +4452,28 @@ export function runBaseTests<Context>(config: {
// default geoadd
expect(await client.geoadd(key1, membersToCoordinates)).toBe(2);

let geopos = await client.geopos(key1, [
"Palermo",
"Catania",
"New York",
]);
// inner array is possibly null, we need a null check or a cast
expect(geopos[0]?.[0]).toBeCloseTo(13.361389, 5);
expect(geopos[0]?.[1]).toBeCloseTo(38.115556, 5);
expect(geopos[1]?.[0]).toBeCloseTo(15.087269, 5);
expect(geopos[1]?.[1]).toBeCloseTo(37.502669, 5);
expect(geopos[2]).toBeNull();

// empty array of places
geopos = await client.geopos(key1, []);
expect(geopos).toEqual([]);

// not existing key
geopos = await client.geopos(key2, []);
expect(geopos).toEqual([]);
geopos = await client.geopos(key2, ["Palermo"]);
expect(geopos).toEqual([null]);

// with update mode options
membersToCoordinates.set(
"Catania",
Expand Down Expand Up @@ -4499,6 +4521,7 @@ export function runBaseTests<Context>(config: {
await expect(
client.geoadd(key2, membersToCoordinates),
).rejects.toThrow();
await expect(client.geopos(key2, ["*_*"])).rejects.toThrow();
}, protocol);
},
config.timeout,
Expand Down
5 changes: 5 additions & 0 deletions node/tests/TestUtilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -667,6 +667,11 @@ export async function transactionTest(
]),
);
args.push(2);
baseTransaction.geopos(key18, ["Palermo", "Catania"]);
args.push([
[13.36138933897018433, 38.11555639549629859],
[15.08726745843887329, 37.50266842333162032],
]);

const libName = "mylib1C" + uuidv4().replaceAll("-", "");
const funcName = "myfunc1c" + uuidv4().replaceAll("-", "");
Expand Down

0 comments on commit 52a7ed6

Please sign in to comment.