-
Notifications
You must be signed in to change notification settings - Fork 53
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* feat: add zunion command implementation * test: add zunion tests * feat: add zunion to pipeline command * test: add zunion test for pipeline command --------- Co-authored-by: Oiseje Ojeikere <oiseje.ojeikere@kindredgroup.com> Co-authored-by: chronark <dev@chronark.com>
- Loading branch information
1 parent
77aefc4
commit 6460921
Showing
7 changed files
with
375 additions
and
1 deletion.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.