Skip to content

Commit

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

* PR suggestions

* Use backticks for cross-references

* Fix doc

---------

Co-authored-by: Andrew Carbonetto <[email protected]>
  • Loading branch information
aaron-congo and acarbonetto authored Jun 11, 2024
1 parent fb18f35 commit da42822
Show file tree
Hide file tree
Showing 7 changed files with 160 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
* Node: Added SUNIONSTORE command ([#1549](https:/aws/glide-for-redis/pull/1549))
* Node: Added PFCOUNT command ([#1545](https:/aws/glide-for-redis/pull/1545))
* Node: Added OBJECT FREQ command ([#1542](https:/aws/glide-for-redis/pull/1542))
* Node: Added LINSERT command ([#1544](https:/aws/glide-for-redis/pull/1544))

### Breaking Changes
* Node: Update XREAD to return a Map of Map ([#1494](https:/aws/glide-for-redis/pull/1494))
Expand Down
2 changes: 2 additions & 0 deletions node/npm/glide/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ function initialize() {
Logger,
ExpireOptions,
InfoOptions,
InsertPosition,
SetOptions,
ZaddOptions,
ScoreBoundry,
Expand Down Expand Up @@ -129,6 +130,7 @@ function initialize() {
Logger,
ExpireOptions,
InfoOptions,
InsertPosition,
SetOptions,
ZaddOptions,
ScoreBoundry,
Expand Down
33 changes: 33 additions & 0 deletions node/src/BaseClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { Buffer, BufferWriter, Reader, Writer } from "protobufjs";
import {
AggregationType,
ExpireOptions,
InsertPosition,
KeyWeight,
RangeByIndex,
RangeByLex,
Expand Down Expand Up @@ -47,6 +48,7 @@ import {
createIncrBy,
createIncrByFloat,
createLIndex,
createLInsert,
createLLen,
createLPop,
createLPush,
Expand Down Expand Up @@ -2306,6 +2308,37 @@ export class BaseClient {
return this.createWritePromise(createLIndex(key, index));
}

/**
* Inserts `element` in the list at `key` either before or after the `pivot`.
*
* See https://valkey.io/commands/linsert/ for more details.
*
* @param key - The key of the list.
* @param position - The relative position to insert into - either `InsertPosition.Before` or
* `InsertPosition.After` the `pivot`.
* @param pivot - An element of the list.
* @param element - The new element to insert.
* @returns The list length after a successful insert operation.
* If the `key` doesn't exist returns `-1`.
* If the `pivot` wasn't found, returns `0`.
*
* @example
* ```typescript
* const length = await client.linsert("my_list", InsertPosition.Before, "World", "There");
* console.log(length); // Output: 2 - The list has a length of 2 after performing the insert.
* ```
*/
public linsert(
key: string,
position: InsertPosition,
pivot: string,
element: string,
): Promise<number> {
return this.createWritePromise(
createLInsert(key, position, pivot, element),
);
}

/** Remove the existing timeout on `key`, turning the key from volatile (a key with an expire set) to
* persistent (a key that will never expire as no timeout is associated).
* See https://redis.io/commands/persist/ for more details.
Expand Down
26 changes: 26 additions & 0 deletions node/src/Commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1110,6 +1110,32 @@ export function createLIndex(
return createCommand(RequestType.LIndex, [key, index.toString()]);
}

/**
* Defines where to insert new elements into a list.
*/
export enum InsertPosition {
/**
* Insert new element before the pivot.
*/
Before = "before",
/**
* Insert new element after the pivot.
*/
After = "after",
}

/**
* @internal
*/
export function createLInsert(
key: string,
position: InsertPosition,
pivot: string,
element: string,
): redis_request.Command {
return createCommand(RequestType.LInsert, [key, position, pivot, element]);
}

/**
* @internal
*/
Expand Down
26 changes: 26 additions & 0 deletions node/src/Transaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
AggregationType,
ExpireOptions,
InfoOptions,
InsertPosition,
KeyWeight,
RangeByIndex,
RangeByLex,
Expand Down Expand Up @@ -49,6 +50,7 @@ import {
createIncrByFloat,
createInfo,
createLIndex,
createLInsert,
createLLen,
createLPop,
createLPush,
Expand Down Expand Up @@ -1266,6 +1268,30 @@ export class BaseTransaction<T extends BaseTransaction<T>> {
return this.addAndReturn(createLIndex(key, index));
}

/**
* Inserts `element` in the list at `key` either before or after the `pivot`.
*
* See https://valkey.io/commands/linsert/ for more details.
*
* @param key - The key of the list.
* @param position - The relative position to insert into - either `InsertPosition.Before` or
* `InsertPosition.After` the `pivot`.
* @param pivot - An element of the list.
* @param element - The new element to insert.
*
* Command Response - The list length after a successful insert operation.
* If the `key` doesn't exist returns `-1`.
* If the `pivot` wasn't found, returns `0`.
*/
public linsert(
key: string,
position: InsertPosition,
pivot: string,
element: string,
): T {
return this.addAndReturn(createLInsert(key, position, pivot, element));
}

/**
* Adds an entry to the specified stream stored at `key`. If the `key` doesn't exist, the stream is created.
* See https://redis.io/commands/xadd/ for more details.
Expand Down
64 changes: 64 additions & 0 deletions node/tests/SharedTests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
ClosingError,
ExpireOptions,
InfoOptions,
InsertPosition,
ProtocolVersion,
RedisClient,
RedisClusterClient,
Expand Down Expand Up @@ -2176,6 +2177,69 @@ export function runBaseTests<Context>(config: {
config.timeout,
);

it.each([ProtocolVersion.RESP2, ProtocolVersion.RESP3])(
`linsert test_%p`,
async (protocol) => {
await runTest(async (client: BaseClient) => {
const key1 = uuidv4();
const stringKey = uuidv4();
const nonExistingKey = uuidv4();

expect(await client.lpush(key1, ["4", "3", "2", "1"])).toEqual(
4,
);
expect(
await client.linsert(
key1,
InsertPosition.Before,
"2",
"1.5",
),
).toEqual(5);
expect(
await client.linsert(
key1,
InsertPosition.After,
"3",
"3.5",
),
).toEqual(6);
expect(await client.lrange(key1, 0, -1)).toEqual([
"1",
"1.5",
"2",
"3",
"3.5",
"4",
]);

expect(
await client.linsert(
key1,
InsertPosition.Before,
"nonExistingPivot",
"4",
),
).toEqual(-1);
expect(
await client.linsert(
nonExistingKey,
InsertPosition.Before,
"pivot",
"elem",
),
).toEqual(0);

// key exists, but it is not a list
expect(await client.set(stringKey, "value")).toEqual("OK");
await expect(
client.linsert(stringKey, InsertPosition.Before, "a", "b"),
).rejects.toThrow();
}, protocol);
},
config.timeout,
);

it.each([ProtocolVersion.RESP2, ProtocolVersion.RESP3])(
`zpopmin test_%p`,
async (protocol) => {
Expand Down
8 changes: 8 additions & 0 deletions node/tests/TestUtilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
BaseClient,
BaseClientConfiguration,
ClusterTransaction,
InsertPosition,
Logger,
ProtocolVersion,
RedisClient,
Expand Down Expand Up @@ -295,6 +296,13 @@ export async function transactionTest(
args.push([field + "3", field + "2"]);
baseTransaction.lpopCount(key5, 2);
args.push([field + "3", field + "2"]);
baseTransaction.linsert(
key5,
InsertPosition.Before,
"nonExistingPivot",
"element",
);
args.push(0);
baseTransaction.rpush(key6, [field + "1", field + "2", field + "3"]);
args.push(3);
baseTransaction.lindex(key6, 0);
Expand Down

0 comments on commit da42822

Please sign in to comment.