From e5fc2c03a9b06b8ab6b14b240377d5b1d528227d Mon Sep 17 00:00:00 2001 From: ogzhanolguncu Date: Wed, 18 Oct 2023 18:30:49 +0300 Subject: [PATCH 1/5] Add GEOPOS command to sdk --- pkg/commands/geo_pos.ts | 39 ++++++++++++++++++++++++++++ pkg/commands/geo_pos_test.ts | 50 ++++++++++++++++++++++++++++++++++++ pkg/commands/mod.ts | 1 + pkg/pipeline.ts | 7 +++++ pkg/redis.ts | 17 ++++++++---- 5 files changed, 109 insertions(+), 5 deletions(-) create mode 100644 pkg/commands/geo_pos.ts create mode 100644 pkg/commands/geo_pos_test.ts diff --git a/pkg/commands/geo_pos.ts b/pkg/commands/geo_pos.ts new file mode 100644 index 00000000..17b1f716 --- /dev/null +++ b/pkg/commands/geo_pos.ts @@ -0,0 +1,39 @@ +import { Command, CommandOptions } from "./command.ts"; + +type Coordinates = { + lng: string; + lat: string; +}; + +/** + * @see https://redis.io/commands/geopos + */ +export class GeoPosCommand extends Command< + (string | null)[][], + TData +> { + constructor( + cmd: [string, ...string[]] | [string, string[]], + opts?: CommandOptions<(string | null)[][], TData> + ) { + const [key] = cmd; + // Check if the second argument is an array of strings (members). + // If it is, use it directly; if not, it means the members were passed individually, + // so we slice the cmd from the second element onwards to get the members. + const members = Array.isArray(cmd[1]) ? cmd[1] : cmd.slice(1); + + super(["GEOPOS", key, ...members], { + deserialize: (result) => transform(result), + ...opts, + }); + } +} + +function transform(result: (string | null)[][]): TData { + const final: Coordinates[] = []; + for (const pos of result) { + if (!pos?.[0] || !pos?.[1]) continue; + final.push({ lng: pos[0], lat: pos[1] }); + } + return final as TData; +} diff --git a/pkg/commands/geo_pos_test.ts b/pkg/commands/geo_pos_test.ts new file mode 100644 index 00000000..7ed14334 --- /dev/null +++ b/pkg/commands/geo_pos_test.ts @@ -0,0 +1,50 @@ +import { + assertEquals, + assertStrictEquals, +} from "https://deno.land/std@0.177.0/testing/asserts.ts"; +import { newHttpClient } from "../test-utils.ts"; +import { GeoAddCommand } from "./geo_add.ts"; +import { GeoPosCommand } from "./geo_pos.ts"; + +const client = newHttpClient(); + +Deno.test("should swallow non-existing member and return only the valid ones", async () => { + const key = "Sicily"; + const members = ["Palermo", "Catania", "Marsala"]; + await new GeoAddCommand([ + key, + { longitude: 13.361389, latitude: 38.115556, member: members[0] }, + { longitude: 15.087269, latitude: 37.502669, member: members[1] }, + { longitude: 12.4372, latitude: 37.7981, member: members[2] }, + ]).exec(client); + const response = await new GeoPosCommand([key, [...members, "FooBar"]]).exec( + client, + ); + assertEquals(response.length, 3); +}); + +Deno.test("should return three valid positions", async () => { + const key = "Sicily"; + const members = ["Palermo", "Catania", "Marsala"]; + await new GeoAddCommand([ + key, + { longitude: 13.361389, latitude: 38.115556, member: members[0] }, + { longitude: 15.087269, latitude: 37.502669, member: members[1] }, + { longitude: 12.4372, latitude: 37.7981, member: members[2] }, + ]).exec(client); + + const response = await new GeoPosCommand([key, members]).exec(client); + + assertEquals(response.every(Boolean), true); +}); + +Deno.test("should return empty array due to null value FooBar", async () => { + const key = "Sicily"; + const members = ["Palermo"]; + await new GeoAddCommand([ + key, + { longitude: 13.361389, latitude: 38.115556, member: members[0] }, + ]).exec(client); + const response = await new GeoPosCommand([key, "FooBar"]).exec(client); + assertEquals(response, []); +}); diff --git a/pkg/commands/mod.ts b/pkg/commands/mod.ts index 10dbaa00..a912fa0f 100644 --- a/pkg/commands/mod.ts +++ b/pkg/commands/mod.ts @@ -16,6 +16,7 @@ export * from "./expireat.ts"; export * from "./flushall.ts"; export * from "./flushdb.ts"; export * from "./geo_add.ts"; +export * from "./geo_pos.ts"; export * from "./get.ts"; export * from "./getbit.ts"; export * from "./getdel.ts"; diff --git a/pkg/pipeline.ts b/pkg/pipeline.ts index 7e46ec14..715d7e98 100644 --- a/pkg/pipeline.ts +++ b/pkg/pipeline.ts @@ -15,6 +15,7 @@ import { ExpireCommand, FlushAllCommand, FlushDBCommand, + GeoPosCommand, GetBitCommand, GetCommand, GetDelCommand, @@ -1152,6 +1153,12 @@ export class Pipeline[] = []> { forget: (...args: CommandArgs) => this.chain(new JsonForgetCommand(args, this.commandOptions)), + /** + * @see https://redis.io/commands/geopos + */ + geopos: (...args: CommandArgs) => + new GeoPosCommand(args, this.commandOptions).exec(this.client), + /** * @see https://redis.io/commands/json.get */ diff --git a/pkg/redis.ts b/pkg/redis.ts index 0d5d1fb4..ea3bb8e6 100644 --- a/pkg/redis.ts +++ b/pkg/redis.ts @@ -17,6 +17,7 @@ import { FlushAllCommand, FlushDBCommand, GeoAddCommand, + GeoPosCommand, GetBitCommand, GetCommand, GetDelCommand, @@ -248,6 +249,12 @@ export class Redis { geoadd: (...args: CommandArgs) => new GeoAddCommand(args, this.opts).exec(this.client), + /** + * @see https://redis.io/commands/geopos + */ + geopos: (...args: CommandArgs) => + new GeoPosCommand(args, this.opts).exec(this.client), + /** * @see https://redis.io/commands/json.get */ @@ -416,7 +423,9 @@ export class Redis { new BitOpCommand( [op as any, destinationKey, sourceKey, ...sourceKeys], this.opts, - ).exec(this.client); + ).exec( + this.client, + ); /** * @see https://redis.io/commands/bitpos @@ -604,10 +613,8 @@ export class Redis { count?: number, withValues?: boolean, ) => - new HRandFieldCommand( - [key, count, withValues] as any, - this.opts, - ).exec(this.client); + new HRandFieldCommand([key, count, withValues] as any, this.opts) + .exec(this.client); /** * @see https://redis.io/commands/hscan From 3895c2d1ef245961bd01b0441631550fa549592b Mon Sep 17 00:00:00 2001 From: chronark Date: Thu, 19 Oct 2023 10:58:44 +0200 Subject: [PATCH 2/5] fix: members are generic --- .../{geo_pos_test.ts => geo_pos.test.ts} | 19 +++++++++++++++++++ pkg/commands/geo_pos.ts | 12 +++++++----- 2 files changed, 26 insertions(+), 5 deletions(-) rename pkg/commands/{geo_pos_test.ts => geo_pos.test.ts} (74%) diff --git a/pkg/commands/geo_pos_test.ts b/pkg/commands/geo_pos.test.ts similarity index 74% rename from pkg/commands/geo_pos_test.ts rename to pkg/commands/geo_pos.test.ts index 7ed14334..c01efb71 100644 --- a/pkg/commands/geo_pos_test.ts +++ b/pkg/commands/geo_pos.test.ts @@ -48,3 +48,22 @@ Deno.test("should return empty array due to null value FooBar", async () => { const response = await new GeoPosCommand([key, "FooBar"]).exec(client); assertEquals(response, []); }); + +Deno.test("should work with object members", async () => { + const key = "Sicily"; + const members = [ + { name: "Palermo" }, + { name: "Catania" }, + { name: "Marsala" }, + ]; + await new GeoAddCommand([ + key, + { longitude: 13.361389, latitude: 38.115556, member: members[0] }, + { longitude: 15.087269, latitude: 37.502669, member: members[1] }, + { longitude: 12.4372, latitude: 37.7981, member: members[2] }, + ]).exec(client); + const response = await new GeoPosCommand([key, [...members, "FooBar"]]).exec( + client, + ); + assertEquals(response.length, 3); +}); diff --git a/pkg/commands/geo_pos.ts b/pkg/commands/geo_pos.ts index 17b1f716..3f64fd06 100644 --- a/pkg/commands/geo_pos.ts +++ b/pkg/commands/geo_pos.ts @@ -8,13 +8,13 @@ type Coordinates = { /** * @see https://redis.io/commands/geopos */ -export class GeoPosCommand extends Command< +export class GeoPosCommand extends Command< (string | null)[][], - TData + Coordinates[] > { constructor( - cmd: [string, ...string[]] | [string, string[]], - opts?: CommandOptions<(string | null)[][], TData> + cmd: [string, ...TMember[] | TMember[]], + opts?: CommandOptions<(string | null)[][], Coordinates[]>, ) { const [key] = cmd; // Check if the second argument is an array of strings (members). @@ -29,7 +29,9 @@ export class GeoPosCommand extends Command< } } -function transform(result: (string | null)[][]): TData { +function transform( + result: (string | null)[][], +): TData { const final: Coordinates[] = []; for (const pos of result) { if (!pos?.[0] || !pos?.[1]) continue; From 0235c4c90b707ba2cef876f9a2914b82835a63c7 Mon Sep 17 00:00:00 2001 From: ogzhanolguncu Date: Thu, 19 Oct 2023 16:31:28 +0300 Subject: [PATCH 3/5] Remove generics from transform function --- pkg/commands/geo_pos.test.ts | 19 ++++--------------- pkg/commands/geo_pos.ts | 15 +++++---------- 2 files changed, 9 insertions(+), 25 deletions(-) diff --git a/pkg/commands/geo_pos.test.ts b/pkg/commands/geo_pos.test.ts index c01efb71..734961c2 100644 --- a/pkg/commands/geo_pos.test.ts +++ b/pkg/commands/geo_pos.test.ts @@ -1,7 +1,4 @@ -import { - assertEquals, - assertStrictEquals, -} from "https://deno.land/std@0.177.0/testing/asserts.ts"; +import { assertEquals } from "https://deno.land/std@0.177.0/testing/asserts.ts"; import { newHttpClient } from "../test-utils.ts"; import { GeoAddCommand } from "./geo_add.ts"; import { GeoPosCommand } from "./geo_pos.ts"; @@ -17,9 +14,7 @@ Deno.test("should swallow non-existing member and return only the valid ones", a { longitude: 15.087269, latitude: 37.502669, member: members[1] }, { longitude: 12.4372, latitude: 37.7981, member: members[2] }, ]).exec(client); - const response = await new GeoPosCommand([key, [...members, "FooBar"]]).exec( - client, - ); + const response = await new GeoPosCommand([key, [...members, "FooBar"]]).exec(client); assertEquals(response.length, 3); }); @@ -51,19 +46,13 @@ Deno.test("should return empty array due to null value FooBar", async () => { Deno.test("should work with object members", async () => { const key = "Sicily"; - const members = [ - { name: "Palermo" }, - { name: "Catania" }, - { name: "Marsala" }, - ]; + const members = [{ name: "Palermo" }, { name: "Catania" }, { name: "Marsala" }]; await new GeoAddCommand([ key, { longitude: 13.361389, latitude: 38.115556, member: members[0] }, { longitude: 15.087269, latitude: 37.502669, member: members[1] }, { longitude: 12.4372, latitude: 37.7981, member: members[2] }, ]).exec(client); - const response = await new GeoPosCommand([key, [...members, "FooBar"]]).exec( - client, - ); + const response = await new GeoPosCommand([key, [...members, "FooBar"]]).exec(client); assertEquals(response.length, 3); }); diff --git a/pkg/commands/geo_pos.ts b/pkg/commands/geo_pos.ts index 3f64fd06..bc744078 100644 --- a/pkg/commands/geo_pos.ts +++ b/pkg/commands/geo_pos.ts @@ -8,13 +8,10 @@ type Coordinates = { /** * @see https://redis.io/commands/geopos */ -export class GeoPosCommand extends Command< - (string | null)[][], - Coordinates[] -> { +export class GeoPosCommand extends Command<(string | null)[][], Coordinates[]> { constructor( - cmd: [string, ...TMember[] | TMember[]], - opts?: CommandOptions<(string | null)[][], Coordinates[]>, + cmd: [string, ...(TMember[] | TMember[])], + opts?: CommandOptions<(string | null)[][], Coordinates[]> ) { const [key] = cmd; // Check if the second argument is an array of strings (members). @@ -29,13 +26,11 @@ export class GeoPosCommand extends Command< } } -function transform( - result: (string | null)[][], -): TData { +function transform(result: (string | null)[][]): Coordinates[] { const final: Coordinates[] = []; for (const pos of result) { if (!pos?.[0] || !pos?.[1]) continue; final.push({ lng: pos[0], lat: pos[1] }); } - return final as TData; + return final; } From 61ad4c079682e56fdd9a5b46491cb28448cc03ab Mon Sep 17 00:00:00 2001 From: ogzhanolguncu Date: Thu, 19 Oct 2023 16:41:49 +0300 Subject: [PATCH 4/5] Fix import isse and run formatter --- pkg/commands/geo_pos.test.ts | 12 ++- pkg/commands/geo_pos.ts | 5 +- pkg/pipeline.ts | 139 +++++++++++++++++++++++------------ pkg/redis.ts | 102 +++++++++++++++---------- 4 files changed, 171 insertions(+), 87 deletions(-) diff --git a/pkg/commands/geo_pos.test.ts b/pkg/commands/geo_pos.test.ts index 734961c2..a51ccf4a 100644 --- a/pkg/commands/geo_pos.test.ts +++ b/pkg/commands/geo_pos.test.ts @@ -14,7 +14,9 @@ Deno.test("should swallow non-existing member and return only the valid ones", a { longitude: 15.087269, latitude: 37.502669, member: members[1] }, { longitude: 12.4372, latitude: 37.7981, member: members[2] }, ]).exec(client); - const response = await new GeoPosCommand([key, [...members, "FooBar"]]).exec(client); + const response = await new GeoPosCommand([key, [...members, "FooBar"]]).exec( + client, + ); assertEquals(response.length, 3); }); @@ -46,13 +48,17 @@ Deno.test("should return empty array due to null value FooBar", async () => { Deno.test("should work with object members", async () => { const key = "Sicily"; - const members = [{ name: "Palermo" }, { name: "Catania" }, { name: "Marsala" }]; + const members = [{ name: "Palermo" }, { name: "Catania" }, { + name: "Marsala", + }]; await new GeoAddCommand([ key, { longitude: 13.361389, latitude: 38.115556, member: members[0] }, { longitude: 15.087269, latitude: 37.502669, member: members[1] }, { longitude: 12.4372, latitude: 37.7981, member: members[2] }, ]).exec(client); - const response = await new GeoPosCommand([key, [...members, "FooBar"]]).exec(client); + const response = await new GeoPosCommand([key, [...members, "FooBar"]]).exec( + client, + ); assertEquals(response.length, 3); }); diff --git a/pkg/commands/geo_pos.ts b/pkg/commands/geo_pos.ts index bc744078..d0f1d609 100644 --- a/pkg/commands/geo_pos.ts +++ b/pkg/commands/geo_pos.ts @@ -8,10 +8,11 @@ type Coordinates = { /** * @see https://redis.io/commands/geopos */ -export class GeoPosCommand extends Command<(string | null)[][], Coordinates[]> { +export class GeoPosCommand + extends Command<(string | null)[][], Coordinates[]> { constructor( cmd: [string, ...(TMember[] | TMember[])], - opts?: CommandOptions<(string | null)[][], Coordinates[]> + opts?: CommandOptions<(string | null)[][], Coordinates[]>, ) { const [key] = cmd; // Check if the second argument is an array of strings (members). diff --git a/pkg/pipeline.ts b/pkg/pipeline.ts index e5ccd015..761e1bfb 100644 --- a/pkg/pipeline.ts +++ b/pkg/pipeline.ts @@ -15,8 +15,9 @@ import { ExpireCommand, FlushAllCommand, FlushDBCommand, - GeoPosCommand, GeoAddCommand, + GeoDistCommand, + GeoPosCommand, GetBitCommand, GetCommand, GetDelCommand, @@ -154,7 +155,6 @@ import { CommandArgs } from "./types.ts"; import { ZMScoreCommand } from "./commands/zmscore.ts"; import { HRandFieldCommand } from "./commands/hrandfield.ts"; import { ZDiffStoreCommand } from "./commands/zdiffstore.ts"; -import { GeoDistCommand } from "./commands/geo_dist.ts"; // Given a tuple of commands, returns a tuple of the response data of each command type InferResponseData = { @@ -229,9 +229,8 @@ export class Pipeline[] = []> { * ``` */ exec = async < - TCommandResults extends unknown[] = [] extends TCommands - ? unknown[] - : InferResponseData + TCommandResults extends unknown[] = [] extends TCommands ? unknown[] + : InferResponseData, >(): Promise => { if (this.commands.length === 0) { throw new Error("Pipeline is empty"); @@ -245,7 +244,9 @@ export class Pipeline[] = []> { return res.map(({ error, result }, i) => { if (error) { throw new UpstashError( - `Command ${i + 1} [ ${this.commands[i].command[0]} ] failed: ${error}` + `Command ${i + 1} [ ${ + this.commands[i].command[0] + } ] failed: ${error}`, ); } @@ -264,7 +265,9 @@ export class Pipeline[] = []> { * Pushes a command into the pipeline and returns a chainable instance of the * pipeline */ - private chain(command: Command): Pipeline<[...TCommands, Command]> { + private chain( + command: Command, + ): Pipeline<[...TCommands, Command]> { this.commands.push(command); return this as any; // TS thinks we're returning Pipeline<[]> here, because we're not creating a new instance of the class, hence the cast } @@ -291,7 +294,11 @@ export class Pipeline[] = []> { sourceKey: string, ...sourceKeys: string[] ): Pipeline<[...TCommands, BitOpCommand]>; - (op: "not", destinationKey: string, sourceKey: string): Pipeline<[...TCommands, BitOpCommand]>; + ( + op: "not", + destinationKey: string, + sourceKey: string, + ): Pipeline<[...TCommands, BitOpCommand]>; } = ( op: "and" | "or" | "xor" | "not", destinationKey: string, @@ -299,7 +306,10 @@ export class Pipeline[] = []> { ...sourceKeys: string[] ) => this.chain( - new BitOpCommand([op as any, destinationKey, sourceKey, ...sourceKeys], this.commandOptions) + new BitOpCommand( + [op as any, destinationKey, sourceKey, ...sourceKeys], + this.commandOptions, + ), ); /** @@ -436,8 +446,9 @@ export class Pipeline[] = []> { /** * @see https://redis.io/commands/hgetall */ - hgetall = >(...args: CommandArgs) => - this.chain(new HGetAllCommand(args, this.commandOptions)); + hgetall = >( + ...args: CommandArgs + ) => this.chain(new HGetAllCommand(args, this.commandOptions)); /** * @see https://redis.io/commands/hincrby @@ -466,8 +477,9 @@ export class Pipeline[] = []> { /** * @see https://redis.io/commands/hmget */ - hmget = >(...args: CommandArgs) => - this.chain(new HMGetCommand(args, this.commandOptions)); + hmget = >( + ...args: CommandArgs + ) => this.chain(new HMGetCommand(args, this.commandOptions)); /** * @see https://redis.io/commands/hmset @@ -481,9 +493,14 @@ export class Pipeline[] = []> { hrandfield = >( key: string, count?: number, - withValues?: boolean + withValues?: boolean, ) => - this.chain(new HRandFieldCommand([key, count, withValues] as any, this.commandOptions)); + this.chain( + new HRandFieldCommand( + [key, count, withValues] as any, + this.commandOptions, + ), + ); /** * @see https://redis.io/commands/hscan @@ -501,7 +518,9 @@ export class Pipeline[] = []> { * @see https://redis.io/commands/hsetnx */ hsetnx = (key: string, field: string, value: TData) => - this.chain(new HSetNXCommand([key, field, value], this.commandOptions)); + this.chain( + new HSetNXCommand([key, field, value], this.commandOptions), + ); /** * @see https://redis.io/commands/hstrlen @@ -548,8 +567,18 @@ export class Pipeline[] = []> { /** * @see https://redis.io/commands/linsert */ - linsert = (key: string, direction: "before" | "after", pivot: TData, value: TData) => - this.chain(new LInsertCommand([key, direction, pivot, value], this.commandOptions)); + linsert = ( + key: string, + direction: "before" | "after", + pivot: TData, + value: TData, + ) => + this.chain( + new LInsertCommand( + [key, direction, pivot, value], + this.commandOptions, + ), + ); /** * @see https://redis.io/commands/llen @@ -579,13 +608,17 @@ export class Pipeline[] = []> { * @see https://redis.io/commands/lpush */ lpush = (key: string, ...elements: TData[]) => - this.chain(new LPushCommand([key, ...elements], this.commandOptions)); + this.chain( + new LPushCommand([key, ...elements], this.commandOptions), + ); /** * @see https://redis.io/commands/lpushx */ lpushx = (key: string, ...elements: TData[]) => - this.chain(new LPushXCommand([key, ...elements], this.commandOptions)); + this.chain( + new LPushXCommand([key, ...elements], this.commandOptions), + ); /** * @see https://redis.io/commands/lrange @@ -657,7 +690,9 @@ export class Pipeline[] = []> { * @see https://redis.io/commands/psetex */ psetex = (key: string, ttl: number, value: TData) => - this.chain(new PSetEXCommand([key, ttl, value], this.commandOptions)); + this.chain( + new PSetEXCommand([key, ttl, value], this.commandOptions), + ); /** * @see https://redis.io/commands/pttl @@ -804,20 +839,28 @@ export class Pipeline[] = []> { /** * @see https://redis.io/commands/smembers */ - smembers = (...args: CommandArgs) => - this.chain(new SMembersCommand(args, this.commandOptions)); + smembers = ( + ...args: CommandArgs + ) => this.chain(new SMembersCommand(args, this.commandOptions)); /** * @see https://redis.io/commands/smismember */ smismember = (key: string, members: TMembers) => - this.chain(new SMIsMemberCommand([key, members], this.commandOptions)); + this.chain( + new SMIsMemberCommand([key, members], this.commandOptions), + ); /** * @see https://redis.io/commands/smove */ smove = (source: string, destination: string, member: TData) => - this.chain(new SMoveCommand([source, destination, member], this.commandOptions)); + this.chain( + new SMoveCommand( + [source, destination, member], + this.commandOptions, + ), + ); /** * @see https://redis.io/commands/spop @@ -895,27 +938,31 @@ export class Pipeline[] = []> { */ zadd = ( ...args: - | [key: string, scoreMember: ScoreMember, ...scoreMemberPairs: ScoreMember[]] | [ - key: string, - opts: ZAddCommandOptions | ZAddCommandOptionsWithIncr, - ...scoreMemberPairs: [ScoreMember, ...ScoreMember[]] - ] + key: string, + scoreMember: ScoreMember, + ...scoreMemberPairs: ScoreMember[], + ] + | [ + key: string, + opts: ZAddCommandOptions | ZAddCommandOptionsWithIncr, + ...scoreMemberPairs: [ScoreMember, ...ScoreMember[]], + ] ) => { if ("score" in args[1]) { return this.chain( new ZAddCommand( [args[0], args[1] as ScoreMember, ...(args.slice(2) as any)], - this.commandOptions - ) + this.commandOptions, + ), ); } return this.chain( new ZAddCommand( [args[0], args[1] as any, ...(args.slice(2) as any)], - this.commandOptions - ) + this.commandOptions, + ), ); }; @@ -935,7 +982,9 @@ export class Pipeline[] = []> { * @see https://redis.io/commands/zincrby */ zincrby = (key: string, increment: number, member: TData) => - this.chain(new ZIncrByCommand([key, increment, member], this.commandOptions)); + this.chain( + new ZIncrByCommand([key, increment, member], this.commandOptions), + ); /** * @see https://redis.io/commands/zinterstore @@ -974,17 +1023,17 @@ export class Pipeline[] = []> { ...args: | [key: string, min: number, max: number, opts?: ZRangeCommandOptions] | [ - key: string, - min: `(${string}` | `[${string}` | "-" | "+", - max: `(${string}` | `[${string}` | "-" | "+", - opts: { byLex: true } & ZRangeCommandOptions - ] + key: string, + min: `(${string}` | `[${string}` | "-" | "+", + max: `(${string}` | `[${string}` | "-" | "+", + opts: { byLex: true } & ZRangeCommandOptions, + ] | [ - key: string, - min: number | `(${number}` | "-inf" | "+inf", - max: number | `(${number}` | "-inf" | "+inf", - opts: { byScore: true } & ZRangeCommandOptions - ] + key: string, + min: number | `(${number}` | "-inf" | "+inf", + max: number | `(${number}` | "-inf" | "+inf", + opts: { byScore: true } & ZRangeCommandOptions, + ] ) => this.chain(new ZRangeCommand(args as any, this.commandOptions)); /** diff --git a/pkg/redis.ts b/pkg/redis.ts index c6192d81..247c7853 100644 --- a/pkg/redis.ts +++ b/pkg/redis.ts @@ -17,8 +17,8 @@ import { FlushAllCommand, FlushDBCommand, GeoAddCommand, - GeoPosCommand, GeoDistCommand, + GeoPosCommand, GetBitCommand, GetCommand, GetDelCommand, @@ -341,11 +341,14 @@ export class Redis { use = ( middleware: ( r: UpstashRequest, - next: (req: UpstashRequest) => Promise> - ) => Promise> + next: ( + req: UpstashRequest, + ) => Promise>, + ) => Promise>, ) => { const makeRequest = this.client.request.bind(this.client); - this.client.request = (req: UpstashRequest) => middleware(req, makeRequest) as any; + this.client.request = (req: UpstashRequest) => + middleware(req, makeRequest) as any; }; /** @@ -424,8 +427,11 @@ export class Redis { sourceKey: string, ...sourceKeys: string[] ) => - new BitOpCommand([op as any, destinationKey, sourceKey, ...sourceKeys], this.opts).exec( - this.client + new BitOpCommand( + [op as any, destinationKey, sourceKey, ...sourceKeys], + this.opts, + ).exec( + this.client, ); /** @@ -557,8 +563,9 @@ export class Redis { /** * @see https://redis.io/commands/hgetall */ - hgetall = >(...args: CommandArgs) => - new HGetAllCommand(args, this.opts).exec(this.client); + hgetall = >( + ...args: CommandArgs + ) => new HGetAllCommand(args, this.opts).exec(this.client); /** * @see https://redis.io/commands/hincrby @@ -587,8 +594,9 @@ export class Redis { /** * @see https://redis.io/commands/hmget */ - hmget = >(...args: CommandArgs) => - new HMGetCommand(args, this.opts).exec(this.client); + hmget = >( + ...args: CommandArgs + ) => new HMGetCommand(args, this.opts).exec(this.client); /** * @see https://redis.io/commands/hmset @@ -605,13 +613,15 @@ export class Redis { >( key: string, count: number, - withValues: boolean + withValues: boolean, ): Promise>; } = >( key: string, count?: number, - withValues?: boolean - ) => new HRandFieldCommand([key, count, withValues] as any, this.opts).exec(this.client); + withValues?: boolean, + ) => + new HRandFieldCommand([key, count, withValues] as any, this.opts) + .exec(this.client); /** * @see https://redis.io/commands/hscan @@ -676,8 +686,15 @@ export class Redis { /** * @see https://redis.io/commands/linsert */ - linsert = (key: string, direction: "before" | "after", pivot: TData, value: TData) => - new LInsertCommand([key, direction, pivot, value], this.opts).exec(this.client); + linsert = ( + key: string, + direction: "before" | "after", + pivot: TData, + value: TData, + ) => + new LInsertCommand([key, direction, pivot, value], this.opts).exec( + this.client, + ); /** * @see https://redis.io/commands/llen @@ -933,19 +950,24 @@ export class Redis { * @see https://redis.io/commands/smismember */ smismember = (key: string, members: TMembers) => - new SMIsMemberCommand([key, members], this.opts).exec(this.client); + new SMIsMemberCommand([key, members], this.opts).exec( + this.client, + ); /** * @see https://redis.io/commands/smembers */ - smembers = (...args: CommandArgs) => - new SMembersCommand(args, this.opts).exec(this.client); + smembers = ( + ...args: CommandArgs + ) => new SMembersCommand(args, this.opts).exec(this.client); /** * @see https://redis.io/commands/smove */ smove = (source: string, destination: string, member: TData) => - new SMoveCommand([source, destination, member], this.opts).exec(this.client); + new SMoveCommand([source, destination, member], this.opts).exec( + this.client, + ); /** * @see https://redis.io/commands/spop @@ -1035,23 +1057,27 @@ export class Redis { */ zadd = ( ...args: - | [key: string, scoreMember: ScoreMember, ...scoreMemberPairs: ScoreMember[]] | [ - key: string, - opts: ZAddCommandOptions | ZAddCommandOptionsWithIncr, - ...scoreMemberPairs: [ScoreMember, ...ScoreMember[]] - ] + key: string, + scoreMember: ScoreMember, + ...scoreMemberPairs: ScoreMember[], + ] + | [ + key: string, + opts: ZAddCommandOptions | ZAddCommandOptionsWithIncr, + ...scoreMemberPairs: [ScoreMember, ...ScoreMember[]], + ] ) => { if ("score" in args[1]) { return new ZAddCommand( [args[0], args[1] as ScoreMember, ...(args.slice(2) as any)], - this.opts + this.opts, ).exec(this.client); } return new ZAddCommand( [args[0], args[1] as any, ...(args.slice(2) as any)], - this.opts + this.opts, ).exec(this.client); }; /** @@ -1076,7 +1102,9 @@ export class Redis { * @see https://redis.io/commands/zincrby */ zincrby = (key: string, increment: number, member: TData) => - new ZIncrByCommand([key, increment, member], this.opts).exec(this.client); + new ZIncrByCommand([key, increment, member], this.opts).exec( + this.client, + ); /** * @see https://redis.io/commands/zinterstore @@ -1115,17 +1143,17 @@ export class Redis { ...args: | [key: string, min: number, max: number, opts?: ZRangeCommandOptions] | [ - key: string, - min: `(${string}` | `[${string}` | "-" | "+", - max: `(${string}` | `[${string}` | "-" | "+", - opts: { byLex: true } & ZRangeCommandOptions - ] + key: string, + min: `(${string}` | `[${string}` | "-" | "+", + max: `(${string}` | `[${string}` | "-" | "+", + opts: { byLex: true } & ZRangeCommandOptions, + ] | [ - key: string, - min: number | `(${number}` | "-inf" | "+inf", - max: number | `(${number}` | "-inf" | "+inf", - opts: { byScore: true } & ZRangeCommandOptions - ] + key: string, + min: number | `(${number}` | "-inf" | "+inf", + max: number | `(${number}` | "-inf" | "+inf", + opts: { byScore: true } & ZRangeCommandOptions, + ] ) => new ZRangeCommand(args as any, this.opts).exec(this.client); /** From 8eb1beb34bac016feab86ae2b55c94ed73f0f2ae Mon Sep 17 00:00:00 2001 From: ogzhanolguncu Date: Mon, 23 Oct 2023 09:57:02 +0300 Subject: [PATCH 5/5] Return float instead of string from geopos --- pkg/commands/geo_pos.test.ts | 18 +++++++++--------- pkg/commands/geo_pos.ts | 11 +++++------ 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/pkg/commands/geo_pos.test.ts b/pkg/commands/geo_pos.test.ts index a51ccf4a..3b81bb62 100644 --- a/pkg/commands/geo_pos.test.ts +++ b/pkg/commands/geo_pos.test.ts @@ -14,9 +14,7 @@ Deno.test("should swallow non-existing member and return only the valid ones", a { longitude: 15.087269, latitude: 37.502669, member: members[1] }, { longitude: 12.4372, latitude: 37.7981, member: members[2] }, ]).exec(client); - const response = await new GeoPosCommand([key, [...members, "FooBar"]]).exec( - client, - ); + const response = await new GeoPosCommand([key, [...members, "FooBar"]]).exec(client); assertEquals(response.length, 3); }); @@ -48,17 +46,19 @@ Deno.test("should return empty array due to null value FooBar", async () => { Deno.test("should work with object members", async () => { const key = "Sicily"; - const members = [{ name: "Palermo" }, { name: "Catania" }, { - name: "Marsala", - }]; + const members = [ + { name: "Palermo" }, + { name: "Catania" }, + { + name: "Marsala", + }, + ]; await new GeoAddCommand([ key, { longitude: 13.361389, latitude: 38.115556, member: members[0] }, { longitude: 15.087269, latitude: 37.502669, member: members[1] }, { longitude: 12.4372, latitude: 37.7981, member: members[2] }, ]).exec(client); - const response = await new GeoPosCommand([key, [...members, "FooBar"]]).exec( - client, - ); + const response = await new GeoPosCommand([key, [...members, "FooBar"]]).exec(client); assertEquals(response.length, 3); }); diff --git a/pkg/commands/geo_pos.ts b/pkg/commands/geo_pos.ts index d0f1d609..e2b085fd 100644 --- a/pkg/commands/geo_pos.ts +++ b/pkg/commands/geo_pos.ts @@ -1,18 +1,17 @@ import { Command, CommandOptions } from "./command.ts"; type Coordinates = { - lng: string; - lat: string; + lng: number; + lat: number; }; /** * @see https://redis.io/commands/geopos */ -export class GeoPosCommand - extends Command<(string | null)[][], Coordinates[]> { +export class GeoPosCommand extends Command<(string | null)[][], Coordinates[]> { constructor( cmd: [string, ...(TMember[] | TMember[])], - opts?: CommandOptions<(string | null)[][], Coordinates[]>, + opts?: CommandOptions<(string | null)[][], Coordinates[]> ) { const [key] = cmd; // Check if the second argument is an array of strings (members). @@ -31,7 +30,7 @@ function transform(result: (string | null)[][]): Coordinates[] { const final: Coordinates[] = []; for (const pos of result) { if (!pos?.[0] || !pos?.[1]) continue; - final.push({ lng: pos[0], lat: pos[1] }); + final.push({ lng: parseFloat(pos[0]), lat: parseFloat(pos[1]) }); } return final; }