Skip to content

Commit

Permalink
Node: added zrange and zrangeWithScores commands.
Browse files Browse the repository at this point in the history
  • Loading branch information
Adan committed Mar 13, 2024
1 parent 48f77ae commit 47e00a5
Show file tree
Hide file tree
Showing 6 changed files with 458 additions and 19 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
* Node: Added STRLEN command ([#993](https://github.com/aws/glide-for-redis/pull/993))
* Node: Added LINDEX command ([#999](https://github.com/aws/glide-for-redis/pull/999))
* Python, Node: Added ZPOPMAX command ([#996](https://github.com/aws/glide-for-redis/pull/996), [#1009](https://github.com/aws/glide-for-redis/pull/1009))
* Python: Added ZRANGE command ([#906](https://github.com/aws/glide-for-redis/pull/906))
* Python, Node: Added ZRANGE command ([#906](https://github.com/aws/glide-for-redis/pull/906), [#1115](https://github.com/aws/glide-for-redis/pull/1115))
* Python, Node: Added PTTL command ([#1036](https://github.com/aws/glide-for-redis/pull/1036), [#1082](https://github.com/aws/glide-for-redis/pull/1082))
* Node: Added HVAL command ([#1022](https://github.com/aws/glide-for-redis/pull/1022))
* Node: Added PERSIST command ([#1023](https://github.com/aws/glide-for-redis/pull/1023))
Expand Down
50 changes: 50 additions & 0 deletions node/src/BaseClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import * as net from "net";
import { Buffer, BufferWriter, Reader, Writer } from "protobufjs";
import {
ExpireOptions,
RangeByIndex,
RangeByScoreOrLex,
ScoreLimit,
SetOptions,
StreamAddOptions,
Expand Down Expand Up @@ -71,6 +73,8 @@ import {
createZcount,
createZpopmax,
createZpopmin,
createZrange,
createZrangeWithScores,
createZrem,
createZremRangeByRank,
createZremRangeByScore,
Expand Down Expand Up @@ -1129,6 +1133,52 @@ export class BaseClient {
return this.createWritePromise(createZcount(key, minScore, maxScore));
}

/** Returns the specified range of elements in the sorted set stored at `key`.
* ZRANGE can perform different types of range queries: by index (rank), by the score, or by lexicographical order.
*
* See https://redis.io/commands/zrange/ for more details.
* To get the elements with their scores, see zrangeWithScores.
*
* @param key - The key of the sorted set.
* @param rangeQuery - The range query object representing the type of range query to perform.
* For range queries by index (rank), use RangeByIndex.
* For range queries by lexicographical order, use RangeByScoreOrLex with `isLex` is set to true.
* For range queries by score, use RangeByScoreOrLex.
* @param reverse - If true, reverses the sorted set, with index 0 as the element with the highest score.
* @returns A list of elements within the specified range.
* If `key` does not exist, it is treated as an empty sorted set, and the command returns an empty array.
*/
public zrange(
key: string,
rangeQuery: RangeByIndex | RangeByScoreOrLex,
reverse: boolean = false,
): Promise<string[]> {
return this.createWritePromise(createZrange(key, rangeQuery, reverse));
}

/** Returns the specified range of elements with their scores in the sorted set stored at `key`.
* Similar to ZRANGE but with a WITHSCORE flag.
* See https://redis.io/commands/zrange/ for more details.
*
* @param key - The key of the sorted set.
* @param rangeQuery - The range query object representing the type of range query to perform.
* For range queries by index (rank), use RangeByIndex.
* For range queries by lexicographical order, use RangeByScoreOrLex with `isLex` is set to true.
* For range queries by score, use RangeByScoreOrLex.
* @param reverse - If true, reverses the sorted set, with index 0 as the element with the highest score.
* @returns A map of elements and their scores within the specified range.
* If `key` does not exist, it is treated as an empty sorted set, and the command returns an empty map.
*/
public zrangeWithScores(
key: string,
rangeQuery: RangeByIndex | RangeByScoreOrLex,
reverse: boolean = false,
): Promise<Record<string, number>> {
return this.createWritePromise(
createZrangeWithScores(key, rangeQuery, reverse),
);
}

/** Returns the length of the string value stored at `key`.
* See https://redis.io/commands/strlen/ for more details.
*
Expand Down
146 changes: 138 additions & 8 deletions node/src/Commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -804,27 +804,133 @@ export function createZscore(
}

export type ScoreLimit =
/**
* Positive infinity bound for sorted set.
*/
| `positiveInfinity`
/**
* Negative infinity bound for sorted set.
*/
| `negativeInfinity`
/**
* Represents a specific numeric score boundary in a sorted set.
*/
| {
bound: number;
/**
* The score value.
*/
value: number | string;
/**
* Whether the score value is inclusive. Defaults to True.
*/
isInclusive?: boolean;
};

function getScoreLimitArg(score: ScoreLimit): string {
/**
* Represents a range by index (rank) in a sorted set.
* The `start` and `stop` arguments represent zero-based indexes.
*/
export type RangeByIndex = {
/**
* The start index of the range.
*/
start: number;
/**
* The stop index of the range.
*/
stop: number;
};

/**
* Represents a range by score or a range by lex in a sorted set.
* The `start` and `stop` arguments represent score boundaries.
*/
export type RangeByScoreOrLex = {
/**
* The start score boundary.
*/
start: ScoreLimit;
/**
* The stop score boundary.
*/
stop: ScoreLimit;
/**
* The limit argument for a range query.
* Represents a limit argument for a range query in a sorted set to
* be used in [ZRANGE](https://redis.io/commands/zrange) command.
*
* The optional LIMIT argument can be used to obtain a sub-range from the matching elements
* (similar to SELECT LIMIT offset, count in SQL).
*/
limit?: {
/**
* The offset from the start of the range.
*/
offset: number;
/**
* The number of elements to include in the range.
* A negative count returns all elements from the offset.
*/
count: number;
};
/**
* Indicates whether the range is by lex or by score.
*/
isLex?: boolean;
};

function getScoreLimitArg(score: ScoreLimit, isLex: boolean = false): string {
if (score == "positiveInfinity") {
return "+inf";
return isLex ? "+" : "+inf";
} else if (score == "negativeInfinity") {
return "-inf";
return isLex ? "-" : "-inf";
}

if (score.isInclusive == false) {
return "(" + score.value.toString();
}

const value =
score.isInclusive == false
? "(" + score.bound.toString()
: score.bound.toString();
const value = isLex ? "[" + score.value.toString() : score.value.toString();
return value;
}

function createZrangeArgs(
key: string,
rangeQuery: RangeByScoreOrLex | RangeByIndex,
reverse: boolean,
withScores: boolean,
): string[] {
const args: string[] = [key];

if (typeof rangeQuery.start != "number") {
rangeQuery = rangeQuery as RangeByScoreOrLex;
args.push(getScoreLimitArg(rangeQuery.start, rangeQuery.isLex));
args.push(getScoreLimitArg(rangeQuery.stop, rangeQuery.isLex));
args.push(rangeQuery.isLex == true ? "BYLEX" : "BYSCORE");
} else {
args.push(rangeQuery.start.toString());
args.push(rangeQuery.stop.toString());
}

if (reverse) {
args.push("REV");
}

if ("limit" in rangeQuery && rangeQuery.limit !== undefined) {
args.push(
"LIMIT",
String(rangeQuery.limit.offset),
String(rangeQuery.limit.count),
);
}

if (withScores) {
args.push("WITHSCORES");
}

return args;
}

/**
* @internal
*/
Expand All @@ -839,6 +945,30 @@ export function createZcount(
return createCommand(RequestType.Zcount, args);
}

/**
* @internal
*/
export function createZrange(
key: string,
rangeQuery: RangeByIndex | RangeByScoreOrLex,
reverse: boolean = false,
): redis_request.Command {
const args = createZrangeArgs(key, rangeQuery, reverse, false);
return createCommand(RequestType.Zrange, args);
}

/**
* @internal
*/
export function createZrangeWithScores(
key: string,
rangeQuery: RangeByIndex | RangeByScoreOrLex,
reverse: boolean = false,
): redis_request.Command {
const args = createZrangeArgs(key, rangeQuery, reverse, true);
return createCommand(RequestType.Zrange, args);
}

/**
* @internal
*/
Expand Down
52 changes: 52 additions & 0 deletions node/src/Transaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
import {
ExpireOptions,
InfoOptions,
RangeByIndex,
RangeByScoreOrLex,
ScoreLimit,
SetOptions,
StreamAddOptions,
Expand Down Expand Up @@ -74,6 +76,8 @@ import {
createZcount,
createZpopmax,
createZpopmin,
createZrange,
createZrangeWithScores,
createZrem,
createZremRangeByRank,
createZremRangeByScore,
Expand Down Expand Up @@ -896,6 +900,54 @@ export class BaseTransaction<T extends BaseTransaction<T>> {
return this.addAndReturn(createZcount(key, minScore, maxScore));
}

/** Returns the specified range of elements in the sorted set stored at `key`.
* ZRANGE can perform different types of range queries: by index (rank), by the score, or by lexicographical order.
*
* See https://redis.io/commands/zrange/ for more details.
* To get the elements with their scores, see zrangeWithScores.
*
* @param key - The key of the sorted set.
* @param rangeQuery - The range query object representing the type of range query to perform.
* For range queries by index (rank), use RangeByIndex.
* For range queries by lexicographical order, use RangeByScoreOrLex with `isLex` is set to true.
* For range queries by score, use RangeByScoreOrLex.
* @param reverse - If true, reverses the sorted set, with index 0 as the element with the highest score.
*
* Command Response - A list of elements within the specified range.
* If `key` does not exist, it is treated as an empty sorted set, and the command returns an empty array.
*/
public zrange(
key: string,
rangeQuery: RangeByIndex | RangeByScoreOrLex,
reverse: boolean = false,
): T {
return this.addAndReturn(createZrange(key, rangeQuery, reverse));
}

/** Returns the specified range of elements with their scores in the sorted set stored at `key`.
* Similar to ZRANGE but with a WITHSCORE flag.
* See https://redis.io/commands/zrange/ for more details.
*
* @param key - The key of the sorted set.
* @param rangeQuery - The range query object representing the type of range query to perform.
* For range queries by index (rank), use RangeByIndex.
* For range queries by lexicographical order, use RangeByScoreOrLex with `isLex` is set to true.
* For range queries by score, use RangeByScoreOrLex.
* @param reverse - If true, reverses the sorted set, with index 0 as the element with the highest score.
*
* Command Response - A map of elements and their scores within the specified range.
* If `key` does not exist, it is treated as an empty sorted set, and the command returns an empty map.
*/
public zrangeWithScores(
key: string,
rangeQuery: RangeByIndex | RangeByScoreOrLex,
reverse: boolean = false,
): T {
return this.addAndReturn(
createZrangeWithScores(key, rangeQuery, reverse),
);
}

/** Returns the string representation of the type of the value stored at `key`.
* See https://redis.io/commands/type/ for more details.
*
Expand Down
Loading

0 comments on commit 47e00a5

Please sign in to comment.