Skip to content

Commit

Permalink
feat: add nx and xx options to ZAddOpts
Browse files Browse the repository at this point in the history
  • Loading branch information
uki00a committed Oct 24, 2024
1 parent 08193b7 commit d92305b
Show file tree
Hide file tree
Showing 5 changed files with 107 additions and 49 deletions.
29 changes: 20 additions & 9 deletions command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -178,9 +178,20 @@ export type SScanOpts = BaseScanOpts;
export type ZScanOpts = BaseScanOpts;

export interface ZAddOpts {
/** @deprecated Use {@linkcode ZAddOpts.nx}/{@linkcode ZAddOpts.xx} instead. This option will be removed in the future. */
mode?: "NX" | "XX";
ch?: boolean;
/** Enables `NX` option */
nx?: boolean;
/** Enables `XX` option */
xx?: boolean;
}
/** Return type for {@linkcode RedisCommands.zadd} */
export type ZAddReply<T extends ZAddOpts> = T extends { mode: "NX" | "XX" }
? Integer | BulkNil
: T extends { nx: true } ? Integer | BulkNil
: T extends { xx: true } ? Integer | BulkNil
: Integer;

interface ZStoreOpts {
aggregate?: "SUM" | "MIN" | "MAX";
Expand Down Expand Up @@ -1006,22 +1017,22 @@ XRANGE somestream - +
timeout: number,
...keys: string[]
): Promise<[BulkString, BulkString, BulkString] | BulkNil>;
zadd(
zadd<TZAddOpts extends ZAddOpts = ZAddOpts>(
key: string,
score: number,
member: RedisValue,
opts?: ZAddOpts,
): Promise<Integer>;
zadd(
opts?: TZAddOpts,
): Promise<ZAddReply<TZAddOpts>>;
zadd<TZAddOpts extends ZAddOpts = ZAddOpts>(
key: string,
score_members: [number, RedisValue][],
opts?: ZAddOpts,
): Promise<Integer>;
zadd(
opts?: TZAddOpts,
): Promise<ZAddReply<TZAddOpts>>;
zadd<TZAddOpts extends ZAddOpts = ZAddOpts>(
key: string,
member_scores: Record<string | number, number>,
opts?: ZAddOpts,
): Promise<Integer>;
opts?: TZAddOpts,
): Promise<ZAddReply<TZAddOpts>>;
zaddIncr(
key: string,
score: number,
Expand Down
1 change: 1 addition & 0 deletions mod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ export type {
StralgoOpts,
StralgoTarget,
ZAddOpts,
ZAddReply,
ZInterOpts,
ZInterstoreOpts,
ZRangeByLexOpts,
Expand Down
1 change: 1 addition & 0 deletions protocol/deno_streams/reply.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ async function readBulkReply(
}

const size = parseSize(line);
console.info("bulkReply", { size, line: new TextDecoder().decode(line) });
if (size < 0) {
// nil bulk reply
return null;
Expand Down
43 changes: 28 additions & 15 deletions redis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import type {
StralgoOpts,
StralgoTarget,
ZAddOpts,
ZAddReply,
ZInterOpts,
ZInterstoreOpts,
ZRangeByLexOpts,
Expand Down Expand Up @@ -2095,55 +2096,67 @@ class RedisImpl implements Redis {
return this.execIntegerReply("XTRIM", key, "MAXLEN", ...args);
}

zadd(
zadd<TZAddOpts extends ZAddOpts = ZAddOpts>(
key: string,
score: number,
member: string,
opts?: ZAddOpts,
): Promise<Integer>;
zadd(
opts?: TZAddOpts,
): Promise<ZAddReply<TZAddOpts>>;
zadd<TZAddOpts extends ZAddOpts = ZAddOpts>(
key: string,
scoreMembers: [number, string][],
opts?: ZAddOpts,
): Promise<Integer>;
zadd(
opts?: TZAddOpts,
): Promise<ZAddReply<TZAddOpts>>;
zadd<TZAddOpts extends ZAddOpts = ZAddOpts>(
key: string,
memberScores: Record<string, number>,
opts?: ZAddOpts,
): Promise<Integer>;
opts?: TZAddOpts,
): Promise<ZAddReply<TZAddOpts>>;
zadd(
key: string,
param1: number | [number, string][] | Record<string, number>,
param2?: string | ZAddOpts,
opts?: ZAddOpts,
) {
const args: (string | number)[] = [key];
let isAbleToReturnNil = false;
if (Array.isArray(param1)) {
this.pushZAddOpts(args, param2 as ZAddOpts);
isAbleToReturnNil = this.pushZAddOpts(args, param2 as ZAddOpts);
args.push(...param1.flatMap((e) => e));
opts = param2 as ZAddOpts;
} else if (typeof param1 === "object") {
this.pushZAddOpts(args, param2 as ZAddOpts);
isAbleToReturnNil = this.pushZAddOpts(args, param2 as ZAddOpts);
for (const [member, score] of Object.entries(param1)) {
args.push(score as number, member);
}
} else {
this.pushZAddOpts(args, opts);
isAbleToReturnNil = this.pushZAddOpts(args, opts);
args.push(param1, param2 as string);
}
return this.execIntegerReply("ZADD", ...args);
return isAbleToReturnNil
? this.execIntegerOrNilReply("ZADD", ...args)
: this.execIntegerReply("ZADD", ...args);
}

private pushZAddOpts(
args: (string | number)[],
opts?: ZAddOpts,
): void {
if (opts?.mode) {
): boolean {
let isAbleToReturnNil = false;
if (opts?.nx) {
args.push("NX");
isAbleToReturnNil = true;
} else if (opts?.xx) {
args.push("XX");
isAbleToReturnNil = true;
} else if (opts?.mode) {
args.push(opts.mode);
isAbleToReturnNil = true;
}
if (opts?.ch) {
args.push("CH");
}
return isAbleToReturnNil;
}

zaddIncr(
Expand Down
82 changes: 57 additions & 25 deletions tests/commands/sorted_set.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
import { assert, assertEquals } from "../../deps/std/assert.ts";
import { afterAll, beforeAll, beforeEach, it } from "../../deps/std/testing.ts";
import type { IsExact } from "../../deps/std/testing.ts";
import {
afterAll,
assertType,
beforeAll,
beforeEach,
describe,
it,
} from "../../deps/std/testing.ts";
import type { Connector, TestServer } from "../test_util.ts";
import type { Redis } from "../../mod.ts";

Expand Down Expand Up @@ -39,35 +47,59 @@ export function zsetTests(
assertEquals(arr, null);
});

it("zadd", async () => {
assertEquals(await client.zadd("key", { "1": 1, "2": 2 }), 2);
assertEquals(await client.zadd("key", 3, "3"), 1);
assertEquals(
await client.zadd("key", [
describe("zadd", () => {
it("adds specified members to a sorted set", async () => {
const v = await client.zadd("key", { "1": 1, "2": 2 });
assertEquals(v, 2);

const v2 = await client.zadd("key", 3, "3");
assertEquals(v2, 1);

const v3 = await client.zadd("key", [
[4, "4"],
[5, "5"],
]),
2,
);
});
]);
assertEquals(
v3,
2,
);
assertType<IsExact<typeof v3, number>>(true);
});

it("zaddWithMode", async () => {
assertEquals(await client.zadd("key", 1, "1", { mode: "NX" }), 1);
assertEquals(await client.zadd("key", { "1": 1 }, { mode: "XX" }), 0);
assertEquals(
await client.zadd("key", [[1, "1"], [2, "2"]], { mode: "NX" }),
1,
);
});
it("supports `NX` and `XX`", async () => {
const v = await client.zadd("zaddWithNXOrXX", 1, "1", { nx: true });
assertEquals(v, 1);
assertType<IsExact<typeof v, number | null>>(true);

it("zaddWithCH", async () => {
assertEquals(await client.zadd("key", [[1, "foo"], [2, "bar"]]), 2);
assertEquals(
await client.zadd("key", { "foo": 1, "bar": 3, "baz": 4 }, { ch: true }),
2,
);
});
const v2 = await client.zadd("zaddWithNXOrXX", 2, "1", { mode: "NX" });
assertEquals(v2, null);
assertType<IsExact<typeof v2, number | null>>(true);

const v3 = await client.zadd("zaddWithNXOrXX", 3, "1", { xx: true });
assertEquals(v3, 1);
assertType<IsExact<typeof v3, number | null>>(true);

const v4 = await client.zadd("zaddWithNXOrXX", [[1, "2"]], {
mode: "NX",
});
assertEquals(v4, null);
assertType<IsExact<typeof v4, number | null>>(true);
});

it("supports `CH`", async () => {
assertEquals(await client.zadd("keyWithCH", [[1, "foo"], [2, "bar"]]), 2);
const v = await client.zadd(
"keyWithCH",
{ "foo": 1, "bar": 3, "baz": 4 },
{ ch: true },
);
assertEquals(
v,
2,
);
assertType<IsExact<typeof v, number>>(true);
});
});
it("zaddIncr", async () => {
await client.zadd("key", 1, "a");
await client.zaddIncr("key", 2, "a");
Expand Down

0 comments on commit d92305b

Please sign in to comment.