Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add zunion to sdk command #549

Merged
merged 5 commits into from
Oct 17, 2023
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions deno.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions pkg/commands/mod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,4 +137,5 @@ export * from "./zremrangebyscore.ts";
export * from "./zrevrank.ts";
export * from "./zscan.ts";
export * from "./zscore.ts";
export * from "./zunion.ts";
export * from "./zunionstore.ts";
261 changes: 261 additions & 0 deletions pkg/commands/zunion.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,261 @@
import { keygen, newHttpClient, randomID } from "../test-utils.ts";

import { afterAll } from "https://deno.land/std@0.177.0/testing/bdd.ts";
import { assertEquals } from "https://deno.land/std@0.177.0/testing/asserts.ts";
import { ZUnionCommand } from "./zunion.ts";
import { ZAddCommand } from "./zadd.ts";

const client = newHttpClient();

const { newKey, cleanup } = keygen();
afterAll(cleanup);

Deno.test("command format", async (t) => {
await t.step("without options", async (t) => {
await t.step("builds the correct command", () => {
assertEquals(new ZUnionCommand([1, "key"]).command, [
"zunion",
1,
"key",
]);
});
});
await t.step("with multiple keys", async (t) => {
await t.step("builds the correct command", () => {
assertEquals(
new ZUnionCommand([2, ["key1", "key2"]]).command,
["zunion", 2, "key1", "key2"],
);
});
});
await t.step("with single weight", async (t) => {
await t.step("builds the correct command", () => {
assertEquals(
new ZUnionCommand([1, "key", { weight: 4 }])
.command,
["zunion", 1, "key", "weights", 4],
);
});
});
await t.step("with multiple weights", async (t) => {
await t.step("builds the correct command", () => {
assertEquals(
new ZUnionCommand([2, ["key1", "key2"], {
weights: [2, 3],
}]).command,
[
"zunion",
2,
"key1",
"key2",
"weights",
2,
3,
],
);
});
await t.step("with aggregate", async (t) => {
await t.step("sum", async (t) => {
await t.step("builds the correct command", () => {
assertEquals(
new ZUnionCommand([1, "key", {
aggregate: "sum",
}]).command,
["zunion", 1, "key", "aggregate", "sum"],
);
});
});
await t.step("min", async (t) => {
await t.step("builds the correct command", () => {
assertEquals(
new ZUnionCommand([1, "key", {
aggregate: "min",
}]).command,
["zunion", 1, "key", "aggregate", "min"],
);
});
});
await t.step("max", async (t) => {
await t.step("builds the correct command", () => {
assertEquals(
new ZUnionCommand([1, "key", {
aggregate: "max",
}]).command,
["zunion", 1, "key", "aggregate", "max"],
);
});
});
});
await t.step("complex", async (t) => {
await t.step("builds the correct command", () => {
assertEquals(
new ZUnionCommand([2, ["key1", "key2"], {
weights: [4, 2],
aggregate: "max",
}]).command,
[
"zunion",
2,
"key1",
"key2",
"weights",
4,
2,
"aggregate",
"max",
],
);
});
});
});
});

Deno.test("without options", async (t) => {
await t.step("returns the union", async () => {
const key1 = newKey();
const key2 = newKey();
const score1 = 1;
const member1 = randomID();
const score2 = 2;
const member2 = randomID();

await new ZAddCommand([key1, { score: score1, member: member1 }]).exec(
client,
);
await new ZAddCommand([key2, { score: score2, member: member2 }]).exec(
client,
);

const res = await new ZUnionCommand([2, [key1, key2]])
.exec(
client,
);

assertEquals(res.length, 2);
assertEquals(res?.sort(), [member1, member2].sort());
});
});

Deno.test("with weights", async (t) => {
await t.step("returns the set", async () => {
const key1 = newKey();
const key2 = newKey();
const score1 = 1;
const member1 = randomID();
const score2 = 2;
const member2 = randomID();

await new ZAddCommand([key1, { score: score1, member: member1 }]).exec(
client,
);

await new ZAddCommand([key2, { score: score2, member: member2 }]).exec(
client,
);

const res = await new ZUnionCommand([2, [key1, key2], {
weights: [2, 3],
}]).exec(client);

assertEquals(res.length, 2);
});
});

Deno.test("aggregate", async (t) => {
await t.step("sum", async (t) => {
await t.step("returns the set", async () => {
const key1 = newKey();
const key2 = newKey();
const score1 = 1;
const member1 = randomID();
const score2 = 2;
const member2 = randomID();

await new ZAddCommand([key1, { score: score1, member: member1 }]).exec(
client,
);
await new ZAddCommand([key2, { score: score2, member: member2 }]).exec(
client,
);

const res = await new ZUnionCommand([2, [key1, key2], {
aggregate: "sum",
}]).exec(client);

assertEquals(Array.isArray(res), true);
assertEquals(res.length, 2);
});
});
await t.step("min", async (t) => {
await t.step("returns the set ", async () => {
const key1 = newKey();
const key2 = newKey();
const score1 = 1;
const member1 = randomID();
const score2 = 2;
const member2 = randomID();

await new ZAddCommand([key1, { score: score1, member: member1 }]).exec(
client,
);
await new ZAddCommand([key2, { score: score2, member: member2 }]).exec(
client,
);

const res = await new ZUnionCommand([2, [key1, key2], {
aggregate: "min",
}]).exec(client);
assertEquals(res.length, 2);
});
});
await t.step("max", async (t) => {
await t.step("returns the set ", async () => {
const key1 = newKey();
const key2 = newKey();
const score1 = 1;
const member1 = randomID();
const score2 = 2;
const member2 = randomID();

await new ZAddCommand([key1, { score: score1, member: member1 }]).exec(
client,
);
await new ZAddCommand([key2, { score: score2, member: member2 }]).exec(
client,
);

const res = await new ZUnionCommand([2, [key1, key2], {
aggregate: "max",
}]).exec(client);
assertEquals(res.length, 2);
});
});
});

Deno.test("withscores", async (t) => {
await t.step("returns the set", async () => {
const key1 = newKey();
const score1 = 1;
const member1 = randomID();

const key2 = newKey();
const member2 = randomID();
const score2 = 5;

await new ZAddCommand([key1, { score: score1, member: member1 }]).exec(
client,
);

await new ZAddCommand([key2, { score: score2, member: member2 }]).exec(
client,
);

const res = await new ZUnionCommand([2, [key1, key2], {
withScores: true,
}]).exec(client);

assertEquals(res.length, 4);
assertEquals(res[0], member1);
assertEquals(res[1], score1);
});
});
64 changes: 64 additions & 0 deletions pkg/commands/zunion.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { Command, CommandOptions } from "./command.ts";

export type ZUnionCommandOptions =
& {
withScores?: boolean;
aggregate?: "sum" | "min" | "max";
}
& (
| { weight: number; weights?: never }
| { weight?: never; weights: number[] }
| { weight?: never; weights?: never }
);

/**
* @see https://redis.io/commands/zunion
*/
export class ZUnionCommand<TData extends unknown[]>
extends Command<string[], TData> {
constructor(
cmd: [
numKeys: 1,
key: string,
opts?: ZUnionCommandOptions,
],
cmdOpts?: CommandOptions<string[], TData>,
);
constructor(
cmd: [
numKeys: number,
keys: string[],
opts?: ZUnionCommandOptions,
],
cmdOpts?: CommandOptions<string[], TData>,
);
constructor(
[numKeys, keyOrKeys, opts]: [
numKeys: number,
keyOrKeys: string | string[],
opts?: ZUnionCommandOptions,
],
cmdOpts?: CommandOptions<string[], TData>,
) {
const command: unknown[] = ["zunion", numKeys];
if (Array.isArray(keyOrKeys)) {
command.push(...keyOrKeys);
} else {
command.push(keyOrKeys);
}
if (opts) {
if ("weights" in opts && opts.weights) {
command.push("weights", ...opts.weights);
} else if ("weight" in opts && typeof opts.weight === "number") {
command.push("weights", opts.weight);
}
if ("aggregate" in opts) {
command.push("aggregate", opts.aggregate);
}
if (opts?.withScores) {
command.push("withscores");
}
}
super(command, cmdOpts);
}
}
3 changes: 2 additions & 1 deletion pkg/pipeline.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -217,9 +217,10 @@ Deno.test("use all the things", async (t) => {
.zscan(newKey(), 0)
.zscore(newKey(), "member")
.zunionstore(newKey(), 1, [newKey()])
.zunion(1, [newKey()])
.json.set(newKey(), "$", { hello: "world" });

const res = await p.exec();
assertEquals(res.length, 120);
assertEquals(res.length, 121);
});
});
Loading