Skip to content

Commit

Permalink
Node: add ZINTERCARD command (#1553)
Browse files Browse the repository at this point in the history
* Node: add ZINTERCARD command

* PR suggestions
  • Loading branch information
aaron-congo authored Jun 12, 2024
1 parent 2c8b8ff commit 25246b7
Show file tree
Hide file tree
Showing 7 changed files with 124 additions and 3 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
* Node: Added OBJECT FREQ command ([#1542](https://github.com/aws/glide-for-redis/pull/1542))
* Node: Added LINSERT command ([#1544](https://github.com/aws/glide-for-redis/pull/1544))
* Node: Added XLEN command ([#1555](https://github.com/aws/glide-for-redis/pull/1555))
* Node: Added ZINTERCARD command ([#1553](https://github.com/aws/glide-for-redis/pull/1553))

### Breaking Changes
* Node: Update XREAD to return a Map of Map ([#1494](https://github.com/aws/glide-for-redis/pull/1494))
Expand Down
24 changes: 24 additions & 0 deletions node/src/BaseClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ import {
createZScore,
createSUnionStore,
createXLen,
createZInterCard,
} from "./Commands";
import {
ClosingError,
Expand Down Expand Up @@ -1762,6 +1763,29 @@ export class BaseClient {
return this.createWritePromise(createZCard(key));
}

/**
* Returns the cardinality of the intersection of the sorted sets specified by `keys`.
*
* See https://valkey.io/commands/zintercard/ for more details.
*
* @remarks When in cluster mode, all `keys` must map to the same hash slot.
* @param keys - The keys of the sorted sets to intersect.
* @param limit - An optional argument that can be used to specify a maximum number for the
* intersection cardinality. If limit is not supplied, or if it is set to `0`, there will be no limit.
* @returns The cardinality of the intersection of the given sorted sets.
*
* since - Redis version 7.0.0.
*
* @example
* ```typescript
* const cardinality = await client.zintercard(["key1", "key2"], 10);
* console.log(cardinality); // Output: 3 - The intersection of the sorted sets at "key1" and "key2" has a cardinality of 3.
* ```
*/
public zintercard(keys: string[], limit?: number): Promise<number> {
return this.createWritePromise(createZInterCard(keys, limit));
}

/** Returns the score of `member` in the sorted set stored at `key`.
* See https://redis.io/commands/zscore/ for more details.
*
Expand Down
17 changes: 17 additions & 0 deletions node/src/Commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -898,6 +898,23 @@ export function createZCard(key: string): redis_request.Command {
return createCommand(RequestType.ZCard, [key]);
}

/**
* @internal
*/
export function createZInterCard(
keys: string[],
limit?: number,
): redis_request.Command {
let args: string[] = keys;
args.unshift(keys.length.toString());

if (limit != undefined) {
args = args.concat(["LIMIT", limit.toString()]);
}

return createCommand(RequestType.ZInterCard, args);
}

/**
* @internal
*/
Expand Down
18 changes: 18 additions & 0 deletions node/src/Transaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ import {
createZScore,
createSUnionStore,
createXLen,
createZInterCard,
} from "./Commands";
import { redis_request } from "./ProtobufMessage";

Expand Down Expand Up @@ -979,6 +980,23 @@ export class BaseTransaction<T extends BaseTransaction<T>> {
return this.addAndReturn(createZCard(key));
}

/**
* Returns the cardinality of the intersection of the sorted sets specified by `keys`.
*
* See https://valkey.io/commands/zintercard/ for more details.
*
* @param keys - The keys of the sorted sets to intersect.
* @param limit - An optional argument that can be used to specify a maximum number for the
* intersection cardinality. If limit is not supplied, or if it is set to `0`, there will be no limit.
*
* Command Response - The cardinality of the intersection of the given sorted sets.
*
* since - Redis version 7.0.0.
*/
public zintercard(keys: string[], limit?: number): T {
return this.addAndReturn(createZInterCard(keys, limit));
}

/** Returns the score of `member` in the sorted set stored at `key`.
* See https://redis.io/commands/zscore/ for more details.
*
Expand Down
11 changes: 9 additions & 2 deletions node/tests/RedisClusterClient.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import {
RedisClusterClient,
} from "..";
import { RedisCluster } from "../../utils/TestUtils.js";
import { runBaseTests } from "./SharedTests";
import { checkIfServerVersionLessThan, runBaseTests } from "./SharedTests";
import {
flushAndCloseClient,
getClientConfigurationOption,
Expand Down Expand Up @@ -281,7 +281,10 @@ describe("RedisClusterClient", () => {
getClientConfigurationOption(cluster.getAddresses(), protocol),
);

const promises = [
const versionLessThan7 =
await checkIfServerVersionLessThan("7.0.0");

const promises: Promise<unknown>[] = [
client.blpop(["abc", "zxy", "lkn"], 0.1),
client.rename("abc", "zxy"),
client.brpop(["abc", "zxy", "lkn"], 0.1),
Expand All @@ -294,6 +297,10 @@ describe("RedisClusterClient", () => {
// TODO all rest multi-key commands except ones tested below
];

if (!versionLessThan7) {
promises.push(client.zintercard(["abc", "zxy", "lkn"]));
}

for (const promise of promises) {
try {
await promise;
Expand Down
43 changes: 43 additions & 0 deletions node/tests/SharedTests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1636,6 +1636,49 @@ export function runBaseTests<Context>(config: {
config.timeout,
);

it.each([ProtocolVersion.RESP2, ProtocolVersion.RESP3])(
`zintercard test_%p`,
async (protocol) => {
await runTest(async (client: BaseClient) => {
if (await checkIfServerVersionLessThan("7.0.0")) {
return;
}

const key1 = `{key}:${uuidv4()}`;
const key2 = `{key}:${uuidv4()}`;
const stringKey = `{key}:${uuidv4()}`;
const nonExistingKey = `{key}:${uuidv4()}`;
const memberScores1 = { one: 1, two: 2, three: 3 };
const memberScores2 = { two: 2, three: 3, four: 4 };

expect(await client.zadd(key1, memberScores1)).toEqual(3);
expect(await client.zadd(key2, memberScores2)).toEqual(3);

expect(await client.zintercard([key1, key2])).toEqual(2);
expect(await client.zintercard([key1, nonExistingKey])).toEqual(
0,
);

expect(await client.zintercard([key1, key2], 0)).toEqual(2);
expect(await client.zintercard([key1, key2], 1)).toEqual(1);
expect(await client.zintercard([key1, key2], 2)).toEqual(2);

// invalid argument - key list must not be empty
await expect(client.zintercard([])).rejects.toThrow();

// invalid argument - limit must be non-negative
await expect(
client.zintercard([key1, key2], -1),
).rejects.toThrow();

// key exists, but it is not a sorted set
expect(await client.set(stringKey, "foo")).toEqual("OK");
await expect(client.zintercard([stringKey])).rejects.toThrow();
}, protocol);
},
config.timeout,
);

it.each([ProtocolVersion.RESP2, ProtocolVersion.RESP3])(
`zscore test_%p`,
async (protocol) => {
Expand Down
13 changes: 12 additions & 1 deletion node/tests/TestUtilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,7 @@ export async function transactionTest(
const key11 = "{key}" + uuidv4(); // hyper log log
const key12 = "{key}" + uuidv4();
const key13 = "{key}" + uuidv4();
const key14 = "{key}" + uuidv4(); // sorted set
const field = uuidv4();
const value = uuidv4();
const args: ReturnType[] = [];
Expand Down Expand Up @@ -380,7 +381,17 @@ export async function transactionTest(
"negativeInfinity",
"positiveInfinity",
);
args.push(1);
args.push(1); // key8 is now empty

if (!(await checkIfServerVersionLessThan("7.0.0"))) {
baseTransaction.zadd(key14, { one: 1.0, two: 2.0 });
args.push(2);
baseTransaction.zintercard([key8, key14]);
args.push(0);
baseTransaction.zintercard([key8, key14], 1);
args.push(0);
}

baseTransaction.xadd(key9, [["field", "value1"]], { id: "0-1" });
args.push("0-1");
baseTransaction.xadd(key9, [["field", "value2"]], { id: "0-2" });
Expand Down

0 comments on commit 25246b7

Please sign in to comment.