diff --git a/docs.json b/docs.json index 2b79c5a3..0f262d9a 100644 --- a/docs.json +++ b/docs.json @@ -752,6 +752,7 @@ "redis/search/getting-started", "redis/search/index-management", "redis/search/document-updates", + "redis/search/streams", "redis/search/schema-definition", "redis/search/querying", "redis/search/aggregations", @@ -944,6 +945,30 @@ "group": "Command Reference", "pages": [ "redis/commands/overview", + { + "group": "Array", + "pages": [ + "redis/commands/array/overview", + "redis/commands/array/arset", + "redis/commands/array/armset", + "redis/commands/array/arget", + "redis/commands/array/armget", + "redis/commands/array/argetrange", + "redis/commands/array/arscan", + "redis/commands/array/argrep", + "redis/commands/array/ardel", + "redis/commands/array/ardelrange", + "redis/commands/array/arcount", + "redis/commands/array/arlen", + "redis/commands/array/arinsert", + "redis/commands/array/arring", + "redis/commands/array/arlastitems", + "redis/commands/array/arnext", + "redis/commands/array/arseek", + "redis/commands/array/arop", + "redis/commands/array/arinfo" + ] + }, { "group": "Bitmap", "pages": [ @@ -1317,6 +1342,7 @@ "redis/commands/string/incrbyfloat", "redis/commands/string/mget", "redis/commands/string/mset", + "redis/commands/string/msetex", "redis/commands/string/msetnx", "redis/commands/string/psetex", "redis/commands/string/set", @@ -1336,6 +1362,20 @@ "redis/commands/transactions/unwatch", "redis/commands/transactions/watch" ] + }, + { + "group": "Vector", + "pages": [ + "redis/commands/vector/overview", + "redis/commands/vector/vector-create", + "redis/commands/vector/vector-add", + "redis/commands/vector/vector-get", + "redis/commands/vector/vector-query", + "redis/commands/vector/vector-del", + "redis/commands/vector/vector-count", + "redis/commands/vector/vector-info", + "redis/commands/vector/vector-drop" + ] } ] } diff --git a/llms-full.txt b/llms-full.txt index 0c86faff..55531541 100644 --- a/llms-full.txt +++ b/llms-full.txt @@ -17229,27 +17229,28 @@ For debugging or monitoring purposes, you can use Realtime Dashboard in console. -# BITCOUNT -Source: https://upstash.com/docs/redis/commands/bitmap/bitcount +# ARCOUNT +Source: https://upstash.com/docs/redis/commands/array/arcount -Use `BITCOUNT` to count the bits set to 1 in the string stored at a key. +Use `ARCOUNT` to get the number of occupied slots in an array. -Without a range the whole value is counted. `` and `` restrict the count to a part of the value and are interpreted as byte offsets by default, or as bit offsets when `BIT` is given. Both ends are inclusive and may be negative to count backwards from the end of the value, where `-1` is the last byte or bit. A missing key is treated as an empty string and returns `0`. +This is the count of values actually stored, so holes left by [`ARDEL`](/docs/redis/commands/array/ardel) or by writing to scattered indexes are not counted. It is therefore different from [`ARLEN`](/docs/redis/commands/array/arlen), which reports how far the array extends. For a densely filled array the two agree; for a sparse one, `ARCOUNT` is the smaller number and the one that tracks stored data. -`BITCOUNT` is the usual way to read a bitmap built with [`SETBIT`](/docs/redis/commands/bitmap/setbit), for example to count how many users were active on a given day when each user has a fixed bit position. +A key that does not exist counts as `0`. + +See the [array command overview](/docs/redis/commands/array/overview) for the data model these commands share. ## Syntax ```redis -BITCOUNT [ [BYTE | BIT]] +ARCOUNT ``` ## Arguments | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | -| `` | Yes | No | Redis key targeted by the command. | -| ` [BYTE \| BIT]` | No | No | Range to count. Offsets are byte-based unless `BIT` is given; negative offsets count from the end. | +| `` | Yes | No | Array key targeted by the command. | ## Response @@ -17273,32 +17274,24 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -BITCOUNT my-key +ARCOUNT my-array ``` -```ts -import { Redis } from "@upstash/redis"; - -const redis = Redis.fromEnv(); - -const bits = await redis.bitcount(key); -``` + + This command is not supported yet in `@upstash/redis`. + -```python -from upstash_redis import Redis - -redis = Redis.from_env() -result = redis.bitcount("my-key") -print(result) -``` + + This command is not supported yet in `upstash_redis`. + @@ -17308,7 +17301,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.bitcount("my-key"); +const result = await redis.arcount("my-array"); console.log(result); ``` @@ -17322,7 +17315,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.bitCount("my-key"); +const result = await client.arCount("my-array"); console.log(result); ``` @@ -17335,7 +17328,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.bitcount("my-key") +result = client.arcount("my-array") print(result) ``` @@ -17360,7 +17353,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.BitCount(context.Background(), "my-key", nil).Result() + result, err := client.ARCount(context.Background(), "my-array").Result() if err != nil { panic(err) } @@ -17378,7 +17371,7 @@ import java.net.URI; import redis.clients.jedis.Jedis; try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.bitcount("my-key"); + Object result = jedis.arcount("my-array"); System.out.println(result); } ``` @@ -17388,14 +17381,14 @@ try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { ```rust -use redis::TypedCommands; - fn main() -> redis::RedisResult<()> { let url = std::env::var("REDIS_URL").expect("REDIS_URL is not set"); let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let result = connection.bitcount("my-key")?; + let mut command = redis::cmd("ARCOUNT"); + command.arg("my-array"); + let result: redis::Value = command.query(&mut connection)?; println!("{result:?}"); Ok(()) } @@ -17405,38 +17398,29 @@ fn main() -> redis::RedisResult<()> { -# BITFIELD -Source: https://upstash.com/docs/redis/commands/bitmap/bitfield +# ARDEL +Source: https://upstash.com/docs/redis/commands/array/ardel -Use `BITFIELD` to treat a string as an array of packed integers and run several operations on it in a single atomic call. +Use `ARDEL` to empty one or more array slots. -Every operation names an encoding and a bit offset. The encoding is `u` for unsigned integers (up to 63 bits) or `i` for signed integers (up to 64 bits). The offset is counted in bits from the start of the value or, when prefixed with `#`, in units of the encoding width, so `#2` with `u8` addresses the third 8-bit field. The string grows automatically with zero bits when an operation addresses an offset past its current end. +Deleting a slot leaves a hole rather than shifting later values down, so every other index keeps its meaning. The reply counts the slots that actually held a value, so indexes that were already empty do not inflate it. Removing the last remaining value deletes the key. -`GET` reads a field, `SET` writes one and returns its previous value, and `INCRBY` adds a possibly negative increment and returns the new value. `OVERFLOW` sets how the `SET` and `INCRBY` operations that follow it behave when a value does not fit the encoding: `WRAP` wraps around like modular arithmetic and is the default, `SAT` saturates at the minimum or maximum of the encoding, and `FAIL` leaves the field unchanged and returns null for that operation. The reply is an array with one entry per operation, in the order the operations were given. +Deletion does not move the append cursor: [`ARNEXT`](/docs/redis/commands/array/arnext) still reports the slot after the highest index ever appended, so an [`ARINSERT`](/docs/redis/commands/array/arinsert) will not silently reuse a freed index. -Packing many small counters into a single key this way saves memory and keeps the whole update atomic, which makes it a good fit for rate limiters and compact per-user counters. +See the [array command overview](/docs/redis/commands/array/overview) for the data model these commands share. ## Syntax ```redis -BITFIELD - [GET | - [OVERFLOW WRAP | SAT | FAIL] - (SET | - INCRBY ) - [GET | - [OVERFLOW WRAP | SAT | FAIL] - (SET | - INCRBY ) - ...]] +ARDEL [ ...] ``` ## Arguments | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | -| `` | Yes | No | Redis key targeted by the command. | -| `(GET \| [OVERFLOW WRAP \| SAT \| FAIL] (SET \| INCRBY ))` | No | Yes | An operation on a field of `` (`u` unsigned up to 63 bits, or `i` signed up to 64 bits) at `` bits, or at `#` to address the n-th field of that width: `GET` reads it, `SET` writes it and returns the previous value, and `INCRBY` adds an increment and returns the new value. `OVERFLOW` sets how the `SET` and `INCRBY` operations after it handle a value that does not fit: `WRAP` wraps around (the default), `SAT` saturates at the encoding's limits, and `FAIL` leaves the field unchanged and returns null. Repeat to run several operations in one atomic call. | +| `` | Yes | No | Array key targeted by the command. | +| `` | Yes | Yes | Zero-based index to clear. Repeat to clear several slots in one call. | ## Response @@ -17444,8 +17428,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Array of integer or null replies, one per subcommand | -| RESP3 | Array of integer or null replies, one per subcommand | +| RESP2 | Integer | +| RESP3 | Integer | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -17460,32 +17444,24 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -BITFIELD my-key GET u8 0 +ARDEL my-array 0 1 ``` -```ts -import { Redis } from "@upstash/redis"; - -const redis = Redis.fromEnv(); -const result = await redis.bitfield("my-key").get("u8", 0).exec(); -console.log(result); -``` + + This command is not supported yet in `@upstash/redis`. + -```python -from upstash_redis import Redis - -redis = Redis.from_env() -result = redis.bitfield("my-key").get("u8", 0).execute() -print(result) -``` + + This command is not supported yet in `upstash_redis`. + @@ -17495,7 +17471,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.bitfield("my-key", "GET", "u8", "0"); +const result = await redis.ardel("my-array", 0, 1); console.log(result); ``` @@ -17509,7 +17485,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.bitField("my-key", [{ operation: "GET", encoding: "u8", offset: 0 }]); +const result = await client.arDel("my-array", [0, 1]); console.log(result); ``` @@ -17522,7 +17498,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.bitfield("my-key").get("u8", 0).execute() +result = client.ardel("my-array", 0, 1) print(result) ``` @@ -17547,7 +17523,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.BitField(context.Background(), "my-key", "GET", "u8", 0).Result() + result, err := client.ARDel(context.Background(), "my-array", 0, 1).Result() if err != nil { panic(err) } @@ -17565,7 +17541,7 @@ import java.net.URI; import redis.clients.jedis.Jedis; try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.bitfield("my-key", "GET", "u8", "0"); + Object result = jedis.ardel("my-array", 0, 1); System.out.println(result); } ``` @@ -17580,8 +17556,10 @@ fn main() -> redis::RedisResult<()> { let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let mut command = redis::cmd("BITFIELD"); - command.arg("my-key"); + let mut command = redis::cmd("ARDEL"); + command.arg("my-array"); + command.arg("0"); + command.arg("1"); let result: redis::Value = command.query(&mut connection)?; println!("{result:?}"); Ok(()) @@ -17592,25 +17570,34 @@ fn main() -> redis::RedisResult<()> { -# BITFIELD_RO -Source: https://upstash.com/docs/redis/commands/bitmap/bitfield-ro +# ARDELRANGE +Source: https://upstash.com/docs/redis/commands/array/ardelrange -Use `BITFIELD_RO` to read one or more bitfield values without modifying the key. +Use `ARDELRANGE` to empty every occupied slot inside one or more inclusive index ranges. -It is the read-only form of [`BITFIELD`](/docs/redis/commands/bitmap/bitfield) and accepts `GET` operations only, so it is safe to run on replicas and from read-only scripts. Each `GET` names an encoding, `u` for unsigned or `i` for signed integers, and a bit offset that can be written as `#` to address the n-th field of that width. The reply holds one integer per `GET`, and any part of a field that lies past the end of the stored string reads as zero. +Several ranges can be given in one call and the reply is the total number of values removed across all of them, which makes it the efficient way to trim a window of an array without listing every index. As with [`ARDEL`](/docs/redis/commands/array/ardel), deleting leaves holes instead of shifting values, and removing the last remaining value deletes the key. + +Ranges that overlap are allowed; a value is counted once, when it is removed. + +See the [array command overview](/docs/redis/commands/array/overview) for the data model these commands share. ## Syntax ```redis -BITFIELD_RO [GET [GET ...]] +ARDELRANGE [ ...] ``` ## Arguments | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | -| `` | Yes | No | Redis key targeted by the command. | -| `GET ` | No | Yes | Read the value at `` using ``, such as `u8` or `i16`. Repeat to read several fields. | +| `` | Yes | No | Array key targeted by the command. | +| ` ` | Yes | Yes | Inclusive index range to clear. Repeat to clear several ranges in one call. | + +## Important points + +* The number of arguments after the key must be even; an odd count returns a wrong number of arguments error. +* Each range is applied in the order given, and the reply is the total across all of them. ## Response @@ -17618,8 +17605,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Array of integer or null replies, one per subcommand | -| RESP3 | Array of integer or null replies, one per subcommand | +| RESP2 | Integer | +| RESP3 | Integer | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -17634,7 +17621,7 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -BITFIELD_RO my-key GET u8 0 +ARDELRANGE my-array 0 99 200 299 ``` @@ -17649,13 +17636,9 @@ BITFIELD_RO my-key GET u8 0 -```python -from upstash_redis import Redis - -redis = Redis.from_env() -result = redis.bitfield_ro("my-key").get("u8", 0).execute() -print(result) -``` + + This command is not supported yet in `upstash_redis`. + @@ -17665,7 +17648,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.bitfield_ro("my-key", "GET", "u8", "0"); +const result = await redis.ardelrange("my-array", 0, 99, 200, 299); console.log(result); ``` @@ -17679,7 +17662,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.bitFieldRo("my-key", [{ encoding: "u8", offset: 0 }]); +const result = await client.arDelRange("my-array", [[0, 99], [200, 299]]); console.log(result); ``` @@ -17692,7 +17675,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.bitfield_ro("my-key", "u8", 0) +result = client.ardelrange("my-array", (0, 99), (200, 299)) print(result) ``` @@ -17717,7 +17700,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.BitFieldRO(context.Background(), "my-key", "GET", "u8", 0).Result() + result, err := client.ARDelRange(context.Background(), "my-array", redis.ARRange{Start: 0, End: 99}, redis.ARRange{Start: 200, End: 299}).Result() if err != nil { panic(err) } @@ -17733,9 +17716,10 @@ func main() { import java.net.URI; import redis.clients.jedis.Jedis; +import redis.clients.jedis.args.LongRange; try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.bitfieldReadonly("my-key", "GET", "u8", "0"); + Object result = jedis.ardelrange("my-array", LongRange.of(0, 99), LongRange.of(200, 299)); System.out.println(result); } ``` @@ -17750,8 +17734,12 @@ fn main() -> redis::RedisResult<()> { let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let mut command = redis::cmd("BITFIELD_RO"); - command.arg("my-key"); + let mut command = redis::cmd("ARDELRANGE"); + command.arg("my-array"); + command.arg("0"); + command.arg("99"); + command.arg("200"); + command.arg("299"); let result: redis::Value = command.query(&mut connection)?; println!("{result:?}"); Ok(()) @@ -17762,30 +17750,27 @@ fn main() -> redis::RedisResult<()> { -# BITOP -Source: https://upstash.com/docs/redis/commands/bitmap/bitop - -Use `BITOP` to combine several strings with a bitwise operation and store the result in another key. +# ARGET +Source: https://upstash.com/docs/redis/commands/array/arget -Source strings are combined bit by bit, and shorter ones are treated as if they were padded with zero bits up to the length of the longest input, so the destination always ends up as long as the longest source. A missing key counts as an empty string, and if the result is empty the destination key is deleted. The reply is the length of the stored value in bytes. +Use `ARGET` to read the value stored at a single array index. -`AND`, `OR`, and `XOR` accept any number of source keys and `NOT` accepts exactly one. The remaining operators compare the first key with the rest: `DIFF` keeps the bits set in the first key and in none of the others, `DIFF1` keeps the bits set in at least one of the other keys but not in the first, `ANDOR` keeps the bits set in the first key and in at least one of the others, and `ONE` keeps the bits set in exactly one of the source keys. `DIFF`, `DIFF1`, and `ANDOR` each require at least two source keys. +The reply is null when the slot is empty and when the key does not exist at all, so a missing array and a hole inside an existing one look the same. Use [`ARCOUNT`](/docs/redis/commands/array/arcount) or [`EXISTS`](/docs/redis/commands/generic/exists) when the difference matters. To read many slots at once, [`ARMGET`](/docs/redis/commands/array/armget) takes a list of indexes and [`ARGETRANGE`](/docs/redis/commands/array/argetrange) takes a range. -This is how bitmaps are used as sets: with one bit per user, `AND` gives users present in every bitmap and `OR` gives users present in any of them, and [`BITCOUNT`](/docs/redis/commands/bitmap/bitcount) then turns the result into a number. +See the [array command overview](/docs/redis/commands/array/overview) for the data model these commands share. ## Syntax ```redis -BITOP (AND | OR | XOR | NOT | DIFF | DIFF1 | ANDOR | ONE) [ ...] +ARGET ``` ## Arguments | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | -| `(AND \| OR \| XOR \| NOT \| DIFF \| DIFF1 \| ANDOR \| ONE)` | Yes | No | The bitwise operation to apply. `AND`, `OR`, and `XOR` combine any number of source keys and `NOT` inverts exactly one. The rest compare the first key with the others: `DIFF` keeps bits set in the first key and in none of the others, `DIFF1` keeps bits set in at least one of the others but not in the first, `ANDOR` keeps bits set in the first key and in at least one of the others, and `ONE` keeps bits set in exactly one source key. | -| `` | Yes | No | Redis key used as destkey. | -| `` | Yes | Yes | Redis key targeted by the command. | +| `` | Yes | No | Array key targeted by the command. | +| `` | Yes | No | Zero-based index to read. Must be between `0` and `18446744073709551614`. | ## Response @@ -17793,8 +17778,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Integer | -| RESP3 | Integer | +| RESP2 | Bulk string or Null bulk string or null array | +| RESP3 | Bulk string or Null | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -17809,42 +17794,24 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -BITOP AND destination-key source-key-1 source-key-2 +ARGET my-array 0 ``` -```ts -import { Redis } from "@upstash/redis"; - -const redis = Redis.fromEnv(); - -// AND operation -await redis.bitop("and", "destKey", "sourceKey1", "sourceKey2"); - -// OR operation -await redis.bitop("or", "destKey", "sourceKey1", "sourceKey2"); - -// XOR operation -await redis.bitop("xor", "destKey", "sourceKey1", "sourceKey2"); - -// NOT operation (only accepts one source key) -await redis.bitop("not", "destKey", "sourceKey"); -``` + + This command is not supported yet in `@upstash/redis`. + -```python -from upstash_redis import Redis - -redis = Redis.from_env() -result = redis.bitop("AND", "destination-key", "source-key-1", "source-key-2") -print(result) -``` + + This command is not supported yet in `upstash_redis`. + @@ -17854,7 +17821,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.bitop("AND", "destination-key", "source-key-1", "source-key-2"); +const result = await redis.arget("my-array", 0); console.log(result); ``` @@ -17868,7 +17835,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.bitOp("AND", "destination-key", ["source-key-1", "source-key-2"]); +const result = await client.arGet("my-array", 0); console.log(result); ``` @@ -17881,7 +17848,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.bitop("AND", "destination-key", "source-key-1", "source-key-2") +result = client.arget("my-array", 0) print(result) ``` @@ -17906,7 +17873,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.BitOpAnd(context.Background(), "destination-key", "source-key-1", "source-key-2").Result() + result, err := client.ARGet(context.Background(), "my-array", 0).Result() if err != nil { panic(err) } @@ -17924,7 +17891,7 @@ import java.net.URI; import redis.clients.jedis.Jedis; try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.bitop(redis.clients.jedis.args.BitOP.AND, "destination-key", "source-key-1", "source-key-2"); + Object result = jedis.arget("my-array", 0); System.out.println(result); } ``` @@ -17934,14 +17901,15 @@ try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { ```rust -use redis::TypedCommands; - fn main() -> redis::RedisResult<()> { let url = std::env::var("REDIS_URL").expect("REDIS_URL is not set"); let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let result = connection.bit_and("destination-key", &["source-key-1", "source-key-2"])?; + let mut command = redis::cmd("ARGET"); + command.arg("my-array"); + command.arg("0"); + let result: redis::Value = command.query(&mut connection)?; println!("{result:?}"); Ok(()) } @@ -17951,28 +17919,36 @@ fn main() -> redis::RedisResult<()> { -# BITPOS -Source: https://upstash.com/docs/redis/commands/bitmap/bitpos +# ARGETRANGE +Source: https://upstash.com/docs/redis/commands/array/argetrange -Use `BITPOS` to find the position of the first bit set to `0` or `1` in a string. +Use `ARGETRANGE` to read every slot in an inclusive index range. -The whole value is searched unless `` and `` are given, and those are byte offsets by default or bit offsets when `BIT` is given. Both ends are inclusive and may be negative to count backwards from the end of the value. The reply is always an absolute bit position counted from the start of the string, or `-1` when no matching bit is found. +The reply always has exactly one element per index in the range, with null for the slots that are empty, so the position of a value in the reply tells you its index without sending the indexes back. That makes it the right command when the range is mostly populated; for a sparse range, [`ARSCAN`](/docs/redis/commands/array/arscan) returns only the occupied slots together with their indexes. -One edge case is worth remembering: when you look for a `0` in a string of all ones and give no explicit end, the reply is the position of the first bit past the end of the string, because the value is treated as if it were followed by an infinite run of zero bits. Bounding the search with an explicit range returns `-1` in the same situation. +Passing a start greater than the end reverses the order of the reply rather than returning an error, which is how you read a window newest-first. A range wider than 1,000,000 indexes is rejected, because the reply is materialized in full. + +See the [array command overview](/docs/redis/commands/array/overview) for the data model these commands share. ## Syntax ```redis -BITPOS [ [ [BYTE | BIT]]] +ARGETRANGE ``` ## Arguments | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | -| `` | Yes | No | Redis key targeted by the command. | -| `` | Yes | No | Bit value to look for: `0` or `1`. | -| ` [ [BYTE \| BIT]]` | No | No | Range to search. Offsets are byte-based unless `BIT` is given; negative offsets count from the end. | +| `` | Yes | No | Array key targeted by the command. | +| `` | Yes | No | First index of the range, inclusive. | +| `` | Yes | No | Last index of the range, inclusive. | + +## Important points + +* The range is inclusive at both ends and the reply always has `|end - start| + 1` elements. +* When `` is greater than ``, the reply is ordered from the higher index down to the lower one. +* A range covering more than 1,000,000 indexes returns `ERR range exceeds maximum of 1000000 items`. ## Response @@ -17980,8 +17956,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Integer: the position of the first matching bit, or `-1` | -| RESP3 | Integer: the position of the first matching bit, or `-1` | +| RESP2 | Array of bulk strings and null bulk strings | +| RESP3 | Array of bulk strings and nulls | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -17996,32 +17972,24 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -BITPOS my-key 1 +ARGETRANGE my-array 0 9 ``` -```ts -import { Redis } from "@upstash/redis"; - -const redis = Redis.fromEnv(); - -await redis.bitpos("key", 1); -``` + + This command is not supported yet in `@upstash/redis`. + -```python -from upstash_redis import Redis - -redis = Redis.from_env() -result = redis.bitpos("my-key", 1) -print(result) -``` + + This command is not supported yet in `upstash_redis`. + @@ -18031,7 +17999,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.bitpos("my-key", "1"); +const result = await redis.argetrange("my-array", 0, 9); console.log(result); ``` @@ -18045,7 +18013,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.bitPos("my-key", 1); +const result = await client.arGetRange("my-array", 0, 9); console.log(result); ``` @@ -18058,7 +18026,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.bitpos("my-key", 1) +result = client.argetrange("my-array", 0, 9) print(result) ``` @@ -18083,7 +18051,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.BitPos(context.Background(), "my-key", 1).Result() + result, err := client.ARGetRange(context.Background(), "my-array", 0, 9).Result() if err != nil { panic(err) } @@ -18101,7 +18069,7 @@ import java.net.URI; import redis.clients.jedis.Jedis; try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.bitpos("my-key", true); + Object result = jedis.argetrange("my-array", 0, 9); System.out.println(result); } ``` @@ -18116,9 +18084,10 @@ fn main() -> redis::RedisResult<()> { let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let mut command = redis::cmd("BITPOS"); - command.arg("my-key"); - command.arg("1"); + let mut command = redis::cmd("ARGETRANGE"); + command.arg("my-array"); + command.arg("0"); + command.arg("9"); let result: redis::Value = command.query(&mut connection)?; println!("{result:?}"); Ok(()) @@ -18129,25 +18098,46 @@ fn main() -> redis::RedisResult<()> { -# GETBIT -Source: https://upstash.com/docs/redis/commands/bitmap/getbit +# ARGREP +Source: https://upstash.com/docs/redis/commands/array/argrep -Use `GETBIT` to read a single bit of the string stored at a key. +Use `ARGREP` to find the array slots whose value matches one or more predicates. -The offset is counted in bits from the start of the value, so offset `0` is the most significant bit of the first byte. When the key does not exist, or the offset lies past the end of the stored string, the bit reads as `0` rather than producing an error. +Each predicate is a keyword and a pattern: `EXACT` compares the whole value, `MATCH` looks for a substring, `GLOB` applies a glob pattern with `*` and `?`, and `RE` applies a regular expression. Several predicates can be given in one call; by default a slot matches when any of them matches, and `AND` switches that to requiring all of them. `NOCASE` makes every predicate in the call case-insensitive. + +The bounds accept `-` and `+` as shorthand for the first and last possible index, so a whole array can be searched without knowing its extent. By default the reply is the list of matching indexes; `WITHVALUES` returns index-value pairs instead, which saves a follow-up [`ARMGET`](/docs/redis/commands/array/armget) when you need the data as well as its position. + +See the [array command overview](/docs/redis/commands/array/overview) for the data model these commands share. ## Syntax ```redis -GETBIT +ARGREP + | MATCH | GLOB | RE > [...] + [AND | OR] [NOCASE] [WITHVALUES] [LIMIT ] ``` ## Arguments | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | -| `` | Yes | No | Redis key targeted by the command. | -| `` | Yes | No | Zero-based bit offset to read. | +| `` | Yes | No | Array key targeted by the command. | +| `` | Yes | No | First index of the range, inclusive. `-` means the lowest possible index. | +| `` | Yes | No | Last index of the range, inclusive. `+` means the highest possible index. | +| `EXACT ` | No | Yes | Match slots whose value equals ``. | +| `MATCH ` | No | Yes | Match slots whose value contains ``. | +| `GLOB ` | No | Yes | Match slots whose value matches the glob ``. | +| `RE ` | No | Yes | Match slots whose value matches the regular expression ``. | +| `(AND \| OR)` | No | No | Combine several predicates. `OR` matches a slot when any predicate matches and is the default; `AND` requires all of them. | +| `NOCASE` | No | No | Compare case-insensitively. | +| `WITHVALUES` | No | No | Return index-value pairs instead of bare indexes. | +| `LIMIT ` | No | No | Maximum number of matches to return. Must be greater than `0`. | + +## Important points + +* At least one predicate is required, and a call may carry at most 250 of them. +* A regular expression may be at most 2048 bytes long, and backreferences (`\1` through `\9`) are not supported. +* `AND` and `OR` apply to the whole call, not to the predicate they follow. ## Response @@ -18155,8 +18145,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Integer: the bit at the offset, `0` or `1` | -| RESP3 | Integer: the bit at the offset, `0` or `1` | +| RESP2 | Array of indexes, or array of two-element index-value arrays with `WITHVALUES` | +| RESP3 | Array of indexes, or array of two-element index-value arrays with `WITHVALUES` | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -18171,32 +18161,24 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -GETBIT my-key 0 +ARGREP my-array - + GLOB error:* LIMIT 10 ``` -```ts -import { Redis } from "@upstash/redis"; - -const redis = Redis.fromEnv(); - -const bit = await redis.getbit(key, 4); -``` + + This command is not supported yet in `@upstash/redis`. + -```python -from upstash_redis import Redis - -redis = Redis.from_env() -result = redis.getbit("my-key", 0) -print(result) -``` + + This command is not supported yet in `upstash_redis`. + @@ -18206,7 +18188,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.getbit("my-key", "0"); +const result = await redis.argrep("my-array", "-", "+", "GLOB", "error:*", "LIMIT", 10); console.log(result); ``` @@ -18220,7 +18202,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.getBit("my-key", 0); +const result = await client.arGrep("my-array", "-", "+", [["GLOB", "error:*"]], { LIMIT: 10 }); console.log(result); ``` @@ -18232,8 +18214,10 @@ console.log(result); import os import redis +from redis.commands.core import ArrayPredicateType + client = redis.from_url(os.environ["REDIS_URL"]) -result = client.getbit("my-key", 0) +result = client.argrep("my-array", "-", "+", [(ArrayPredicateType.GLOB, "error:*")], limit=10) print(result) ``` @@ -18258,7 +18242,10 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.GetBit(context.Background(), "my-key", 0).Result() + result, err := client.ARGrep(context.Background(), "my-array", "-", "+", &redis.ARGrepArgs{ + Predicates: []redis.ARGrepPredicate{{Type: redis.ARGrepGlob, Value: "error:*"}}, + Limit: 10, + }).Result() if err != nil { panic(err) } @@ -18274,9 +18261,10 @@ func main() { import java.net.URI; import redis.clients.jedis.Jedis; +import redis.clients.jedis.params.ArgrepParams; try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.getbit("my-key", 0); + Object result = jedis.argrep("my-array", ArgrepParams.unbounded().glob("error:*").limit(10)); System.out.println(result); } ``` @@ -18286,14 +18274,20 @@ try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { ```rust -use redis::TypedCommands; - fn main() -> redis::RedisResult<()> { let url = std::env::var("REDIS_URL").expect("REDIS_URL is not set"); let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let result = connection.getbit("my-key", 0)?; + let mut command = redis::cmd("ARGREP"); + command.arg("my-array"); + command.arg("-"); + command.arg("+"); + command.arg("GLOB"); + command.arg("error:*"); + command.arg("LIMIT"); + command.arg("10"); + let result: redis::Value = command.query(&mut connection)?; println!("{result:?}"); Ok(()) } @@ -18303,41 +18297,33 @@ fn main() -> redis::RedisResult<()> { -# Bitmap commands -Source: https://upstash.com/docs/redis/commands/bitmap/overview - - -Count set bits in a string -Perform arbitrary bitfield operations -Read-only bitfield operations -Perform bitwise operations between strings -Find first bit set or clear in a string -Get the bit value at offset -Set or clear the bit at offset - +# ARINFO +Source: https://upstash.com/docs/redis/commands/array/arinfo -# SETBIT -Source: https://upstash.com/docs/redis/commands/bitmap/setbit +Use `ARINFO` to inspect how an array is laid out in memory. -Use `SETBIT` to set a single bit of the string stored at a key to `0` or `1`. +Alongside `len` and `count`, which [`ARLEN`](/docs/redis/commands/array/arlen) and [`ARCOUNT`](/docs/redis/commands/array/arcount) also report, the reply describes the slice structure the array is stored in and where the append cursor sits. That is useful when a sparse array is not behaving the way its access pattern suggests it should: a low fill ratio across many slices means the indexes in use are spread thinly, which costs more memory per stored value than a denser layout would. -The offset is counted in bits from the start of the value and the reply is the bit's previous value. If the key does not exist, or the offset lies past the end of the current value, the string is first extended with zero bits, so writing to a large offset allocates every byte up to it. Keep offsets dense, for example by mapping each user to a small sequential id. +`FULL` adds statistics about the dense and sparse slices, which requires walking the whole array rather than reading summary counters. -Bitmaps built this way are read back with [`GETBIT`](/docs/redis/commands/bitmap/getbit), counted with [`BITCOUNT`](/docs/redis/commands/bitmap/bitcount), and combined with [`BITOP`](/docs/redis/commands/bitmap/bitop). +See the [array command overview](/docs/redis/commands/array/overview) for the data model these commands share. ## Syntax ```redis -SETBIT +ARINFO [FULL] ``` ## Arguments | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | -| `` | Yes | No | Redis key targeted by the command. | -| `` | Yes | No | Zero-based bit offset to write. | -| `` | Yes | No | Bit value to write: `0` or `1`. | +| `` | Yes | No | Array key targeted by the command. | +| `FULL` | No | No | Also report per-slice statistics. This walks the whole array. | + +## Important points + +* A key that does not exist returns `ERR no such key`. ## Response @@ -18345,8 +18331,25 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Integer: the previous bit at the offset, `0` or `1` | -| RESP3 | Integer: the previous bit at the offset, `0` or `1` | +| RESP2 | Flat array of alternating field names and values | +| RESP3 | Map | + +### Fields + +| Field | Description | +| --- | --- | +| `len` | Highest occupied index plus one, as reported by [`ARLEN`](/docs/redis/commands/array/arlen). | +| `count` | Number of occupied slots, as reported by [`ARCOUNT`](/docs/redis/commands/array/arcount). | +| `slice-size` | Number of indexes covered by one slice. | +| `slices` | Number of slices currently allocated. | +| `directory-size` | Number of entries in the slice directory. | +| `super-dir-entries` | Number of entries in the top-level directory. | +| `next-insert-index` | Index the next [`ARINSERT`](/docs/redis/commands/array/arinsert) would write to. | +| `dense-slices` | Slices stored in dense form. Only with `FULL`. | +| `sparse-slices` | Slices stored in sparse form. Only with `FULL`. | +| `avg-dense-size` | Average number of values in a dense slice. Only with `FULL`. | +| `avg-dense-fill` | Average fill ratio of a dense slice. Only with `FULL`. | +| `avg-sparse-size` | Average number of values in a sparse slice. Only with `FULL`. | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -18361,32 +18364,24 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -SETBIT my-key 0 1 +ARINFO my-array ``` -```ts -import { Redis } from "@upstash/redis"; - -const redis = Redis.fromEnv(); - -const originalBit = await redis.setbit(key, 4, 1); -``` + + This command is not supported yet in `@upstash/redis`. + -```python -from upstash_redis import Redis - -redis = Redis.from_env() -result = redis.setbit("my-key", 0, 1) -print(result) -``` + + This command is not supported yet in `upstash_redis`. + @@ -18396,7 +18391,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.setbit("my-key", "0", "1"); +const result = await redis.arinfo("my-array"); console.log(result); ``` @@ -18410,7 +18405,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.setBit("my-key", 0, 1); +const result = await client.arInfo("my-array"); console.log(result); ``` @@ -18423,7 +18418,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.setbit("my-key", 0, 1) +result = client.arinfo("my-array") print(result) ``` @@ -18448,7 +18443,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.SetBit(context.Background(), "my-key", 0, 1).Result() + result, err := client.ARInfo(context.Background(), "my-array").Result() if err != nil { panic(err) } @@ -18466,7 +18461,7 @@ import java.net.URI; import redis.clients.jedis.Jedis; try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.setbit("my-key", 0, true); + Object result = jedis.arinfo("my-array"); System.out.println(result); } ``` @@ -18476,14 +18471,14 @@ try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { ```rust -use redis::TypedCommands; - fn main() -> redis::RedisResult<()> { let url = std::env::var("REDIS_URL").expect("REDIS_URL is not set"); let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let result = connection.setbit("my-key", 0, true)?; + let mut command = redis::cmd("ARINFO"); + command.arg("my-array"); + let result: redis::Value = command.query(&mut connection)?; println!("{result:?}"); Ok(()) } @@ -18493,29 +18488,34 @@ fn main() -> redis::RedisResult<()> { -# AUTH -Source: https://upstash.com/docs/redis/commands/connection/auth +# ARINSERT +Source: https://upstash.com/docs/redis/commands/array/arinsert -Use `AUTH` to authenticate the current connection, with a password alone or with a username and password when ACL users are configured. +Use `ARINSERT` to append one or more values to the end of an array. -Until the connection is authenticated the server rejects other commands with an error. Client libraries usually send `AUTH` for you as part of connecting when the credentials are part of the connection string, so applications rarely call it directly. The password is sent to the server on every connection, which is why Upstash endpoints use TLS. +Each array keeps an append cursor holding the last index written by an append, so `ARINSERT` does not need to look up where the data ends: the first value goes to the slot after the cursor, the rest follow it, and the cursor moves to the last one. The reply is the index that last value received, which is what a producer records to point at what it just wrote. + +Because the cursor is separate from the contents, appending is unaffected by deletions: freeing slots with [`ARDEL`](/docs/redis/commands/array/ardel) never causes a later append to reuse an index. Use [`ARSEEK`](/docs/redis/commands/array/arseek) to move the cursor deliberately and [`ARNEXT`](/docs/redis/commands/array/arnext) to read where the next append would land. + +See the [array command overview](/docs/redis/commands/array/overview) for the data model these commands share. ## Syntax ```redis -AUTH [] +ARINSERT [ ...] ``` ## Arguments | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | -| `` | No | No | Username to authenticate as. | -| `` | Yes | No | Password to authenticate with. | +| `` | Yes | No | Array key targeted by the command. | +| `` | Yes | Yes | Value to append. Repeat to append several values in one call. | ## Important points -* This is a connection-oriented command and is available over native Redis TCP, not the stateless REST endpoint. +* The reply is the index of the last value written, not the number of values written. +* Appending past the highest supported index returns `ERR insert index overflow`. ## Response @@ -18523,8 +18523,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Simple string `OK` | -| RESP3 | Simple string `OK` | +| RESP2 | Integer, or bulk string when the index exceeds the signed 64-bit range | +| RESP3 | Integer, or Big number when the index exceeds the signed 64-bit range | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -18539,18 +18539,34 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -AUTH password +ARINSERT my-array hello world ``` + + + + This command is not supported yet in `@upstash/redis`. + + + + + + + + This command is not supported yet in `upstash_redis`. + + + + ```ts import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.auth("password"); +const result = await redis.arinsert("my-array", "hello", "world"); console.log(result); ``` @@ -18564,7 +18580,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.auth({ password: "password" }); +const result = await client.arInsert("my-array", ["hello", "world"]); console.log(result); ``` @@ -18577,7 +18593,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.auth("password") +result = client.arinsert("my-array", "hello", "world") print(result) ``` @@ -18602,7 +18618,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.Do(context.Background(), "AUTH", "password").Result() + result, err := client.ARInsert(context.Background(), "my-array", "hello", "world").Result() if err != nil { panic(err) } @@ -18620,7 +18636,7 @@ import java.net.URI; import redis.clients.jedis.Jedis; try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.auth("password"); + Object result = jedis.arinsert("my-array", "hello", "world"); System.out.println(result); } ``` @@ -18635,8 +18651,10 @@ fn main() -> redis::RedisResult<()> { let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let mut command = redis::cmd("AUTH"); - command.arg("password"); + let mut command = redis::cmd("ARINSERT"); + command.arg("my-array"); + command.arg("hello"); + command.arg("world"); let result: redis::Value = command.query(&mut connection)?; println!("{result:?}"); Ok(()) @@ -18647,27 +18665,30 @@ fn main() -> redis::RedisResult<()> { -# CLIENT GETNAME -Source: https://upstash.com/docs/redis/commands/connection/client-getname +# ARLASTITEMS +Source: https://upstash.com/docs/redis/commands/array/arlastitems -Use `CLIENT GETNAME` to read the name assigned to the current connection with [`CLIENT SETNAME`](/docs/redis/commands/connection/client-setname). +Use `ARLASTITEMS` to read the values written most recently, without knowing where the cursor currently sits. -Connections start without a name and the reply is null until one is set. The name belongs to a single connection and is lost when it closes; it exists to make connections recognizable in [`CLIENT LIST`](/docs/redis/commands/connection/client-list) output when you are debugging which part of an application is doing what. +The command walks backward from the append cursor, wrapping to the end of the array when it passes index `0`, and collects up to `` values. That wrapping is what makes it the natural reader for a ring built with [`ARRING`](/docs/redis/commands/array/arring): it returns the current window in write order regardless of where the ring has wrapped to. On a plain array appended to with [`ARINSERT`](/docs/redis/commands/array/arinsert), it returns the tail. + +By default the values come back oldest-first, which is the order they were written in. `REV` reverses that, returning the newest value first. Slots that are empty come back as null, and `` is capped at the number of values the array holds. + +See the [array command overview](/docs/redis/commands/array/overview) for the data model these commands share. ## Syntax ```redis -CLIENT GETNAME +ARLASTITEMS [REV] ``` ## Arguments -This command takes no arguments. - -## Important points - -* This is a connection-oriented command and is available over native Redis TCP, not the stateless REST endpoint. -* This command can expose administrative information or make a broad destructive change. Restrict it to trusted code paths. +| Argument | Required | Repeatable | Description | +| --- | --- | --- | --- | +| `` | Yes | No | Array key targeted by the command. | +| `` | Yes | No | Maximum number of values to return. Capped at the number of values stored. | +| `REV` | No | No | Return the newest value first instead of last. | ## Response @@ -18675,8 +18696,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Null bulk string or null array or Bulk string | -| RESP3 | Null or Bulk string | +| RESP2 | Array of bulk strings and null bulk strings | +| RESP3 | Array of bulk strings and nulls | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -18691,18 +18712,34 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -CLIENT GETNAME +ARLASTITEMS my-array 10 ``` + + + + This command is not supported yet in `@upstash/redis`. + + + + + + + + This command is not supported yet in `upstash_redis`. + + + + ```ts import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.client("GETNAME"); +const result = await redis.arlastitems("my-array", 10); console.log(result); ``` @@ -18716,7 +18753,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.clientGetName(); +const result = await client.arLastItems("my-array", 10); console.log(result); ``` @@ -18729,7 +18766,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.client_getname() +result = client.arlastitems("my-array", 10) print(result) ``` @@ -18754,7 +18791,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.ClientGetName(context.Background()).Result() + result, err := client.ARLastItems(context.Background(), "my-array", 10, false).Result() if err != nil { panic(err) } @@ -18772,7 +18809,7 @@ import java.net.URI; import redis.clients.jedis.Jedis; try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.clientGetname(); + Object result = jedis.arlastitems("my-array", 10); System.out.println(result); } ``` @@ -18782,14 +18819,15 @@ try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { ```rust -use redis::TypedCommands; - fn main() -> redis::RedisResult<()> { let url = std::env::var("REDIS_URL").expect("REDIS_URL is not set"); let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let result = connection.client_getname()?; + let mut command = redis::cmd("ARLASTITEMS"); + command.arg("my-array"); + command.arg("10"); + let result: redis::Value = command.query(&mut connection)?; println!("{result:?}"); Ok(()) } @@ -18799,27 +18837,32 @@ fn main() -> redis::RedisResult<()> { -# CLIENT ID -Source: https://upstash.com/docs/redis/commands/connection/client-id +# ARLEN +Source: https://upstash.com/docs/redis/commands/array/arlen -Use `CLIENT ID` to get the unique identifier the server assigned to the current connection. +Use `ARLEN` to get the length of an array, meaning the highest occupied index plus one. -IDs are integers that never repeat and always increase, so a larger ID means a connection that was established later. The ID identifies this connection in [`CLIENT LIST`](/docs/redis/commands/connection/client-list) output, which makes it useful when correlating application logs with server-side connection state. +Because arrays are sparse, this is a measure of extent rather than of contents: writing a single value at index 1000 makes `ARLEN` report `1001` while [`ARCOUNT`](/docs/redis/commands/array/arcount) still reports `1`. Read it as the exclusive upper bound for any full scan of the array. + +A key that does not exist has length `0`. + +See the [array command overview](/docs/redis/commands/array/overview) for the data model these commands share. ## Syntax ```redis -CLIENT ID +ARLEN ``` ## Arguments -This command takes no arguments. +| Argument | Required | Repeatable | Description | +| --- | --- | --- | --- | +| `` | Yes | No | Array key targeted by the command. | ## Important points -* This is a connection-oriented command and is available over native Redis TCP, not the stateless REST endpoint. -* This command can expose administrative information or make a broad destructive change. Restrict it to trusted code paths. +* A length above the signed 64-bit range is returned as a big number reply rather than an integer. ## Response @@ -18827,8 +18870,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Integer | -| RESP3 | Integer | +| RESP2 | Integer, or bulk string when the length exceeds the signed 64-bit range | +| RESP3 | Integer, or Big number when the length exceeds the signed 64-bit range | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -18843,18 +18886,34 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -CLIENT ID +ARLEN my-array ``` + + + + This command is not supported yet in `@upstash/redis`. + + + + + + + + This command is not supported yet in `upstash_redis`. + + + + ```ts import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.client("ID"); +const result = await redis.arlen("my-array"); console.log(result); ``` @@ -18868,7 +18927,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.clientId(); +const result = await client.arLen("my-array"); console.log(result); ``` @@ -18881,7 +18940,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.client_id() +result = client.arlen("my-array") print(result) ``` @@ -18906,7 +18965,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.ClientID(context.Background()).Result() + result, err := client.ARLen(context.Background(), "my-array").Result() if err != nil { panic(err) } @@ -18924,7 +18983,7 @@ import java.net.URI; import redis.clients.jedis.Jedis; try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.clientId(); + Object result = jedis.arlen("my-array"); System.out.println(result); } ``` @@ -18934,14 +18993,14 @@ try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { ```rust -use redis::TypedCommands; - fn main() -> redis::RedisResult<()> { let url = std::env::var("REDIS_URL").expect("REDIS_URL is not set"); let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let result = connection.client_id()?; + let mut command = redis::cmd("ARLEN"); + command.arg("my-array"); + let result: redis::Value = command.query(&mut connection)?; println!("{result:?}"); Ok(()) } @@ -18951,27 +19010,29 @@ fn main() -> redis::RedisResult<()> { -# CLIENT INFO -Source: https://upstash.com/docs/redis/commands/connection/client-info +# ARMGET +Source: https://upstash.com/docs/redis/commands/array/armget -Use `CLIENT INFO` to get a line of statistics about the connection issuing the command. +Use `ARMGET` to read several array slots in one call. -The reply is a single line of space-separated `field=value` pairs describing the connection: its id, name, address, age, idle time, the number of commands it has run, and the last command it executed. It reports the same fields as one line of [`CLIENT LIST`](/docs/redis/commands/connection/client-list), limited to your own connection, which makes it a cheap way to confirm what the server thinks of the connection you are on. Parse it defensively, since fields can be added over time. +The reply has one element per requested index, in the order the indexes were given, with null for an empty slot. That positional guarantee is what makes it usable as a gather step: the caller can zip the reply back onto its own list of indexes without re-checking which ones existed. + +A key that does not exist behaves like an array where every slot is empty, so the reply is a list of nulls rather than an error. + +See the [array command overview](/docs/redis/commands/array/overview) for the data model these commands share. ## Syntax ```redis -CLIENT INFO +ARMGET [ ...] ``` ## Arguments -This command takes no arguments. - -## Important points - -* This is a connection-oriented command and is available over native Redis TCP, not the stateless REST endpoint. -* This command can expose administrative information or make a broad destructive change. Restrict it to trusted code paths. +| Argument | Required | Repeatable | Description | +| --- | --- | --- | --- | +| `` | Yes | No | Array key targeted by the command. | +| `` | Yes | Yes | Zero-based index to read. Repeat to read several slots in one call. | ## Response @@ -18979,8 +19040,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Bulk string | -| RESP3 | Bulk string | +| RESP2 | Array of bulk strings and null bulk strings | +| RESP3 | Array of bulk strings and nulls | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -18995,18 +19056,34 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -CLIENT INFO +ARMGET my-array 0 1 5 ``` + + + + This command is not supported yet in `@upstash/redis`. + + + + + + + + This command is not supported yet in `upstash_redis`. + + + + ```ts import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.client("INFO"); +const result = await redis.armget("my-array", 0, 1, 5); console.log(result); ``` @@ -19020,7 +19097,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.clientInfo(); +const result = await client.arMGet("my-array", [0, 1, 5]); console.log(result); ``` @@ -19033,7 +19110,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.client_info() +result = client.armget("my-array", 0, 1, 5) print(result) ``` @@ -19058,7 +19135,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.ClientInfo(context.Background()).Result() + result, err := client.ARMGet(context.Background(), "my-array", 0, 1, 5).Result() if err != nil { panic(err) } @@ -19076,7 +19153,7 @@ import java.net.URI; import redis.clients.jedis.Jedis; try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.clientInfo(); + Object result = jedis.armget("my-array", 0, 1, 5); System.out.println(result); } ``` @@ -19091,8 +19168,11 @@ fn main() -> redis::RedisResult<()> { let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let mut command = redis::cmd("CLIENT"); - command.arg("INFO"); + let mut command = redis::cmd("ARMGET"); + command.arg("my-array"); + command.arg("0"); + command.arg("1"); + command.arg("5"); let result: redis::Value = command.query(&mut connection)?; println!("{result:?}"); Ok(()) @@ -19103,27 +19183,34 @@ fn main() -> redis::RedisResult<()> { -# CLIENT LIST -Source: https://upstash.com/docs/redis/commands/connection/client-list +# ARMSET +Source: https://upstash.com/docs/redis/commands/array/armset -Use `CLIENT LIST` to get one line of statistics for every client connection to the server. +Use `ARMSET` to write several index-value pairs into an array in one atomic call. -Each line holds space-separated `field=value` pairs with the connection's id, name, address, age, idle time, protocol version, and last command, so the reply gives a snapshot of who is connected and what they are doing. It is the usual starting point for tracking down connection leaks and idle connections. The reply grows with the number of clients, so avoid calling it on a hot path, and parse it defensively because fields can be added over time. +Unlike [`ARSET`](/docs/redis/commands/array/arset), which fills a contiguous run from a starting index, `ARMSET` takes an explicit index for every value, so scattered slots can be updated together without a round trip each. The reply counts the slots that were newly occupied; pairs that overwrote an existing value contribute nothing to it. + +If the same index appears more than once in one call, the last value given for it wins. + +See the [array command overview](/docs/redis/commands/array/overview) for the data model these commands share. ## Syntax ```redis -CLIENT LIST +ARMSET [ ...] ``` ## Arguments -This command takes no arguments. +| Argument | Required | Repeatable | Description | +| --- | --- | --- | --- | +| `` | Yes | No | Array key targeted by the command. | +| ` ` | Yes | Yes | Index and the value to store in it. Repeat to write several slots in one call. | ## Important points -* This is a connection-oriented command and is available over native Redis TCP, not the stateless REST endpoint. -* This command can expose administrative information or make a broad destructive change. Restrict it to trusted code paths. +* The number of arguments after the key must be even; an odd count returns a wrong number of arguments error. +* The reply counts newly occupied slots only. Overwriting an existing value contributes `0`. ## Response @@ -19131,8 +19218,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Bulk string | -| RESP3 | Bulk string | +| RESP2 | Integer | +| RESP3 | Integer | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -19147,18 +19234,34 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -CLIENT LIST +ARMSET my-array 0 hello 5 world ``` + + + + This command is not supported yet in `@upstash/redis`. + + + + + + + + This command is not supported yet in `upstash_redis`. + + + + ```ts import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.client("LIST"); +const result = await redis.armset("my-array", 0, "hello", 5, "world"); console.log(result); ``` @@ -19172,7 +19275,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.clientList(); +const result = await client.arMSet("my-array", { 0: "hello", 5: "world" }); console.log(result); ``` @@ -19185,7 +19288,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.client_list() +result = client.armset("my-array", {0: "hello", 5: "world"}) print(result) ``` @@ -19210,7 +19313,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.ClientList(context.Background()).Result() + result, err := client.ARMSet(context.Background(), "my-array", redis.AREntry{Index: 0, Value: "hello"}, redis.AREntry{Index: 5, Value: "world"}).Result() if err != nil { panic(err) } @@ -19224,11 +19327,12 @@ func main() { ```java import java.net.URI; +import java.util.Map; import redis.clients.jedis.Jedis; try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.clientList(); + Object result = jedis.armset("my-array", Map.of(0L, "hello", 5L, "world")); System.out.println(result); } ``` @@ -19243,8 +19347,12 @@ fn main() -> redis::RedisResult<()> { let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let mut command = redis::cmd("CLIENT"); - command.arg("LIST"); + let mut command = redis::cmd("ARMSET"); + command.arg("my-array"); + command.arg("0"); + command.arg("hello"); + command.arg("5"); + command.arg("world"); let result: redis::Value = command.query(&mut connection)?; println!("{result:?}"); Ok(()) @@ -19255,30 +19363,33 @@ fn main() -> redis::RedisResult<()> { -# CLIENT SETINFO -Source: https://upstash.com/docs/redis/commands/connection/client-setinfo +# ARNEXT +Source: https://upstash.com/docs/redis/commands/array/arnext -Use `CLIENT SETINFO` to attach library identification to the current connection. +Use `ARNEXT` to read the index that the next [`ARINSERT`](/docs/redis/commands/array/arinsert) would write to. -`LIB-NAME` records the name of the client library and `LIB-VER` its version. Both values then appear in [`CLIENT INFO`](/docs/redis/commands/connection/client-info) and [`CLIENT LIST`](/docs/redis/commands/connection/client-list) output, which makes it possible to tell which application or SDK version owns a connection. Most client libraries send this during the handshake, so applications rarely call it themselves. +It reports the append cursor without moving it, so a producer can find out where its next write will land, or a reader can learn how far appends have progressed, without writing anything. The reply is `0` both when the key does not exist and when it exists but has never been appended to, since in both cases the next append goes to index `0`. + +Because the cursor tracks appends rather than contents, `ARNEXT` is unaffected by [`ARSET`](/docs/redis/commands/array/arset) writes and by deletions. [`ARSEEK`](/docs/redis/commands/array/arseek) is the command that moves it. + +See the [array command overview](/docs/redis/commands/array/overview) for the data model these commands share. ## Syntax ```redis -CLIENT SETINFO +ARNEXT ``` ## Arguments | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | -| `LIB-NAME \| LIB-VER` | Yes | No | Attribute to update: the client library name or version. | -| `value` | Yes | No | Value to store for the selected client attribute. | +| `` | Yes | No | Array key targeted by the command. | ## Important points -* This is a connection-oriented command and is available over native Redis TCP, not the stateless REST endpoint. -* This command can expose administrative information or make a broad destructive change. Restrict it to trusted code paths. +* The reply is `0` for a key that does not exist and for one that has never been appended to. +* The reply is null when the cursor already sits on the highest supported index, so no further append is possible. ## Response @@ -19286,8 +19397,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Simple string `OK` | -| RESP3 | Simple string `OK` | +| RESP2 | Integer, or bulk string when the index exceeds the signed 64-bit range, or Null bulk string | +| RESP3 | Integer, or Big number when the index exceeds the signed 64-bit range, or Null | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -19302,18 +19413,34 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -CLIENT SETINFO LIB-NAME my-client +ARNEXT my-array ``` + + + + This command is not supported yet in `@upstash/redis`. + + + + + + + + This command is not supported yet in `upstash_redis`. + + + + ```ts import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.call("CLIENT", "SETINFO", "LIB-NAME", "my-client"); +const result = await redis.arnext("my-array"); console.log(result); ``` @@ -19327,7 +19454,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.sendCommand(["CLIENT", "SETINFO", "LIB-NAME", "my-client"]); +const result = await client.arNext("my-array"); console.log(result); ``` @@ -19340,7 +19467,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.client_setinfo("LIB-NAME", "my-client") +result = client.arnext("my-array") print(result) ``` @@ -19365,7 +19492,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.Do(context.Background(), "CLIENT", "SETINFO", "LIB-NAME", "my-client").Result() + result, err := client.ARNext(context.Background(), "my-array").Result() if err != nil { panic(err) } @@ -19383,7 +19510,7 @@ import java.net.URI; import redis.clients.jedis.Jedis; try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.clientSetInfo(redis.clients.jedis.args.ClientAttributeOption.LIB_NAME, "my-client"); + Object result = jedis.arnext("my-array"); System.out.println(result); } ``` @@ -19398,10 +19525,8 @@ fn main() -> redis::RedisResult<()> { let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let mut command = redis::cmd("CLIENT"); - command.arg("SETINFO"); - command.arg("LIB-NAME"); - command.arg("my-client"); + let mut command = redis::cmd("ARNEXT"); + command.arg("my-array"); let result: redis::Value = command.query(&mut connection)?; println!("{result:?}"); Ok(()) @@ -19412,29 +19537,44 @@ fn main() -> redis::RedisResult<()> { -# CLIENT SETNAME -Source: https://upstash.com/docs/redis/commands/connection/client-setname +# AROP +Source: https://upstash.com/docs/redis/commands/array/arop -Use `CLIENT SETNAME` to label the current connection with a name of your choice. +Use `AROP` to reduce the values in an index range to a single number, on the server. -The name shows up in [`CLIENT LIST`](/docs/redis/commands/connection/client-list) and [`CLIENT INFO`](/docs/redis/commands/connection/client-info) output, which is handy when several components of an application share one database and you want to tell their connections apart. The name may not contain spaces or newlines, each call replaces the previous name, and passing an empty string clears it. It lives only as long as the connection. +`SUM`, `MIN`, and `MAX` treat the values as numbers; `AND`, `OR`, and `XOR` fold them together as 64-bit integers, with fractional values truncated. All six ignore values that cannot be parsed as a number, and reply with null when the range contains nothing they could use, which distinguishes "no data" from a real result of `0`. `USED` counts the occupied slots in the range and `MATCH` counts the slots whose value equals a given string; both always reply with an integer. + +Running the reduction where the data lives keeps a range scan off the wire, which is the point when the array is a window of samples and you only need its total, extremes, or population. + +See the [array command overview](/docs/redis/commands/array/overview) for the data model these commands share. ## Syntax ```redis -CLIENT SETNAME +AROP > ``` ## Arguments | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | -| `connection-name` | Yes | No | Name to associate with this TCP connection. | +| `` | Yes | No | Array key targeted by the command. | +| `` | Yes | No | First index of the range, inclusive. | +| `` | Yes | No | Last index of the range, inclusive. | +| `SUM` | No | No | Sum of the numeric values in the range. | +| `MIN` | No | No | Smallest numeric value in the range. | +| `MAX` | No | No | Largest numeric value in the range. | +| `AND` | No | No | Bitwise AND of the values in the range, as 64-bit integers. | +| `OR` | No | No | Bitwise OR of the values in the range, as 64-bit integers. | +| `XOR` | No | No | Bitwise XOR of the values in the range, as 64-bit integers. | +| `USED` | No | No | Number of occupied slots in the range. | +| `MATCH ` | No | No | Number of slots in the range whose value equals ``. | ## Important points -* This is a connection-oriented command and is available over native Redis TCP, not the stateless REST endpoint. -* This command can expose administrative information or make a broad destructive change. Restrict it to trusted code paths. +* Exactly one operation must be given. +* Values that are not numbers are skipped by `SUM`, `MIN`, `MAX`, `AND`, `OR`, and `XOR`, and counted normally by `USED` and `MATCH`. +* `SUM`, `MIN`, `MAX`, `AND`, `OR`, and `XOR` reply with null when no value in the range could be used. `USED` and `MATCH` reply with `0`. ## Response @@ -19442,8 +19582,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Simple string `OK` | -| RESP3 | Simple string `OK` | +| RESP2 | Bulk string for `SUM`, `MIN`, and `MAX`; Integer for `AND`, `OR`, `XOR`, `USED`, and `MATCH`; Null bulk string when there is nothing to aggregate | +| RESP3 | Bulk string for `SUM`, `MIN`, and `MAX`; Integer for `AND`, `OR`, `XOR`, `USED`, and `MATCH`; Null when there is nothing to aggregate | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -19458,18 +19598,34 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -CLIENT SETNAME worker-1 +AROP my-array 0 999 SUM ``` + + + + This command is not supported yet in `@upstash/redis`. + + + + + + + + This command is not supported yet in `upstash_redis`. + + + + ```ts import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.client("SETNAME", "worker-1"); +const result = await redis.arop("my-array", 0, 999, "SUM"); console.log(result); ``` @@ -19483,7 +19639,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.clientSetName("worker-1"); +const result = await client.arOp("my-array", 0, 999, "SUM"); console.log(result); ``` @@ -19495,8 +19651,10 @@ console.log(result); import os import redis +from redis.commands.core import ArrayAggregateOperations + client = redis.from_url(os.environ["REDIS_URL"]) -result = client.client_setname("worker-1") +result = client.arop("my-array", 0, 999, ArrayAggregateOperations.SUM) print(result) ``` @@ -19521,7 +19679,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.Do(context.Background(), "CLIENT", "SETNAME", "worker-1").Result() + result, err := client.AROpSum(context.Background(), "my-array", 0, 999).Result() if err != nil { panic(err) } @@ -19537,9 +19695,10 @@ func main() { import java.net.URI; import redis.clients.jedis.Jedis; +import redis.clients.jedis.args.ArrayAggregate; try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.clientSetname("worker-1"); + Object result = jedis.aropAggregate("my-array", 0, 999, ArrayAggregate.SUM); System.out.println(result); } ``` @@ -19549,14 +19708,17 @@ try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { ```rust -use redis::TypedCommands; - fn main() -> redis::RedisResult<()> { let url = std::env::var("REDIS_URL").expect("REDIS_URL is not set"); let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let result = connection.client_setname("worker-1")?; + let mut command = redis::cmd("AROP"); + command.arg("my-array"); + command.arg("0"); + command.arg("999"); + command.arg("SUM"); + let result: redis::Value = command.query(&mut connection)?; println!("{result:?}"); Ok(()) } @@ -19566,24 +19728,36 @@ fn main() -> redis::RedisResult<()> { -# ECHO -Source: https://upstash.com/docs/redis/commands/connection/echo +# ARRING +Source: https://upstash.com/docs/redis/commands/array/arring -Use `ECHO` to have the server send the given message back unchanged. +Use `ARRING` to append values to an array that wraps around after a fixed number of slots. -The command performs no work beyond the round trip, which makes it a simple way to verify that a connection is alive and that values survive the client library's encoding and decoding. For plain liveness checks [`PING`](/docs/redis/commands/connection/ping) is the more common choice. +The array is confined to indexes `0` through ` - 1`. Appends advance the same cursor [`ARINSERT`](/docs/redis/commands/array/arinsert) uses, but wrap back to `0` on reaching the end, so the newest `` values are kept and older ones are overwritten in place. That bounds the memory a stream of writes can consume without any trimming command, which is what makes it a fit for rolling windows such as the last N samples or the last N log lines. + +The size is stored with the key and every call restates it. Calling `ARRING` with a different size reshapes the ring: the most recent values that still fit are kept and relaid from index `0`, and anything outside the new window is dropped. The reply is the index the last value was written to; read the window back with [`ARLASTITEMS`](/docs/redis/commands/array/arlastitems), which follows the cursor and returns the values in write order. + +See the [array command overview](/docs/redis/commands/array/overview) for the data model these commands share. ## Syntax ```redis -ECHO +ARRING [ ...] ``` ## Arguments | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | -| `` | Yes | No | Message payload. | +| `` | Yes | No | Array key targeted by the command. | +| `` | Yes | No | Number of slots in the ring. Must be greater than `0`. | +| `` | Yes | Yes | Value to append. Repeat to append several values in one call. | + +## Important points + +* The reply is the index the last value was written to, not the number of values written. +* Calling `ARRING` with a size different from the stored one rebuilds the ring, keeping the most recent values that fit in the new size. +* Writing more values than `` in one call leaves only the last `` of them. ## Response @@ -19591,8 +19765,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Bulk string | -| RESP3 | Bulk string | +| RESP2 | Integer, or bulk string when the index exceeds the signed 64-bit range | +| RESP3 | Integer, or Big number when the index exceeds the signed 64-bit range | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -19607,33 +19781,24 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -ECHO hello +ARRING my-array 100 hello world ``` -```ts -import { Redis } from "@upstash/redis"; - -const redis = Redis.fromEnv(); - -const response = await redis.echo("hello world"); -console.log(response); // "hello world" -``` + + This command is not supported yet in `@upstash/redis`. + -```python -from upstash_redis import Redis - -redis = Redis.from_env() -result = redis.echo("hello") -print(result) -``` + + This command is not supported yet in `upstash_redis`. + @@ -19643,7 +19808,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.echo("hello"); +const result = await redis.arring("my-array", 100, "hello", "world"); console.log(result); ``` @@ -19657,7 +19822,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.echo("hello"); +const result = await client.arRing("my-array", 100, ["hello", "world"]); console.log(result); ``` @@ -19670,7 +19835,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.echo("hello") +result = client.arring("my-array", 100, "hello", "world") print(result) ``` @@ -19695,7 +19860,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.Echo(context.Background(), "hello").Result() + result, err := client.ARRing(context.Background(), "my-array", 100, "hello", "world").Result() if err != nil { panic(err) } @@ -19713,7 +19878,7 @@ import java.net.URI; import redis.clients.jedis.Jedis; try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.echo("hello"); + Object result = jedis.arring("my-array", 100, "hello", "world"); System.out.println(result); } ``` @@ -19728,8 +19893,11 @@ fn main() -> redis::RedisResult<()> { let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let mut command = redis::cmd("ECHO"); + let mut command = redis::cmd("ARRING"); + command.arg("my-array"); + command.arg("100"); command.arg("hello"); + command.arg("world"); let result: redis::Value = command.query(&mut connection)?; println!("{result:?}"); Ok(()) @@ -19740,33 +19908,36 @@ fn main() -> redis::RedisResult<()> { -# HELLO -Source: https://upstash.com/docs/redis/commands/connection/hello +# ARSCAN +Source: https://upstash.com/docs/redis/commands/array/arscan -Use `HELLO` to negotiate the protocol version of the connection and read the server handshake information. +Use `ARSCAN` to list the occupied slots of an array in an index range. -Passing `` switches the connection to RESP2 or RESP3. RESP3 adds native map, set, double, and push replies, so commands such as [`HGETALL`](/docs/redis/commands/hash/hgetall) or [`CONFIG GET`](/docs/redis/commands/server/config-get) come back as maps instead of flat arrays, and pub/sub messages arrive as push replies that do not block ordinary commands. `AUTH` authenticates in the same call and `SETNAME` names the connection, which lets a client complete its handshake in one round trip. +Unlike [`ARGETRANGE`](/docs/redis/commands/array/argetrange), which returns a null for every hole, `ARSCAN` skips empty slots and pairs each value with its index, so the cost of the reply tracks the data actually stored rather than the width of the range. That is what makes it usable over a sparse array whose indexes are, for instance, timestamps. -Called without arguments, `HELLO` only reports the server version, the protocol in use, the connection id, and the current role, leaving the protocol unchanged. +Results come back in ascending index order. `LIMIT` caps how many slots are returned, so a scan over a wide range can be walked in pages by using the last index of one page as the start of the next. + +See the [array command overview](/docs/redis/commands/array/overview) for the data model these commands share. ## Syntax ```redis -HELLO - [ - [AUTH ] - [SETNAME ]] +ARSCAN [LIMIT ] ``` ## Arguments | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | -| ` [AUTH ] [SETNAME ]` | No | No | Protocol version to switch to, optionally with credentials and a connection name. | +| `` | Yes | No | Array key targeted by the command. | +| `` | Yes | No | First index of the range, inclusive. | +| `` | Yes | No | Last index of the range, inclusive. | +| `LIMIT ` | No | No | Maximum number of slots to return. Must be greater than `0`. | ## Important points -* This is a connection-oriented command and is available over native Redis TCP, not the stateless REST endpoint. +* Only occupied slots are returned, in ascending index order. +* `` must not be greater than ``; unlike [`ARGETRANGE`](/docs/redis/commands/array/argetrange), `ARSCAN` does not reverse the range. ## Response @@ -19774,8 +19945,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Flat array of alternating keys and values | -| RESP3 | Map | +| RESP2 | Array of two-element arrays, each an index and its value | +| RESP3 | Array of two-element arrays, each an index and its value | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -19790,18 +19961,34 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -HELLO +ARSCAN my-array 0 1000 LIMIT 10 ``` + + + + This command is not supported yet in `@upstash/redis`. + + + + + + + + This command is not supported yet in `upstash_redis`. + + + + ```ts import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.hello(); +const result = await redis.arscan("my-array", 0, 1000, "LIMIT", 10); console.log(result); ``` @@ -19815,7 +20002,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.hello(); +const result = await client.arScan("my-array", 0, 1000, { LIMIT: 10 }); console.log(result); ``` @@ -19828,7 +20015,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.hello() +result = client.arscan("my-array", 0, 1000, limit=10) print(result) ``` @@ -19853,7 +20040,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.Do(context.Background(), "HELLO").Result() + result, err := client.ARScan(context.Background(), "my-array", 0, 1000, &redis.ARScanArgs{Limit: 10}).Result() if err != nil { panic(err) } @@ -19867,13 +20054,11 @@ func main() { ```java import java.net.URI; -import java.nio.charset.StandardCharsets; + import redis.clients.jedis.Jedis; -import redis.clients.jedis.commands.ProtocolCommand; -ProtocolCommand command = () -> "HELLO".getBytes(StandardCharsets.UTF_8); try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.sendCommand(command); + Object result = jedis.arscan("my-array", 0, 1000, 10); System.out.println(result); } ``` @@ -19888,8 +20073,12 @@ fn main() -> redis::RedisResult<()> { let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let mut command = redis::cmd("HELLO"); - + let mut command = redis::cmd("ARSCAN"); + command.arg("my-array"); + command.arg("0"); + command.arg("1000"); + command.arg("LIMIT"); + command.arg("10"); let result: redis::Value = command.query(&mut connection)?; println!("{result:?}"); Ok(()) @@ -19900,48 +20089,34 @@ fn main() -> redis::RedisResult<()> { -# Connection commands -Source: https://upstash.com/docs/redis/commands/connection/overview +# ARSEEK +Source: https://upstash.com/docs/redis/commands/array/arseek - -Authenticate to the server -Get the current connection name -Get the current client ID -Get info about current connection -List all client connections -Set client connection attributes -Set the connection name -Echo the given string -Handshake with Redis protocol -Ping the server -Close the connection -Reset the connection -Select the database by index - +Use `ARSEEK` to set the index that the next [`ARINSERT`](/docs/redis/commands/array/arinsert) will write to. -# PING -Source: https://upstash.com/docs/redis/commands/connection/ping +Appending normally continues from wherever the last append left off. `ARSEEK` overrides that, which is how a producer restarts a run at a known offset, or rewinds the cursor so that a region is rewritten rather than extended. Seeking to `0` returns the array to its never-appended state, so the next append goes to index `0`. -Use `PING` to check that the connection and the server are alive. +Moving the cursor does not read, write, or delete any value, so seeking backward over occupied slots leaves them in place until an append overwrites them. The reply is `1` when the cursor was moved and `0` when the key does not exist. -Without arguments the server replies `PONG`. With a message it echoes that message back instead, which lets a client match a reply to the exact request that produced it. `PING` is the standard health check for a connection pool, both to test a connection before handing it out and to keep an otherwise idle connection from being closed by intermediate proxies. It also works while the connection is subscribed to channels, where it doubles as a keepalive. +See the [array command overview](/docs/redis/commands/array/overview) for the data model these commands share. ## Syntax ```redis -PING [] +ARSEEK ``` ## Arguments | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | -| `` | No | No | Message payload. | +| `` | Yes | No | Array key targeted by the command. | +| `` | Yes | No | Index that the next append should write to. | ## Important points -* Without arguments the reply is `PONG`; with a message, the message is echoed back as a bulk string. -* While the connection is subscribed under RESP2, `PING` replies with a two-element array holding `pong` and the message instead. +* `ARSEEK` does not create the key. Seeking on a key that does not exist replies `0` and changes nothing. +* The values already stored are left untouched; only the cursor moves. ## Response @@ -19949,8 +20124,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Simple string `PONG`, or the message as a bulk string; a two-element `pong`/message array while subscribed | -| RESP3 | Simple string `PONG`, or the message as a bulk string | +| RESP2 | Integer | +| RESP3 | Integer | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -19965,33 +20140,24 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -PING +ARSEEK my-array 0 ``` -```ts -import { Redis } from "@upstash/redis"; - -const redis = Redis.fromEnv(); - -const response = await redis.ping(); -console.log(response); // "PONG" -``` + + This command is not supported yet in `@upstash/redis`. + -```python -from upstash_redis import Redis - -redis = Redis.from_env() -result = redis.ping() -print(result) -``` + + This command is not supported yet in `upstash_redis`. + @@ -20001,7 +20167,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.ping(); +const result = await redis.arseek("my-array", 0); console.log(result); ``` @@ -20015,7 +20181,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.ping(); +const result = await client.arSeek("my-array", 0); console.log(result); ``` @@ -20028,7 +20194,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.ping() +result = client.arseek("my-array", 0) print(result) ``` @@ -20053,7 +20219,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.Ping(context.Background()).Result() + result, err := client.ARSeek(context.Background(), "my-array", 0).Result() if err != nil { panic(err) } @@ -20071,7 +20237,7 @@ import java.net.URI; import redis.clients.jedis.Jedis; try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.ping(); + Object result = jedis.arseek("my-array", 0); System.out.println(result); } ``` @@ -20081,14 +20247,15 @@ try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { ```rust -use redis::TypedCommands; - fn main() -> redis::RedisResult<()> { let url = std::env::var("REDIS_URL").expect("REDIS_URL is not set"); let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let result = connection.ping()?; + let mut command = redis::cmd("ARSEEK"); + command.arg("my-array"); + command.arg("0"); + let result: redis::Value = command.query(&mut connection)?; println!("{result:?}"); Ok(()) } @@ -20098,30 +20265,35 @@ fn main() -> redis::RedisResult<()> { -# QUIT -Source: https://upstash.com/docs/redis/commands/connection/quit +# ARSET +Source: https://upstash.com/docs/redis/commands/array/arset - - Prefer closing the connection from the client in new code, which avoids leaving `TIME_WAIT` sockets on the server. - +Use `ARSET` to write one or more values into an array, starting at a given index. -Use `QUIT` to ask the server to close the connection once all pending replies have been sent. +The first value goes to ``, the next to ` + 1`, and so on, so a batch of readings can be placed at a known offset in one call. Any slot in the range that was empty becomes occupied, and any slot that already held a value is overwritten. The reply counts only the slots that were newly occupied, which makes it a cheap way to tell how much of a write was new data rather than a correction. -The server replies `OK` and then terminates the connection, so no reply is lost and the shutdown is clean from both sides. Modern clients usually just close the socket, and pooled connections should be returned to the pool rather than closed, so `QUIT` is mostly useful in scripts and interactive sessions. +Writing past the end of the array does not shift anything: an array is sparse, so the slots between the previous highest index and the new one simply stay empty. `ARSET` never moves the append cursor used by [`ARINSERT`](/docs/redis/commands/array/arinsert), so mixing positional writes with appends is safe. + +See the [array command overview](/docs/redis/commands/array/overview) for the data model these commands share. ## Syntax ```redis -QUIT +ARSET [ ...] ``` ## Arguments -This command takes no arguments. +| Argument | Required | Repeatable | Description | +| --- | --- | --- | --- | +| `` | Yes | No | Array key targeted by the command. | +| `` | Yes | No | Zero-based index of the first value. Must be between `0` and `18446744073709551614`. | +| `` | Yes | Yes | Value to store. Repeat to fill consecutive indexes starting at ``. | ## Important points -* This is a connection-oriented command and is available over native Redis TCP, not the stateless REST endpoint. +* The reply counts newly occupied slots only. Overwriting an existing value contributes `0`. +* An index outside the supported range, or a batch whose last index would exceed it, returns `ERR invalid array index`. ## Response @@ -20129,8 +20301,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Simple string `OK` | -| RESP3 | Simple string `OK` | +| RESP2 | Integer | +| RESP3 | Integer | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -20145,18 +20317,34 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -QUIT +ARSET my-array 0 hello world ``` + + + + This command is not supported yet in `@upstash/redis`. + + + + + + + + This command is not supported yet in `upstash_redis`. + + + + ```ts import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.quit(); +const result = await redis.arset("my-array", 0, "hello", "world"); console.log(result); ``` @@ -20170,7 +20358,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.sendCommand(["QUIT"]); +const result = await client.arSet("my-array", 0, ["hello", "world"]); console.log(result); ``` @@ -20183,7 +20371,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.quit() +result = client.arset("my-array", 0, "hello", "world") print(result) ``` @@ -20208,7 +20396,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.Quit(context.Background()).Result() + result, err := client.ARSet(context.Background(), "my-array", 0, "hello", "world").Result() if err != nil { panic(err) } @@ -20222,13 +20410,11 @@ func main() { ```java import java.net.URI; -import java.nio.charset.StandardCharsets; + import redis.clients.jedis.Jedis; -import redis.clients.jedis.commands.ProtocolCommand; -ProtocolCommand command = () -> "QUIT".getBytes(StandardCharsets.UTF_8); try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.sendCommand(command); + Object result = jedis.arset("my-array", 0, "hello", "world"); System.out.println(result); } ``` @@ -20243,8 +20429,11 @@ fn main() -> redis::RedisResult<()> { let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let mut command = redis::cmd("QUIT"); - + let mut command = redis::cmd("ARSET"); + command.arg("my-array"); + command.arg("0"); + command.arg("hello"); + command.arg("world"); let result: redis::Value = command.query(&mut connection)?; println!("{result:?}"); Ok(()) @@ -20255,26 +20444,93 @@ fn main() -> redis::RedisResult<()> { -# RESET -Source: https://upstash.com/docs/redis/commands/connection/reset +# Array commands +Source: https://upstash.com/docs/redis/commands/array/overview -Use `RESET` to return the connection to the state it had right after connecting. +An array stores string values under unsigned integer indexes, from `0` up to `18446744073709551614`. It is sparse: an index either holds a value or is empty, and writing to a distant index does not allocate the slots in between. Nothing shifts when a value is deleted, so an index keeps its meaning for the lifetime of the key. That makes an array the right shape for data that already has a natural numeric key, such as a sequence number or a bucketed timestamp, where a [list](/docs/redis/commands/list/overview) would force a scan and a [hash](/docs/redis/commands/hash/overview) would store the index as a string. -It discards an open [`MULTI`](/docs/redis/commands/transactions/multi) block, unwatches every key watched with [`WATCH`](/docs/redis/commands/transactions/watch), leaves subscriber and monitor modes, clears the connection name, re-enables replies, and de-authenticates the connection when the database requires a password. This makes it the safe way to hand a connection back to a pool after an error, since the next user cannot inherit a half-finished transaction or a leftover subscription. +Two numbers describe an array and they are not the same. [`ARCOUNT`](/docs/redis/commands/array/arcount) is how many slots hold a value; [`ARLEN`](/docs/redis/commands/array/arlen) is the highest occupied index plus one. They agree only when the array is densely filled from `0`. + +Each array also keeps an append cursor, holding the index the last append wrote to. [`ARINSERT`](/docs/redis/commands/array/arinsert) appends after it, [`ARRING`](/docs/redis/commands/array/arring) appends after it and wraps within a fixed number of slots, [`ARNEXT`](/docs/redis/commands/array/arnext) reads where the next append will land, and [`ARSEEK`](/docs/redis/commands/array/arseek) moves it. The cursor tracks appends only: positional writes with [`ARSET`](/docs/redis/commands/array/arset) and deletions never move it, so a freed index is never silently reused. + + + + Set contiguous array values from an index + + + Set values at several array indexes + + + Get the value at an array index + + + Get the values at several array indexes + + + Get array values in an index range + + + Scan the populated slots of an array + + + Search array values with predicates + + + Delete values at the given array indexes + + + Delete array values in one or more index ranges + + + Count the values stored in an array + + + Get the length of an array + + + Append values to an array + + + Append values to a fixed-size ring + + + Read the most recently written array values + + + Get the index the next append will use + + + Move the array append cursor + + + Aggregate array values in an index range + + + Inspect the internal layout of an array + + + +# BITCOUNT +Source: https://upstash.com/docs/redis/commands/bitmap/bitcount + +Use `BITCOUNT` to count the bits set to 1 in the string stored at a key. + +Without a range the whole value is counted. `` and `` restrict the count to a part of the value and are interpreted as byte offsets by default, or as bit offsets when `BIT` is given. Both ends are inclusive and may be negative to count backwards from the end of the value, where `-1` is the last byte or bit. A missing key is treated as an empty string and returns `0`. + +`BITCOUNT` is the usual way to read a bitmap built with [`SETBIT`](/docs/redis/commands/bitmap/setbit), for example to count how many users were active on a given day when each user has a fixed bit position. ## Syntax ```redis -RESET +BITCOUNT [ [BYTE | BIT]] ``` ## Arguments -This command takes no arguments. - -## Important points - -* This is a connection-oriented command and is available over native Redis TCP, not the stateless REST endpoint. +| Argument | Required | Repeatable | Description | +| --- | --- | --- | --- | +| `` | Yes | No | Redis key targeted by the command. | +| ` [BYTE \| BIT]` | No | No | Range to count. Offsets are byte-based unless `BIT` is given; negative offsets count from the end. | ## Response @@ -20282,8 +20538,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Simple string `RESET` | -| RESP3 | Simple string `RESET` | +| RESP2 | Integer | +| RESP3 | Integer | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -20298,7 +20554,31 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -RESET +BITCOUNT my-key +``` + + + + + +```ts +import { Redis } from "@upstash/redis"; + +const redis = Redis.fromEnv(); + +const bits = await redis.bitcount(key); +``` + + + + + +```python +from upstash_redis import Redis + +redis = Redis.from_env() +result = redis.bitcount("my-key") +print(result) ``` @@ -20309,7 +20589,7 @@ RESET import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.reset(); +const result = await redis.bitcount("my-key"); console.log(result); ``` @@ -20323,7 +20603,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.sendCommand(["RESET"]); +const result = await client.bitCount("my-key"); console.log(result); ``` @@ -20336,7 +20616,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.reset() +result = client.bitcount("my-key") print(result) ``` @@ -20361,7 +20641,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.Do(context.Background(), "RESET").Result() + result, err := client.BitCount(context.Background(), "my-key", nil).Result() if err != nil { panic(err) } @@ -20379,7 +20659,7 @@ import java.net.URI; import redis.clients.jedis.Jedis; try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.reset(); + Object result = jedis.bitcount("my-key"); System.out.println(result); } ``` @@ -20389,14 +20669,14 @@ try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { ```rust +use redis::TypedCommands; + fn main() -> redis::RedisResult<()> { let url = std::env::var("REDIS_URL").expect("REDIS_URL is not set"); let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let mut command = redis::cmd("RESET"); - - let result: redis::Value = command.query(&mut connection)?; + let result = connection.bitcount("my-key")?; println!("{result:?}"); Ok(()) } @@ -20406,28 +20686,38 @@ fn main() -> redis::RedisResult<()> { -# SELECT -Source: https://upstash.com/docs/redis/commands/connection/select +# BITFIELD +Source: https://upstash.com/docs/redis/commands/bitmap/bitfield -Use `SELECT` to switch the connection to another database index. +Use `BITFIELD` to treat a string as an array of packed integers and run several operations on it in a single atomic call. -Upstash exposes a single logical database, so `0` is the only valid index and anything else returns an error. The command is accepted so that clients and frameworks that issue `SELECT 0` while setting up a connection keep working unchanged. To separate concerns inside one database, use key prefixes instead of numbered databases. +Every operation names an encoding and a bit offset. The encoding is `u` for unsigned integers (up to 63 bits) or `i` for signed integers (up to 64 bits). The offset is counted in bits from the start of the value or, when prefixed with `#`, in units of the encoding width, so `#2` with `u8` addresses the third 8-bit field. The string grows automatically with zero bits when an operation addresses an offset past its current end. + +`GET` reads a field, `SET` writes one and returns its previous value, and `INCRBY` adds a possibly negative increment and returns the new value. `OVERFLOW` sets how the `SET` and `INCRBY` operations that follow it behave when a value does not fit the encoding: `WRAP` wraps around like modular arithmetic and is the default, `SAT` saturates at the minimum or maximum of the encoding, and `FAIL` leaves the field unchanged and returns null for that operation. The reply is an array with one entry per operation, in the order the operations were given. + +Packing many small counters into a single key this way saves memory and keeps the whole update atomic, which makes it a good fit for rate limiters and compact per-user counters. ## Syntax ```redis -SELECT 0 +BITFIELD + [GET | + [OVERFLOW WRAP | SAT | FAIL] + (SET | + INCRBY ) + [GET | + [OVERFLOW WRAP | SAT | FAIL] + (SET | + INCRBY ) + ...]] ``` ## Arguments | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | -| `0` | Yes | No | Database index. Upstash supports only database 0. | - -## Important points - -* Upstash exposes a single logical database. Any index other than `0` returns an error. +| `` | Yes | No | Redis key targeted by the command. | +| `(GET \| [OVERFLOW WRAP \| SAT \| FAIL] (SET \| INCRBY ))` | No | Yes | An operation on a field of `` (`u` unsigned up to 63 bits, or `i` signed up to 64 bits) at `` bits, or at `#` to address the n-th field of that width: `GET` reads it, `SET` writes it and returns the previous value, and `INCRBY` adds an increment and returns the new value. `OVERFLOW` sets how the `SET` and `INCRBY` operations after it handle a value that does not fit: `WRAP` wraps around (the default), `SAT` saturates at the encoding's limits, and `FAIL` leaves the field unchanged and returns null. Repeat to run several operations in one atomic call. | ## Response @@ -20435,8 +20725,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Simple string `OK` | -| RESP3 | Simple string `OK` | +| RESP2 | Array of integer or null replies, one per subcommand | +| RESP3 | Array of integer or null replies, one per subcommand | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -20451,24 +20741,32 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -SELECT 0 +BITFIELD my-key GET u8 0 ``` - - This command is not supported yet in `@upstash/redis`. - +```ts +import { Redis } from "@upstash/redis"; + +const redis = Redis.fromEnv(); +const result = await redis.bitfield("my-key").get("u8", 0).exec(); +console.log(result); +``` - - This command is not supported yet in `upstash_redis`. - +```python +from upstash_redis import Redis + +redis = Redis.from_env() +result = redis.bitfield("my-key").get("u8", 0).execute() +print(result) +``` @@ -20478,7 +20776,7 @@ SELECT 0 import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.select("0"); +const result = await redis.bitfield("my-key", "GET", "u8", "0"); console.log(result); ``` @@ -20492,7 +20790,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.sendCommand(["SELECT", "0"]); +const result = await client.bitField("my-key", [{ operation: "GET", encoding: "u8", offset: 0 }]); console.log(result); ``` @@ -20505,7 +20803,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.select("0") +result = client.bitfield("my-key").get("u8", 0).execute() print(result) ``` @@ -20530,7 +20828,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.Do(context.Background(), "SELECT", "0").Result() + result, err := client.BitField(context.Background(), "my-key", "GET", "u8", 0).Result() if err != nil { panic(err) } @@ -20548,7 +20846,7 @@ import java.net.URI; import redis.clients.jedis.Jedis; try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.select(0); + Object result = jedis.bitfield("my-key", "GET", "u8", "0"); System.out.println(result); } ``` @@ -20563,8 +20861,8 @@ fn main() -> redis::RedisResult<()> { let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let mut command = redis::cmd("SELECT"); - command.arg("0"); + let mut command = redis::cmd("BITFIELD"); + command.arg("my-key"); let result: redis::Value = command.query(&mut connection)?; println!("{result:?}"); Ok(()) @@ -20575,57 +20873,25 @@ fn main() -> redis::RedisResult<()> { -# FCALL -Source: https://upstash.com/docs/redis/commands/functions/fcall - -Use `FCALL` to invoke a function from a library loaded with [`FUNCTION LOAD`](/docs/redis/commands/functions/function-load). - -`` tells the server how many of the arguments that follow are key names. Those keys reach the function in `KEYS` and every remaining argument in `ARGV`. Passing key names as keys instead of hardcoding them in the function body matters, because Redis uses that list for routing and access checks. - -The function runs on the server as a single atomic step, so a sequence of reads and writes that would otherwise need several round trips and a transaction becomes one command. Use [`FCALL_RO`](/docs/redis/commands/functions/fcall-ro) when the function only reads. Functions are the successor to [`EVAL`](/docs/redis/commands/scripting/eval) scripts: they are named, registered once as part of a library, and persisted with the dataset instead of being sent or looked up by digest on every call. - -Upstash runs a function under the global lock by default, since the engine cannot know in advance which keys it will touch. Registering the function with the `allow-key-locking` flag makes the call lock only the keys passed in the key list, so calls that work on disjoint keys run in parallel: - -```lua -redis.register_function{ - function_name='incr_quota', - callback=incr_quota, - flags={'allow-key-locking'} -} -``` +# BITFIELD_RO +Source: https://upstash.com/docs/redis/commands/bitmap/bitfield-ro -Unlike Lua scripts, where the flag goes on the library shebang, this flag is set per registered function. With it set, every key the function touches must be passed as a key in the `FCALL` call: keys sent as ordinary arguments are not locked, and commands that need database-wide access, such as `FLUSHDB`, are rejected. See [Key-Based Locking](/docs/redis/features/key-locking) for the full rules. +Use `BITFIELD_RO` to read one or more bitfield values without modifying the key. - - Pass every key the function touches in the key list, even when it runs under - the global lock. Upstash keeps idle entries - [on disk](/docs/redis/features/durability): keys given in the key list are loaded - before the function starts and the lock is released during that read, but a - key that the function builds from `ARGV` while it runs is read from disk with - the lock held, stalling every command waiting on it. See - [Dynamic Keys and Latency](/docs/redis/features/key-locking#dynamic-keys-and-latency). - +It is the read-only form of [`BITFIELD`](/docs/redis/commands/bitmap/bitfield) and accepts `GET` operations only, so it is safe to run on replicas and from read-only scripts. Each `GET` names an encoding, `u` for unsigned or `i` for signed integers, and a bit offset that can be written as `#` to address the n-th field of that width. The reply holds one integer per `GET`, and any part of a field that lies past the end of the stored string reads as zero. ## Syntax ```redis -FCALL [ [ ...]] [ [ ...]] +BITFIELD_RO [GET [GET ...]] ``` ## Arguments | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | -| `` | Yes | No | Name of the registered function to call. | -| `` | Yes | No | Number of key arguments that follow. | -| `` | No | Yes | Redis key targeted by the command. | -| `` | No | Yes | Additional argument passed to the function. | - -## Important points - -* `numkeys` must equal the number of key arguments that immediately follow it; remaining arguments are available to the script or function as ordinary arguments. -* The function takes the global lock unless it was registered with the `allow-key-locking` flag, in which case only the keys passed in the key list are locked. See [Key-Based Locking](/docs/redis/features/key-locking). -* Pass every key the function touches in the key list whether or not `allow-key-locking` is set. A key built inside the function is read from disk under the lock when it is not in memory, and it is rejected outright when the flag is set. See [Dynamic Keys and Latency](/docs/redis/features/key-locking#dynamic-keys-and-latency). +| `` | Yes | No | Redis key targeted by the command. | +| `GET ` | No | Yes | Read the value at `` using ``, such as `u8` or `i16`. Repeat to read several fields. | ## Response @@ -20633,8 +20899,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Reply returned by the invoked function | -| RESP3 | Reply returned by the invoked function | +| RESP2 | Array of integer or null replies, one per subcommand | +| RESP3 | Array of integer or null replies, one per subcommand | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -20649,40 +20915,28 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -FCALL my_function 1 my-key value +BITFIELD_RO my-key GET u8 0 ``` -```ts -import { Redis } from "@upstash/redis"; - -const redis = Redis.fromEnv(); - -const code = ` -#!lua name=mylib -redis.register_function('helloworld', - function() - return 'Hello World!' - end -) -`; - -await redis.functions.load({ code, replace: true }); - -const res = await redis.functions.call("helloworld"); -console.log(res); // "Hello World!" -``` + + This command is not supported yet in `@upstash/redis`. + - - This command is not supported yet in `upstash_redis`. - +```python +from upstash_redis import Redis + +redis = Redis.from_env() +result = redis.bitfield_ro("my-key").get("u8", 0).execute() +print(result) +``` @@ -20692,7 +20946,7 @@ console.log(res); // "Hello World!" import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.fcall("my_function", "1", "my-key", "value"); +const result = await redis.bitfield_ro("my-key", "GET", "u8", "0"); console.log(result); ``` @@ -20706,7 +20960,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.fCall("my_function", { keys: ["my-key"], arguments: ["value"] }); +const result = await client.bitFieldRo("my-key", [{ encoding: "u8", offset: 0 }]); console.log(result); ``` @@ -20719,7 +20973,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.fcall("my_function", 1, "my-key", "value") +result = client.bitfield_ro("my-key", "u8", 0) print(result) ``` @@ -20744,7 +20998,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.FCall(context.Background(), "my_function", []string{"my-key"}, "value").Result() + result, err := client.BitFieldRO(context.Background(), "my-key", "GET", "u8", 0).Result() if err != nil { panic(err) } @@ -20762,7 +21016,7 @@ import java.net.URI; import redis.clients.jedis.Jedis; try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.fcall("my_function", java.util.List.of("my-key"), java.util.List.of("value")); + Object result = jedis.bitfieldReadonly("my-key", "GET", "u8", "0"); System.out.println(result); } ``` @@ -20777,9 +21031,8 @@ fn main() -> redis::RedisResult<()> { let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let mut command = redis::cmd("FCALL"); - command.arg("my_function"); - command.arg("1"); + let mut command = redis::cmd("BITFIELD_RO"); + command.arg("my-key"); let result: redis::Value = command.query(&mut connection)?; println!("{result:?}"); Ok(()) @@ -20790,37 +21043,30 @@ fn main() -> redis::RedisResult<()> { -# FCALL_RO -Source: https://upstash.com/docs/redis/commands/functions/fcall-ro +# BITOP +Source: https://upstash.com/docs/redis/commands/bitmap/bitop -Use `FCALL_RO` to invoke a function that is declared read-only. +Use `BITOP` to combine several strings with a bitwise operation and store the result in another key. -The function must have been registered with the `no-writes` flag; calling a function without it returns an error. In exchange the server knows the call cannot modify data, so it can serve it on replicas and reject accidental writes outright. +Source strings are combined bit by bit, and shorter ones are treated as if they were padded with zero bits up to the length of the longest input, so the destination always ends up as long as the longest source. A missing key counts as an empty string, and if the result is empty the destination key is deleted. The reply is the length of the stored value in bytes. -Apart from that restriction it behaves like [`FCALL`](/docs/redis/commands/functions/fcall): `` splits the arguments into the keys the function receives in `KEYS` and the plain arguments it receives in `ARGV`. +`AND`, `OR`, and `XOR` accept any number of source keys and `NOT` accepts exactly one. The remaining operators compare the first key with the rest: `DIFF` keeps the bits set in the first key and in none of the others, `DIFF1` keeps the bits set in at least one of the other keys but not in the first, `ANDOR` keeps the bits set in the first key and in at least one of the others, and `ONE` keeps the bits set in exactly one of the source keys. `DIFF`, `DIFF1`, and `ANDOR` each require at least two source keys. -Being read-only does not by itself make the call concurrent with others. The function takes the global lock unless it was also registered with the `allow-key-locking` flag, as in `flags={'no-writes', 'allow-key-locking'}`. With both flags, the call takes shared read locks on the keys passed in the key list, so several readers of the same key proceed together. See [Key-Based Locking](/docs/redis/features/key-locking). +This is how bitmaps are used as sets: with one bit per user, `AND` gives users present in every bitmap and `OR` gives users present in any of them, and [`BITCOUNT`](/docs/redis/commands/bitmap/bitcount) then turns the result into a number. ## Syntax ```redis -FCALL_RO [ [ ...]] [ [ ...]] +BITOP (AND | OR | XOR | NOT | DIFF | DIFF1 | ANDOR | ONE) [ ...] ``` ## Arguments | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | -| `` | Yes | No | Name of the registered function to call. | -| `` | Yes | No | Number of key arguments that follow. | -| `` | No | Yes | Redis key targeted by the command. | -| `` | No | Yes | Additional argument passed to the function. | - -## Important points - -* `numkeys` must equal the number of key arguments that immediately follow it; remaining arguments are available to the script or function as ordinary arguments. -* A `no-writes` function still takes the global lock unless it was also registered with the `allow-key-locking` flag. See [Key-Based Locking](/docs/redis/features/key-locking). -* Pass every key the function reads in the key list whether or not `allow-key-locking` is set. A key built inside the function is read from disk under the lock when it is not in memory, and it is rejected outright when the flag is set. See [Dynamic Keys and Latency](/docs/redis/features/key-locking#dynamic-keys-and-latency). +| `(AND \| OR \| XOR \| NOT \| DIFF \| DIFF1 \| ANDOR \| ONE)` | Yes | No | The bitwise operation to apply. `AND`, `OR`, and `XOR` combine any number of source keys and `NOT` inverts exactly one. The rest compare the first key with the others: `DIFF` keeps bits set in the first key and in none of the others, `DIFF1` keeps bits set in at least one of the others but not in the first, `ANDOR` keeps bits set in the first key and in at least one of the others, and `ONE` keeps bits set in exactly one source key. | +| `` | Yes | No | Redis key used as destkey. | +| `` | Yes | Yes | Redis key targeted by the command. | ## Response @@ -20828,8 +21074,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Reply returned by the invoked read-only function | -| RESP3 | Reply returned by the invoked read-only function | +| RESP2 | Integer | +| RESP3 | Integer | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -20844,7 +21090,7 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -FCALL_RO my_function 1 my-key value +BITOP AND destination-key source-key-1 source-key-2 ``` @@ -20856,35 +21102,30 @@ import { Redis } from "@upstash/redis"; const redis = Redis.fromEnv(); -const code = ` -#!lua name=ro_lib - -local function get_value(keys, args) - return redis.call('GET', keys[1]) -end +// AND operation +await redis.bitop("and", "destKey", "sourceKey1", "sourceKey2"); -redis.register_function({ - function_name='get_value', - callback=get_value, - flags={ 'no-writes' } -}) -`; +// OR operation +await redis.bitop("or", "destKey", "sourceKey1", "sourceKey2"); -await redis.functions.load({ code, replace: true }); +// XOR operation +await redis.bitop("xor", "destKey", "sourceKey1", "sourceKey2"); -// Call the read-only function -// Note: We can modify the keys usage here, but since it represents a read-only operation -// and we marked it with 'no-writes', it is safe to use callRo. -const value = await redis.functions.callRo("get_value", ["mykey"]) +// NOT operation (only accepts one source key) +await redis.bitop("not", "destKey", "sourceKey"); ``` - - This command is not supported yet in `upstash_redis`. - +```python +from upstash_redis import Redis + +redis = Redis.from_env() +result = redis.bitop("AND", "destination-key", "source-key-1", "source-key-2") +print(result) +``` @@ -20894,7 +21135,7 @@ const value = await redis.functions.callRo("get_value", ["mykey"]) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.fcall_ro("my_function", "1", "my-key", "value"); +const result = await redis.bitop("AND", "destination-key", "source-key-1", "source-key-2"); console.log(result); ``` @@ -20908,7 +21149,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.fCallRo("my_function", { keys: ["my-key"], arguments: ["value"] }); +const result = await client.bitOp("AND", "destination-key", ["source-key-1", "source-key-2"]); console.log(result); ``` @@ -20921,7 +21162,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.fcall_ro("my_function", 1, "my-key", "value") +result = client.bitop("AND", "destination-key", "source-key-1", "source-key-2") print(result) ``` @@ -20946,7 +21187,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.FCallRO(context.Background(), "my_function", []string{"my-key"}, "value").Result() + result, err := client.BitOpAnd(context.Background(), "destination-key", "source-key-1", "source-key-2").Result() if err != nil { panic(err) } @@ -20964,7 +21205,7 @@ import java.net.URI; import redis.clients.jedis.Jedis; try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.fcallReadonly("my_function", java.util.List.of("my-key"), java.util.List.of("value")); + Object result = jedis.bitop(redis.clients.jedis.args.BitOP.AND, "destination-key", "source-key-1", "source-key-2"); System.out.println(result); } ``` @@ -20974,15 +21215,14 @@ try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { ```rust +use redis::TypedCommands; + fn main() -> redis::RedisResult<()> { let url = std::env::var("REDIS_URL").expect("REDIS_URL is not set"); let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let mut command = redis::cmd("FCALL_RO"); - command.arg("my_function"); - command.arg("1"); - let result: redis::Value = command.query(&mut connection)?; + let result = connection.bit_and("destination-key", &["source-key-1", "source-key-2"])?; println!("{result:?}"); Ok(()) } @@ -20992,24 +21232,28 @@ fn main() -> redis::RedisResult<()> { -# FUNCTION DELETE -Source: https://upstash.com/docs/redis/commands/functions/function-delete +# BITPOS +Source: https://upstash.com/docs/redis/commands/bitmap/bitpos -Use `FUNCTION DELETE` to remove a function library and every function it registered. +Use `BITPOS` to find the position of the first bit set to `0` or `1` in a string. -The argument is the library name declared when the library was loaded, not the name of a single function, and there is no way to delete one function from a library: reload the library with `REPLACE` instead. Deleting a library that does not exist returns an error, and calls to its functions fail until the library is loaded again. +The whole value is searched unless `` and `` are given, and those are byte offsets by default or bit offsets when `BIT` is given. Both ends are inclusive and may be negative to count backwards from the end of the value. The reply is always an absolute bit position counted from the start of the string, or `-1` when no matching bit is found. + +One edge case is worth remembering: when you look for a `0` in a string of all ones and give no explicit end, the reply is the position of the first bit past the end of the string, because the value is treated as if it were followed by an infinite run of zero bits. Bounding the search with an explicit range returns `-1` in the same situation. ## Syntax ```redis -FUNCTION DELETE +BITPOS [ [ [BYTE | BIT]]] ``` ## Arguments | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | -| `` | Yes | No | Name of the function library. | +| `` | Yes | No | Redis key targeted by the command. | +| `` | Yes | No | Bit value to look for: `0` or `1`. | +| ` [ [BYTE \| BIT]]` | No | No | Range to search. Offsets are byte-based unless `BIT` is given; negative offsets count from the end. | ## Response @@ -21017,8 +21261,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Simple string `OK` | -| RESP3 | Simple string `OK` | +| RESP2 | Integer: the position of the first matching bit, or `-1` | +| RESP3 | Integer: the position of the first matching bit, or `-1` | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -21033,7 +21277,7 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -FUNCTION DELETE mylib +BITPOS my-key 1 ``` @@ -21045,16 +21289,20 @@ import { Redis } from "@upstash/redis"; const redis = Redis.fromEnv(); -await redis.functions.delete("mylib") +await redis.bitpos("key", 1); ``` - - This command is not supported yet in `upstash_redis`. - +```python +from upstash_redis import Redis + +redis = Redis.from_env() +result = redis.bitpos("my-key", 1) +print(result) +``` @@ -21064,7 +21312,7 @@ await redis.functions.delete("mylib") import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.function("DELETE", "library-name"); +const result = await redis.bitpos("my-key", "1"); console.log(result); ``` @@ -21078,7 +21326,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.functionDelete("library-name"); +const result = await client.bitPos("my-key", 1); console.log(result); ``` @@ -21091,7 +21339,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.function_delete("library-name") +result = client.bitpos("my-key", 1) print(result) ``` @@ -21116,7 +21364,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.FunctionDelete(context.Background(), "library-name").Result() + result, err := client.BitPos(context.Background(), "my-key", 1).Result() if err != nil { panic(err) } @@ -21134,7 +21382,7 @@ import java.net.URI; import redis.clients.jedis.Jedis; try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.functionDelete("library-name"); + Object result = jedis.bitpos("my-key", true); System.out.println(result); } ``` @@ -21149,9 +21397,9 @@ fn main() -> redis::RedisResult<()> { let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let mut command = redis::cmd("FUNCTION"); - command.arg("DELETE"); - command.arg("library-name"); + let mut command = redis::cmd("BITPOS"); + command.arg("my-key"); + command.arg("1"); let result: redis::Value = command.query(&mut connection)?; println!("{result:?}"); Ok(()) @@ -21162,24 +21410,25 @@ fn main() -> redis::RedisResult<()> { -# FUNCTION FLUSH -Source: https://upstash.com/docs/redis/commands/functions/function-flush +# GETBIT +Source: https://upstash.com/docs/redis/commands/bitmap/getbit -Use `FUNCTION FLUSH` to remove every function library from the database. +Use `GETBIT` to read a single bit of the string stored at a key. -This wipes all registered libraries and functions at once and cannot be undone, so it belongs in test setup and provisioning tooling rather than application code. `ASYNC` reclaims the memory in the background and `SYNC` reclaims it before the reply is sent. +The offset is counted in bits from the start of the value, so offset `0` is the most significant bit of the first byte. When the key does not exist, or the offset lies past the end of the stored string, the bit reads as `0` rather than producing an error. ## Syntax ```redis -FUNCTION FLUSH [ASYNC | SYNC] +GETBIT ``` ## Arguments | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | -| `(ASYNC \| SYNC)` | No | No | Choose one form: `ASYNC` (request asynchronous cleanup); `SYNC` (request synchronous cleanup). | +| `` | Yes | No | Redis key targeted by the command. | +| `` | Yes | No | Zero-based bit offset to read. | ## Response @@ -21187,8 +21436,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Simple string `OK` | -| RESP3 | Simple string `OK` | +| RESP2 | Integer: the bit at the offset, `0` or `1` | +| RESP3 | Integer: the bit at the offset, `0` or `1` | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -21203,7 +21452,7 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -FUNCTION FLUSH +GETBIT my-key 0 ``` @@ -21215,16 +21464,20 @@ import { Redis } from "@upstash/redis"; const redis = Redis.fromEnv(); -await redis.functions.flush() +const bit = await redis.getbit(key, 4); ``` - - This command is not supported yet in `upstash_redis`. - +```python +from upstash_redis import Redis + +redis = Redis.from_env() +result = redis.getbit("my-key", 0) +print(result) +``` @@ -21234,7 +21487,7 @@ await redis.functions.flush() import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.function("FLUSH"); +const result = await redis.getbit("my-key", "0"); console.log(result); ``` @@ -21248,7 +21501,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.functionFlush(); +const result = await client.getBit("my-key", 0); console.log(result); ``` @@ -21261,7 +21514,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.function_flush() +result = client.getbit("my-key", 0) print(result) ``` @@ -21286,7 +21539,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.FunctionFlush(context.Background()).Result() + result, err := client.GetBit(context.Background(), "my-key", 0).Result() if err != nil { panic(err) } @@ -21304,7 +21557,7 @@ import java.net.URI; import redis.clients.jedis.Jedis; try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.functionFlush(); + Object result = jedis.getbit("my-key", 0); System.out.println(result); } ``` @@ -21314,14 +21567,14 @@ try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { ```rust +use redis::TypedCommands; + fn main() -> redis::RedisResult<()> { let url = std::env::var("REDIS_URL").expect("REDIS_URL is not set"); let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let mut command = redis::cmd("FUNCTION"); - command.arg("FLUSH"); - let result: redis::Value = command.query(&mut connection)?; + let result = connection.getbit("my-key", 0)?; println!("{result:?}"); Ok(()) } @@ -21331,28 +21584,41 @@ fn main() -> redis::RedisResult<()> { -# FUNCTION KILL -Source: https://upstash.com/docs/redis/commands/functions/function-kill +# Bitmap commands +Source: https://upstash.com/docs/redis/commands/bitmap/overview -Use `FUNCTION KILL` to stop a function that is currently running and has not yet written anything. + +Count set bits in a string +Perform arbitrary bitfield operations +Read-only bitfield operations +Perform bitwise operations between strings +Find first bit set or clear in a string +Get the bit value at offset +Set or clear the bit at offset + -A function that has already modified data cannot be killed, because stopping it halfway would leave the dataset in a state that no atomic step could produce. Check [`FUNCTION STATS`](/docs/redis/commands/functions/function-stats) to see whether a function is running before calling this. +# SETBIT +Source: https://upstash.com/docs/redis/commands/bitmap/setbit -The current Upstash deployment recognizes the command but has no interruptible running-function state to act on, so it replies with a `NOTBUSY` error. +Use `SETBIT` to set a single bit of the string stored at a key to `0` or `1`. + +The offset is counted in bits from the start of the value and the reply is the bit's previous value. If the key does not exist, or the offset lies past the end of the current value, the string is first extended with zero bits, so writing to a large offset allocates every byte up to it. Keep offsets dense, for example by mapping each user to a small sequential id. + +Bitmaps built this way are read back with [`GETBIT`](/docs/redis/commands/bitmap/getbit), counted with [`BITCOUNT`](/docs/redis/commands/bitmap/bitcount), and combined with [`BITOP`](/docs/redis/commands/bitmap/bitop). ## Syntax ```redis -FUNCTION KILL +SETBIT ``` ## Arguments -This command takes no arguments. - -## Important points - -* The current deployment recognizes this command but reports `NOTBUSY` because it does not expose an interruptible running-function state. +| Argument | Required | Repeatable | Description | +| --- | --- | --- | --- | +| `` | Yes | No | Redis key targeted by the command. | +| `` | Yes | No | Zero-based bit offset to write. | +| `` | Yes | No | Bit value to write: `0` or `1`. | ## Response @@ -21360,8 +21626,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Error reply (`NOTBUSY` on the current deployment) | -| RESP3 | Error reply (`NOTBUSY` on the current deployment) | +| RESP2 | Integer: the previous bit at the offset, `0` or `1` | +| RESP3 | Integer: the previous bit at the offset, `0` or `1` | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -21376,24 +21642,32 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -FUNCTION KILL +SETBIT my-key 0 1 ``` - - This command is not supported yet in `@upstash/redis`. - +```ts +import { Redis } from "@upstash/redis"; + +const redis = Redis.fromEnv(); + +const originalBit = await redis.setbit(key, 4, 1); +``` - - This command is not supported yet in `upstash_redis`. - +```python +from upstash_redis import Redis + +redis = Redis.from_env() +result = redis.setbit("my-key", 0, 1) +print(result) +``` @@ -21403,7 +21677,7 @@ FUNCTION KILL import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.function("KILL"); +const result = await redis.setbit("my-key", "0", "1"); console.log(result); ``` @@ -21417,7 +21691,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.functionKill(); +const result = await client.setBit("my-key", 0, 1); console.log(result); ``` @@ -21430,7 +21704,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.function_kill() +result = client.setbit("my-key", 0, 1) print(result) ``` @@ -21455,7 +21729,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.FunctionKill(context.Background()).Result() + result, err := client.SetBit(context.Background(), "my-key", 0, 1).Result() if err != nil { panic(err) } @@ -21473,7 +21747,7 @@ import java.net.URI; import redis.clients.jedis.Jedis; try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.functionKill(); + Object result = jedis.setbit("my-key", 0, true); System.out.println(result); } ``` @@ -21483,14 +21757,14 @@ try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { ```rust +use redis::TypedCommands; + fn main() -> redis::RedisResult<()> { let url = std::env::var("REDIS_URL").expect("REDIS_URL is not set"); let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let mut command = redis::cmd("FUNCTION"); - command.arg("KILL"); - let result: redis::Value = command.query(&mut connection)?; + let result = connection.setbit("my-key", 0, true)?; println!("{result:?}"); Ok(()) } @@ -21500,25 +21774,29 @@ fn main() -> redis::RedisResult<()> { -# FUNCTION LIST -Source: https://upstash.com/docs/redis/commands/functions/function-list +# AUTH +Source: https://upstash.com/docs/redis/commands/connection/auth -Use `FUNCTION LIST` to inspect the function libraries loaded in the database. +Use `AUTH` to authenticate the current connection, with a password alone or with a username and password when ACL users are configured. -The reply describes each library with its name, the engine it runs on, and the functions it registers, including each function's description and flags such as `no-writes` and [`allow-key-locking`](/docs/redis/features/key-locking), which is the way to check whether a deployed function locks only its keys or the whole database. `LIBRARYNAME` filters the reply to library names matching a pattern, and `WITHCODE` includes the full source of each library, which is how you recover the code of a library that is deployed but no longer at hand. +Until the connection is authenticated the server rejects other commands with an error. Client libraries usually send `AUTH` for you as part of connecting when the credentials are part of the connection string, so applications rarely call it directly. The password is sent to the server on every connection, which is why Upstash endpoints use TLS. ## Syntax ```redis -FUNCTION LIST [LIBRARYNAME ] [WITHCODE] +AUTH [] ``` ## Arguments | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | -| `LIBRARYNAME ` | No | No | Return only libraries whose name matches this pattern. | -| `WITHCODE` | No | No | Include each library's source code. | +| `` | No | No | Username to authenticate as. | +| `` | Yes | No | Password to authenticate with. | + +## Important points + +* This is a connection-oriented command and is available over native Redis TCP, not the stateless REST endpoint. ## Response @@ -21526,8 +21804,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Array of flat arrays containing library metadata | -| RESP3 | Array of maps containing library metadata | +| RESP2 | Simple string `OK` | +| RESP3 | Simple string `OK` | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -21542,55 +21820,18 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -FUNCTION LIST -``` - - - - - -```ts -import { Redis } from "@upstash/redis"; - -const redis = Redis.fromEnv(); - -const libs = await redis.functions.list({ - libraryName: "mylib", - withCode: true -}) - -console.log(libs) -// [ -// { -// libraryName: "mylib", -// engine: "LUA", -// functions: [{ -// name: "my_func", -// description: null, -// flags: [ "no-writes" ] -// }], -// libraryCode: "#!lua name=mylib ..." -// } -// ] +AUTH password ``` - - - - This command is not supported yet in `upstash_redis`. - - - - ```ts import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.function("LIST"); +const result = await redis.auth("password"); console.log(result); ``` @@ -21604,7 +21845,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.functionList(); +const result = await client.auth({ password: "password" }); console.log(result); ``` @@ -21617,7 +21858,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.function_list() +result = client.auth("password") print(result) ``` @@ -21642,7 +21883,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.FunctionList(context.Background(), redis.FunctionListQuery{}).Result() + result, err := client.Do(context.Background(), "AUTH", "password").Result() if err != nil { panic(err) } @@ -21660,7 +21901,7 @@ import java.net.URI; import redis.clients.jedis.Jedis; try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.functionList(); + Object result = jedis.auth("password"); System.out.println(result); } ``` @@ -21675,8 +21916,8 @@ fn main() -> redis::RedisResult<()> { let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let mut command = redis::cmd("FUNCTION"); - command.arg("LIST"); + let mut command = redis::cmd("AUTH"); + command.arg("password"); let result: redis::Value = command.query(&mut connection)?; println!("{result:?}"); Ok(()) @@ -21687,31 +21928,27 @@ fn main() -> redis::RedisResult<()> { -# FUNCTION LOAD -Source: https://upstash.com/docs/redis/commands/functions/function-load - -Use `FUNCTION LOAD` to register a library of functions in the database. - -The payload is the library source code. It must begin with a shebang line naming the engine and the library, such as `#!lua name=mylib`, and register each function with `redis.register_function`, giving it a name, a callback, and optional flags such as `no-writes`. The reply is the library name. - -`allow-key-locking` is one of those flags. It opts a function out of the global lock so that a call locks only the keys passed in its key list, which lets calls on disjoint keys run in parallel. Unlike Lua scripts, where the flag goes on the shebang line, it is declared per function in `redis.register_function`, and it is fixed until the library is loaded again. See [Key-Based Locking](/docs/redis/features/key-locking). +# CLIENT GETNAME +Source: https://upstash.com/docs/redis/commands/connection/client-getname -Whether or not you set that flag, write functions so that every key they touch arrives in the key list rather than being assembled from `ARGV` inside the function, since an undeclared key can force a disk read while the lock is held. See [Dynamic Keys and Latency](/docs/redis/features/key-locking#dynamic-keys-and-latency). +Use `CLIENT GETNAME` to read the name assigned to the current connection with [`CLIENT SETNAME`](/docs/redis/commands/connection/client-setname). -Loading fails when the library name is already in use unless `REPLACE` is given, which is how you deploy a new version of a library. Once loaded, functions are called by name with [`FCALL`](/docs/redis/commands/functions/fcall) or [`FCALL_RO`](/docs/redis/commands/functions/fcall-ro). Unlike scripts cached by [`SCRIPT LOAD`](/docs/redis/commands/scripting/script-load), libraries are part of the dataset, so they survive restarts and do not need to be re-sent by clients. +Connections start without a name and the reply is null until one is set. The name belongs to a single connection and is lost when it closes; it exists to make connections recognizable in [`CLIENT LIST`](/docs/redis/commands/connection/client-list) output when you are debugging which part of an application is doing what. ## Syntax ```redis -FUNCTION LOAD [REPLACE] +CLIENT GETNAME ``` ## Arguments -| Argument | Required | Repeatable | Description | -| --- | --- | --- | --- | -| `REPLACE` | No | No | Allow replacement of an existing destination. | -| `` | Yes | No | Library source, including its `#!lua name=` shebang. | +This command takes no arguments. + +## Important points + +* This is a connection-oriented command and is available over native Redis TCP, not the stateless REST endpoint. +* This command can expose administrative information or make a broad destructive change. Restrict it to trusted code paths. ## Response @@ -21719,8 +21956,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Bulk string | -| RESP3 | Bulk string | +| RESP2 | Null bulk string or null array or Bulk string | +| RESP3 | Null or Bulk string | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -21735,58 +21972,18 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -FUNCTION LOAD "#!lua name=mylib\nredis.register_function('helloworld', function() return 'Hello World!' end)" -``` - - - - - -```ts -import { Redis } from "@upstash/redis"; - -const redis = Redis.fromEnv(); - -const code = `#!lua name=mylib - - -- Simple function that returns a string - redis.register_function( - 'helloworld', - function() return 'Hello World!' end - ) - - -- Complex function that modifies data with logic - local function my_hset(keys, args) - local hash = keys[1] - local time = redis.call('TIME')[1] - return redis.call('HSET', hash, '_last_modified_', time, unpack(args)) - end - - redis.register_function('my_hset', my_hset) -`; - -const libraryName = await redis.functions.load({ code, replace: true }); - -console.log(libraryName); // "mylib" +CLIENT GETNAME ``` - - - - This command is not supported yet in `upstash_redis`. - - - - ```ts import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.function("LOAD", "function-code"); +const result = await redis.client("GETNAME"); console.log(result); ``` @@ -21800,7 +21997,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.functionLoad("function-code"); +const result = await client.clientGetName(); console.log(result); ``` @@ -21813,7 +22010,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.function_load("function-code") +result = client.client_getname() print(result) ``` @@ -21838,7 +22035,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.FunctionLoad(context.Background(), "function-code").Result() + result, err := client.ClientGetName(context.Background()).Result() if err != nil { panic(err) } @@ -21856,7 +22053,7 @@ import java.net.URI; import redis.clients.jedis.Jedis; try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.functionLoad("function-code"); + Object result = jedis.clientGetname(); System.out.println(result); } ``` @@ -21866,15 +22063,14 @@ try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { ```rust +use redis::TypedCommands; + fn main() -> redis::RedisResult<()> { let url = std::env::var("REDIS_URL").expect("REDIS_URL is not set"); let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let mut command = redis::cmd("FUNCTION"); - command.arg("LOAD"); - command.arg("function-code"); - let result: redis::Value = command.query(&mut connection)?; + let result = connection.client_getname()?; println!("{result:?}"); Ok(()) } @@ -21884,31 +22080,36 @@ fn main() -> redis::RedisResult<()> { -# FUNCTION STATS -Source: https://upstash.com/docs/redis/commands/functions/function-stats +# CLIENT ID +Source: https://upstash.com/docs/redis/commands/connection/client-id -Use `FUNCTION STATS` to read the current state of the function engine. +Use `CLIENT ID` to get the unique identifier the server assigned to the current connection. -The reply reports the function that is running right now, if any, together with how long it has been running and the command that started it, plus per-engine counts of loaded libraries and functions. It is the usual way to check whether a long-running function is in progress before deciding to call [`FUNCTION KILL`](/docs/redis/commands/functions/function-kill). +IDs are integers that never repeat and always increase, so a larger ID means a connection that was established later. The ID identifies this connection in [`CLIENT LIST`](/docs/redis/commands/connection/client-list) output, which makes it useful when correlating application logs with server-side connection state. ## Syntax ```redis -FUNCTION STATS +CLIENT ID ``` ## Arguments This command takes no arguments. +## Important points + +* This is a connection-oriented command and is available over native Redis TCP, not the stateless REST endpoint. +* This command can expose administrative information or make a broad destructive change. Restrict it to trusted code paths. + ## Response The reply reports the result of the operation. Error replies have the same shape in RESP2 and RESP3 and are surfaced as exceptions by the SDKs below. | Protocol | Reply | | --- | --- | -| RESP2 | Flat array of alternating keys and values | -| RESP3 | Map | +| RESP2 | Integer | +| RESP3 | Integer | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -21923,48 +22124,18 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -FUNCTION STATS -``` - - - - - -```ts -import { Redis } from "@upstash/redis"; - -const redis = Redis.fromEnv(); - -const stats = await redis.functions.stats() - -console.log(stats) -// { -// engines: { -// LUA: { -// librariesCount: 3, -// functionsCount: 15 -// } -// } -// } +CLIENT ID ``` - - - - This command is not supported yet in `upstash_redis`. - - - - ```ts import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.function("STATS"); +const result = await redis.client("ID"); console.log(result); ``` @@ -21978,7 +22149,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.functionStats(); +const result = await client.clientId(); console.log(result); ``` @@ -21991,7 +22162,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.function_stats() +result = client.client_id() print(result) ``` @@ -22016,7 +22187,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.FunctionStats(context.Background()).Result() + result, err := client.ClientID(context.Background()).Result() if err != nil { panic(err) } @@ -22034,7 +22205,7 @@ import java.net.URI; import redis.clients.jedis.Jedis; try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.functionStats(); + Object result = jedis.clientId(); System.out.println(result); } ``` @@ -22044,14 +22215,14 @@ try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { ```rust +use redis::TypedCommands; + fn main() -> redis::RedisResult<()> { let url = std::env::var("REDIS_URL").expect("REDIS_URL is not set"); let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let mut command = redis::cmd("FUNCTION"); - command.arg("STATS"); - let result: redis::Value = command.query(&mut connection)?; + let result = connection.client_id()?; println!("{result:?}"); Ok(()) } @@ -22061,43 +22232,27 @@ fn main() -> redis::RedisResult<()> { -# Functions commands -Source: https://upstash.com/docs/redis/commands/functions/overview - - -Call a function -Call a read-only function -Delete a library -Delete all libraries -Kill a running function -List all libraries -Load a library -Get function execution stats - - -# COPY -Source: https://upstash.com/docs/redis/commands/generic/copy - -Use `COPY` to copy the value stored at one key to another key. +# CLIENT INFO +Source: https://upstash.com/docs/redis/commands/connection/client-info -The destination gets an independent deep copy of the value, so later changes to either key do not affect the other, and the source key's remaining time to live is copied along with it. Any type can be copied. +Use `CLIENT INFO` to get a line of statistics about the connection issuing the command. -By default the command does nothing and returns `0` when the destination key already exists; `REPLACE` overwrites it instead. `DB` selects the destination database index, which on Upstash is always `0`. +The reply is a single line of space-separated `field=value` pairs describing the connection: its id, name, address, age, idle time, the number of commands it has run, and the last command it executed. It reports the same fields as one line of [`CLIENT LIST`](/docs/redis/commands/connection/client-list), limited to your own connection, which makes it a cheap way to confirm what the server thinks of the connection you are on. Parse it defensively, since fields can be added over time. ## Syntax ```redis -COPY [DB ] [REPLACE] +CLIENT INFO ``` ## Arguments -| Argument | Required | Repeatable | Description | -| --- | --- | --- | --- | -| `` | Yes | No | Redis key used as source. | -| `` | Yes | No | Redis key used as destination. | -| `DB ` | No | No | Index of the database to copy the key into. | -| `REPLACE` | No | No | Allow replacement of an existing destination. | +This command takes no arguments. + +## Important points + +* This is a connection-oriented command and is available over native Redis TCP, not the stateless REST endpoint. +* This command can expose administrative information or make a broad destructive change. Restrict it to trusted code paths. ## Response @@ -22105,8 +22260,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Integer: `1` if the key was copied, `0` otherwise | -| RESP3 | Integer: `1` if the key was copied, `0` otherwise | +| RESP2 | Bulk string | +| RESP3 | Bulk string | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -22121,31 +22276,7 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -COPY source-key destination-key -``` - - - - - -```ts -import { Redis } from "@upstash/redis"; - -const redis = Redis.fromEnv(); -const result = await redis.copy("source-key", "destination-key"); -console.log(result); -``` - - - - - -```python -from upstash_redis import Redis - -redis = Redis.from_env() -result = redis.copy("source-key", "destination-key") -print(result) +CLIENT INFO ``` @@ -22156,7 +22287,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.copy("source-key", "destination-key"); +const result = await redis.client("INFO"); console.log(result); ``` @@ -22170,7 +22301,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.copy("source-key", "destination-key"); +const result = await client.clientInfo(); console.log(result); ``` @@ -22183,7 +22314,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.copy("source-key", "destination-key") +result = client.client_info() print(result) ``` @@ -22208,7 +22339,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.Copy(context.Background(), "source-key", "destination-key", 0, false).Result() + result, err := client.ClientInfo(context.Background()).Result() if err != nil { panic(err) } @@ -22226,7 +22357,7 @@ import java.net.URI; import redis.clients.jedis.Jedis; try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.copy("source-key", "destination-key", false); + Object result = jedis.clientInfo(); System.out.println(result); } ``` @@ -22236,14 +22367,14 @@ try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { ```rust -use redis::TypedCommands; - fn main() -> redis::RedisResult<()> { let url = std::env::var("REDIS_URL").expect("REDIS_URL is not set"); let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let result = connection.copy("source-key", "destination-key", redis::CopyOptions::default())?; + let mut command = redis::cmd("CLIENT"); + command.arg("INFO"); + let result: redis::Value = command.query(&mut connection)?; println!("{result:?}"); Ok(()) } @@ -22253,24 +22384,27 @@ fn main() -> redis::RedisResult<()> { -# DEL -Source: https://upstash.com/docs/redis/commands/generic/del +# CLIENT LIST +Source: https://upstash.com/docs/redis/commands/connection/client-list -Use `DEL` to delete one or more keys and the values they hold, whatever their type. +Use `CLIENT LIST` to get one line of statistics for every client connection to the server. -The reply counts only the keys that actually existed, so deleting a key that is already gone is not an error and the count tells you how many were really removed. The memory is freed as part of the command, which for very large collections can take noticeable time; [`UNLINK`](/docs/redis/commands/generic/unlink) removes the keys just as immediately but frees their memory in the background. +Each line holds space-separated `field=value` pairs with the connection's id, name, address, age, idle time, protocol version, and last command, so the reply gives a snapshot of who is connected and what they are doing. It is the usual starting point for tracking down connection leaks and idle connections. The reply grows with the number of clients, so avoid calling it on a hot path, and parse it defensively because fields can be added over time. ## Syntax ```redis -DEL [ ...] +CLIENT LIST ``` ## Arguments -| Argument | Required | Repeatable | Description | -| --- | --- | --- | --- | -| `` | Yes | Yes | Redis key targeted by the command. | +This command takes no arguments. + +## Important points + +* This is a connection-oriented command and is available over native Redis TCP, not the stateless REST endpoint. +* This command can expose administrative information or make a broad destructive change. Restrict it to trusted code paths. ## Response @@ -22278,8 +22412,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Integer | -| RESP3 | Integer | +| RESP2 | Bulk string | +| RESP3 | Bulk string | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -22294,31 +22428,7 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -DEL my-key -``` - - - - - -```ts -import { Redis } from "@upstash/redis"; - -const redis = Redis.fromEnv(); - -await redis.del("key1", "key2"); -``` - - - - - -```python -from upstash_redis import Redis - -redis = Redis.from_env() -result = redis.delete("my-key") -print(result) +CLIENT LIST ``` @@ -22329,7 +22439,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.del("my-key"); +const result = await redis.client("LIST"); console.log(result); ``` @@ -22343,7 +22453,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.del("my-key"); +const result = await client.clientList(); console.log(result); ``` @@ -22356,7 +22466,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.delete("my-key") +result = client.client_list() print(result) ``` @@ -22381,7 +22491,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.Del(context.Background(), "my-key").Result() + result, err := client.ClientList(context.Background()).Result() if err != nil { panic(err) } @@ -22399,7 +22509,7 @@ import java.net.URI; import redis.clients.jedis.Jedis; try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.del("my-key"); + Object result = jedis.clientList(); System.out.println(result); } ``` @@ -22409,14 +22519,14 @@ try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { ```rust -use redis::TypedCommands; - fn main() -> redis::RedisResult<()> { let url = std::env::var("REDIS_URL").expect("REDIS_URL is not set"); let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let result = connection.del("my-key")?; + let mut command = redis::cmd("CLIENT"); + command.arg("LIST"); + let result: redis::Value = command.query(&mut connection)?; println!("{result:?}"); Ok(()) } @@ -22426,26 +22536,30 @@ fn main() -> redis::RedisResult<()> { -# DUMP -Source: https://upstash.com/docs/redis/commands/generic/dump - -Use `DUMP` to serialize the value stored at a key into a portable, Redis-specific binary blob. +# CLIENT SETINFO +Source: https://upstash.com/docs/redis/commands/connection/client-setinfo -The blob carries the value together with a version stamp and a checksum, but not the key name and not its time to live. Feeding it to [`RESTORE`](/docs/redis/commands/generic/restore) recreates the value under any key name, in the same database or in another one, which makes the pair the standard way to move or back up individual keys. A missing key returns null. +Use `CLIENT SETINFO` to attach library identification to the current connection. -The reply is raw binary data, not text: keep it in a byte-safe container and avoid string encodings that would corrupt it. +`LIB-NAME` records the name of the client library and `LIB-VER` its version. Both values then appear in [`CLIENT INFO`](/docs/redis/commands/connection/client-info) and [`CLIENT LIST`](/docs/redis/commands/connection/client-list) output, which makes it possible to tell which application or SDK version owns a connection. Most client libraries send this during the handshake, so applications rarely call it themselves. ## Syntax ```redis -DUMP +CLIENT SETINFO ``` ## Arguments | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | -| `` | Yes | No | Redis key targeted by the command. | +| `LIB-NAME \| LIB-VER` | Yes | No | Attribute to update: the client library name or version. | +| `value` | Yes | No | Value to store for the selected client attribute. | + +## Important points + +* This is a connection-oriented command and is available over native Redis TCP, not the stateless REST endpoint. +* This command can expose administrative information or make a broad destructive change. Restrict it to trusted code paths. ## Response @@ -22453,8 +22567,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Bulk string or Null bulk string or null array | -| RESP3 | Bulk string or Null | +| RESP2 | Simple string `OK` | +| RESP3 | Simple string `OK` | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -22469,34 +22583,18 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -DUMP my-key +CLIENT SETINFO LIB-NAME my-client ``` - - - - This command is not supported yet in `@upstash/redis`. - - - - - - - - This command is not supported yet in `upstash_redis`. - - - - ```ts import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.dump("my-key"); +const result = await redis.client("SETINFO", "LIB-NAME", "my-client"); console.log(result); ``` @@ -22510,7 +22608,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.dump("my-key"); +const result = await client.sendCommand(["CLIENT", "SETINFO", "LIB-NAME", "my-client"]); console.log(result); ``` @@ -22523,7 +22621,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.dump("my-key") +result = client.client_setinfo("LIB-NAME", "my-client") print(result) ``` @@ -22548,7 +22646,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.Dump(context.Background(), "my-key").Result() + result, err := client.Do(context.Background(), "CLIENT", "SETINFO", "LIB-NAME", "my-client").Result() if err != nil { panic(err) } @@ -22566,7 +22664,7 @@ import java.net.URI; import redis.clients.jedis.Jedis; try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.dump("my-key"); + Object result = jedis.clientSetInfo(redis.clients.jedis.args.ClientAttributeOption.LIB_NAME, "my-client"); System.out.println(result); } ``` @@ -22581,8 +22679,10 @@ fn main() -> redis::RedisResult<()> { let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let mut command = redis::cmd("DUMP"); - command.arg("my-key"); + let mut command = redis::cmd("CLIENT"); + command.arg("SETINFO"); + command.arg("LIB-NAME"); + command.arg("my-client"); let result: redis::Value = command.query(&mut connection)?; println!("{result:?}"); Ok(()) @@ -22593,24 +22693,29 @@ fn main() -> redis::RedisResult<()> { -# EXISTS -Source: https://upstash.com/docs/redis/commands/generic/exists +# CLIENT SETNAME +Source: https://upstash.com/docs/redis/commands/connection/client-setname -Use `EXISTS` to check whether one or more keys exist. +Use `CLIENT SETNAME` to label the current connection with a name of your choice. -The reply is the number of the given keys that exist, so with a single key it is simply `1` or `0`. A key listed several times is counted each time it is present, which means `EXISTS k k` returns `2` when `k` exists. Checking existence never transfers the value, so it stays cheap even for large values. +The name shows up in [`CLIENT LIST`](/docs/redis/commands/connection/client-list) and [`CLIENT INFO`](/docs/redis/commands/connection/client-info) output, which is handy when several components of an application share one database and you want to tell their connections apart. The name may not contain spaces or newlines, each call replaces the previous name, and passing an empty string clears it. It lives only as long as the connection. ## Syntax ```redis -EXISTS [ ...] +CLIENT SETNAME ``` ## Arguments | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | -| `` | Yes | Yes | Redis key targeted by the command. | +| `connection-name` | Yes | No | Name to associate with this TCP connection. | + +## Important points + +* This is a connection-oriented command and is available over native Redis TCP, not the stateless REST endpoint. +* This command can expose administrative information or make a broad destructive change. Restrict it to trusted code paths. ## Response @@ -22618,8 +22723,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Integer | -| RESP3 | Integer | +| RESP2 | Simple string `OK` | +| RESP3 | Simple string `OK` | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -22634,34 +22739,7 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -EXISTS my-key -``` - - - - - -```ts -import { Redis } from "@upstash/redis"; - -const redis = Redis.fromEnv(); - -await redis.set("key1", "value1") -await redis.set("key2", "value2") -const keys = await redis.exists("key1", "key2", "key3"); -console.log(keys) // 2 -``` - - - - - -```python -from upstash_redis import Redis - -redis = Redis.from_env() -result = redis.exists("my-key") -print(result) +CLIENT SETNAME worker-1 ``` @@ -22672,7 +22750,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.exists("my-key"); +const result = await redis.client("SETNAME", "worker-1"); console.log(result); ``` @@ -22686,7 +22764,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.exists("my-key"); +const result = await client.clientSetName("worker-1"); console.log(result); ``` @@ -22699,7 +22777,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.exists("my-key") +result = client.client_setname("worker-1") print(result) ``` @@ -22724,7 +22802,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.Exists(context.Background(), "my-key").Result() + result, err := client.Do(context.Background(), "CLIENT", "SETNAME", "worker-1").Result() if err != nil { panic(err) } @@ -22742,7 +22820,7 @@ import java.net.URI; import redis.clients.jedis.Jedis; try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.exists("my-key"); + Object result = jedis.clientSetname("worker-1"); System.out.println(result); } ``` @@ -22759,7 +22837,7 @@ fn main() -> redis::RedisResult<()> { let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let result = connection.exists("my-key")?; + let result = connection.client_setname("worker-1")?; println!("{result:?}"); Ok(()) } @@ -22769,35 +22847,24 @@ fn main() -> redis::RedisResult<()> { -# EXPIRE -Source: https://upstash.com/docs/redis/commands/generic/expire - -Use `EXPIRE` to give a key a lifetime in seconds, after which the key is deleted automatically. - -Whether the expiration survives later writes depends on the command: replacing the value with [`SET`](/docs/redis/commands/string/set) clears it, while commands that modify a value in place, such as [`INCR`](/docs/redis/commands/string/incr), [`LPUSH`](/docs/redis/commands/list/lpush) or [`HSET`](/docs/redis/commands/hash/hset), leave it untouched. A negative lifetime deletes the key immediately. +# ECHO +Source: https://upstash.com/docs/redis/commands/connection/echo -The optional condition decides when the new expiration is applied: `NX` only when the key currently has none, `XX` only when it already has one, `GT` only when the new expiration is later than the current one, and `LT` only when it is earlier. Since a key without an expiration counts as living forever, `GT` never adds one and `LT` always does. The reply is `1` when the expiration was set and `0` when the key does not exist or the condition was not met. +Use `ECHO` to have the server send the given message back unchanged. -Read the remaining lifetime with [`TTL`](/docs/redis/commands/generic/ttl) and remove it again with [`PERSIST`](/docs/redis/commands/generic/persist). +The command performs no work beyond the round trip, which makes it a simple way to verify that a connection is alive and that values survive the client library's encoding and decoding. For plain liveness checks [`PING`](/docs/redis/commands/connection/ping) is the more common choice. ## Syntax ```redis -EXPIRE [NX | XX | GT | LT] +ECHO ``` ## Arguments | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | -| `` | Yes | No | Redis key targeted by the command. | -| `` | Yes | No | Lifetime in seconds. | -| `(NX \| XX \| GT \| LT)` | No | No | Choose one form: `NX` (only when the key has no expiration); `XX` (only when the key already has one); `GT` (only when the new expiration is later than the current one); `LT` (only when it is earlier). | - -## Important points - -* `NX` cannot be combined with `XX`, `GT`, or `LT`, and `GT` and `LT` cannot be used together. -* A key with no expiration counts as an infinite one, so `GT` never sets an expiration on such a key and `LT` always does. +| `` | Yes | No | Message payload. | ## Response @@ -22805,8 +22872,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Integer: `1` if the timeout was set, `0` otherwise | -| RESP3 | Integer: `1` if the timeout was set, `0` otherwise | +| RESP2 | Bulk string | +| RESP3 | Bulk string | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -22821,7 +22888,7 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -EXPIRE my-key 1000 +ECHO hello ``` @@ -22833,8 +22900,8 @@ import { Redis } from "@upstash/redis"; const redis = Redis.fromEnv(); -await redis.set("mykey", "Hello"); -await redis.expire("mykey", 10); +const response = await redis.echo("hello world"); +console.log(response); // "hello world" ``` @@ -22845,7 +22912,7 @@ await redis.expire("mykey", 10); from upstash_redis import Redis redis = Redis.from_env() -result = redis.expire("my-key", 1000) +result = redis.echo("hello") print(result) ``` @@ -22857,7 +22924,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.expire("my-key", "1000"); +const result = await redis.echo("hello"); console.log(result); ``` @@ -22871,7 +22938,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.expire("my-key", 1000); +const result = await client.echo("hello"); console.log(result); ``` @@ -22884,7 +22951,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.expire("my-key", 1000) +result = client.echo("hello") print(result) ``` @@ -22899,7 +22966,6 @@ import ( "context" "fmt" "os" - "time" "github.com/redis/go-redis/v9" ) @@ -22910,7 +22976,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.Expire(context.Background(), "my-key", 1000*time.Second).Result() + result, err := client.Echo(context.Background(), "hello").Result() if err != nil { panic(err) } @@ -22928,7 +22994,7 @@ import java.net.URI; import redis.clients.jedis.Jedis; try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.expire("my-key", 1000); + Object result = jedis.echo("hello"); System.out.println(result); } ``` @@ -22938,14 +23004,14 @@ try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { ```rust -use redis::TypedCommands; - fn main() -> redis::RedisResult<()> { let url = std::env::var("REDIS_URL").expect("REDIS_URL is not set"); let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let result = connection.expire("my-key", 1000)?; + let mut command = redis::cmd("ECHO"); + command.arg("hello"); + let result: redis::Value = command.query(&mut connection)?; println!("{result:?}"); Ok(()) } @@ -22955,33 +23021,33 @@ fn main() -> redis::RedisResult<()> { -# EXPIREAT -Source: https://upstash.com/docs/redis/commands/generic/expireat +# HELLO +Source: https://upstash.com/docs/redis/commands/connection/hello -Use `EXPIREAT` to schedule a key for automatic deletion at a fixed point in time, given as a Unix timestamp in seconds. +Use `HELLO` to negotiate the protocol version of the connection and read the server handshake information. -It behaves exactly like [`EXPIRE`](/docs/redis/commands/generic/expire) except that the deadline is absolute rather than relative, which is what you want when several keys must expire at the same moment, such as the end of an hour or of a billing period. Computing the deadline once and reusing it also avoids the drift that repeated relative expirations introduce. A timestamp in the past deletes the key right away. +Passing `` switches the connection to RESP2 or RESP3. RESP3 adds native map, set, double, and push replies, so commands such as [`HGETALL`](/docs/redis/commands/hash/hgetall) or [`CONFIG GET`](/docs/redis/commands/server/config-get) come back as maps instead of flat arrays, and pub/sub messages arrive as push replies that do not block ordinary commands. `AUTH` authenticates in the same call and `SETNAME` names the connection, which lets a client complete its handshake in one round trip. -The optional condition works as it does for `EXPIRE`: `NX` only when the key has no expiration, `XX` only when it already has one, `GT` only when the new deadline is later than the current one, and `LT` only when it is earlier. +Called without arguments, `HELLO` only reports the server version, the protocol in use, the connection id, and the current role, leaving the protocol unchanged. ## Syntax ```redis -EXPIREAT [NX | XX | GT | LT] +HELLO + [ + [AUTH ] + [SETNAME ]] ``` ## Arguments | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | -| `` | Yes | No | Redis key targeted by the command. | -| `` | Yes | No | Expiration time as a Unix timestamp in seconds. | -| `(NX \| XX \| GT \| LT)` | No | No | Choose one form: `NX` (only when the key has no expiration); `XX` (only when the key already has one); `GT` (only when the new expiration is later than the current one); `LT` (only when it is earlier). | +| ` [AUTH ] [SETNAME ]` | No | No | Protocol version to switch to, optionally with credentials and a connection name. | ## Important points -* `NX` cannot be combined with `XX`, `GT`, or `LT`, and `GT` and `LT` cannot be used together. -* A key with no expiration counts as an infinite one, so `GT` never sets an expiration on such a key and `LT` always does. +* This is a connection-oriented command and is available over native Redis TCP, not the stateless REST endpoint. ## Response @@ -22989,8 +23055,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Integer: `1` if the timeout was set, `0` otherwise | -| RESP3 | Integer: `1` if the timeout was set, `0` otherwise | +| RESP2 | Flat array of alternating keys and values | +| RESP3 | Map | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -23005,33 +23071,7 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -EXPIREAT my-key 1735689600 -``` - - - - - -```ts -import { Redis } from "@upstash/redis"; - -const redis = Redis.fromEnv(); - -await redis.set("mykey", "Hello"); -const tenSecondsFromNow = Math.floor(Date.now() / 1000) + 10; -await redis.expireat("mykey", tenSecondsFromNow); -``` - - - - - -```python -from upstash_redis import Redis - -redis = Redis.from_env() -result = redis.expireat("my-key", 1735689600) -print(result) +HELLO ``` @@ -23042,7 +23082,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.expireat("my-key", "1735689600"); +const result = await redis.hello(); console.log(result); ``` @@ -23056,7 +23096,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.expireAt("my-key", 1735689600); +const result = await client.hello(); console.log(result); ``` @@ -23069,7 +23109,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.expireat("my-key", 1735689600) +result = client.hello() print(result) ``` @@ -23084,7 +23124,6 @@ import ( "context" "fmt" "os" - "time" "github.com/redis/go-redis/v9" ) @@ -23095,7 +23134,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.ExpireAt(context.Background(), "my-key", time.Unix(1735689600, 0)).Result() + result, err := client.Do(context.Background(), "HELLO").Result() if err != nil { panic(err) } @@ -23109,11 +23148,13 @@ func main() { ```java import java.net.URI; - +import java.nio.charset.StandardCharsets; import redis.clients.jedis.Jedis; +import redis.clients.jedis.commands.ProtocolCommand; +ProtocolCommand command = () -> "HELLO".getBytes(StandardCharsets.UTF_8); try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.expireAt("my-key", 1735689600); + Object result = jedis.sendCommand(command); System.out.println(result); } ``` @@ -23123,14 +23164,14 @@ try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { ```rust -use redis::TypedCommands; - fn main() -> redis::RedisResult<()> { let url = std::env::var("REDIS_URL").expect("REDIS_URL is not set"); let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let result = connection.expire_at("my-key", 1735689600)?; + let mut command = redis::cmd("HELLO"); + + let result: redis::Value = command.query(&mut connection)?; println!("{result:?}"); Ok(()) } @@ -23140,28 +23181,48 @@ fn main() -> redis::RedisResult<()> { -# EXPIRETIME -Source: https://upstash.com/docs/redis/commands/generic/expiretime +# Connection commands +Source: https://upstash.com/docs/redis/commands/connection/overview -Use `EXPIRETIME` to read the absolute time at which a key will expire, as a Unix timestamp in seconds. + +Authenticate to the server +Get the current connection name +Get the current client ID +Get info about current connection +List all client connections +Set client connection attributes +Set the connection name +Echo the given string +Handshake with Redis protocol +Ping the server +Close the connection +Reset the connection +Select the database by index + -The reply is `-1` when the key exists but has no expiration, and `-2` when the key does not exist, so the two cases can be told apart. Use [`TTL`](/docs/redis/commands/generic/ttl) when you need the remaining lifetime instead of the deadline, and [`PEXPIRETIME`](/docs/redis/commands/generic/pexpiretime) when you need millisecond precision. +# PING +Source: https://upstash.com/docs/redis/commands/connection/ping + +Use `PING` to check that the connection and the server are alive. + +Without arguments the server replies `PONG`. With a message it echoes that message back instead, which lets a client match a reply to the exact request that produced it. `PING` is the standard health check for a connection pool, both to test a connection before handing it out and to keep an otherwise idle connection from being closed by intermediate proxies. It also works while the connection is subscribed to channels, where it doubles as a keepalive. ## Syntax ```redis -EXPIRETIME +PING [] ``` ## Arguments | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | -| `` | Yes | No | Redis key targeted by the command. | +| `` | No | No | Message payload. | ## Important points -* Negative integer replies are sentinel values, not durations or timestamps; see the response description for missing or persistent data. +* Without arguments the reply is `PONG`; with a message, the message is echoed back as a bulk string. +* While the connection is subscribed under RESP2, `PING` replies with a two-element array holding `pong` and the message instead. ## Response @@ -23169,8 +23230,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Integer: expiration Unix time in seconds, `-1` if the key has no expiration, `-2` if the key does not exist | -| RESP3 | Integer: expiration Unix time in seconds, `-1` if the key has no expiration, `-2` if the key does not exist | +| RESP2 | Simple string `PONG`, or the message as a bulk string; a two-element `pong`/message array while subscribed | +| RESP3 | Simple string `PONG`, or the message as a bulk string | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -23185,24 +23246,33 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -EXPIRETIME my-key +PING ``` - - This command is not supported yet in `@upstash/redis`. - +```ts +import { Redis } from "@upstash/redis"; + +const redis = Redis.fromEnv(); + +const response = await redis.ping(); +console.log(response); // "PONG" +``` - - This command is not supported yet in `upstash_redis`. - +```python +from upstash_redis import Redis + +redis = Redis.from_env() +result = redis.ping() +print(result) +``` @@ -23212,7 +23282,7 @@ EXPIRETIME my-key import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.expiretime("my-key"); +const result = await redis.ping(); console.log(result); ``` @@ -23226,7 +23296,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.expireTime("my-key"); +const result = await client.ping(); console.log(result); ``` @@ -23239,7 +23309,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.expiretime("my-key") +result = client.ping() print(result) ``` @@ -23264,7 +23334,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.ExpireTime(context.Background(), "my-key").Result() + result, err := client.Ping(context.Background()).Result() if err != nil { panic(err) } @@ -23282,7 +23352,7 @@ import java.net.URI; import redis.clients.jedis.Jedis; try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.expireTime("my-key"); + Object result = jedis.ping(); System.out.println(result); } ``` @@ -23299,7 +23369,7 @@ fn main() -> redis::RedisResult<()> { let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let result = connection.expire_time("my-key")?; + let result = connection.ping()?; println!("{result:?}"); Ok(()) } @@ -23309,31 +23379,30 @@ fn main() -> redis::RedisResult<()> { -# KEYS -Source: https://upstash.com/docs/redis/commands/generic/keys +# QUIT +Source: https://upstash.com/docs/redis/commands/connection/quit -Use `KEYS` to list every key in the database whose name matches a glob-style pattern. + + Prefer closing the connection from the client in new code, which avoids leaving `TIME_WAIT` sockets on the server. + -The pattern supports `*` for any sequence of characters, `?` for a single character, `[...]` for character classes, and `\` to escape a literal, so `user:*:session` matches all session keys of all users and `*` matches everything. +Use `QUIT` to ask the server to close the connection once all pending replies have been sent. -The command walks the entire keyspace and returns all matches in one reply, which on a database of any real size means a long block and a very large response. Treat it as a debugging and maintenance tool: on hot paths use [`SCAN`](/docs/redis/commands/generic/scan), which accepts the same `MATCH` patterns but walks the keyspace in small batches, or keep an index of your keys in a set instead. +The server replies `OK` and then terminates the connection, so no reply is lost and the shutdown is clean from both sides. Modern clients usually just close the socket, and pooled connections should be returned to the pool rather than closed, so `QUIT` is mostly useful in scripts and interactive sessions. ## Syntax ```redis -KEYS +QUIT ``` ## Arguments -| Argument | Required | Repeatable | Description | -| --- | --- | --- | --- | -| `` | Yes | No | Glob-style pattern to match keys against. | +This command takes no arguments. ## Important points -* This command can expose administrative information or make a broad destructive change. Restrict it to trusted code paths. -* This operation can inspect a large part of the database. Prefer cursor-based scans where possible and avoid unbounded use on hot paths. +* This is a connection-oriented command and is available over native Redis TCP, not the stateless REST endpoint. ## Response @@ -23341,8 +23410,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Array of bulk-string keys | -| RESP3 | Array of bulk-string keys | +| RESP2 | Simple string `OK` | +| RESP3 | Simple string `OK` | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -23357,31 +23426,7 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -KEYS * -``` - - - - - -```ts -import { Redis } from "@upstash/redis"; - -const redis = Redis.fromEnv(); - -const keys = await redis.keys("prefix*"); -``` - - - - - -```python -from upstash_redis import Redis - -redis = Redis.from_env() -result = redis.keys("*") -print(result) +QUIT ``` @@ -23392,7 +23437,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.keys("*"); +const result = await redis.quit(); console.log(result); ``` @@ -23406,7 +23451,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.keys("*"); +const result = await client.sendCommand(["QUIT"]); console.log(result); ``` @@ -23419,7 +23464,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.keys("*") +result = client.quit() print(result) ``` @@ -23444,7 +23489,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.Keys(context.Background(), "*").Result() + result, err := client.Quit(context.Background()).Result() if err != nil { panic(err) } @@ -23458,11 +23503,13 @@ func main() { ```java import java.net.URI; - +import java.nio.charset.StandardCharsets; import redis.clients.jedis.Jedis; +import redis.clients.jedis.commands.ProtocolCommand; +ProtocolCommand command = () -> "QUIT".getBytes(StandardCharsets.UTF_8); try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.keys("*"); + Object result = jedis.sendCommand(command); System.out.println(result); } ``` @@ -23472,14 +23519,14 @@ try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { ```rust -use redis::TypedCommands; - fn main() -> redis::RedisResult<()> { let url = std::env::var("REDIS_URL").expect("REDIS_URL is not set"); let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let result = connection.keys("*")?; + let mut command = redis::cmd("QUIT"); + + let result: redis::Value = command.query(&mut connection)?; println!("{result:?}"); Ok(()) } @@ -23489,32 +23536,26 @@ fn main() -> redis::RedisResult<()> { -# MEMORY USAGE -Source: https://upstash.com/docs/redis/commands/generic/memory-usage - -Use `MEMORY USAGE` to estimate how many bytes a key and its value occupy in memory. +# RESET +Source: https://upstash.com/docs/redis/commands/connection/reset -The figure covers the stored data along with its internal overhead, so it is larger than the raw size of the value and is meant for comparing keys rather than for exact accounting. For aggregate types such as hashes, lists, sets, sorted sets, and streams the value is sampled instead of fully traversed: `SAMPLES` sets how many nested elements are inspected, and the value is clamped to the range this deployment supports, so it tunes the estimate rather than forcing an exact traversal. A missing key returns null. +Use `RESET` to return the connection to the state it had right after connecting. -It is the usual way to find out which keys are responsible for memory growth before deciding what to trim or restructure. +It discards an open [`MULTI`](/docs/redis/commands/transactions/multi) block, unwatches every key watched with [`WATCH`](/docs/redis/commands/transactions/watch), leaves subscriber and monitor modes, clears the connection name, re-enables replies, and de-authenticates the connection when the database requires a password. This makes it the safe way to hand a connection back to a pool after an error, since the next user cannot inherit a half-finished transaction or a leftover subscription. ## Syntax ```redis -MEMORY USAGE [SAMPLES ] +RESET ``` ## Arguments -| Argument | Required | Repeatable | Description | -| --- | --- | --- | --- | -| `key` | Yes | No | Key whose in-memory footprint should be estimated. | -| `SAMPLES count` | No | No | Sampling count used when estimating large stream values. | +This command takes no arguments. ## Important points -* The result is an estimate in bytes and can change as the internal representation changes. -* A missing key returns null. The sampling count is clamped to the deployment's supported range. +* This is a connection-oriented command and is available over native Redis TCP, not the stateless REST endpoint. ## Response @@ -23522,8 +23563,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Integer or Null bulk string or null array | -| RESP3 | Integer or Null | +| RESP2 | Simple string `RESET` | +| RESP3 | Simple string `RESET` | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -23538,34 +23579,18 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -MEMORY USAGE my-key SAMPLES 10 +RESET ``` - - - - This command is not supported yet in `@upstash/redis`. - - - - - - - - This command is not supported yet in `upstash_redis`. - - - - ```ts import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.memory("USAGE", "my-key", "SAMPLES", "10"); +const result = await redis.reset(); console.log(result); ``` @@ -23579,7 +23604,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.memoryUsage("my-key", { SAMPLES: 10 }); +const result = await client.sendCommand(["RESET"]); console.log(result); ``` @@ -23592,7 +23617,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.memory_usage("my-key", samples=10) +result = client.reset() print(result) ``` @@ -23617,7 +23642,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.MemoryUsage(context.Background(), "my-key", 10).Result() + result, err := client.Do(context.Background(), "RESET").Result() if err != nil { panic(err) } @@ -23635,7 +23660,7 @@ import java.net.URI; import redis.clients.jedis.Jedis; try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.memoryUsage("my-key", 10); + Object result = jedis.reset(); System.out.println(result); } ``` @@ -23650,11 +23675,8 @@ fn main() -> redis::RedisResult<()> { let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let mut command = redis::cmd("MEMORY"); - command.arg("USAGE"); - command.arg("my-key"); - command.arg("SAMPLES"); - command.arg("10"); + let mut command = redis::cmd("RESET"); + let result: redis::Value = command.query(&mut connection)?; println!("{result:?}"); Ok(()) @@ -23665,55 +23687,28 @@ fn main() -> redis::RedisResult<()> { -# Generic commands -Source: https://upstash.com/docs/redis/commands/generic/overview - - -Copy a key to another key -Delete one or more keys -Serialize a key's value -Check if keys exist -Set a key's TTL in seconds -Set expiry as Unix timestamp -Get expiry as Unix timestamp -Find keys matching a pattern -Estimate memory used by a key -Remove the expiration from a key -Set a key's TTL in milliseconds -Set expiry as Unix ms timestamp -Get expiry as Unix ms timestamp -Get TTL in milliseconds -Return a random key -Rename a key -Rename a key if new key doesn't exist -Deserialize and restore a key -Incrementally iterate keys -Update last access time of keys -Get TTL in seconds -Get the type of a key -Delete keys asynchronously -Wait for replica acknowledgements -Wait for local and replica persistence - - -# PERSIST -Source: https://upstash.com/docs/redis/commands/generic/persist +# SELECT +Source: https://upstash.com/docs/redis/commands/connection/select -Use `PERSIST` to remove the expiration from a key so that it stops being deleted automatically and lives until it is removed explicitly. +Use `SELECT` to switch the connection to another database index. -The reply is `1` when an expiration was removed and `0` when the key does not exist or had no expiration to begin with. This is how a value gets promoted from temporary to permanent without rewriting it, for example when a trial record becomes a persistent one. +Upstash exposes a single logical database, so `0` is the only valid index and anything else returns an error. The command is accepted so that clients and frameworks that issue `SELECT 0` while setting up a connection keep working unchanged. To separate concerns inside one database, use key prefixes instead of numbered databases. ## Syntax ```redis -PERSIST +SELECT 0 ``` ## Arguments | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | -| `` | Yes | No | Redis key targeted by the command. | +| `0` | Yes | No | Database index. Upstash supports only database 0. | + +## Important points + +* Upstash exposes a single logical database. Any index other than `0` returns an error. ## Response @@ -23721,8 +23716,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Integer: `1` if the timeout was removed, `0` otherwise | -| RESP3 | Integer: `1` if the timeout was removed, `0` otherwise | +| RESP2 | Simple string `OK` | +| RESP3 | Simple string `OK` | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -23737,32 +23732,24 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -PERSIST my-key +SELECT 0 ``` -```ts -import { Redis } from "@upstash/redis"; - -const redis = Redis.fromEnv(); - -await redis.persist(key); -``` + + This command is not supported yet in `@upstash/redis`. + -```python -from upstash_redis import Redis - -redis = Redis.from_env() -result = redis.persist("my-key") -print(result) -``` + + This command is not supported yet in `upstash_redis`. + @@ -23772,7 +23759,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.persist("my-key"); +const result = await redis.select("0"); console.log(result); ``` @@ -23786,7 +23773,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.persist("my-key"); +const result = await client.sendCommand(["SELECT", "0"]); console.log(result); ``` @@ -23799,7 +23786,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.persist("my-key") +result = client.select("0") print(result) ``` @@ -23824,7 +23811,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.Persist(context.Background(), "my-key").Result() + result, err := client.Do(context.Background(), "SELECT", "0").Result() if err != nil { panic(err) } @@ -23842,7 +23829,7 @@ import java.net.URI; import redis.clients.jedis.Jedis; try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.persist("my-key"); + Object result = jedis.select(0); System.out.println(result); } ``` @@ -23852,14 +23839,14 @@ try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { ```rust -use redis::TypedCommands; - fn main() -> redis::RedisResult<()> { let url = std::env::var("REDIS_URL").expect("REDIS_URL is not set"); let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let result = connection.persist("my-key")?; + let mut command = redis::cmd("SELECT"); + command.arg("0"); + let result: redis::Value = command.query(&mut connection)?; println!("{result:?}"); Ok(()) } @@ -23869,33 +23856,61 @@ fn main() -> redis::RedisResult<()> { -# PEXPIRE -Source: https://upstash.com/docs/redis/commands/generic/pexpire +# FCALL +Source: https://upstash.com/docs/redis/commands/functions/fcall -Use `PEXPIRE` to give a key a lifetime in milliseconds, after which the key is deleted automatically. +Use `FCALL` to invoke a function from a library loaded with [`FUNCTION LOAD`](/docs/redis/commands/functions/function-load). -It is the millisecond form of [`EXPIRE`](/docs/redis/commands/generic/expire) and behaves identically otherwise: replacing the value with [`SET`](/docs/redis/commands/string/set) clears the expiration, while in-place updates keep it, and a negative lifetime deletes the key immediately. The sub-second precision matters for short-lived keys such as locks and rate limit windows. +`` tells the server how many of the arguments that follow are key names. Those keys reach the function in `KEYS` and every remaining argument in `ARGV`. Passing key names as keys instead of hardcoding them in the function body matters, because Redis uses that list for routing and access checks. -The optional condition decides when the new expiration is applied: `NX` only when the key has none, `XX` only when it already has one, `GT` only when the new expiration is later than the current one, and `LT` only when it is earlier. +The function runs on the server as a single atomic step, so a sequence of reads and writes that would otherwise need several round trips and a transaction becomes one command. Use [`FCALL_RO`](/docs/redis/commands/functions/fcall-ro) when the function only reads. Functions are the successor to [`EVAL`](/docs/redis/commands/scripting/eval) scripts: they are named, registered once as part of a library, and persisted with the dataset instead of being sent or looked up by digest on every call. + +Upstash runs a function under the global lock by default, since the engine cannot know in advance which keys it will touch. Registering the function with the `allow-key-locking` flag makes the call lock only the keys passed in the key list, so calls that work on disjoint keys run in parallel: + +```lua +redis.register_function{ + function_name='incr_quota', + callback=incr_quota, + flags={'allow-key-locking'} +} +``` + +Unlike Lua scripts, where the flag goes on the library shebang, this flag is set per registered function. With it set, every key the function touches must be passed as a key in the `FCALL` call: keys sent as ordinary arguments are not locked, and commands that need database-wide access, such as `FLUSHDB`, are rejected. See [Key-Based Locking](/docs/redis/features/key-locking) for the full rules. + + + Pass every key the function touches in the key list, even when it runs under + the global lock. Upstash keeps idle entries + [on disk](/docs/redis/features/durability): keys given in the key list are loaded + before the function starts and the lock is released during that read, but a + key that the function builds from `ARGV` while it runs is read from disk with + the lock held, stalling every command waiting on it. See + [Dynamic Keys and Latency](/docs/redis/features/key-locking#dynamic-keys-and-latency). + ## Syntax ```redis -PEXPIRE [NX | XX | GT | LT] +FCALL [ [ ...]] [ [ ...]] ``` ## Arguments | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | -| `` | Yes | No | Redis key targeted by the command. | -| `` | Yes | No | Lifetime in milliseconds. | -| `(NX \| XX \| GT \| LT)` | No | No | Choose one form: `NX` (only when the key has no expiration); `XX` (only when the key already has one); `GT` (only when the new expiration is later than the current one); `LT` (only when it is earlier). | +| `` | Yes | No | Name of the registered function to call. | +| `` | Yes | No | Number of key arguments that follow. | +| `` | No | Yes | Redis key targeted by the command. | +| `` | No | Yes | Additional argument passed to the function. | ## Important points -* `NX` cannot be combined with `XX`, `GT`, or `LT`, and `GT` and `LT` cannot be used together. -* A key with no expiration counts as an infinite one, so `GT` never sets an expiration on such a key and `LT` always does. +* `numkeys` must equal the number of key arguments that immediately follow it; remaining arguments are available to the script or function as ordinary arguments. +* The function takes the global lock unless it was registered with the `allow-key-locking` flag, in which case only the keys passed in the key list are locked. See [Key-Based Locking](/docs/redis/features/key-locking). +* Pass every key the function touches in the key list whether or not `allow-key-locking` is set. A key built inside the function is read from disk under the lock when it is not in memory, and it is rejected outright when the flag is set. See [Dynamic Keys and Latency](/docs/redis/features/key-locking#dynamic-keys-and-latency). + +## Reply conversion + +`redis.setresp()` and the RESP2 and RESP3 conversions applied to `redis.call` replies work exactly as they do for [`EVAL`](/docs/redis/commands/scripting/eval#reply-conversion). ## Response @@ -23903,8 +23918,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Integer: `1` if the timeout was set, `0` otherwise | -| RESP3 | Integer: `1` if the timeout was set, `0` otherwise | +| RESP2 | Reply returned by the invoked function | +| RESP3 | Reply returned by the invoked function | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -23919,7 +23934,7 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -PEXPIRE my-key 1000 +FCALL my_function 1 my-key value ``` @@ -23931,20 +23946,28 @@ import { Redis } from "@upstash/redis"; const redis = Redis.fromEnv(); -await redis.pexpire(key, 60_000); // 1 minute +const code = ` +#!lua name=mylib +redis.register_function('helloworld', + function() + return 'Hello World!' + end +) +`; + +await redis.functions.load({ code, replace: true }); + +const res = await redis.functions.call("helloworld"); +console.log(res); // "Hello World!" ``` -```python -from upstash_redis import Redis - -redis = Redis.from_env() -result = redis.pexpire("my-key", 1000) -print(result) -``` + + This command is not supported yet in `upstash_redis`. + @@ -23954,7 +23977,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.pexpire("my-key", "1000"); +const result = await redis.fcall("my_function", "1", "my-key", "value"); console.log(result); ``` @@ -23968,7 +23991,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.pExpire("my-key", 1000); +const result = await client.fCall("my_function", { keys: ["my-key"], arguments: ["value"] }); console.log(result); ``` @@ -23981,7 +24004,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.pexpire("my-key", 1000) +result = client.fcall("my_function", 1, "my-key", "value") print(result) ``` @@ -23996,7 +24019,6 @@ import ( "context" "fmt" "os" - "time" "github.com/redis/go-redis/v9" ) @@ -24007,7 +24029,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.PExpire(context.Background(), "my-key", time.Second).Result() + result, err := client.FCall(context.Background(), "my_function", []string{"my-key"}, "value").Result() if err != nil { panic(err) } @@ -24025,7 +24047,7 @@ import java.net.URI; import redis.clients.jedis.Jedis; try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.pexpire("my-key", 1000); + Object result = jedis.fcall("my_function", java.util.List.of("my-key"), java.util.List.of("value")); System.out.println(result); } ``` @@ -24035,14 +24057,15 @@ try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { ```rust -use redis::TypedCommands; - fn main() -> redis::RedisResult<()> { let url = std::env::var("REDIS_URL").expect("REDIS_URL is not set"); let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let result = connection.pexpire("my-key", 1000)?; + let mut command = redis::cmd("FCALL"); + command.arg("my_function"); + command.arg("1"); + let result: redis::Value = command.query(&mut connection)?; println!("{result:?}"); Ok(()) } @@ -24052,33 +24075,41 @@ fn main() -> redis::RedisResult<()> { -# PEXPIREAT -Source: https://upstash.com/docs/redis/commands/generic/pexpireat +# FCALL_RO +Source: https://upstash.com/docs/redis/commands/functions/fcall-ro -Use `PEXPIREAT` to schedule a key for automatic deletion at a fixed point in time, given as a Unix timestamp in milliseconds. +Use `FCALL_RO` to invoke a function that is declared read-only. -It combines the absolute deadline of [`EXPIREAT`](/docs/redis/commands/generic/expireat) with the millisecond precision of [`PEXPIRE`](/docs/redis/commands/generic/pexpire), which is what you need when many keys must expire at exactly the same instant. A timestamp in the past deletes the key right away. +The function must have been registered with the `no-writes` flag; calling a function without it returns an error. In exchange the server knows the call cannot modify data, so it can serve it on replicas and reject accidental writes outright. -The optional condition works as elsewhere: `NX` only when the key has no expiration, `XX` only when it already has one, `GT` only when the new deadline is later than the current one, and `LT` only when it is earlier. +Apart from that restriction it behaves like [`FCALL`](/docs/redis/commands/functions/fcall): `` splits the arguments into the keys the function receives in `KEYS` and the plain arguments it receives in `ARGV`. + +Being read-only does not by itself make the call concurrent with others. The function takes the global lock unless it was also registered with the `allow-key-locking` flag, as in `flags={'no-writes', 'allow-key-locking'}`. With both flags, the call takes shared read locks on the keys passed in the key list, so several readers of the same key proceed together. See [Key-Based Locking](/docs/redis/features/key-locking). ## Syntax ```redis -PEXPIREAT [NX | XX | GT | LT] +FCALL_RO [ [ ...]] [ [ ...]] ``` ## Arguments | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | -| `` | Yes | No | Redis key targeted by the command. | -| `` | Yes | No | Expiration time as a Unix timestamp in milliseconds. | -| `(NX \| XX \| GT \| LT)` | No | No | Choose one form: `NX` (only when the key has no expiration); `XX` (only when the key already has one); `GT` (only when the new expiration is later than the current one); `LT` (only when it is earlier). | +| `` | Yes | No | Name of the registered function to call. | +| `` | Yes | No | Number of key arguments that follow. | +| `` | No | Yes | Redis key targeted by the command. | +| `` | No | Yes | Additional argument passed to the function. | ## Important points -* `NX` cannot be combined with `XX`, `GT`, or `LT`, and `GT` and `LT` cannot be used together. -* A key with no expiration counts as an infinite one, so `GT` never sets an expiration on such a key and `LT` always does. +* `numkeys` must equal the number of key arguments that immediately follow it; remaining arguments are available to the script or function as ordinary arguments. +* A `no-writes` function still takes the global lock unless it was also registered with the `allow-key-locking` flag. See [Key-Based Locking](/docs/redis/features/key-locking). +* Pass every key the function reads in the key list whether or not `allow-key-locking` is set. A key built inside the function is read from disk under the lock when it is not in memory, and it is rejected outright when the flag is set. See [Dynamic Keys and Latency](/docs/redis/features/key-locking#dynamic-keys-and-latency). + +## Reply conversion + +`redis.setresp()` and the RESP2 and RESP3 conversions applied to `redis.call` replies work exactly as they do for [`EVAL`](/docs/redis/commands/scripting/eval#reply-conversion). ## Response @@ -24086,8 +24117,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Integer: `1` if the timeout was set, `0` otherwise | -| RESP3 | Integer: `1` if the timeout was set, `0` otherwise | +| RESP2 | Reply returned by the invoked read-only function | +| RESP3 | Reply returned by the invoked read-only function | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -24102,7 +24133,7 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -PEXPIREAT my-key 1735689600 +FCALL_RO my_function 1 my-key value ``` @@ -24114,22 +24145,35 @@ import { Redis } from "@upstash/redis"; const redis = Redis.fromEnv(); -await redis.set("mykey", "Hello"); -const tenMinutesFromNow = Date.now() + 10 * 60 * 1000; -await redis.pexpireat("mykey", tenMinutesFromNow); +const code = ` +#!lua name=ro_lib + +local function get_value(keys, args) + return redis.call('GET', keys[1]) +end + +redis.register_function({ + function_name='get_value', + callback=get_value, + flags={ 'no-writes' } +}) +`; + +await redis.functions.load({ code, replace: true }); + +// Call the read-only function +// Note: We can modify the keys usage here, but since it represents a read-only operation +// and we marked it with 'no-writes', it is safe to use callRo. +const value = await redis.functions.callRo("get_value", ["mykey"]) ``` -```python -from upstash_redis import Redis - -redis = Redis.from_env() -result = redis.pexpireat("my-key", 1735689600) -print(result) -``` + + This command is not supported yet in `upstash_redis`. + @@ -24139,7 +24183,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.pexpireat("my-key", "1735689600"); +const result = await redis.fcall_ro("my_function", "1", "my-key", "value"); console.log(result); ``` @@ -24153,7 +24197,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.pExpireAt("my-key", 1735689600); +const result = await client.fCallRo("my_function", { keys: ["my-key"], arguments: ["value"] }); console.log(result); ``` @@ -24166,7 +24210,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.pexpireat("my-key", 1735689600) +result = client.fcall_ro("my_function", 1, "my-key", "value") print(result) ``` @@ -24181,7 +24225,6 @@ import ( "context" "fmt" "os" - "time" "github.com/redis/go-redis/v9" ) @@ -24192,7 +24235,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.PExpireAt(context.Background(), "my-key", time.UnixMilli(1735689600)).Result() + result, err := client.FCallRO(context.Background(), "my_function", []string{"my-key"}, "value").Result() if err != nil { panic(err) } @@ -24210,7 +24253,7 @@ import java.net.URI; import redis.clients.jedis.Jedis; try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.pexpireAt("my-key", 1735689600); + Object result = jedis.fcallReadonly("my_function", java.util.List.of("my-key"), java.util.List.of("value")); System.out.println(result); } ``` @@ -24220,14 +24263,15 @@ try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { ```rust -use redis::TypedCommands; - fn main() -> redis::RedisResult<()> { let url = std::env::var("REDIS_URL").expect("REDIS_URL is not set"); let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let result = connection.pexpire_at("my-key", 1735689600)?; + let mut command = redis::cmd("FCALL_RO"); + command.arg("my_function"); + command.arg("1"); + let result: redis::Value = command.query(&mut connection)?; println!("{result:?}"); Ok(()) } @@ -24237,28 +24281,24 @@ fn main() -> redis::RedisResult<()> { -# PEXPIRETIME -Source: https://upstash.com/docs/redis/commands/generic/pexpiretime +# FUNCTION DELETE +Source: https://upstash.com/docs/redis/commands/functions/function-delete -Use `PEXPIRETIME` to read the absolute time at which a key will expire, as a Unix timestamp in milliseconds. +Use `FUNCTION DELETE` to remove a function library and every function it registered. -The reply is `-1` when the key exists but has no expiration and `-2` when the key does not exist. It is the millisecond form of [`EXPIRETIME`](/docs/redis/commands/generic/expiretime), and it reports a deadline rather than a remaining lifetime, which makes it the value to compare against a clock when you need to know exactly when something is due. +The argument is the library name declared when the library was loaded, not the name of a single function, and there is no way to delete one function from a library: reload the library with `REPLACE` instead. Deleting a library that does not exist returns an error, and calls to its functions fail until the library is loaded again. ## Syntax ```redis -PEXPIRETIME +FUNCTION DELETE ``` ## Arguments | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | -| `` | Yes | No | Redis key targeted by the command. | - -## Important points - -* Negative integer replies are sentinel values, not durations or timestamps; see the response description for missing or persistent data. +| `` | Yes | No | Name of the function library. | ## Response @@ -24266,8 +24306,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Integer: expiration Unix time in milliseconds, `-1` if the key has no expiration, `-2` if the key does not exist | -| RESP3 | Integer: expiration Unix time in milliseconds, `-1` if the key has no expiration, `-2` if the key does not exist | +| RESP2 | Simple string `OK` | +| RESP3 | Simple string `OK` | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -24282,16 +24322,20 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -PEXPIRETIME my-key +FUNCTION DELETE mylib ``` - - This command is not supported yet in `@upstash/redis`. - +```ts +import { Redis } from "@upstash/redis"; + +const redis = Redis.fromEnv(); + +await redis.functions.delete("mylib") +``` @@ -24309,7 +24353,7 @@ PEXPIRETIME my-key import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.pexpiretime("my-key"); +const result = await redis.function("DELETE", "library-name"); console.log(result); ``` @@ -24323,7 +24367,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.pExpireTime("my-key"); +const result = await client.functionDelete("library-name"); console.log(result); ``` @@ -24336,7 +24380,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.pexpiretime("my-key") +result = client.function_delete("library-name") print(result) ``` @@ -24361,7 +24405,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.PExpireTime(context.Background(), "my-key").Result() + result, err := client.FunctionDelete(context.Background(), "library-name").Result() if err != nil { panic(err) } @@ -24379,7 +24423,7 @@ import java.net.URI; import redis.clients.jedis.Jedis; try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.pexpireTime("my-key"); + Object result = jedis.functionDelete("library-name"); System.out.println(result); } ``` @@ -24389,14 +24433,15 @@ try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { ```rust -use redis::TypedCommands; - fn main() -> redis::RedisResult<()> { let url = std::env::var("REDIS_URL").expect("REDIS_URL is not set"); let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let result = connection.pexpire_time("my-key")?; + let mut command = redis::cmd("FUNCTION"); + command.arg("DELETE"); + command.arg("library-name"); + let result: redis::Value = command.query(&mut connection)?; println!("{result:?}"); Ok(()) } @@ -24406,28 +24451,24 @@ fn main() -> redis::RedisResult<()> { -# PTTL -Source: https://upstash.com/docs/redis/commands/generic/pttl +# FUNCTION FLUSH +Source: https://upstash.com/docs/redis/commands/functions/function-flush -Use `PTTL` to read how much longer a key will live, in milliseconds. +Use `FUNCTION FLUSH` to remove every function library from the database. -The reply is `-1` when the key exists but has no expiration and `-2` when the key does not exist, so a missing key and a permanent one are easy to tell apart. It is the millisecond form of [`TTL`](/docs/redis/commands/generic/ttl), and the extra precision matters for short-lived keys such as locks, where rounding to whole seconds hides most of the remaining lifetime. +This wipes all registered libraries and functions at once and cannot be undone, so it belongs in test setup and provisioning tooling rather than application code. `ASYNC` reclaims the memory in the background and `SYNC` reclaims it before the reply is sent. ## Syntax ```redis -PTTL +FUNCTION FLUSH [ASYNC | SYNC] ``` ## Arguments | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | -| `` | Yes | No | Redis key targeted by the command. | - -## Important points - -* Negative integer replies are sentinel values, not durations or timestamps; see the response description for missing or persistent data. +| `(ASYNC \| SYNC)` | No | No | Choose one form: `ASYNC` (request asynchronous cleanup); `SYNC` (request synchronous cleanup). | ## Response @@ -24435,8 +24476,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Integer: remaining lifetime in milliseconds, `-1` if the key has no expiration, `-2` if the key does not exist | -| RESP3 | Integer: remaining lifetime in milliseconds, `-1` if the key has no expiration, `-2` if the key does not exist | +| RESP2 | Simple string `OK` | +| RESP3 | Simple string `OK` | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -24451,7 +24492,7 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -PTTL my-key +FUNCTION FLUSH ``` @@ -24463,20 +24504,16 @@ import { Redis } from "@upstash/redis"; const redis = Redis.fromEnv(); -const millis = await redis.pttl(key); +await redis.functions.flush() ``` -```python -from upstash_redis import Redis - -redis = Redis.from_env() -result = redis.pttl("my-key") -print(result) -``` + + This command is not supported yet in `upstash_redis`. + @@ -24486,7 +24523,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.pttl("my-key"); +const result = await redis.function("FLUSH"); console.log(result); ``` @@ -24500,7 +24537,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.pTTL("my-key"); +const result = await client.functionFlush(); console.log(result); ``` @@ -24513,7 +24550,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.pttl("my-key") +result = client.function_flush() print(result) ``` @@ -24538,7 +24575,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.PTTL(context.Background(), "my-key").Result() + result, err := client.FunctionFlush(context.Background()).Result() if err != nil { panic(err) } @@ -24556,7 +24593,7 @@ import java.net.URI; import redis.clients.jedis.Jedis; try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.pttl("my-key"); + Object result = jedis.functionFlush(); System.out.println(result); } ``` @@ -24566,14 +24603,14 @@ try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { ```rust -use redis::TypedCommands; - fn main() -> redis::RedisResult<()> { let url = std::env::var("REDIS_URL").expect("REDIS_URL is not set"); let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let result = connection.pttl("my-key")?; + let mut command = redis::cmd("FUNCTION"); + command.arg("FLUSH"); + let result: redis::Value = command.query(&mut connection)?; println!("{result:?}"); Ok(()) } @@ -24583,31 +24620,37 @@ fn main() -> redis::RedisResult<()> { -# RANDOMKEY -Source: https://upstash.com/docs/redis/commands/generic/randomkey +# FUNCTION KILL +Source: https://upstash.com/docs/redis/commands/functions/function-kill -Use `RANDOMKEY` to get the name of a random key from the database without reading its value. +Use `FUNCTION KILL` to stop a function that is currently running and has not yet written anything. -The reply is null when the database is empty. Keys are picked by sampling the keyspace rather than by drawing uniformly from it, and nothing prevents the same key from coming up repeatedly, so treat it as a way to look at a sample of your data while debugging rather than as a way to iterate over it. Use [`SCAN`](/docs/redis/commands/generic/scan) when you need to cover every key. +A function that has already modified data cannot be killed, because stopping it halfway would leave the dataset in a state that no atomic step could produce. Check [`FUNCTION STATS`](/docs/redis/commands/functions/function-stats) to see whether a function is running before calling this. + +The current Upstash deployment recognizes the command but has no interruptible running-function state to act on, so it replies with a `NOTBUSY` error. ## Syntax ```redis -RANDOMKEY +FUNCTION KILL ``` ## Arguments This command takes no arguments. +## Important points + +* The current deployment recognizes this command but reports `NOTBUSY` because it does not expose an interruptible running-function state. + ## Response The reply reports the result of the operation. Error replies have the same shape in RESP2 and RESP3 and are surfaced as exceptions by the SDKs below. | Protocol | Reply | | --- | --- | -| RESP2 | Null bulk string or null array or Bulk string | -| RESP3 | Null or Bulk string | +| RESP2 | Error reply (`NOTBUSY` on the current deployment) | +| RESP3 | Error reply (`NOTBUSY` on the current deployment) | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -24622,32 +24665,24 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -RANDOMKEY +FUNCTION KILL ``` -```ts -import { Redis } from "@upstash/redis"; - -const redis = Redis.fromEnv(); - -const key = await redis.randomkey(); -``` + + This command is not supported yet in `@upstash/redis`. + -```python -from upstash_redis import Redis - -redis = Redis.from_env() -result = redis.randomkey() -print(result) -``` + + This command is not supported yet in `upstash_redis`. + @@ -24657,7 +24692,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.randomkey(); +const result = await redis.function("KILL"); console.log(result); ``` @@ -24671,7 +24706,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.randomKey(); +const result = await client.functionKill(); console.log(result); ``` @@ -24684,7 +24719,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.randomkey() +result = client.function_kill() print(result) ``` @@ -24709,7 +24744,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.RandomKey(context.Background()).Result() + result, err := client.FunctionKill(context.Background()).Result() if err != nil { panic(err) } @@ -24727,7 +24762,7 @@ import java.net.URI; import redis.clients.jedis.Jedis; try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.randomKey(); + Object result = jedis.functionKill(); System.out.println(result); } ``` @@ -24742,8 +24777,8 @@ fn main() -> redis::RedisResult<()> { let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let mut command = redis::cmd("RANDOMKEY"); - + let mut command = redis::cmd("FUNCTION"); + command.arg("KILL"); let result: redis::Value = command.query(&mut connection)?; println!("{result:?}"); Ok(()) @@ -24754,27 +24789,25 @@ fn main() -> redis::RedisResult<()> { -# RENAME -Source: https://upstash.com/docs/redis/commands/generic/rename - -Use `RENAME` to give an existing key a new name. +# FUNCTION LIST +Source: https://upstash.com/docs/redis/commands/functions/function-list -The value moves with the key and so does its remaining time to live, and the operation is atomic, so no client ever sees both names or neither. If a key with the destination name already exists it is overwritten and its old value is deleted. Renaming a key that does not exist returns an error. +Use `FUNCTION LIST` to inspect the function libraries loaded in the database. -Use [`RENAMENX`](/docs/redis/commands/generic/renamenx) when the destination must not be overwritten. A common pattern is to build a replacement value under a temporary key and then rename it over the live key, which swaps the data in one atomic step. +The reply describes each library with its name, the engine it runs on, and the functions it registers, including each function's description and flags such as `no-writes` and [`allow-key-locking`](/docs/redis/features/key-locking), which is the way to check whether a deployed function locks only its keys or the whole database. `LIBRARYNAME` filters the reply to library names matching a pattern, and `WITHCODE` includes the full source of each library, which is how you recover the code of a library that is deployed but no longer at hand. ## Syntax ```redis -RENAME +FUNCTION LIST [LIBRARYNAME ] [WITHCODE] ``` ## Arguments | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | -| `` | Yes | No | Redis key targeted by the command. | -| `` | Yes | No | Redis key used as newkey. | +| `LIBRARYNAME ` | No | No | Return only libraries whose name matches this pattern. | +| `WITHCODE` | No | No | Include each library's source code. | ## Response @@ -24782,8 +24815,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Simple string `OK` | -| RESP3 | Simple string `OK` | +| RESP2 | Array of flat arrays containing library metadata | +| RESP3 | Array of maps containing library metadata | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -24798,7 +24831,7 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -RENAME old-key new-key +FUNCTION LIST ``` @@ -24810,20 +24843,33 @@ import { Redis } from "@upstash/redis"; const redis = Redis.fromEnv(); -await redis.rename("old", "new"); +const libs = await redis.functions.list({ + libraryName: "mylib", + withCode: true +}) + +console.log(libs) +// [ +// { +// libraryName: "mylib", +// engine: "LUA", +// functions: [{ +// name: "my_func", +// description: null, +// flags: [ "no-writes" ] +// }], +// libraryCode: "#!lua name=mylib ..." +// } +// ] ``` -```python -from upstash_redis import Redis - -redis = Redis.from_env() -result = redis.rename("old-key", "new-key") -print(result) -``` + + This command is not supported yet in `upstash_redis`. + @@ -24833,7 +24879,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.rename("old-key", "new-key"); +const result = await redis.function("LIST"); console.log(result); ``` @@ -24847,7 +24893,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.rename("old-key", "new-key"); +const result = await client.functionList(); console.log(result); ``` @@ -24860,7 +24906,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.rename("old-key", "new-key") +result = client.function_list() print(result) ``` @@ -24885,7 +24931,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.Rename(context.Background(), "old-key", "new-key").Result() + result, err := client.FunctionList(context.Background(), redis.FunctionListQuery{}).Result() if err != nil { panic(err) } @@ -24903,7 +24949,7 @@ import java.net.URI; import redis.clients.jedis.Jedis; try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.rename("old-key", "new-key"); + Object result = jedis.functionList(); System.out.println(result); } ``` @@ -24913,14 +24959,14 @@ try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { ```rust -use redis::TypedCommands; - fn main() -> redis::RedisResult<()> { let url = std::env::var("REDIS_URL").expect("REDIS_URL is not set"); let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let result = connection.rename("old-key", "new-key")?; + let mut command = redis::cmd("FUNCTION"); + command.arg("LIST"); + let result: redis::Value = command.query(&mut connection)?; println!("{result:?}"); Ok(()) } @@ -24930,25 +24976,31 @@ fn main() -> redis::RedisResult<()> { -# RENAMENX -Source: https://upstash.com/docs/redis/commands/generic/renamenx +# FUNCTION LOAD +Source: https://upstash.com/docs/redis/commands/functions/function-load -Use `RENAMENX` to rename a key only when the new name is not already in use. +Use `FUNCTION LOAD` to register a library of functions in the database. -The reply is `1` when the rename happened and `0` when the destination already existed and nothing was changed; renaming a key that does not exist returns an error. Because the check and the rename are one atomic step, the command can serve as a way to claim a name: only one of several clients trying to rename onto the same destination succeeds. +The payload is the library source code. It must begin with a shebang line naming the engine and the library, such as `#!lua name=mylib`, and register each function with `redis.register_function`, giving it a name, a callback, and optional flags such as `no-writes`. The reply is the library name. + +`allow-key-locking` is one of those flags. It opts a function out of the global lock so that a call locks only the keys passed in its key list, which lets calls on disjoint keys run in parallel. Unlike Lua scripts, where the flag goes on the shebang line, it is declared per function in `redis.register_function`, and it is fixed until the library is loaded again. See [Key-Based Locking](/docs/redis/features/key-locking). + +Whether or not you set that flag, write functions so that every key they touch arrives in the key list rather than being assembled from `ARGV` inside the function, since an undeclared key can force a disk read while the lock is held. See [Dynamic Keys and Latency](/docs/redis/features/key-locking#dynamic-keys-and-latency). + +Loading fails when the library name is already in use unless `REPLACE` is given, which is how you deploy a new version of a library. Once loaded, functions are called by name with [`FCALL`](/docs/redis/commands/functions/fcall) or [`FCALL_RO`](/docs/redis/commands/functions/fcall-ro). Unlike scripts cached by [`SCRIPT LOAD`](/docs/redis/commands/scripting/script-load), libraries are part of the dataset, so they survive restarts and do not need to be re-sent by clients. ## Syntax ```redis -RENAMENX +FUNCTION LOAD [REPLACE] ``` ## Arguments | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | -| `` | Yes | No | Redis key targeted by the command. | -| `` | Yes | No | Redis key used as newkey. | +| `REPLACE` | No | No | Allow replacement of an existing destination. | +| `` | Yes | No | Library source, including its `#!lua name=` shebang. | ## Response @@ -24956,8 +25008,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Integer: `1` if the key was renamed, `0` if the destination already exists | -| RESP3 | Integer: `1` if the key was renamed, `0` if the destination already exists | +| RESP2 | Bulk string | +| RESP3 | Bulk string | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -24972,7 +25024,7 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -RENAMENX old-key new-key +FUNCTION LOAD "#!lua name=mylib\nredis.register_function('helloworld', function() return 'Hello World!' end)" ``` @@ -24984,20 +25036,36 @@ import { Redis } from "@upstash/redis"; const redis = Redis.fromEnv(); -const renamed = await redis.renamenx("old", "new"); +const code = `#!lua name=mylib + + -- Simple function that returns a string + redis.register_function( + 'helloworld', + function() return 'Hello World!' end + ) + + -- Complex function that modifies data with logic + local function my_hset(keys, args) + local hash = keys[1] + local time = redis.call('TIME')[1] + return redis.call('HSET', hash, '_last_modified_', time, unpack(args)) + end + + redis.register_function('my_hset', my_hset) +`; + +const libraryName = await redis.functions.load({ code, replace: true }); + +console.log(libraryName); // "mylib" ``` -```python -from upstash_redis import Redis - -redis = Redis.from_env() -result = redis.renamenx("old-key", "new-key") -print(result) -``` + + This command is not supported yet in `upstash_redis`. + @@ -25007,7 +25075,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.renamenx("old-key", "new-key"); +const result = await redis.function("LOAD", "function-code"); console.log(result); ``` @@ -25021,7 +25089,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.renameNX("old-key", "new-key"); +const result = await client.functionLoad("function-code"); console.log(result); ``` @@ -25034,7 +25102,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.renamenx("old-key", "new-key") +result = client.function_load("function-code") print(result) ``` @@ -25059,7 +25127,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.RenameNX(context.Background(), "old-key", "new-key").Result() + result, err := client.FunctionLoad(context.Background(), "function-code").Result() if err != nil { panic(err) } @@ -25077,7 +25145,7 @@ import java.net.URI; import redis.clients.jedis.Jedis; try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.renamenx("old-key", "new-key"); + Object result = jedis.functionLoad("function-code"); System.out.println(result); } ``` @@ -25087,14 +25155,15 @@ try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { ```rust -use redis::TypedCommands; - fn main() -> redis::RedisResult<()> { let url = std::env::var("REDIS_URL").expect("REDIS_URL is not set"); let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let result = connection.rename_nx("old-key", "new-key")?; + let mut command = redis::cmd("FUNCTION"); + command.arg("LOAD"); + command.arg("function-code"); + let result: redis::Value = command.query(&mut connection)?; println!("{result:?}"); Ok(()) } @@ -25104,40 +25173,22 @@ fn main() -> redis::RedisResult<()> { -# RESTORE -Source: https://upstash.com/docs/redis/commands/generic/restore - -Use `RESTORE` to recreate a key from a payload produced by [`DUMP`](/docs/redis/commands/generic/dump). +# FUNCTION STATS +Source: https://upstash.com/docs/redis/commands/functions/function-stats -`` gives the new key a lifetime in milliseconds, where `0` means no expiration; with `ABSTTL` the same number is read as an absolute Unix timestamp in milliseconds instead. The command fails if the key already exists unless `REPLACE` is given. +Use `FUNCTION STATS` to read the current state of the function engine. -The payload's version stamp and checksum are verified before anything is written, so a truncated, corrupted, or foreign payload is rejected rather than loaded. `IDLETIME` and `FREQ` seed the eviction metadata of the new key so that a restored key does not automatically look freshly used. +The reply reports the function that is running right now, if any, together with how long it has been running and the command that started it, plus per-engine counts of loaded libraries and functions. It is the usual way to check whether a long-running function is in progress before deciding to call [`FUNCTION KILL`](/docs/redis/commands/functions/function-kill). ## Syntax ```redis -RESTORE - [REPLACE] - [ABSTTL] - [IDLETIME ] - [FREQ ] +FUNCTION STATS ``` ## Arguments -| Argument | Required | Repeatable | Description | -| --- | --- | --- | --- | -| `` | Yes | No | Redis key targeted by the command. | -| `` | Yes | No | Lifetime in milliseconds; `0` restores the key without an expiration. | -| `` | Yes | No | Payload produced by `DUMP`. | -| `REPLACE` | No | No | Allow replacement of an existing destination. | -| `ABSTTL` | No | No | Treat `` as an absolute Unix timestamp in milliseconds. | -| `IDLETIME ` | No | No | Set the key's idle time, in seconds. | -| `FREQ ` | No | No | Set the key's access frequency counter. | - -## Important points - -* This command can expose administrative information or make a broad destructive change. Restrict it to trusted code paths. +This command takes no arguments. ## Response @@ -25145,8 +25196,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Simple string `OK` | -| RESP3 | Simple string `OK` | +| RESP2 | Flat array of alternating keys and values | +| RESP3 | Map | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -25161,16 +25212,30 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -RESTORE my-key 1 serialized-value +FUNCTION STATS ``` - - This command is not supported yet in `@upstash/redis`. - +```ts +import { Redis } from "@upstash/redis"; + +const redis = Redis.fromEnv(); + +const stats = await redis.functions.stats() + +console.log(stats) +// { +// engines: { +// LUA: { +// librariesCount: 3, +// functionsCount: 15 +// } +// } +// } +``` @@ -25188,7 +25253,7 @@ RESTORE my-key 1 serialized-value import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.restore("my-key", "1", "serialized-value"); +const result = await redis.function("STATS"); console.log(result); ``` @@ -25202,7 +25267,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.restore("my-key", 1, "serialized-value"); +const result = await client.functionStats(); console.log(result); ``` @@ -25215,7 +25280,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.restore("my-key", 1, "serialized-value") +result = client.function_stats() print(result) ``` @@ -25230,7 +25295,6 @@ import ( "context" "fmt" "os" - "time" "github.com/redis/go-redis/v9" ) @@ -25241,7 +25305,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.Restore(context.Background(), "my-key", time.Millisecond, "serialized-value").Result() + result, err := client.FunctionStats(context.Background()).Result() if err != nil { panic(err) } @@ -25259,7 +25323,7 @@ import java.net.URI; import redis.clients.jedis.Jedis; try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.restore("my-key", 1, "serialized-value".getBytes()); + Object result = jedis.functionStats(); System.out.println(result); } ``` @@ -25274,10 +25338,8 @@ fn main() -> redis::RedisResult<()> { let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let mut command = redis::cmd("RESTORE"); - command.arg("my-key"); - command.arg("1"); - command.arg("serialized-value"); + let mut command = redis::cmd("FUNCTION"); + command.arg("STATS"); let result: redis::Value = command.query(&mut connection)?; println!("{result:?}"); Ok(()) @@ -25288,36 +25350,43 @@ fn main() -> redis::RedisResult<()> { -# SCAN -Source: https://upstash.com/docs/redis/commands/generic/scan +# Functions commands +Source: https://upstash.com/docs/redis/commands/functions/overview -Use `SCAN` to walk through the keys of the database incrementally, a batch at a time. + +Call a function +Call a read-only function +Delete a library +Delete all libraries +Kill a running function +List all libraries +Load a library +Get function execution stats + -Each call takes a cursor and returns the next cursor together with a batch of keys. Start with cursor `0` and keep calling with the cursor from the previous reply until the server returns `0` again, which marks the end of the iteration. Because the work is split over many short calls, `SCAN` never blocks the server the way [`KEYS`](/docs/redis/commands/generic/keys) can on a large keyspace. +# COPY +Source: https://upstash.com/docs/redis/commands/generic/copy -`MATCH` filters the returned keys with a glob-style pattern, `COUNT` hints at how much work each call should do (a hint about effort, not a page size, so batches vary in length), and `TYPE` limits the reply to keys of one type. Filtering is applied after a batch has been read, so a call can legitimately return no keys at all while the cursor is still non-zero: only the cursor tells you when the iteration is over. +Use `COPY` to copy the value stored at one key to another key. -The guarantee is that every key present for the whole iteration is returned at least once. Keys added or removed while the scan runs may or may not show up, and a key can be returned more than once, so make the processing of each key idempotent. [`HSCAN`](/docs/redis/commands/hash/hscan), [`SSCAN`](/docs/redis/commands/set/sscan), and [`ZSCAN`](/docs/redis/commands/sorted-set/zscan) apply the same mechanism inside a single collection. +The destination gets an independent deep copy of the value, so later changes to either key do not affect the other, and the source key's remaining time to live is copied along with it. Any type can be copied. + +By default the command does nothing and returns `0` when the destination key already exists; `REPLACE` overwrites it instead. `DB` selects the destination database index, which on Upstash is always `0`. ## Syntax ```redis -SCAN [MATCH ] [COUNT ] [TYPE ] +COPY [DB ] [REPLACE] ``` ## Arguments | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | -| `` | Yes | No | Cursor returned by the previous call; start at `0`. | -| `MATCH ` | No | No | Return only elements matching this glob-style pattern. | -| `COUNT ` | No | No | Hint for how much work each iteration should do. | -| `TYPE ` | No | No | Return only keys of this type, such as `string`, `list`, or `hash`. | - -## Important points - -* This operation can inspect a large part of the database. Prefer cursor-based scans where possible and avoid unbounded use on hot paths. -* The cursor is opaque. Start with `0` and continue until the server returns cursor `0`; a single iteration may return no elements. +| `` | Yes | No | Redis key used as source. | +| `` | Yes | No | Redis key used as destination. | +| `DB ` | No | No | Index of the database to copy the key into. | +| `REPLACE` | No | No | Allow replacement of an existing destination. | ## Response @@ -25325,8 +25394,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Two-element array: cursor and array of bulk-string keys | -| RESP3 | Two-element array: cursor and array of bulk-string keys | +| RESP2 | Integer: `1` if the key was copied, `0` otherwise | +| RESP3 | Integer: `1` if the key was copied, `0` otherwise | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -25341,7 +25410,7 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -SCAN 0 +COPY source-key destination-key ``` @@ -25352,8 +25421,8 @@ SCAN 0 import { Redis } from "@upstash/redis"; const redis = Redis.fromEnv(); - -const [cursor, keys] = await redis.scan(0, { match: "*" }); +const result = await redis.copy("source-key", "destination-key"); +console.log(result); ``` @@ -25364,7 +25433,7 @@ const [cursor, keys] = await redis.scan(0, { match: "*" }); from upstash_redis import Redis redis = Redis.from_env() -result = redis.scan(0) +result = redis.copy("source-key", "destination-key") print(result) ``` @@ -25376,7 +25445,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.scan("0"); +const result = await redis.copy("source-key", "destination-key"); console.log(result); ``` @@ -25390,7 +25459,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.scan("0"); +const result = await client.copy("source-key", "destination-key"); console.log(result); ``` @@ -25403,7 +25472,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.scan(0) +result = client.copy("source-key", "destination-key") print(result) ``` @@ -25428,7 +25497,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, _, err := client.Scan(context.Background(), 0, "*", 0).Result() + result, err := client.Copy(context.Background(), "source-key", "destination-key", 0, false).Result() if err != nil { panic(err) } @@ -25446,7 +25515,7 @@ import java.net.URI; import redis.clients.jedis.Jedis; try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.scan("0"); + Object result = jedis.copy("source-key", "destination-key", false); System.out.println(result); } ``` @@ -25463,10 +25532,8 @@ fn main() -> redis::RedisResult<()> { let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let iter: redis::Iter = connection.scan()?; - for key in iter { - println!("{key}"); - } + let result = connection.copy("source-key", "destination-key", redis::CopyOptions::default())?; + println!("{result:?}"); Ok(()) } ``` @@ -25475,17 +25542,17 @@ fn main() -> redis::RedisResult<()> { -# TOUCH -Source: https://upstash.com/docs/redis/commands/generic/touch +# DEL +Source: https://upstash.com/docs/redis/commands/generic/del -Use `TOUCH` to update the last access time of one or more keys without reading their values. +Use `DEL` to delete one or more keys and the values they hold, whatever their type. -The reply counts how many of the given keys exist, just like [`EXISTS`](/docs/redis/commands/generic/exists), but the call also refreshes the idle time and access frequency that the LRU and LFU eviction policies rely on. That makes it a way to tell the server that a key is still in use, for example to keep a cached value from being evicted while a slower process is still going to need it. It does not change the key's expiration. +The reply counts only the keys that actually existed, so deleting a key that is already gone is not an error and the count tells you how many were really removed. The memory is freed as part of the command, which for very large collections can take noticeable time; [`UNLINK`](/docs/redis/commands/generic/unlink) removes the keys just as immediately but frees their memory in the background. ## Syntax ```redis -TOUCH [ ...] +DEL [ ...] ``` ## Arguments @@ -25516,7 +25583,7 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -TOUCH my-key +DEL my-key ``` @@ -25528,7 +25595,7 @@ import { Redis } from "@upstash/redis"; const redis = Redis.fromEnv(); -await redis.touch("key1", "key2", "key3"); +await redis.del("key1", "key2"); ``` @@ -25539,7 +25606,7 @@ await redis.touch("key1", "key2", "key3"); from upstash_redis import Redis redis = Redis.from_env() -result = redis.touch("my-key") +result = redis.delete("my-key") print(result) ``` @@ -25551,7 +25618,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.touch("my-key"); +const result = await redis.del("my-key"); console.log(result); ``` @@ -25565,7 +25632,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.touch("my-key"); +const result = await client.del("my-key"); console.log(result); ``` @@ -25578,7 +25645,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.touch("my-key") +result = client.delete("my-key") print(result) ``` @@ -25603,7 +25670,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.Touch(context.Background(), "my-key").Result() + result, err := client.Del(context.Background(), "my-key").Result() if err != nil { panic(err) } @@ -25621,7 +25688,7 @@ import java.net.URI; import redis.clients.jedis.Jedis; try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.touch("my-key"); + Object result = jedis.del("my-key"); System.out.println(result); } ``` @@ -25631,14 +25698,14 @@ try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { ```rust +use redis::TypedCommands; + fn main() -> redis::RedisResult<()> { let url = std::env::var("REDIS_URL").expect("REDIS_URL is not set"); let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let mut command = redis::cmd("TOUCH"); - command.arg("my-key"); - let result: redis::Value = command.query(&mut connection)?; + let result = connection.del("my-key")?; println!("{result:?}"); Ok(()) } @@ -25648,17 +25715,19 @@ fn main() -> redis::RedisResult<()> { -# TTL -Source: https://upstash.com/docs/redis/commands/generic/ttl +# DUMP +Source: https://upstash.com/docs/redis/commands/generic/dump -Use `TTL` to read how much longer a key will live, in seconds. +Use `DUMP` to serialize the value stored at a key into a portable, Redis-specific binary blob. -The reply is `-1` when the key exists but has no expiration and `-2` when the key does not exist, so these two are never confused with a real remaining lifetime. Values are rounded to whole seconds; use [`PTTL`](/docs/redis/commands/generic/pttl) for millisecond precision and [`EXPIRETIME`](/docs/redis/commands/generic/expiretime) when you want the absolute deadline rather than the time left. +The blob carries the value together with a version stamp and a checksum, but not the key name and not its time to live. Feeding it to [`RESTORE`](/docs/redis/commands/generic/restore) recreates the value under any key name, in the same database or in another one, which makes the pair the standard way to move or back up individual keys. A missing key returns null. + +The reply is raw binary data, not text: keep it in a byte-safe container and avoid string encodings that would corrupt it. ## Syntax ```redis -TTL +DUMP ``` ## Arguments @@ -25667,18 +25736,14 @@ TTL | --- | --- | --- | --- | | `` | Yes | No | Redis key targeted by the command. | -## Important points - -* Negative integer replies are sentinel values, not durations or timestamps; see the response description for missing or persistent data. - ## Response The reply reports the result of the operation. Error replies have the same shape in RESP2 and RESP3 and are surfaced as exceptions by the SDKs below. | Protocol | Reply | | --- | --- | -| RESP2 | Integer: remaining lifetime in seconds, `-1` if the key has no expiration, `-2` if the key does not exist | -| RESP3 | Integer: remaining lifetime in seconds, `-1` if the key has no expiration, `-2` if the key does not exist | +| RESP2 | Bulk string or Null bulk string or null array | +| RESP3 | Bulk string or Null | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -25693,32 +25758,24 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -TTL my-key +DUMP my-key ``` -```ts -import { Redis } from "@upstash/redis"; - -const redis = Redis.fromEnv(); - -const seconds = await redis.ttl(key); -``` + + This command is not supported yet in `@upstash/redis`. + -```python -from upstash_redis import Redis - -redis = Redis.from_env() -result = redis.ttl("my-key") -print(result) -``` + + This command is not supported yet in `upstash_redis`. + @@ -25728,7 +25785,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.ttl("my-key"); +const result = await redis.dump("my-key"); console.log(result); ``` @@ -25742,7 +25799,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.ttl("my-key"); +const result = await client.dump("my-key"); console.log(result); ``` @@ -25755,7 +25812,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.ttl("my-key") +result = client.dump("my-key") print(result) ``` @@ -25780,7 +25837,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.TTL(context.Background(), "my-key").Result() + result, err := client.Dump(context.Background(), "my-key").Result() if err != nil { panic(err) } @@ -25798,7 +25855,7 @@ import java.net.URI; import redis.clients.jedis.Jedis; try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.ttl("my-key"); + Object result = jedis.dump("my-key"); System.out.println(result); } ``` @@ -25808,14 +25865,14 @@ try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { ```rust -use redis::TypedCommands; - fn main() -> redis::RedisResult<()> { let url = std::env::var("REDIS_URL").expect("REDIS_URL is not set"); let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let result = connection.ttl("my-key")?; + let mut command = redis::cmd("DUMP"); + command.arg("my-key"); + let result: redis::Value = command.query(&mut connection)?; println!("{result:?}"); Ok(()) } @@ -25825,24 +25882,24 @@ fn main() -> redis::RedisResult<()> { -# TYPE -Source: https://upstash.com/docs/redis/commands/generic/type +# EXISTS +Source: https://upstash.com/docs/redis/commands/generic/exists -Use `TYPE` to find out which data type is stored at a key. +Use `EXISTS` to check whether one or more keys exist. -The reply is one of `string`, `list`, `set`, `zset`, `hash`, or `stream`, and `none` when the key does not exist. It is the way to dispatch generic code over keys of mixed types, since applying a command to the wrong type fails with a `WRONGTYPE` error, and the way to inspect unfamiliar data before deciding how to read it. +The reply is the number of the given keys that exist, so with a single key it is simply `1` or `0`. A key listed several times is counted each time it is present, which means `EXISTS k k` returns `2` when `k` exists. Checking existence never transfers the value, so it stays cheap even for large values. ## Syntax ```redis -TYPE +EXISTS [ ...] ``` ## Arguments | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | -| `` | Yes | No | Redis key targeted by the command. | +| `` | Yes | Yes | Redis key targeted by the command. | ## Response @@ -25850,8 +25907,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Simple string: the type name, or `none` if the key does not exist | -| RESP3 | Simple string: the type name, or `none` if the key does not exist | +| RESP2 | Integer | +| RESP3 | Integer | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -25866,7 +25923,7 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -TYPE my-key +EXISTS my-key ``` @@ -25878,9 +25935,10 @@ import { Redis } from "@upstash/redis"; const redis = Redis.fromEnv(); -await redis.set("key", "value"); -const t = await redis.type("key"); -console.log(t) // "string" +await redis.set("key1", "value1") +await redis.set("key2", "value2") +const keys = await redis.exists("key1", "key2", "key3"); +console.log(keys) // 2 ``` @@ -25891,7 +25949,7 @@ console.log(t) // "string" from upstash_redis import Redis redis = Redis.from_env() -result = redis.type("my-key") +result = redis.exists("my-key") print(result) ``` @@ -25903,7 +25961,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.type("my-key"); +const result = await redis.exists("my-key"); console.log(result); ``` @@ -25917,7 +25975,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.type("my-key"); +const result = await client.exists("my-key"); console.log(result); ``` @@ -25930,7 +25988,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.type("my-key") +result = client.exists("my-key") print(result) ``` @@ -25955,7 +26013,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.Type(context.Background(), "my-key").Result() + result, err := client.Exists(context.Background(), "my-key").Result() if err != nil { panic(err) } @@ -25973,7 +26031,7 @@ import java.net.URI; import redis.clients.jedis.Jedis; try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.type("my-key"); + Object result = jedis.exists("my-key"); System.out.println(result); } ``` @@ -25990,7 +26048,7 @@ fn main() -> redis::RedisResult<()> { let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let result = connection.key_type("my-key")?; + let result = connection.exists("my-key")?; println!("{result:?}"); Ok(()) } @@ -26000,24 +26058,35 @@ fn main() -> redis::RedisResult<()> { -# UNLINK -Source: https://upstash.com/docs/redis/commands/generic/unlink +# EXPIRE +Source: https://upstash.com/docs/redis/commands/generic/expire -Use `UNLINK` to delete keys without freeing their memory in the foreground. +Use `EXPIRE` to give a key a lifetime in seconds, after which the key is deleted automatically. -The keys are removed from the keyspace immediately, so from a client's point of view they are gone as soon as the command returns, but the memory of large values is reclaimed by a background thread. That makes it a safer alternative to [`DEL`](/docs/redis/commands/generic/del) for collections with many elements, where freeing memory synchronously can block the server for a noticeable time. The reply counts the keys that existed. +Whether the expiration survives later writes depends on the command: replacing the value with [`SET`](/docs/redis/commands/string/set) clears it, while commands that modify a value in place, such as [`INCR`](/docs/redis/commands/string/incr), [`LPUSH`](/docs/redis/commands/list/lpush) or [`HSET`](/docs/redis/commands/hash/hset), leave it untouched. A negative lifetime deletes the key immediately. + +The optional condition decides when the new expiration is applied: `NX` only when the key currently has none, `XX` only when it already has one, `GT` only when the new expiration is later than the current one, and `LT` only when it is earlier. Since a key without an expiration counts as living forever, `GT` never adds one and `LT` always does. The reply is `1` when the expiration was set and `0` when the key does not exist or the condition was not met. + +Read the remaining lifetime with [`TTL`](/docs/redis/commands/generic/ttl) and remove it again with [`PERSIST`](/docs/redis/commands/generic/persist). ## Syntax ```redis -UNLINK [ ...] +EXPIRE [NX | XX | GT | LT] ``` ## Arguments | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | -| `` | Yes | Yes | Redis key targeted by the command. | +| `` | Yes | No | Redis key targeted by the command. | +| `` | Yes | No | Lifetime in seconds. | +| `(NX \| XX \| GT \| LT)` | No | No | Choose one form: `NX` (only when the key has no expiration); `XX` (only when the key already has one); `GT` (only when the new expiration is later than the current one); `LT` (only when it is earlier). | + +## Important points + +* `NX` cannot be combined with `XX`, `GT`, or `LT`, and `GT` and `LT` cannot be used together. +* A key with no expiration counts as an infinite one, so `GT` never sets an expiration on such a key and `LT` always does. ## Response @@ -26025,8 +26094,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Integer | -| RESP3 | Integer | +| RESP2 | Integer: `1` if the timeout was set, `0` otherwise | +| RESP3 | Integer: `1` if the timeout was set, `0` otherwise | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -26041,7 +26110,7 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -UNLINK my-key +EXPIRE my-key 1000 ``` @@ -26053,7 +26122,8 @@ import { Redis } from "@upstash/redis"; const redis = Redis.fromEnv(); -await redis.unlink("key1", "key2"); +await redis.set("mykey", "Hello"); +await redis.expire("mykey", 10); ``` @@ -26064,7 +26134,7 @@ await redis.unlink("key1", "key2"); from upstash_redis import Redis redis = Redis.from_env() -result = redis.unlink("my-key") +result = redis.expire("my-key", 1000) print(result) ``` @@ -26076,7 +26146,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.unlink("my-key"); +const result = await redis.expire("my-key", "1000"); console.log(result); ``` @@ -26090,7 +26160,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.unlink("my-key"); +const result = await client.expire("my-key", 1000); console.log(result); ``` @@ -26103,7 +26173,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.unlink("my-key") +result = client.expire("my-key", 1000) print(result) ``` @@ -26118,6 +26188,7 @@ import ( "context" "fmt" "os" + "time" "github.com/redis/go-redis/v9" ) @@ -26128,7 +26199,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.Unlink(context.Background(), "my-key").Result() + result, err := client.Expire(context.Background(), "my-key", 1000*time.Second).Result() if err != nil { panic(err) } @@ -26146,7 +26217,7 @@ import java.net.URI; import redis.clients.jedis.Jedis; try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.unlink("my-key"); + Object result = jedis.expire("my-key", 1000); System.out.println(result); } ``` @@ -26163,7 +26234,7 @@ fn main() -> redis::RedisResult<()> { let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let result = connection.unlink("my-key")?; + let result = connection.expire("my-key", 1000)?; println!("{result:?}"); Ok(()) } @@ -26173,34 +26244,33 @@ fn main() -> redis::RedisResult<()> { -# WAIT -Source: https://upstash.com/docs/redis/commands/generic/wait - -Use `WAIT` to block until preceding writes have been acknowledged by a number of replicas, or until a timeout expires. +# EXPIREAT +Source: https://upstash.com/docs/redis/commands/generic/expireat -The reply is the number of replicas that acknowledged, which can be lower than `` when the timeout is reached, so callers must check it instead of assuming success. A timeout of `0` waits indefinitely. +Use `EXPIREAT` to schedule a key for automatic deletion at a fixed point in time, given as a Unix timestamp in seconds. -`WAIT` raises the durability you can observe for a write, which is useful right before an action that must not be undone by a failover, such as replying to a payment webhook. It does not make Redis strongly consistent: an acknowledged write can still be lost if the primary and the acknowledging replicas fail together. +It behaves exactly like [`EXPIRE`](/docs/redis/commands/generic/expire) except that the deadline is absolute rather than relative, which is what you want when several keys must expire at the same moment, such as the end of an hour or of a billing period. Computing the deadline once and reusing it also avoids the drift that repeated relative expirations introduce. A timestamp in the past deletes the key right away. -On Upstash the command waits for the writes enqueued before it began, including writes made by other connections, which is broader than the per-connection wording used by some Redis clients. +The optional condition works as it does for `EXPIRE`: `NX` only when the key has no expiration, `XX` only when it already has one, `GT` only when the new deadline is later than the current one, and `LT` only when it is earlier. ## Syntax ```redis -WAIT +EXPIREAT [NX | XX | GT | LT] ``` ## Arguments | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | -| `numreplicas` | Yes | No | Non-negative number of replicas that should acknowledge prior writes on this connection. | -| `timeout` | Yes | No | Maximum wait in milliseconds; `0` means no timeout. | +| `` | Yes | No | Redis key targeted by the command. | +| `` | Yes | No | Expiration time as a Unix timestamp in seconds. | +| `(NX \| XX \| GT \| LT)` | No | No | Choose one form: `NX` (only when the key has no expiration); `XX` (only when the key already has one); `GT` (only when the new expiration is later than the current one); `LT` (only when it is earlier). | ## Important points -* This deployment waits for writes enqueued before `WAIT` begins, including writes from other connections. That is broader than the per-connection wording used by Redis clients. -* The reply can be lower than `numreplicas` when the timeout expires. This improves observed replication durability but does not make Redis a strongly consistent store. +* `NX` cannot be combined with `XX`, `GT`, or `LT`, and `GT` and `LT` cannot be used together. +* A key with no expiration counts as an infinite one, so `GT` never sets an expiration on such a key and `LT` always does. ## Response @@ -26208,8 +26278,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Integer | -| RESP3 | Integer | +| RESP2 | Integer: `1` if the timeout was set, `0` otherwise | +| RESP3 | Integer: `1` if the timeout was set, `0` otherwise | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -26224,24 +26294,34 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -WAIT 1 1000 +EXPIREAT my-key 1735689600 ``` - - This command is not supported yet in `@upstash/redis`. - +```ts +import { Redis } from "@upstash/redis"; + +const redis = Redis.fromEnv(); + +await redis.set("mykey", "Hello"); +const tenSecondsFromNow = Math.floor(Date.now() / 1000) + 10; +await redis.expireat("mykey", tenSecondsFromNow); +``` - - This command is not supported yet in `upstash_redis`. - +```python +from upstash_redis import Redis + +redis = Redis.from_env() +result = redis.expireat("my-key", 1735689600) +print(result) +``` @@ -26251,7 +26331,7 @@ WAIT 1 1000 import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.wait("1", "1000"); +const result = await redis.expireat("my-key", "1735689600"); console.log(result); ``` @@ -26265,7 +26345,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.wait(1, 1000); +const result = await client.expireAt("my-key", 1735689600); console.log(result); ``` @@ -26278,7 +26358,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.wait("1", "1000") +result = client.expireat("my-key", 1735689600) print(result) ``` @@ -26304,7 +26384,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.Wait(context.Background(), 1, time.Second).Result() + result, err := client.ExpireAt(context.Background(), "my-key", time.Unix(1735689600, 0)).Result() if err != nil { panic(err) } @@ -26322,7 +26402,7 @@ import java.net.URI; import redis.clients.jedis.Jedis; try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.waitReplicas(1, 1000); + Object result = jedis.expireAt("my-key", 1735689600); System.out.println(result); } ``` @@ -26332,15 +26412,14 @@ try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { ```rust +use redis::TypedCommands; + fn main() -> redis::RedisResult<()> { let url = std::env::var("REDIS_URL").expect("REDIS_URL is not set"); let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let mut command = redis::cmd("WAIT"); - command.arg("1"); - command.arg("1000"); - let result: redis::Value = command.query(&mut connection)?; + let result = connection.expire_at("my-key", 1735689600)?; println!("{result:?}"); Ok(()) } @@ -26350,33 +26429,28 @@ fn main() -> redis::RedisResult<()> { -# WAITAOF -Source: https://upstash.com/docs/redis/commands/generic/waitaof - -Use `WAITAOF` to block until preceding writes have been persisted to the append-only file locally and on replicas. +# EXPIRETIME +Source: https://upstash.com/docs/redis/commands/generic/expiretime -`` is how many local acknowledgements to wait for and `` how many replicas must have persisted the writes. The two-element reply gives the local count first and the replica count second, and either can come back lower than requested when the timeout expires, so both need checking. A timeout of `0` waits indefinitely. +Use `EXPIRETIME` to read the absolute time at which a key will expire, as a Unix timestamp in seconds. -Where [`WAIT`](/docs/redis/commands/generic/wait) confirms only that replicas received a write, `WAITAOF` confirms that it reached persistent storage, which is the stronger guarantee to ask for before acknowledging work that must survive a restart. On Upstash it waits for the writes enqueued before it began, including writes made by other connections. +The reply is `-1` when the key exists but has no expiration, and `-2` when the key does not exist, so the two cases can be told apart. Use [`TTL`](/docs/redis/commands/generic/ttl) when you need the remaining lifetime instead of the deadline, and [`PEXPIRETIME`](/docs/redis/commands/generic/pexpiretime) when you need millisecond precision. ## Syntax ```redis -WAITAOF +EXPIRETIME ``` ## Arguments | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | -| `numlocal` | Yes | No | Whether to wait for local persistence: `0` or `1`. | -| `numreplicas` | Yes | No | Non-negative number of replicas whose append-only files should include prior writes. | -| `timeout` | Yes | No | Maximum wait in milliseconds; `0` means no timeout. | +| `` | Yes | No | Redis key targeted by the command. | ## Important points -* This deployment waits for writes enqueued before `WAITAOF` begins, including writes from other connections. -* The two-element reply contains the local persistence acknowledgement first and the replica persistence count second. +* Negative integer replies are sentinel values, not durations or timestamps; see the response description for missing or persistent data. ## Response @@ -26384,8 +26458,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Two-element array of integers: local and replica acknowledgments | -| RESP3 | Two-element array of integers: local and replica acknowledgments | +| RESP2 | Integer: expiration Unix time in seconds, `-1` if the key has no expiration, `-2` if the key does not exist | +| RESP3 | Integer: expiration Unix time in seconds, `-1` if the key has no expiration, `-2` if the key does not exist | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -26400,7 +26474,7 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -WAITAOF 1 1 1000 +EXPIRETIME my-key ``` @@ -26427,7 +26501,7 @@ WAITAOF 1 1 1000 import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.call("WAITAOF", "1", "1", "1000"); +const result = await redis.expiretime("my-key"); console.log(result); ``` @@ -26441,7 +26515,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.sendCommand(["WAITAOF", "1", "1", "1000"]); +const result = await client.expireTime("my-key"); console.log(result); ``` @@ -26454,7 +26528,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.waitaof("1", "1", "1000") +result = client.expiretime("my-key") print(result) ``` @@ -26469,7 +26543,6 @@ import ( "context" "fmt" "os" - "time" "github.com/redis/go-redis/v9" ) @@ -26480,7 +26553,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.WaitAOF(context.Background(), 1, 1, time.Second).Result() + result, err := client.ExpireTime(context.Background(), "my-key").Result() if err != nil { panic(err) } @@ -26498,7 +26571,7 @@ import java.net.URI; import redis.clients.jedis.Jedis; try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.waitAOF(1, 1, 1000); + Object result = jedis.expireTime("my-key"); System.out.println(result); } ``` @@ -26508,16 +26581,14 @@ try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { ```rust +use redis::TypedCommands; + fn main() -> redis::RedisResult<()> { let url = std::env::var("REDIS_URL").expect("REDIS_URL is not set"); let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let mut command = redis::cmd("WAITAOF"); - command.arg("1"); - command.arg("1"); - command.arg("1000"); - let result: redis::Value = command.query(&mut connection)?; + let result = connection.expire_time("my-key")?; println!("{result:?}"); Ok(()) } @@ -26527,37 +26598,31 @@ fn main() -> redis::RedisResult<()> { -# GEOADD -Source: https://upstash.com/docs/redis/commands/geo/geoadd +# KEYS +Source: https://upstash.com/docs/redis/commands/generic/keys -Use `GEOADD` to add longitude, latitude, and member triples to a geospatial index. +Use `KEYS` to list every key in the database whose name matches a glob-style pattern. -Each position is encoded into a 52-bit geohash and stored as the score of the member in a sorted set, so a geospatial key is an ordinary sorted set and commands such as [`ZREM`](/docs/redis/commands/sorted-set/zrem), [`ZCARD`](/docs/redis/commands/sorted-set/zcard), and [`ZSCAN`](/docs/redis/commands/sorted-set/zscan) work on it. Longitude must be between -180 and 180 and latitude between -85.05112878 and 85.05112878; anything outside those bounds returns an error. Adding a member that is already present moves it to the new position, and the encoding means coordinates read back with [`GEOPOS`](/docs/redis/commands/geo/geopos) are very close to, but not exactly, the ones you stored. +The pattern supports `*` for any sequence of characters, `?` for a single character, `[...]` for character classes, and `\` to escape a literal, so `user:*:session` matches all session keys of all users and `*` matches everything. -`NX` only adds members that are not there yet, `XX` only updates members that already exist, and `CH` makes the reply count every member that changed rather than only the ones that were added. +The command walks the entire keyspace and returns all matches in one reply, which on a database of any real size means a long block and a very large response. Treat it as a debugging and maintenance tool: on hot paths use [`SCAN`](/docs/redis/commands/generic/scan), which accepts the same `MATCH` patterns but walks the keyspace in small batches, or keep an index of your keys in a set instead. ## Syntax ```redis -GEOADD - [NX | XX] - [CH] - - [ ...] +KEYS ``` ## Arguments | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | -| `` | Yes | No | Redis key targeted by the command. | -| `(NX \| XX)` | No | No | Choose one form: `NX` (only add new members, never update an existing one); `XX` (only update members that already exist). | -| `CH` | No | No | Count changed members rather than only new members. | -| ` ` | Yes | Yes | Longitude, latitude, and the member they belong to. Repeat to add several members. | +| `` | Yes | No | Glob-style pattern to match keys against. | ## Important points -* `NX` and `XX` are mutually exclusive. +* This command can expose administrative information or make a broad destructive change. Restrict it to trusted code paths. +* This operation can inspect a large part of the database. Prefer cursor-based scans where possible and avoid unbounded use on hot paths. ## Response @@ -26565,8 +26630,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Integer | -| RESP3 | Integer | +| RESP2 | Array of bulk-string keys | +| RESP3 | Array of bulk-string keys | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -26581,7 +26646,7 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -GEOADD my-key 29.0 41.0 member +KEYS * ``` @@ -26592,12 +26657,8 @@ GEOADD my-key 29.0 41.0 member import { Redis } from "@upstash/redis"; const redis = Redis.fromEnv(); -const result = await redis.geoadd("my-key", { - longitude: 29.0, - latitude: 41.0, - member: "member", -}); -console.log(result); + +const keys = await redis.keys("prefix*"); ``` @@ -26608,7 +26669,7 @@ console.log(result); from upstash_redis import Redis redis = Redis.from_env() -result = redis.geoadd("my-key", (29.0, 41.0, "member")) +result = redis.keys("*") print(result) ``` @@ -26620,7 +26681,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.geoadd("my-key", "29.0", "41.0", "member"); +const result = await redis.keys("*"); console.log(result); ``` @@ -26634,7 +26695,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.geoAdd("my-key", { longitude: 29, latitude: 41, member: "member" }); +const result = await client.keys("*"); console.log(result); ``` @@ -26647,7 +26708,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.geoadd("my-key", (29.0, 41.0, "member")) +result = client.keys("*") print(result) ``` @@ -26672,7 +26733,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.GeoAdd(context.Background(), "my-key", &redis.GeoLocation{Longitude: 29.0, Latitude: 41.0, Name: "member"}).Result() + result, err := client.Keys(context.Background(), "*").Result() if err != nil { panic(err) } @@ -26690,7 +26751,7 @@ import java.net.URI; import redis.clients.jedis.Jedis; try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.geoadd("my-key", 29.0, 41.0, "member"); + Object result = jedis.keys("*"); System.out.println(result); } ``` @@ -26707,7 +26768,7 @@ fn main() -> redis::RedisResult<()> { let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let result = connection.geo_add("my-key", (29.0, 41.0, "member"))?; + let result = connection.keys("*")?; println!("{result:?}"); Ok(()) } @@ -26717,31 +26778,32 @@ fn main() -> redis::RedisResult<()> { -# GEODIST -Source: https://upstash.com/docs/redis/commands/geo/geodist +# MEMORY USAGE +Source: https://upstash.com/docs/redis/commands/generic/memory-usage -Use `GEODIST` to get the distance between two members of a geospatial index. +Use `MEMORY USAGE` to estimate how many bytes a key and its value occupy in memory. -The unit defaults to meters and can be set to `m`, `km`, `ft`, or `mi`. The distance is a great-circle distance computed from the stored positions assuming the Earth is a sphere, so it carries the small error of the geohash encoding and, of course, says nothing about the distance actually travelled on roads. If either member is missing from the index the reply is null. +The figure covers the stored data along with its internal overhead, so it is larger than the raw size of the value and is meant for comparing keys rather than for exact accounting. For aggregate types such as hashes, lists, sets, sorted sets, and streams the value is sampled instead of fully traversed: `SAMPLES` sets how many nested elements are inspected, and the value is clamped to the range this deployment supports, so it tunes the estimate rather than forcing an exact traversal. A missing key returns null. + +It is the usual way to find out which keys are responsible for memory growth before deciding what to trim or restructure. ## Syntax ```redis -GEODIST [m | km | ft | mi] +MEMORY USAGE [SAMPLES ] ``` ## Arguments | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | -| `` | Yes | No | Redis key targeted by the command. | -| `` | Yes | No | First member. | -| `` | Yes | No | Second member. | -| `(m \| km \| ft \| mi)` | No | No | Distance unit: `m` (meters), `km` (kilometers), `ft` (feet), or `mi` (miles). Defaults to `m` when omitted. | +| `key` | Yes | No | Key whose in-memory footprint should be estimated. | +| `SAMPLES count` | No | No | Sampling count used when estimating large stream values. | ## Important points -* The distance is always returned as a bulk string, in both RESP2 and RESP3. Client libraries commonly decode it to a language number. +* The result is an estimate in bytes and can change as the internal representation changes. +* A missing key returns null. The sampling count is clamped to the deployment's supported range. ## Response @@ -26749,8 +26811,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Null bulk string or null array or Bulk string | -| RESP3 | Null or Bulk string | +| RESP2 | Integer or Null bulk string or null array | +| RESP3 | Integer or Null | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -26765,32 +26827,24 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -GEODIST my-key member1 member2 +MEMORY USAGE my-key SAMPLES 10 ``` -```ts -import { Redis } from "@upstash/redis"; - -const redis = Redis.fromEnv(); -const result = await redis.geodist("my-key", "member1", "member2"); -console.log(result); -``` + + This command is not supported yet in `@upstash/redis`. + -```python -from upstash_redis import Redis - -redis = Redis.from_env() -result = redis.geodist("my-key", "member1", "member2") -print(result) -``` + + This command is not supported yet in `upstash_redis`. + @@ -26800,7 +26854,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.geodist("my-key", "member1", "member2"); +const result = await redis.memory("USAGE", "my-key", "SAMPLES", "10"); console.log(result); ``` @@ -26814,7 +26868,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.geoDist("my-key", "member1", "member2"); +const result = await client.memoryUsage("my-key", { SAMPLES: 10 }); console.log(result); ``` @@ -26827,7 +26881,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.geodist("my-key", "member1", "member2") +result = client.memory_usage("my-key", samples=10) print(result) ``` @@ -26852,7 +26906,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.GeoDist(context.Background(), "my-key", "member1", "member2", "m").Result() + result, err := client.MemoryUsage(context.Background(), "my-key", 10).Result() if err != nil { panic(err) } @@ -26870,7 +26924,7 @@ import java.net.URI; import redis.clients.jedis.Jedis; try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.geodist("my-key", "member1", "member2"); + Object result = jedis.memoryUsage("my-key", 10); System.out.println(result); } ``` @@ -26880,15 +26934,17 @@ try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { ```rust -use redis::geo::Unit; -use redis::TypedCommands; - fn main() -> redis::RedisResult<()> { let url = std::env::var("REDIS_URL").expect("REDIS_URL is not set"); let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let result = connection.geo_dist("my-key", "member1", "member2", Unit::Meters)?; + let mut command = redis::cmd("MEMORY"); + command.arg("USAGE"); + command.arg("my-key"); + command.arg("SAMPLES"); + command.arg("10"); + let result: redis::Value = command.query(&mut connection)?; println!("{result:?}"); Ok(()) } @@ -26898,17 +26954,48 @@ fn main() -> redis::RedisResult<()> { -# GEOHASH -Source: https://upstash.com/docs/redis/commands/geo/geohash +# Generic commands +Source: https://upstash.com/docs/redis/commands/generic/overview -Use `GEOHASH` to get standard Geohash strings for members of a geospatial index. + +Copy a key to another key +Delete one or more keys +Serialize a key's value +Check if keys exist +Set a key's TTL in seconds +Set expiry as Unix timestamp +Get expiry as Unix timestamp +Find keys matching a pattern +Estimate memory used by a key +Remove the expiration from a key +Set a key's TTL in milliseconds +Set expiry as Unix ms timestamp +Get expiry as Unix ms timestamp +Get TTL in milliseconds +Return a random key +Rename a key +Rename a key if new key doesn't exist +Deserialize and restore a key +Incrementally iterate keys +Update last access time of keys +Get TTL in seconds +Get the type of a key +Delete keys asynchronously +Wait for replica acknowledgements +Wait for local and replica persistence + -The reply holds one 11-character string per requested member, in the order requested, with null for members that are not in the index. These are the strings used by geohash.org and by other geospatial tools, which makes the command the right way to export positions or to share them with systems that speak Geohash. They are derived from the stored 52-bit position, so they reflect the same rounding as [`GEOPOS`](/docs/redis/commands/geo/geopos). +# PERSIST +Source: https://upstash.com/docs/redis/commands/generic/persist + +Use `PERSIST` to remove the expiration from a key so that it stops being deleted automatically and lives until it is removed explicitly. + +The reply is `1` when an expiration was removed and `0` when the key does not exist or had no expiration to begin with. This is how a value gets promoted from temporary to permanent without rewriting it, for example when a trial record becomes a persistent one. ## Syntax ```redis -GEOHASH [ [ ...]] +PERSIST ``` ## Arguments @@ -26916,7 +27003,6 @@ GEOHASH [ [ ...]] | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | | `` | Yes | No | Redis key targeted by the command. | -| `` | No | Yes | Member name. | ## Response @@ -26924,8 +27010,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Array of bulk-string geohashes or null values | -| RESP3 | Array of bulk-string geohashes or null values | +| RESP2 | Integer: `1` if the timeout was removed, `0` otherwise | +| RESP3 | Integer: `1` if the timeout was removed, `0` otherwise | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -26940,7 +27026,7 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -GEOHASH my-key member +PERSIST my-key ``` @@ -26951,8 +27037,8 @@ GEOHASH my-key member import { Redis } from "@upstash/redis"; const redis = Redis.fromEnv(); -const result = await redis.geohash("my-key", "member"); -console.log(result); + +await redis.persist(key); ``` @@ -26963,7 +27049,7 @@ console.log(result); from upstash_redis import Redis redis = Redis.from_env() -result = redis.geohash("my-key", "member") +result = redis.persist("my-key") print(result) ``` @@ -26975,7 +27061,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.geohash("my-key", "member"); +const result = await redis.persist("my-key"); console.log(result); ``` @@ -26989,7 +27075,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.geoHash("my-key", "member"); +const result = await client.persist("my-key"); console.log(result); ``` @@ -27002,7 +27088,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.geohash("my-key", "member") +result = client.persist("my-key") print(result) ``` @@ -27027,7 +27113,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.GeoHash(context.Background(), "my-key", "member").Result() + result, err := client.Persist(context.Background(), "my-key").Result() if err != nil { panic(err) } @@ -27045,7 +27131,7 @@ import java.net.URI; import redis.clients.jedis.Jedis; try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.geohash("my-key", "member"); + Object result = jedis.persist("my-key"); System.out.println(result); } ``` @@ -27062,7 +27148,7 @@ fn main() -> redis::RedisResult<()> { let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let result = connection.geo_hash("my-key", "member")?; + let result = connection.persist("my-key")?; println!("{result:?}"); Ok(()) } @@ -27072,17 +27158,19 @@ fn main() -> redis::RedisResult<()> { -# GEOPOS -Source: https://upstash.com/docs/redis/commands/geo/geopos +# PEXPIRE +Source: https://upstash.com/docs/redis/commands/generic/pexpire -Use `GEOPOS` to get the longitude and latitude of members of a geospatial index. +Use `PEXPIRE` to give a key a lifetime in milliseconds, after which the key is deleted automatically. -The reply holds one entry per requested member, in the order requested, each an array with longitude first and latitude second, and null for members that are not in the index. Positions are decoded from the stored geohash, so they are very close to but not bit-for-bit identical with the coordinates originally passed to [`GEOADD`](/docs/redis/commands/geo/geoadd). +It is the millisecond form of [`EXPIRE`](/docs/redis/commands/generic/expire) and behaves identically otherwise: replacing the value with [`SET`](/docs/redis/commands/string/set) clears the expiration, while in-place updates keep it, and a negative lifetime deletes the key immediately. The sub-second precision matters for short-lived keys such as locks and rate limit windows. + +The optional condition decides when the new expiration is applied: `NX` only when the key has none, `XX` only when it already has one, `GT` only when the new expiration is later than the current one, and `LT` only when it is earlier. ## Syntax ```redis -GEOPOS [ [ ...]] +PEXPIRE [NX | XX | GT | LT] ``` ## Arguments @@ -27090,7 +27178,13 @@ GEOPOS [ [ ...]] | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | | `` | Yes | No | Redis key targeted by the command. | -| `` | No | Yes | Member name. | +| `` | Yes | No | Lifetime in milliseconds. | +| `(NX \| XX \| GT \| LT)` | No | No | Choose one form: `NX` (only when the key has no expiration); `XX` (only when the key already has one); `GT` (only when the new expiration is later than the current one); `LT` (only when it is earlier). | + +## Important points + +* `NX` cannot be combined with `XX`, `GT`, or `LT`, and `GT` and `LT` cannot be used together. +* A key with no expiration counts as an infinite one, so `GT` never sets an expiration on such a key and `LT` always does. ## Response @@ -27098,8 +27192,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Array of coordinate-pair arrays or null values | -| RESP3 | Array of coordinate-pair arrays or null values | +| RESP2 | Integer: `1` if the timeout was set, `0` otherwise | +| RESP3 | Integer: `1` if the timeout was set, `0` otherwise | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -27114,7 +27208,7 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -GEOPOS my-key member +PEXPIRE my-key 1000 ``` @@ -27125,8 +27219,8 @@ GEOPOS my-key member import { Redis } from "@upstash/redis"; const redis = Redis.fromEnv(); -const result = await redis.geopos("my-key", "member"); -console.log(result); + +await redis.pexpire(key, 60_000); // 1 minute ``` @@ -27137,7 +27231,7 @@ console.log(result); from upstash_redis import Redis redis = Redis.from_env() -result = redis.geopos("my-key", "member") +result = redis.pexpire("my-key", 1000) print(result) ``` @@ -27149,7 +27243,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.geopos("my-key", "member"); +const result = await redis.pexpire("my-key", "1000"); console.log(result); ``` @@ -27163,7 +27257,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.geoPos("my-key", "member"); +const result = await client.pExpire("my-key", 1000); console.log(result); ``` @@ -27176,7 +27270,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.geopos("my-key", "member") +result = client.pexpire("my-key", 1000) print(result) ``` @@ -27191,6 +27285,7 @@ import ( "context" "fmt" "os" + "time" "github.com/redis/go-redis/v9" ) @@ -27201,7 +27296,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.GeoPos(context.Background(), "my-key", "member").Result() + result, err := client.PExpire(context.Background(), "my-key", time.Second).Result() if err != nil { panic(err) } @@ -27219,7 +27314,7 @@ import java.net.URI; import redis.clients.jedis.Jedis; try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.geopos("my-key", "member"); + Object result = jedis.pexpire("my-key", 1000); System.out.println(result); } ``` @@ -27236,7 +27331,7 @@ fn main() -> redis::RedisResult<()> { let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let result = connection.geo_pos("my-key", "member")?; + let result = connection.pexpire("my-key", 1000)?; println!("{result:?}"); Ok(()) } @@ -27246,31 +27341,19 @@ fn main() -> redis::RedisResult<()> { -# GEORADIUS -Source: https://upstash.com/docs/redis/commands/geo/georadius - - - Prefer [`GEOSEARCH`](/docs/redis/commands/geo/geosearch) with `FROMLONLAT` and `BYRADIUS` in new code: `GEOSEARCH FROMLONLAT BYRADIUS (m | km | ft | mi)`. In place of `STORE` and `STOREDIST`, use [`GEOSEARCHSTORE`](/docs/redis/commands/geo/geosearchstore) with the same query. - - -Use `GEORADIUS` to find the members of a geospatial index that lie within a given radius of a longitude and latitude point. +# PEXPIREAT +Source: https://upstash.com/docs/redis/commands/generic/pexpireat -By default only member names are returned. `WITHDIST` adds each member's distance from the center in the unit of the query, `WITHCOORD` adds its coordinates, and `WITHHASH` adds its raw 52-bit geohash score. `COUNT` caps the number of results and, combined with `ANY`, lets the server stop as soon as it has enough matches instead of examining the whole area, which is faster but returns an arbitrary subset rather than the nearest ones. `ASC` and `DESC` sort the results by distance from the center. +Use `PEXPIREAT` to schedule a key for automatic deletion at a fixed point in time, given as a Unix timestamp in milliseconds. -`STORE` writes the matching members into a sorted set scored by geohash, so the result stays usable as a geospatial index, while `STOREDIST` scores them by their distance from the center, which makes the result easy to page through by proximity. +It combines the absolute deadline of [`EXPIREAT`](/docs/redis/commands/generic/expireat) with the millisecond precision of [`PEXPIRE`](/docs/redis/commands/generic/pexpire), which is what you need when many keys must expire at exactly the same instant. A timestamp in the past deletes the key right away. -[`GEOSEARCH`](/docs/redis/commands/geo/geosearch) and [`GEOSEARCHSTORE`](/docs/redis/commands/geo/geosearchstore) do the same work and additionally support rectangular areas. Use [`GEORADIUS_RO`](/docs/redis/commands/geo/georadius-ro) if you want a form that cannot write. +The optional condition works as elsewhere: `NX` only when the key has no expiration, `XX` only when it already has one, `GT` only when the new deadline is later than the current one, and `LT` only when it is earlier. ## Syntax ```redis -GEORADIUS (m | km | ft | mi) - [WITHCOORD] - [WITHDIST] - [WITHHASH] - [COUNT [ANY]] - [ASC | DESC] - [STORE | STOREDIST ] +PEXPIREAT [NX | XX | GT | LT] ``` ## Arguments @@ -27278,16 +27361,13 @@ GEORADIUS (m | km | ft | mi) | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | | `` | Yes | No | Redis key targeted by the command. | -| `` | Yes | No | Longitude in degrees, from -180 to 180. | -| `` | Yes | No | Latitude in degrees, from -85.05112878 to 85.05112878. | -| `` | Yes | No | Search radius, in the unit given after it. | -| `(m \| km \| ft \| mi)` | Yes | No | Distance unit: `m` (meters), `km` (kilometers), `ft` (feet), or `mi` (miles). | -| `WITHCOORD` | No | No | Also return the longitude and latitude of each match. | -| `WITHDIST` | No | No | Also return the distance from the center, in the requested unit. | -| `WITHHASH` | No | No | Also return the raw 52-bit geohash score of each match. | -| `COUNT [ANY]` | No | No | Return at most `` matches. `ANY` returns as soon as enough matches are found, instead of sorting every match first. | -| `(ASC \| DESC)` | No | No | Sort the matches by distance from the center: `ASC` (nearest first) or `DESC` (farthest first). Unsorted when omitted. | -| `(STORE \| STOREDIST )` | No | No | Store the matches in a sorted set instead of returning them: `STORE` scores them by geohash, so the destination stays a geospatial index, and `STOREDIST` scores them by their distance from the center. | +| `` | Yes | No | Expiration time as a Unix timestamp in milliseconds. | +| `(NX \| XX \| GT \| LT)` | No | No | Choose one form: `NX` (only when the key has no expiration); `XX` (only when the key already has one); `GT` (only when the new expiration is later than the current one); `LT` (only when it is earlier). | + +## Important points + +* `NX` cannot be combined with `XX`, `GT`, or `LT`, and `GT` and `LT` cannot be used together. +* A key with no expiration counts as an infinite one, so `GT` never sets an expiration on such a key and `LT` always does. ## Response @@ -27295,8 +27375,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Array of bulk-string members or member-detail arrays, or Integer when storing | -| RESP3 | Array of bulk-string members or member-detail arrays, or Integer when storing | +| RESP2 | Integer: `1` if the timeout was set, `0` otherwise | +| RESP3 | Integer: `1` if the timeout was set, `0` otherwise | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -27311,16 +27391,22 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -GEORADIUS my-key 29.0 41.0 1.5 m +PEXPIREAT my-key 1735689600 ``` - - This command is not supported yet in `@upstash/redis`. - +```ts +import { Redis } from "@upstash/redis"; + +const redis = Redis.fromEnv(); + +await redis.set("mykey", "Hello"); +const tenMinutesFromNow = Date.now() + 10 * 60 * 1000; +await redis.pexpireat("mykey", tenMinutesFromNow); +``` @@ -27330,7 +27416,7 @@ GEORADIUS my-key 29.0 41.0 1.5 m from upstash_redis import Redis redis = Redis.from_env() -result = redis.georadius("my-key", 29.0, 41.0, 1.5, "M") +result = redis.pexpireat("my-key", 1735689600) print(result) ``` @@ -27342,7 +27428,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.georadius("my-key", "29.0", "41.0", "1.5", "m"); +const result = await redis.pexpireat("my-key", "1735689600"); console.log(result); ``` @@ -27356,7 +27442,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.geoRadius("my-key", { longitude: 29, latitude: 41 }, 1.5, "m"); +const result = await client.pExpireAt("my-key", 1735689600); console.log(result); ``` @@ -27369,7 +27455,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.georadius("my-key", 29.0, 41.0, 1.5, "m") +result = client.pexpireat("my-key", 1735689600) print(result) ``` @@ -27384,6 +27470,7 @@ import ( "context" "fmt" "os" + "time" "github.com/redis/go-redis/v9" ) @@ -27394,7 +27481,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.GeoRadius(context.Background(), "my-key", 29.0, 41.0, &redis.GeoRadiusQuery{Radius: 1.5, Unit: "m"}).Result() + result, err := client.PExpireAt(context.Background(), "my-key", time.UnixMilli(1735689600)).Result() if err != nil { panic(err) } @@ -27412,7 +27499,7 @@ import java.net.URI; import redis.clients.jedis.Jedis; try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.georadius("my-key", 29.0, 41.0, 1.5, redis.clients.jedis.args.GeoUnit.M); + Object result = jedis.pexpireAt("my-key", 1735689600); System.out.println(result); } ``` @@ -27422,7 +27509,6 @@ try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { ```rust -use redis::geo::{RadiusOptions, Unit}; use redis::TypedCommands; fn main() -> redis::RedisResult<()> { @@ -27430,14 +27516,7 @@ fn main() -> redis::RedisResult<()> { let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let result = connection.geo_radius( - "my-key", - 29.0, - 41.0, - 1.5, - Unit::Meters, - RadiusOptions::default(), - )?; + let result = connection.pexpire_at("my-key", 1735689600)?; println!("{result:?}"); Ok(()) } @@ -27447,28 +27526,17 @@ fn main() -> redis::RedisResult<()> { -# GEORADIUS_RO -Source: https://upstash.com/docs/redis/commands/geo/georadius-ro - - - Prefer [`GEOSEARCH`](/docs/redis/commands/geo/geosearch) with `FROMLONLAT` and `BYRADIUS` in new code: `GEOSEARCH FROMLONLAT BYRADIUS (m | km | ft | mi)`. - - -Use `GEORADIUS_RO` to find members within a radius of a point. It is the read-only form of [`GEORADIUS`](/docs/redis/commands/geo/georadius). +# PEXPIRETIME +Source: https://upstash.com/docs/redis/commands/generic/pexpiretime -It accepts the same query and the same `WITHCOORD`, `WITHDIST`, `WITHHASH`, `COUNT`, and sorting options, but it has no `STORE` or `STOREDIST` clause, so the server knows the call cannot write and can serve it on replicas and from read-only scripts. +Use `PEXPIRETIME` to read the absolute time at which a key will expire, as a Unix timestamp in milliseconds. -[`GEOSEARCH`](/docs/redis/commands/geo/geosearch) is read-only as well and also supports rectangular areas. +The reply is `-1` when the key exists but has no expiration and `-2` when the key does not exist. It is the millisecond form of [`EXPIRETIME`](/docs/redis/commands/generic/expiretime), and it reports a deadline rather than a remaining lifetime, which makes it the value to compare against a clock when you need to know exactly when something is due. ## Syntax ```redis -GEORADIUS_RO (m | km | ft | mi) - [WITHCOORD] - [WITHDIST] - [WITHHASH] - [COUNT [ANY]] - [ASC | DESC] +PEXPIRETIME ``` ## Arguments @@ -27476,15 +27544,10 @@ GEORADIUS_RO (m | km | ft | mi) | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | | `` | Yes | No | Redis key targeted by the command. | -| `` | Yes | No | Longitude in degrees, from -180 to 180. | -| `` | Yes | No | Latitude in degrees, from -85.05112878 to 85.05112878. | -| `` | Yes | No | Search radius, in the unit given after it. | -| `(m \| km \| ft \| mi)` | Yes | No | Distance unit: `m` (meters), `km` (kilometers), `ft` (feet), or `mi` (miles). | -| `WITHCOORD` | No | No | Also return the longitude and latitude of each match. | -| `WITHDIST` | No | No | Also return the distance from the center, in the requested unit. | -| `WITHHASH` | No | No | Also return the raw 52-bit geohash score of each match. | -| `COUNT [ANY]` | No | No | Return at most `` matches. `ANY` returns as soon as enough matches are found, instead of sorting every match first. | -| `(ASC \| DESC)` | No | No | Sort the matches by distance from the center: `ASC` (nearest first) or `DESC` (farthest first). Unsorted when omitted. | + +## Important points + +* Negative integer replies are sentinel values, not durations or timestamps; see the response description for missing or persistent data. ## Response @@ -27492,8 +27555,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Array of bulk-string members or member-detail arrays | -| RESP3 | Array of bulk-string members or member-detail arrays | +| RESP2 | Integer: expiration Unix time in milliseconds, `-1` if the key has no expiration, `-2` if the key does not exist | +| RESP3 | Integer: expiration Unix time in milliseconds, `-1` if the key has no expiration, `-2` if the key does not exist | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -27508,7 +27571,7 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -GEORADIUS_RO my-key 29.0 41.0 1.5 m +PEXPIRETIME my-key ``` @@ -27523,13 +27586,9 @@ GEORADIUS_RO my-key 29.0 41.0 1.5 m -```python -from upstash_redis import Redis - -redis = Redis.from_env() -result = redis.georadius_ro("my-key", 29.0, 41.0, 1.5, "M") -print(result) -``` + + This command is not supported yet in `upstash_redis`. + @@ -27539,7 +27598,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.georadius_ro("my-key", "29.0", "41.0", "1.5", "m"); +const result = await redis.pexpiretime("my-key"); console.log(result); ``` @@ -27553,7 +27612,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.geoRadiusRo("my-key", { longitude: 29, latitude: 41 }, 1.5, "m"); +const result = await client.pExpireTime("my-key"); console.log(result); ``` @@ -27566,7 +27625,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.execute_command("GEORADIUS_RO", "my-key", "29.0", "41.0", "1.5", "m") +result = client.pexpiretime("my-key") print(result) ``` @@ -27591,7 +27650,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.GeoRadius(context.Background(), "my-key", 29.0, 41.0, &redis.GeoRadiusQuery{Radius: 1.5, Unit: "m"}).Result() + result, err := client.PExpireTime(context.Background(), "my-key").Result() if err != nil { panic(err) } @@ -27609,7 +27668,7 @@ import java.net.URI; import redis.clients.jedis.Jedis; try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.georadiusReadonly("my-key", 29.0, 41.0, 1.5, redis.clients.jedis.args.GeoUnit.M); + Object result = jedis.pexpireTime("my-key"); System.out.println(result); } ``` @@ -27619,18 +27678,14 @@ try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { ```rust +use redis::TypedCommands; + fn main() -> redis::RedisResult<()> { let url = std::env::var("REDIS_URL").expect("REDIS_URL is not set"); let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let mut command = redis::cmd("GEORADIUS_RO"); - command.arg("my-key"); - command.arg("29.0"); - command.arg("41.0"); - command.arg("1.5"); - command.arg("m"); - let result: redis::Value = command.query(&mut connection)?; + let result = connection.pexpire_time("my-key")?; println!("{result:?}"); Ok(()) } @@ -27640,29 +27695,17 @@ fn main() -> redis::RedisResult<()> { -# GEORADIUSBYMEMBER -Source: https://upstash.com/docs/redis/commands/geo/georadiusbymember - - - Prefer [`GEOSEARCH`](/docs/redis/commands/geo/geosearch) with `FROMMEMBER` and `BYRADIUS` in new code: `GEOSEARCH FROMMEMBER BYRADIUS (m | km | ft | mi)`. In place of `STORE` and `STOREDIST`, use [`GEOSEARCHSTORE`](/docs/redis/commands/geo/geosearchstore) with the same query. - - -Use `GEORADIUSBYMEMBER` to find the members of a geospatial index that lie within a given radius of another member. +# PTTL +Source: https://upstash.com/docs/redis/commands/generic/pttl -It works exactly like [`GEORADIUS`](/docs/redis/commands/geo/georadius) except that the center is the stored position of a member instead of an explicit coordinate pair, which saves a lookup when the reference point is already in the index (a store, a driver, a landmark). The reference member itself always matches, since its distance from itself is zero, so remember to filter it out or ask for one result more than you need. +Use `PTTL` to read how much longer a key will live, in milliseconds. -The same `WITHCOORD`, `WITHDIST`, `WITHHASH`, `COUNT`, sorting, and `STORE` or `STOREDIST` options apply. +The reply is `-1` when the key exists but has no expiration and `-2` when the key does not exist, so a missing key and a permanent one are easy to tell apart. It is the millisecond form of [`TTL`](/docs/redis/commands/generic/ttl), and the extra precision matters for short-lived keys such as locks, where rounding to whole seconds hides most of the remaining lifetime. ## Syntax ```redis -GEORADIUSBYMEMBER (m | km | ft | mi) - [WITHCOORD] - [WITHDIST] - [WITHHASH] - [COUNT [ANY]] - [ASC | DESC] - [STORE | STOREDIST ] +PTTL ``` ## Arguments @@ -27670,15 +27713,10 @@ GEORADIUSBYMEMBER (m | km | ft | mi) | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | | `` | Yes | No | Redis key targeted by the command. | -| `` | Yes | No | Member name. | -| `` | Yes | No | Search radius, in the unit given after it. | -| `(m \| km \| ft \| mi)` | Yes | No | Distance unit: `m` (meters), `km` (kilometers), `ft` (feet), or `mi` (miles). | -| `WITHCOORD` | No | No | Also return the longitude and latitude of each match. | -| `WITHDIST` | No | No | Also return the distance from the center, in the requested unit. | -| `WITHHASH` | No | No | Also return the raw 52-bit geohash score of each match. | -| `COUNT [ANY]` | No | No | Return at most `` matches. `ANY` returns as soon as enough matches are found, instead of sorting every match first. | -| `(ASC \| DESC)` | No | No | Sort the matches by distance from the center: `ASC` (nearest first) or `DESC` (farthest first). Unsorted when omitted. | -| `(STORE \| STOREDIST )` | No | No | Store the matches in a sorted set instead of returning them: `STORE` scores them by geohash, so the destination stays a geospatial index, and `STOREDIST` scores them by their distance from the center. | + +## Important points + +* Negative integer replies are sentinel values, not durations or timestamps; see the response description for missing or persistent data. ## Response @@ -27686,8 +27724,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Array of bulk-string members or member-detail arrays, or Integer when storing | -| RESP3 | Array of bulk-string members or member-detail arrays, or Integer when storing | +| RESP2 | Integer: remaining lifetime in milliseconds, `-1` if the key has no expiration, `-2` if the key does not exist | +| RESP3 | Integer: remaining lifetime in milliseconds, `-1` if the key has no expiration, `-2` if the key does not exist | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -27702,16 +27740,20 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -GEORADIUSBYMEMBER my-key member 1.5 m +PTTL my-key ``` - - This command is not supported yet in `@upstash/redis`. - +```ts +import { Redis } from "@upstash/redis"; + +const redis = Redis.fromEnv(); + +const millis = await redis.pttl(key); +``` @@ -27721,7 +27763,7 @@ GEORADIUSBYMEMBER my-key member 1.5 m from upstash_redis import Redis redis = Redis.from_env() -result = redis.georadiusbymember("my-key", "member", 1.5, "M") +result = redis.pttl("my-key") print(result) ``` @@ -27733,7 +27775,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.georadiusbymember("my-key", "member", "1.5", "m"); +const result = await redis.pttl("my-key"); console.log(result); ``` @@ -27747,7 +27789,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.geoRadiusByMember("my-key", "member", 1.5, "m"); +const result = await client.pTTL("my-key"); console.log(result); ``` @@ -27760,7 +27802,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.georadiusbymember("my-key", "member", 1.5, "m") +result = client.pttl("my-key") print(result) ``` @@ -27785,7 +27827,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.GeoRadiusByMember(context.Background(), "my-key", "member", &redis.GeoRadiusQuery{Radius: 1.5, Unit: "m"}).Result() + result, err := client.PTTL(context.Background(), "my-key").Result() if err != nil { panic(err) } @@ -27803,7 +27845,7 @@ import java.net.URI; import redis.clients.jedis.Jedis; try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.georadiusByMember("my-key", "member", 1.5, redis.clients.jedis.args.GeoUnit.M); + Object result = jedis.pttl("my-key"); System.out.println(result); } ``` @@ -27813,7 +27855,6 @@ try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { ```rust -use redis::geo::{RadiusOptions, Unit}; use redis::TypedCommands; fn main() -> redis::RedisResult<()> { @@ -27821,13 +27862,7 @@ fn main() -> redis::RedisResult<()> { let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let result = connection.geo_radius_by_member( - "my-key", - "member", - 1.5, - Unit::Meters, - RadiusOptions::default(), - )?; + let result = connection.pttl("my-key")?; println!("{result:?}"); Ok(()) } @@ -27837,41 +27872,22 @@ fn main() -> redis::RedisResult<()> { -# GEORADIUSBYMEMBER_RO -Source: https://upstash.com/docs/redis/commands/geo/georadiusbymember-ro - - - Prefer [`GEOSEARCH`](/docs/redis/commands/geo/geosearch) with `FROMMEMBER` and `BYRADIUS` in new code: `GEOSEARCH FROMMEMBER BYRADIUS (m | km | ft | mi)`. - +# RANDOMKEY +Source: https://upstash.com/docs/redis/commands/generic/randomkey -Use `GEORADIUSBYMEMBER_RO` to find members within a radius of another member. It is the read-only form of [`GEORADIUSBYMEMBER`](/docs/redis/commands/geo/georadiusbymember). +Use `RANDOMKEY` to get the name of a random key from the database without reading its value. -The center is the stored position of the given member, which always appears among the matches at distance zero. The command accepts the same `WITHCOORD`, `WITHDIST`, `WITHHASH`, `COUNT`, and sorting options but has no `STORE` or `STOREDIST` clause, so it can be served on replicas and used from read-only scripts. +The reply is null when the database is empty. Keys are picked by sampling the keyspace rather than by drawing uniformly from it, and nothing prevents the same key from coming up repeatedly, so treat it as a way to look at a sample of your data while debugging rather than as a way to iterate over it. Use [`SCAN`](/docs/redis/commands/generic/scan) when you need to cover every key. ## Syntax ```redis -GEORADIUSBYMEMBER_RO (m | km | ft | mi) - [WITHCOORD] - [WITHDIST] - [WITHHASH] - [COUNT [ANY]] - [ASC | DESC] +RANDOMKEY ``` ## Arguments -| Argument | Required | Repeatable | Description | -| --- | --- | --- | --- | -| `` | Yes | No | Redis key targeted by the command. | -| `` | Yes | No | Member name. | -| `` | Yes | No | Search radius, in the unit given after it. | -| `(m \| km \| ft \| mi)` | Yes | No | Distance unit: `m` (meters), `km` (kilometers), `ft` (feet), or `mi` (miles). | -| `WITHCOORD` | No | No | Also return the longitude and latitude of each match. | -| `WITHDIST` | No | No | Also return the distance from the center, in the requested unit. | -| `WITHHASH` | No | No | Also return the raw 52-bit geohash score of each match. | -| `COUNT [ANY]` | No | No | Return at most `` matches. `ANY` returns as soon as enough matches are found, instead of sorting every match first. | -| `(ASC \| DESC)` | No | No | Sort the matches by distance from the center: `ASC` (nearest first) or `DESC` (farthest first). Unsorted when omitted. | +This command takes no arguments. ## Response @@ -27879,8 +27895,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Array of bulk-string members or member-detail arrays | -| RESP3 | Array of bulk-string members or member-detail arrays | +| RESP2 | Null bulk string or null array or Bulk string | +| RESP3 | Null or Bulk string | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -27895,16 +27911,20 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -GEORADIUSBYMEMBER_RO my-key member 1.5 m +RANDOMKEY ``` - - This command is not supported yet in `@upstash/redis`. - +```ts +import { Redis } from "@upstash/redis"; + +const redis = Redis.fromEnv(); + +const key = await redis.randomkey(); +``` @@ -27914,7 +27934,7 @@ GEORADIUSBYMEMBER_RO my-key member 1.5 m from upstash_redis import Redis redis = Redis.from_env() -result = redis.georadiusbymember_ro("my-key", "member", 1.5, "M") +result = redis.randomkey() print(result) ``` @@ -27926,7 +27946,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.georadiusbymember_ro("my-key", "member", "1.5", "m"); +const result = await redis.randomkey(); console.log(result); ``` @@ -27940,7 +27960,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.geoRadiusByMemberRo("my-key", "member", 1.5, "m"); +const result = await client.randomKey(); console.log(result); ``` @@ -27953,7 +27973,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.execute_command("GEORADIUSBYMEMBER_RO", "my-key", "member", "1.5", "m") +result = client.randomkey() print(result) ``` @@ -27978,7 +27998,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.GeoRadiusByMember(context.Background(), "my-key", "member", &redis.GeoRadiusQuery{Radius: 1.5, Unit: "m"}).Result() + result, err := client.RandomKey(context.Background()).Result() if err != nil { panic(err) } @@ -27996,7 +28016,7 @@ import java.net.URI; import redis.clients.jedis.Jedis; try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.georadiusByMemberReadonly("my-key", "member", 1.5, redis.clients.jedis.args.GeoUnit.M); + Object result = jedis.randomKey(); System.out.println(result); } ``` @@ -28011,11 +28031,8 @@ fn main() -> redis::RedisResult<()> { let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let mut command = redis::cmd("GEORADIUSBYMEMBER_RO"); - command.arg("my-key"); - command.arg("member"); - command.arg("1.5"); - command.arg("m"); + let mut command = redis::cmd("RANDOMKEY"); + let result: redis::Value = command.query(&mut connection)?; println!("{result:?}"); Ok(()) @@ -28026,29 +28043,19 @@ fn main() -> redis::RedisResult<()> { -# GEOSEARCH -Source: https://upstash.com/docs/redis/commands/geo/geosearch - -Use `GEOSEARCH` to find the members of a geospatial index that fall inside a circle or a rectangle. +# RENAME +Source: https://upstash.com/docs/redis/commands/generic/rename -The center is either an existing member of the index (`FROMMEMBER`) or an explicit coordinate pair (`FROMLONLAT`). The area is either a circle of a given radius (`BYRADIUS`) or an axis-aligned box of a given width and height centered on that point (`BYBOX`), which is the shape to use when you are covering a map viewport rather than a "within N km" question. +Use `RENAME` to give an existing key a new name. -By default only member names come back. `WITHDIST` adds the distance from the center in the unit of the query, `WITHCOORD` the member's coordinates, and `WITHHASH` its raw geohash score. `ASC` and `DESC` sort by distance, and `COUNT` caps the number of results; adding `ANY` lets the server return as soon as it has enough matches, which is faster but no longer gives you the nearest ones. +The value moves with the key and so does its remaining time to live, and the operation is atomic, so no client ever sees both names or neither. If a key with the destination name already exists it is overwritten and its old value is deleted. Renaming a key that does not exist returns an error. -`GEOSEARCH` replaces the deprecated `GEORADIUS` and `GEORADIUSBYMEMBER` commands and is the command to use for new code. Use [`GEOSEARCHSTORE`](/docs/redis/commands/geo/geosearchstore) when the result should be stored instead of returned. +Use [`RENAMENX`](/docs/redis/commands/generic/renamenx) when the destination must not be overwritten. A common pattern is to build a replacement value under a temporary key and then rename it over the live key, which swaps the data in one atomic step. ## Syntax ```redis -GEOSEARCH - (FROMMEMBER | FROMLONLAT ) - (BYRADIUS (m | km | ft | mi) | - BYBOX (m | km | ft | mi)) - [ASC | DESC] - [COUNT [ANY]] - [WITHCOORD] - [WITHDIST] - [WITHHASH] +RENAME ``` ## Arguments @@ -28056,13 +28063,7 @@ GEOSEARCH | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | | `` | Yes | No | Redis key targeted by the command. | -| `(FROMMEMBER \| FROMLONLAT )` | Yes | No | Where to center the search: `FROMMEMBER` uses the stored position of an existing member, `FROMLONLAT` uses the given coordinates. | -| `(BYRADIUS (m \| km \| ft \| mi) \| BYBOX (m \| km \| ft \| mi))` | Yes | No | The area to search: `BYRADIUS` a circle of the given radius, `BYBOX` an axis-aligned box of the given width and height centered on the search point. The unit is `m` (meters), `km` (kilometers), `ft` (feet), or `mi` (miles). | -| `(ASC \| DESC)` | No | No | Sort the matches by distance from the center: `ASC` (nearest first) or `DESC` (farthest first). Unsorted when omitted. | -| `COUNT [ANY]` | No | No | Return at most `` matches. `ANY` returns as soon as enough matches are found, instead of sorting every match first. | -| `WITHCOORD` | No | No | Also return the longitude and latitude of each match. | -| `WITHDIST` | No | No | Also return the distance from the center, in the requested unit. | -| `WITHHASH` | No | No | Also return the raw 52-bit geohash score of each match. | +| `` | Yes | No | Redis key used as newkey. | ## Response @@ -28070,8 +28071,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Array of bulk-string members or member-detail arrays | -| RESP3 | Array of bulk-string members or member-detail arrays | +| RESP2 | Simple string `OK` | +| RESP3 | Simple string `OK` | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -28086,7 +28087,7 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -GEOSEARCH my-key FROMMEMBER member BYRADIUS 1.5 m +RENAME old-key new-key ``` @@ -28097,13 +28098,8 @@ GEOSEARCH my-key FROMMEMBER member BYRADIUS 1.5 m import { Redis } from "@upstash/redis"; const redis = Redis.fromEnv(); -const result = await redis.geosearch( - "my-key", - { type: "FROMMEMBER", member: "member" }, - { type: "BYRADIUS", radius: 1.5, radiusType: "M" }, - "ASC", -); -console.log(result); + +await redis.rename("old", "new"); ``` @@ -28114,7 +28110,7 @@ console.log(result); from upstash_redis import Redis redis = Redis.from_env() -result = redis.geosearch("my-key", member="member", radius=1.5, unit="M") +result = redis.rename("old-key", "new-key") print(result) ``` @@ -28126,7 +28122,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.geosearch("my-key", "FROMMEMBER", "member", "BYRADIUS", "1.5", "m"); +const result = await redis.rename("old-key", "new-key"); console.log(result); ``` @@ -28140,7 +28136,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.geoSearch("my-key", "member", { radius: 1.5, unit: "m" }); +const result = await client.rename("old-key", "new-key"); console.log(result); ``` @@ -28153,7 +28149,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.geosearch("my-key", member="member", radius=1.5, unit="m") +result = client.rename("old-key", "new-key") print(result) ``` @@ -28178,7 +28174,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.GeoSearch(context.Background(), "my-key", &redis.GeoSearchQuery{Member: "member", Radius: 1.5, RadiusUnit: "m"}).Result() + result, err := client.Rename(context.Background(), "old-key", "new-key").Result() if err != nil { panic(err) } @@ -28196,7 +28192,7 @@ import java.net.URI; import redis.clients.jedis.Jedis; try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.geosearch("my-key", "member", 1.5, redis.clients.jedis.args.GeoUnit.M); + Object result = jedis.rename("old-key", "new-key"); System.out.println(result); } ``` @@ -28206,17 +28202,14 @@ try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { ```rust +use redis::TypedCommands; + fn main() -> redis::RedisResult<()> { let url = std::env::var("REDIS_URL").expect("REDIS_URL is not set"); let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let mut command = redis::cmd("GEOSEARCH"); - command.arg("my-key"); - command.arg("member"); - command.arg("1.5"); - command.arg("m"); - let result: redis::Value = command.query(&mut connection)?; + let result = connection.rename("old-key", "new-key")?; println!("{result:?}"); Ok(()) } @@ -28226,38 +28219,25 @@ fn main() -> redis::RedisResult<()> { -# GEOSEARCHSTORE -Source: https://upstash.com/docs/redis/commands/geo/geosearchstore - -Use `GEOSEARCHSTORE` to run the same query as [`GEOSEARCH`](/docs/redis/commands/geo/geosearch) and store the matching members in another key instead of returning them. +# RENAMENX +Source: https://upstash.com/docs/redis/commands/generic/renamenx -The destination is a sorted set holding the matches. By default their scores are the raw geohash values, so the destination is itself a valid geospatial index that can be queried further; with `STOREDIST` the score is the distance from the center in the unit of the query, which turns the result into a proximity-ordered list you can page through with [`ZRANGE`](/docs/redis/commands/sorted-set/zrange). +Use `RENAMENX` to rename a key only when the new name is not already in use. -The destination is overwritten on every call, and it is deleted when the query matches nothing. The reply is the number of members stored. This is the usual way to materialize a "nearby" result once and then reuse it for pagination or further set operations. +The reply is `1` when the rename happened and `0` when the destination already existed and nothing was changed; renaming a key that does not exist returns an error. Because the check and the rename are one atomic step, the command can serve as a way to claim a name: only one of several clients trying to rename onto the same destination succeeds. ## Syntax ```redis -GEOSEARCHSTORE - (FROMMEMBER | FROMLONLAT ) - (BYRADIUS (m | km | ft | mi) | - BYBOX (m | km | ft | mi)) - [ASC | DESC] - [COUNT [ANY]] - [STOREDIST] +RENAMENX ``` ## Arguments | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | -| `` | Yes | No | Redis key used as destination. | -| `` | Yes | No | Redis key used as source. | -| `(FROMMEMBER \| FROMLONLAT )` | Yes | No | Where to center the search: `FROMMEMBER` uses the stored position of an existing member, `FROMLONLAT` uses the given coordinates. | -| `(BYRADIUS (m \| km \| ft \| mi) \| BYBOX (m \| km \| ft \| mi))` | Yes | No | The area to search: `BYRADIUS` a circle of the given radius, `BYBOX` an axis-aligned box of the given width and height centered on the search point. The unit is `m` (meters), `km` (kilometers), `ft` (feet), or `mi` (miles). | -| `(ASC \| DESC)` | No | No | Sort the matches by distance from the center: `ASC` (nearest first) or `DESC` (farthest first). Unsorted when omitted. | -| `COUNT [ANY]` | No | No | Return at most `` matches. `ANY` returns as soon as enough matches are found, instead of sorting every match first. | -| `STOREDIST` | No | No | Store each match's distance from the center instead of its geohash score. | +| `` | Yes | No | Redis key targeted by the command. | +| `` | Yes | No | Redis key used as newkey. | ## Response @@ -28265,8 +28245,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Integer | -| RESP3 | Integer | +| RESP2 | Integer: `1` if the key was renamed, `0` if the destination already exists | +| RESP3 | Integer: `1` if the key was renamed, `0` if the destination already exists | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -28281,7 +28261,7 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -GEOSEARCHSTORE destination-key source-key FROMMEMBER member BYRADIUS 1.5 m +RENAMENX old-key new-key ``` @@ -28292,14 +28272,8 @@ GEOSEARCHSTORE destination-key source-key FROMMEMBER member BYRADIUS 1.5 m import { Redis } from "@upstash/redis"; const redis = Redis.fromEnv(); -const result = await redis.geosearchstore( - "destination-key", - "source-key", - { type: "FROMMEMBER", member: "member" }, - { type: "BYRADIUS", radius: 1.5, radiusType: "M" }, - "ASC", -); -console.log(result); + +const renamed = await redis.renamenx("old", "new"); ``` @@ -28310,7 +28284,7 @@ console.log(result); from upstash_redis import Redis redis = Redis.from_env() -result = redis.geosearchstore("destination-key", "source-key", member="member", radius=1.5, unit="M") +result = redis.renamenx("old-key", "new-key") print(result) ``` @@ -28322,7 +28296,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.geosearchstore("destination-key", "source-key", "FROMMEMBER", "member", "BYRADIUS", "1.5", "m"); +const result = await redis.renamenx("old-key", "new-key"); console.log(result); ``` @@ -28336,7 +28310,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.geoSearchStore("destination-key", "source-key", "member", { radius: 1.5, unit: "m" }); +const result = await client.renameNX("old-key", "new-key"); console.log(result); ``` @@ -28349,7 +28323,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.geosearchstore("destination-key", "source-key", member="member", radius=1.5, unit="m") +result = client.renamenx("old-key", "new-key") print(result) ``` @@ -28374,7 +28348,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.GeoSearchStore(context.Background(), "source-key", "destination-key", &redis.GeoSearchStoreQuery{GeoSearchQuery: redis.GeoSearchQuery{Member: "member", Radius: 1.5, RadiusUnit: "m"}}).Result() + result, err := client.RenameNX(context.Background(), "old-key", "new-key").Result() if err != nil { panic(err) } @@ -28392,7 +28366,7 @@ import java.net.URI; import redis.clients.jedis.Jedis; try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.geosearchStore("destination-key", "source-key", "member", 1.5, redis.clients.jedis.args.GeoUnit.M); + Object result = jedis.renamenx("old-key", "new-key"); System.out.println(result); } ``` @@ -28402,18 +28376,14 @@ try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { ```rust +use redis::TypedCommands; + fn main() -> redis::RedisResult<()> { let url = std::env::var("REDIS_URL").expect("REDIS_URL is not set"); let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let mut command = redis::cmd("GEOSEARCHSTORE"); - command.arg("destination-key"); - command.arg("source-key"); - command.arg("member"); - command.arg("1.5"); - command.arg("m"); - let result: redis::Value = command.query(&mut connection)?; + let result = connection.rename_nx("old-key", "new-key")?; println!("{result:?}"); Ok(()) } @@ -28423,33 +28393,23 @@ fn main() -> redis::RedisResult<()> { -# Geo commands -Source: https://upstash.com/docs/redis/commands/geo/overview - - -Add geospatial items -Get distance between two members -Get geohash strings for members -Get coordinates of members -Find members within a radius of a point -Read-only radius query -Find members within a radius of another member -Read-only radius query by member -Search for members in an area -Store geosearch results - +# RESTORE +Source: https://upstash.com/docs/redis/commands/generic/restore -# HDEL -Source: https://upstash.com/docs/redis/commands/hash/hdel +Use `RESTORE` to recreate a key from a payload produced by [`DUMP`](/docs/redis/commands/generic/dump). -Use `HDEL` to remove one or more fields from a hash. +`` gives the new key a lifetime in milliseconds, where `0` means no expiration; with `ABSTTL` the same number is read as an absolute Unix timestamp in milliseconds instead. The command fails if the key already exists unless `REPLACE` is given. -The reply counts only the fields that were actually present, so deleting a field that is already gone is not an error. When the last field of a hash is removed the key itself is deleted, because Redis does not keep empty collections. +The payload's version stamp and checksum are verified before anything is written, so a truncated, corrupted, or foreign payload is rejected rather than loaded. `IDLETIME` and `FREQ` seed the eviction metadata of the new key so that a restored key does not automatically look freshly used. ## Syntax ```redis -HDEL [ ...] +RESTORE + [REPLACE] + [ABSTTL] + [IDLETIME ] + [FREQ ] ``` ## Arguments @@ -28457,7 +28417,16 @@ HDEL [ ...] | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | | `` | Yes | No | Redis key targeted by the command. | -| `` | Yes | Yes | Hash field name. | +| `` | Yes | No | Lifetime in milliseconds; `0` restores the key without an expiration. | +| `` | Yes | No | Payload produced by `DUMP`. | +| `REPLACE` | No | No | Allow replacement of an existing destination. | +| `ABSTTL` | No | No | Treat `` as an absolute Unix timestamp in milliseconds. | +| `IDLETIME ` | No | No | Set the key's idle time, in seconds. | +| `FREQ ` | No | No | Set the key's access frequency counter. | + +## Important points + +* This command can expose administrative information or make a broad destructive change. Restrict it to trusted code paths. ## Response @@ -28465,8 +28434,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Integer | -| RESP3 | Integer | +| RESP2 | Simple string `OK` | +| RESP3 | Simple string `OK` | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -28481,33 +28450,24 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -HDEL my-key field +RESTORE my-key 1 serialized-value ``` -```ts -import { Redis } from "@upstash/redis"; - -const redis = Redis.fromEnv(); - -await redis.hdel(key, 'field1', 'field2'); -// returns 5 -``` + + This command is not supported yet in `@upstash/redis`. + -```python -from upstash_redis import Redis - -redis = Redis.from_env() -result = redis.hdel("my-key", "field") -print(result) -``` + + This command is not supported yet in `upstash_redis`. + @@ -28517,7 +28477,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.hdel("my-key", "field"); +const result = await redis.restore("my-key", "1", "serialized-value"); console.log(result); ``` @@ -28531,7 +28491,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.hDel("my-key", "field"); +const result = await client.restore("my-key", 1, "serialized-value"); console.log(result); ``` @@ -28544,7 +28504,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.hdel("my-key", "field") +result = client.restore("my-key", 1, "serialized-value") print(result) ``` @@ -28559,6 +28519,7 @@ import ( "context" "fmt" "os" + "time" "github.com/redis/go-redis/v9" ) @@ -28569,7 +28530,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.HDel(context.Background(), "my-key", "field").Result() + result, err := client.Restore(context.Background(), "my-key", time.Millisecond, "serialized-value").Result() if err != nil { panic(err) } @@ -28587,7 +28548,7 @@ import java.net.URI; import redis.clients.jedis.Jedis; try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.hdel("my-key", "field"); + Object result = jedis.restore("my-key", 1, "serialized-value".getBytes()); System.out.println(result); } ``` @@ -28597,14 +28558,16 @@ try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { ```rust -use redis::TypedCommands; - fn main() -> redis::RedisResult<()> { let url = std::env::var("REDIS_URL").expect("REDIS_URL is not set"); let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let result = connection.hdel("my-key", "field")?; + let mut command = redis::cmd("RESTORE"); + command.arg("my-key"); + command.arg("1"); + command.arg("serialized-value"); + let result: redis::Value = command.query(&mut connection)?; println!("{result:?}"); Ok(()) } @@ -28614,25 +28577,36 @@ fn main() -> redis::RedisResult<()> { -# HEXISTS -Source: https://upstash.com/docs/redis/commands/hash/hexists +# SCAN +Source: https://upstash.com/docs/redis/commands/generic/scan -Use `HEXISTS` to check whether a field is present in a hash. +Use `SCAN` to walk through the keys of the database incrementally, a batch at a time. -The reply is `1` when the field exists and `0` when either the field or the key is missing. Since the value is never transferred, this is the cheap way to test for presence, and it is also how you tell a missing field apart from a field whose value happens to be empty, which [`HGET`](/docs/redis/commands/hash/hget) cannot do. +Each call takes a cursor and returns the next cursor together with a batch of keys. Start with cursor `0` and keep calling with the cursor from the previous reply until the server returns `0` again, which marks the end of the iteration. Because the work is split over many short calls, `SCAN` never blocks the server the way [`KEYS`](/docs/redis/commands/generic/keys) can on a large keyspace. + +`MATCH` filters the returned keys with a glob-style pattern, `COUNT` hints at how much work each call should do (a hint about effort, not a page size, so batches vary in length), and `TYPE` limits the reply to keys of one type. Filtering is applied after a batch has been read, so a call can legitimately return no keys at all while the cursor is still non-zero: only the cursor tells you when the iteration is over. + +The guarantee is that every key present for the whole iteration is returned at least once. Keys added or removed while the scan runs may or may not show up, and a key can be returned more than once, so make the processing of each key idempotent. [`HSCAN`](/docs/redis/commands/hash/hscan), [`SSCAN`](/docs/redis/commands/set/sscan), and [`ZSCAN`](/docs/redis/commands/sorted-set/zscan) apply the same mechanism inside a single collection. ## Syntax ```redis -HEXISTS +SCAN [MATCH ] [COUNT ] [TYPE ] ``` ## Arguments | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | -| `` | Yes | No | Redis key targeted by the command. | -| `` | Yes | No | Hash field name. | +| `` | Yes | No | Cursor returned by the previous call; start at `0`. | +| `MATCH ` | No | No | Return only elements matching this glob-style pattern. | +| `COUNT ` | No | No | Hint for how much work each iteration should do. | +| `TYPE ` | No | No | Return only keys of this type, such as `string`, `list`, or `hash`. | + +## Important points + +* This operation can inspect a large part of the database. Prefer cursor-based scans where possible and avoid unbounded use on hot paths. +* The cursor is opaque. Start with `0` and continue until the server returns cursor `0`; a single iteration may return no elements. ## Response @@ -28640,8 +28614,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Integer: `1` if the field exists, `0` otherwise | -| RESP3 | Integer: `1` if the field exists, `0` otherwise | +| RESP2 | Two-element array: cursor and array of bulk-string keys | +| RESP3 | Two-element array: cursor and array of bulk-string keys | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -28656,7 +28630,7 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -HEXISTS my-key field +SCAN 0 ``` @@ -28668,10 +28642,7 @@ import { Redis } from "@upstash/redis"; const redis = Redis.fromEnv(); -await redis.hset("key", "field", "value"); -const exists = await redis.hexists("key", "field"); - -console.log(exists); // 1 +const [cursor, keys] = await redis.scan(0, { match: "*" }); ``` @@ -28682,7 +28653,7 @@ console.log(exists); // 1 from upstash_redis import Redis redis = Redis.from_env() -result = redis.hexists("my-key", "field") +result = redis.scan(0) print(result) ``` @@ -28694,7 +28665,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.hexists("my-key", "field"); +const result = await redis.scan("0"); console.log(result); ``` @@ -28708,7 +28679,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.hExists("my-key", "field"); +const result = await client.scan("0"); console.log(result); ``` @@ -28721,7 +28692,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.hexists("my-key", "field") +result = client.scan(0) print(result) ``` @@ -28746,7 +28717,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.HExists(context.Background(), "my-key", "field").Result() + result, _, err := client.Scan(context.Background(), 0, "*", 0).Result() if err != nil { panic(err) } @@ -28764,7 +28735,7 @@ import java.net.URI; import redis.clients.jedis.Jedis; try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.hexists("my-key", "field"); + Object result = jedis.scan("0"); System.out.println(result); } ``` @@ -28781,8 +28752,10 @@ fn main() -> redis::RedisResult<()> { let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let result = connection.hexists("my-key", "field")?; - println!("{result:?}"); + let iter: redis::Iter = connection.scan()?; + for key in iter { + println!("{key}"); + } Ok(()) } ``` @@ -28791,38 +28764,24 @@ fn main() -> redis::RedisResult<()> { -# HEXPIRE -Source: https://upstash.com/docs/redis/commands/hash/hexpire - -Use `HEXPIRE` to give individual hash fields a lifetime in seconds, after which those fields are removed from the hash. - -Expiration here is per field, not per key: the hash itself stays alive as long as it still has fields, and the key is deleted automatically when the last surviving field expires. This makes it possible to keep short-lived and long-lived data in one hash, for example a user record whose verification code expires while the rest of the record stays. +# TOUCH +Source: https://upstash.com/docs/redis/commands/generic/touch -`FIELDS ` introduces the list of fields and the count must match the number of names that follow. The optional condition works as it does on [`EXPIRE`](/docs/redis/commands/generic/expire): `NX` only when the field has no expiration, `XX` only when it already has one, `GT` only when the new expiration is later than the current one, and `LT` only when it is earlier. +Use `TOUCH` to update the last access time of one or more keys without reading their values. -The reply holds one status code per field, in order: `1` when the expiration was set, `0` when the condition prevented it, `2` when the field was deleted immediately because the given lifetime was zero or negative, and `-2` when the field does not exist. +The reply counts how many of the given keys exist, just like [`EXISTS`](/docs/redis/commands/generic/exists), but the call also refreshes the idle time and access frequency that the LRU and LFU eviction policies rely on. That makes it a way to tell the server that a key is still in use, for example to keep a cached value from being evicted while a slower process is still going to need it. It does not change the key's expiration. ## Syntax ```redis -HEXPIRE - [NX | XX | GT | LT] - FIELDS [ ...] +TOUCH [ ...] ``` ## Arguments | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | -| `` | Yes | No | Redis key targeted by the command. | -| `` | Yes | No | Lifetime in seconds. | -| `(NX \| XX \| GT \| LT)` | No | No | Choose one form: `NX` (only when the field has no expiration); `XX` (only when the field already has one); `GT` (only when the new expiration is later than the current one); `LT` (only when it is earlier). | -| `FIELDS [ ...]` | Yes | No | Fields to target. Give the field count first, then that many field names. | - -## Important points - -* `NX` cannot be combined with `XX`, `GT`, or `LT`, and `GT` and `LT` cannot be used together. -* A field with no expiration counts as an infinite one, so `GT` never sets an expiration on such a field and `LT` always does. +| `` | Yes | Yes | Redis key targeted by the command. | ## Response @@ -28830,8 +28789,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Array of integer status codes, one per field | -| RESP3 | Array of integer status codes, one per field | +| RESP2 | Integer | +| RESP3 | Integer | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -28846,7 +28805,7 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -HEXPIRE my-key 1000 FIELDS 1 field +TOUCH my-key ``` @@ -28858,10 +28817,7 @@ import { Redis } from "@upstash/redis"; const redis = Redis.fromEnv(); -await redis.hset("my-key", "my-field", "my-value"); -const expirationSet = await redis.hexpire("my-key", "my-field", 1); - -console.log(expirationSet); // 1 +await redis.touch("key1", "key2", "key3"); ``` @@ -28872,7 +28828,7 @@ console.log(expirationSet); // 1 from upstash_redis import Redis redis = Redis.from_env() -result = redis.hexpire("my-key", "field", 1000) +result = redis.touch("my-key") print(result) ``` @@ -28884,7 +28840,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.hexpire("my-key", "1000", "FIELDS", "1", "field"); +const result = await redis.touch("my-key"); console.log(result); ``` @@ -28898,7 +28854,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.hExpire("my-key", "field", 1000); +const result = await client.touch("my-key"); console.log(result); ``` @@ -28911,7 +28867,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.hexpire("my-key", 1000, "field") +result = client.touch("my-key") print(result) ``` @@ -28936,7 +28892,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.HExpire(context.Background(), "my-key", 1000*time.Second, "field").Result() + result, err := client.Touch(context.Background(), "my-key").Result() if err != nil { panic(err) } @@ -28954,7 +28910,7 @@ import java.net.URI; import redis.clients.jedis.Jedis; try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.hexpire("my-key", 1000, "field"); + Object result = jedis.touch("my-key"); System.out.println(result); } ``` @@ -28964,14 +28920,14 @@ try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { ```rust -use redis::TypedCommands; - fn main() -> redis::RedisResult<()> { let url = std::env::var("REDIS_URL").expect("REDIS_URL is not set"); let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let result = connection.hexpire("my-key", 1000, redis::ExpireOption::NONE, &["field"])?; + let mut command = redis::cmd("TOUCH"); + command.arg("my-key"); + let result: redis::Value = command.query(&mut connection)?; println!("{result:?}"); Ok(()) } @@ -28981,23 +28937,17 @@ fn main() -> redis::RedisResult<()> { -# HEXPIREAT -Source: https://upstash.com/docs/redis/commands/hash/hexpireat - -Use `HEXPIREAT` to schedule individual hash fields for deletion at a fixed point in time, given as a Unix timestamp in seconds. - -It is the absolute-deadline form of [`HEXPIRE`](/docs/redis/commands/hash/hexpire), which is what you want when several fields, or fields across several hashes, must expire at the same moment. A timestamp in the past removes the fields right away. The key is deleted when its last field expires. +# TTL +Source: https://upstash.com/docs/redis/commands/generic/ttl -`FIELDS ` introduces the field list and the count must match. The optional condition applies the deadline only in certain cases: `NX` when the field has no expiration, `XX` when it already has one, `GT` when the new deadline is later than the current one, and `LT` when it is earlier. +Use `TTL` to read how much longer a key will live, in seconds. -The reply holds one status code per field: `1` when the expiration was set, `0` when the condition prevented it, `2` when the field was deleted immediately, and `-2` when the field does not exist. +The reply is `-1` when the key exists but has no expiration and `-2` when the key does not exist, so these two are never confused with a real remaining lifetime. Values are rounded to whole seconds; use [`PTTL`](/docs/redis/commands/generic/pttl) for millisecond precision and [`EXPIRETIME`](/docs/redis/commands/generic/expiretime) when you want the absolute deadline rather than the time left. ## Syntax ```redis -HEXPIREAT - [NX | XX | GT | LT] - FIELDS [ ...] +TTL ``` ## Arguments @@ -29005,14 +28955,10 @@ HEXPIREAT | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | | `` | Yes | No | Redis key targeted by the command. | -| `` | Yes | No | Expiration time as a Unix timestamp in seconds. | -| `(NX \| XX \| GT \| LT)` | No | No | Choose one form: `NX` (only when the field has no expiration); `XX` (only when the field already has one); `GT` (only when the new expiration is later than the current one); `LT` (only when it is earlier). | -| `FIELDS [ ...]` | Yes | No | Fields to target. Give the field count first, then that many field names. | ## Important points -* `NX` cannot be combined with `XX`, `GT`, or `LT`, and `GT` and `LT` cannot be used together. -* A field with no expiration counts as an infinite one, so `GT` never sets an expiration on such a field and `LT` always does. +* Negative integer replies are sentinel values, not durations or timestamps; see the response description for missing or persistent data. ## Response @@ -29020,8 +28966,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Array of integer status codes, one per field | -| RESP3 | Array of integer status codes, one per field | +| RESP2 | Integer: remaining lifetime in seconds, `-1` if the key has no expiration, `-2` if the key does not exist | +| RESP3 | Integer: remaining lifetime in seconds, `-1` if the key has no expiration, `-2` if the key does not exist | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -29036,7 +28982,7 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -HEXPIREAT my-key 1735689600 FIELDS 1 field +TTL my-key ``` @@ -29048,10 +28994,7 @@ import { Redis } from "@upstash/redis"; const redis = Redis.fromEnv(); -await redis.hset("my-key", "my-field", "my-value"); -const expirationSet = await redis.hexpireat("my-key", "my-field", Math.floor(Date.now() / 1000) + 10); - -console.log(expirationSet); // [1] +const seconds = await redis.ttl(key); ``` @@ -29062,7 +29005,7 @@ console.log(expirationSet); // [1] from upstash_redis import Redis redis = Redis.from_env() -result = redis.hexpireat("my-key", "field", 1735689600) +result = redis.ttl("my-key") print(result) ``` @@ -29074,7 +29017,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.hexpireat("my-key", "1735689600", "FIELDS", "1", "field"); +const result = await redis.ttl("my-key"); console.log(result); ``` @@ -29088,7 +29031,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.hExpireAt("my-key", "field", 1735689600); +const result = await client.ttl("my-key"); console.log(result); ``` @@ -29101,7 +29044,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.hexpireat("my-key", 1735689600, "field") +result = client.ttl("my-key") print(result) ``` @@ -29126,7 +29069,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.HExpireAt(context.Background(), "my-key", time.Unix(1735689600, 0), "field").Result() + result, err := client.TTL(context.Background(), "my-key").Result() if err != nil { panic(err) } @@ -29144,7 +29087,7 @@ import java.net.URI; import redis.clients.jedis.Jedis; try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.hexpireAt("my-key", 1735689600, "field"); + Object result = jedis.ttl("my-key"); System.out.println(result); } ``` @@ -29161,7 +29104,7 @@ fn main() -> redis::RedisResult<()> { let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let result = connection.hexpire_at("my-key", 1735689600, redis::ExpireOption::NONE, &["field"])?; + let result = connection.ttl("my-key")?; println!("{result:?}"); Ok(()) } @@ -29171,17 +29114,17 @@ fn main() -> redis::RedisResult<()> { -# HEXPIRETIME -Source: https://upstash.com/docs/redis/commands/hash/hexpiretime +# TYPE +Source: https://upstash.com/docs/redis/commands/generic/type -Use `HEXPIRETIME` to read the absolute expiration time of hash fields, as Unix timestamps in seconds. +Use `TYPE` to find out which data type is stored at a key. -The reply holds one value per requested field, in order: the timestamp when the field expires, `-1` when the field exists but has no expiration, and `-2` when the field or the key does not exist. Use [`HTTL`](/docs/redis/commands/hash/httl) when you want the remaining lifetime instead of the deadline, and [`HPEXPIRETIME`](/docs/redis/commands/hash/hpexpiretime) for millisecond precision. +The reply is one of `string`, `list`, `set`, `zset`, `hash`, or `stream`, and `none` when the key does not exist. It is the way to dispatch generic code over keys of mixed types, since applying a command to the wrong type fails with a `WRONGTYPE` error, and the way to inspect unfamiliar data before deciding how to read it. ## Syntax ```redis -HEXPIRETIME FIELDS [ ...] +TYPE ``` ## Arguments @@ -29189,11 +29132,6 @@ HEXPIRETIME FIELDS [ ...] | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | | `` | Yes | No | Redis key targeted by the command. | -| `FIELDS [ ...]` | Yes | No | Fields to target. Give the field count first, then that many field names. | - -## Important points - -* Negative integer replies are sentinel values, not durations or timestamps; see the response description for missing or persistent data. ## Response @@ -29201,8 +29139,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Array of expiration timestamps or negative integer status codes, one per field | -| RESP3 | Array of expiration timestamps or negative integer status codes, one per field | +| RESP2 | Simple string: the type name, or `none` if the key does not exist | +| RESP3 | Simple string: the type name, or `none` if the key does not exist | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -29217,7 +29155,7 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -HEXPIRETIME my-key FIELDS 1 field +TYPE my-key ``` @@ -29229,11 +29167,9 @@ import { Redis } from "@upstash/redis"; const redis = Redis.fromEnv(); -await redis.hset("my-key", "my-field", "my-value"); -await redis.hexpireat("my-key", "my-field", Math.floor(Date.now() / 1000) + 10); -const expireTime = await redis.hexpiretime("my-key", "my-field"); - -console.log(expireTime); // e.g., [1697059200] +await redis.set("key", "value"); +const t = await redis.type("key"); +console.log(t) // "string" ``` @@ -29244,7 +29180,7 @@ console.log(expireTime); // e.g., [1697059200] from upstash_redis import Redis redis = Redis.from_env() -result = redis.hexpiretime("my-key", "field") +result = redis.type("my-key") print(result) ``` @@ -29256,7 +29192,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.hexpiretime("my-key", "FIELDS", "1", "field"); +const result = await redis.type("my-key"); console.log(result); ``` @@ -29270,7 +29206,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.hExpireTime("my-key", "field"); +const result = await client.type("my-key"); console.log(result); ``` @@ -29283,7 +29219,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.hexpiretime("my-key", "field") +result = client.type("my-key") print(result) ``` @@ -29308,7 +29244,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.HExpireTime(context.Background(), "my-key", "field").Result() + result, err := client.Type(context.Background(), "my-key").Result() if err != nil { panic(err) } @@ -29322,10 +29258,11 @@ func main() { ```java import java.net.URI; + import redis.clients.jedis.Jedis; try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.hexpireTime("my-key", "field"); + Object result = jedis.type("my-key"); System.out.println(result); } ``` @@ -29342,7 +29279,7 @@ fn main() -> redis::RedisResult<()> { let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let result = connection.hexpire_time("my-key", &["field"])?; + let result = connection.key_type("my-key")?; println!("{result:?}"); Ok(()) } @@ -29352,25 +29289,24 @@ fn main() -> redis::RedisResult<()> { -# HGET -Source: https://upstash.com/docs/redis/commands/hash/hget +# UNLINK +Source: https://upstash.com/docs/redis/commands/generic/unlink -Use `HGET` to read the value of a single field of a hash. +Use `UNLINK` to delete keys without freeing their memory in the foreground. -The reply is null when either the field or the whole key is missing, so the two cases cannot be told apart from the reply alone; use [`HEXISTS`](/docs/redis/commands/hash/hexists) when that difference matters. To read several fields use [`HMGET`](/docs/redis/commands/hash/hmget) rather than repeated calls, and to read all of them use [`HGETALL`](/docs/redis/commands/hash/hgetall). +The keys are removed from the keyspace immediately, so from a client's point of view they are gone as soon as the command returns, but the memory of large values is reclaimed by a background thread. That makes it a safer alternative to [`DEL`](/docs/redis/commands/generic/del) for collections with many elements, where freeing memory synchronously can block the server for a noticeable time. The reply counts the keys that existed. ## Syntax ```redis -HGET +UNLINK [ ...] ``` ## Arguments | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | -| `` | Yes | No | Redis key targeted by the command. | -| `` | Yes | No | Hash field name. | +| `` | Yes | Yes | Redis key targeted by the command. | ## Response @@ -29378,8 +29314,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Bulk string or Null bulk string or null array | -| RESP3 | Bulk string or Null | +| RESP2 | Integer | +| RESP3 | Integer | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -29394,7 +29330,7 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -HGET my-key field +UNLINK my-key ``` @@ -29406,9 +29342,7 @@ import { Redis } from "@upstash/redis"; const redis = Redis.fromEnv(); -await redis.hset("key", {field: "value"}); -const field = await redis.hget("key", "field"); -console.log(field); // "value" +await redis.unlink("key1", "key2"); ``` @@ -29419,7 +29353,7 @@ console.log(field); // "value" from upstash_redis import Redis redis = Redis.from_env() -result = redis.hget("my-key", "field") +result = redis.unlink("my-key") print(result) ``` @@ -29431,7 +29365,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.hget("my-key", "field"); +const result = await redis.unlink("my-key"); console.log(result); ``` @@ -29445,7 +29379,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.hGet("my-key", "field"); +const result = await client.unlink("my-key"); console.log(result); ``` @@ -29458,7 +29392,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.hget("my-key", "field") +result = client.unlink("my-key") print(result) ``` @@ -29483,7 +29417,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.HGet(context.Background(), "my-key", "field").Result() + result, err := client.Unlink(context.Background(), "my-key").Result() if err != nil { panic(err) } @@ -29501,7 +29435,7 @@ import java.net.URI; import redis.clients.jedis.Jedis; try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.hget("my-key", "field"); + Object result = jedis.unlink("my-key"); System.out.println(result); } ``` @@ -29518,7 +29452,7 @@ fn main() -> redis::RedisResult<()> { let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let result = connection.hget("my-key", "field")?; + let result = connection.unlink("my-key")?; println!("{result:?}"); Ok(()) } @@ -29528,30 +29462,34 @@ fn main() -> redis::RedisResult<()> { -# HGETALL -Source: https://upstash.com/docs/redis/commands/hash/hgetall +# WAIT +Source: https://upstash.com/docs/redis/commands/generic/wait -Use `HGETALL` to read every field and value of a hash in one call. +Use `WAIT` to block until preceding writes have been acknowledged by a number of replicas, or until a timeout expires. -The reply pairs each field with its value. RESP2 flattens it into a single alternating array while RESP3 returns a map, and client libraries normally decode either form into a native dictionary. A missing key returns an empty result rather than an error. +The reply is the number of replicas that acknowledged, which can be lower than `` when the timeout is reached, so callers must check it instead of assuming success. A timeout of `0` waits indefinitely. -The whole hash is transferred, so on hashes with many fields prefer [`HMGET`](/docs/redis/commands/hash/hmget) when you know which fields you need, or [`HSCAN`](/docs/redis/commands/hash/hscan) to walk the hash in batches. +`WAIT` raises the durability you can observe for a write, which is useful right before an action that must not be undone by a failover, such as replying to a payment webhook. It does not make Redis strongly consistent: an acknowledged write can still be lost if the primary and the acknowledging replicas fail together. + +On Upstash the command waits for the writes enqueued before it began, including writes made by other connections, which is broader than the per-connection wording used by some Redis clients. ## Syntax ```redis -HGETALL +WAIT ``` ## Arguments | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | -| `` | Yes | No | Redis key targeted by the command. | +| `numreplicas` | Yes | No | Non-negative number of replicas that should acknowledge prior writes on this connection. | +| `timeout` | Yes | No | Maximum wait in milliseconds; `0` means no timeout. | ## Important points -* Pair-based results may be flattened into one alternating array in RESP2 while RESP3 preserves nested pairs or a map. +* This deployment waits for writes enqueued before `WAIT` begins, including writes from other connections. That is broader than the per-connection wording used by Redis clients. +* The reply can be lower than `numreplicas` when the timeout expires. This improves observed replication durability but does not make Redis a strongly consistent store. ## Response @@ -29559,8 +29497,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Flat array of alternating keys and values | -| RESP3 | Map | +| RESP2 | Integer | +| RESP3 | Integer | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -29575,37 +29513,24 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -HGETALL my-key +WAIT 1 1000 ``` -```ts -import { Redis } from "@upstash/redis"; - -const redis = Redis.fromEnv(); - -await redis.hset("key", { - field1: "value1", - field2: "value2", - }); -const hash = await redis.hgetall("key"); -console.log(hash); // { field1: "value1", field2: "value2" } -``` + + This command is not supported yet in `@upstash/redis`. + -```python -from upstash_redis import Redis - -redis = Redis.from_env() -result = redis.hgetall("my-key") -print(result) -``` + + This command is not supported yet in `upstash_redis`. + @@ -29615,7 +29540,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.hgetall("my-key"); +const result = await redis.wait("1", "1000"); console.log(result); ``` @@ -29629,7 +29554,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.hGetAll("my-key"); +const result = await client.wait(1, 1000); console.log(result); ``` @@ -29642,7 +29567,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.hgetall("my-key") +result = client.wait("1", "1000") print(result) ``` @@ -29657,6 +29582,7 @@ import ( "context" "fmt" "os" + "time" "github.com/redis/go-redis/v9" ) @@ -29667,7 +29593,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.HGetAll(context.Background(), "my-key").Result() + result, err := client.Wait(context.Background(), 1, time.Second).Result() if err != nil { panic(err) } @@ -29685,7 +29611,7 @@ import java.net.URI; import redis.clients.jedis.Jedis; try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.hgetAll("my-key"); + Object result = jedis.waitReplicas(1, 1000); System.out.println(result); } ``` @@ -29695,14 +29621,15 @@ try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { ```rust -use redis::TypedCommands; - fn main() -> redis::RedisResult<()> { let url = std::env::var("REDIS_URL").expect("REDIS_URL is not set"); let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let result = connection.hgetall("my-key")?; + let mut command = redis::cmd("WAIT"); + command.arg("1"); + command.arg("1000"); + let result: redis::Value = command.query(&mut connection)?; println!("{result:?}"); Ok(()) } @@ -29712,27 +29639,33 @@ fn main() -> redis::RedisResult<()> { -# HGETDEL -Source: https://upstash.com/docs/redis/commands/hash/hgetdel +# WAITAOF +Source: https://upstash.com/docs/redis/commands/generic/waitaof -Use `HGETDEL` to read hash fields and delete them in the same atomic step. +Use `WAITAOF` to block until preceding writes have been persisted to the append-only file locally and on replicas. -The reply holds the previous value of each requested field, in the order requested, with null for fields that were not present. Reading and removing together removes the race that an [`HGET`](/docs/redis/commands/hash/hget) followed by an [`HDEL`](/docs/redis/commands/hash/hdel) would leave open, which makes the command a good fit for one-shot values such as one-time codes, claim tickets, or queued items keyed by name: exactly one caller gets the value. +`` is how many local acknowledgements to wait for and `` how many replicas must have persisted the writes. The two-element reply gives the local count first and the replica count second, and either can come back lower than requested when the timeout expires, so both need checking. A timeout of `0` waits indefinitely. -`FIELDS ` introduces the field list and the count must match. The key is deleted when its last field is removed. +Where [`WAIT`](/docs/redis/commands/generic/wait) confirms only that replicas received a write, `WAITAOF` confirms that it reached persistent storage, which is the stronger guarantee to ask for before acknowledging work that must survive a restart. On Upstash it waits for the writes enqueued before it began, including writes made by other connections. ## Syntax ```redis -HGETDEL FIELDS [ ...] +WAITAOF ``` ## Arguments | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | -| `` | Yes | No | Redis key targeted by the command. | -| `FIELDS [ ...]` | Yes | No | Fields to target. Give the field count first, then that many field names. | +| `numlocal` | Yes | No | Whether to wait for local persistence: `0` or `1`. | +| `numreplicas` | Yes | No | Non-negative number of replicas whose append-only files should include prior writes. | +| `timeout` | Yes | No | Maximum wait in milliseconds; `0` means no timeout. | + +## Important points + +* This deployment waits for writes enqueued before `WAITAOF` begins, including writes from other connections. +* The two-element reply contains the local persistence acknowledgement first and the replica persistence count second. ## Response @@ -29740,8 +29673,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Array of bulk-string values or null values, one per field | -| RESP3 | Array of bulk-string values or null values, one per field | +| RESP2 | Two-element array of integers: local and replica acknowledgments | +| RESP3 | Two-element array of integers: local and replica acknowledgments | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -29756,41 +29689,24 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -HGETDEL my-key FIELDS 1 field +WAITAOF 1 1 1000 ``` -```ts -import { Redis } from "@upstash/redis"; - -const redis = Redis.fromEnv(); - -// Set some hash fields -await redis.hset("user:123", { name: "John", age: "30", email: "john@example.com" }); - -// Get and delete specific fields -const result = await redis.hgetdel("user:123", "name", "email"); -console.log(result); // { name: "John", email: "john@example.com" } - -// Verify fields were deleted -const name = await redis.hget("user:123", "name"); -console.log(name); // null -``` + + This command is not supported yet in `@upstash/redis`. + -```python -from upstash_redis import Redis - -redis = Redis.from_env() -result = redis.hgetdel("my-key", "field") -print(result) -``` + + This command is not supported yet in `upstash_redis`. + @@ -29800,7 +29716,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.hgetdel("my-key", "FIELDS", "1", "field"); +const result = await redis.call("WAITAOF", "1", "1", "1000"); console.log(result); ``` @@ -29814,7 +29730,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.hGetDel("my-key", "field"); +const result = await client.sendCommand(["WAITAOF", "1", "1", "1000"]); console.log(result); ``` @@ -29827,7 +29743,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.hgetdel("my-key", "field") +result = client.waitaof("1", "1", "1000") print(result) ``` @@ -29842,6 +29758,7 @@ import ( "context" "fmt" "os" + "time" "github.com/redis/go-redis/v9" ) @@ -29852,7 +29769,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.HGetDel(context.Background(), "my-key", "1", "field").Result() + result, err := client.WaitAOF(context.Background(), 1, 1, time.Second).Result() if err != nil { panic(err) } @@ -29870,7 +29787,7 @@ import java.net.URI; import redis.clients.jedis.Jedis; try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.hgetdel("my-key", "1", "field"); + Object result = jedis.waitAOF(1, 1, 1000); System.out.println(result); } ``` @@ -29880,14 +29797,16 @@ try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { ```rust -use redis::TypedCommands; - fn main() -> redis::RedisResult<()> { let url = std::env::var("REDIS_URL").expect("REDIS_URL is not set"); let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let result = connection.hget_del("my-key", &["field"])?; + let mut command = redis::cmd("WAITAOF"); + command.arg("1"); + command.arg("1"); + command.arg("1000"); + let result: redis::Value = command.query(&mut connection)?; println!("{result:?}"); Ok(()) } @@ -29897,22 +29816,23 @@ fn main() -> redis::RedisResult<()> { -# HGETEX -Source: https://upstash.com/docs/redis/commands/hash/hgetex +# GEOADD +Source: https://upstash.com/docs/redis/commands/geo/geoadd -Use `HGETEX` to read hash fields and change their expiration in the same call. +Use `GEOADD` to add longitude, latitude, and member triples to a geospatial index. -Without an expiration option it simply returns the values, like [`HMGET`](/docs/redis/commands/hash/hmget). `EX`, `PX`, `EXAT`, and `PXAT` give every requested field a new lifetime or deadline, and `PERSIST` removes the expiration so the fields stop expiring altogether. +Each position is encoded into a 52-bit geohash and stored as the score of the member in a sorted set, so a geospatial key is an ordinary sorted set and commands such as [`ZREM`](/docs/redis/commands/sorted-set/zrem), [`ZCARD`](/docs/redis/commands/sorted-set/zcard), and [`ZSCAN`](/docs/redis/commands/sorted-set/zscan) work on it. Longitude must be between -180 and 180 and latitude between -85.05112878 and 85.05112878; anything outside those bounds returns an error. Adding a member that is already present moves it to the new position, and the encoding means coordinates read back with [`GEOPOS`](/docs/redis/commands/geo/geopos) are very close to, but not exactly, the ones you stored. -Doing both in one command is what makes sliding expirations possible per field: reading a session attribute can extend it, with no window in which another client sees the field without its refreshed lifetime. `FIELDS ` introduces the field list and the count must match. +`NX` only adds members that are not there yet, `XX` only updates members that already exist, and `CH` makes the reply count every member that changed rather than only the ones that were added. ## Syntax ```redis -HGETEX - [EX | PX | EXAT | - PXAT | PERSIST] - FIELDS [ ...] +GEOADD + [NX | XX] + [CH] + + [ ...] ``` ## Arguments @@ -29920,8 +29840,13 @@ HGETEX | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | | `` | Yes | No | Redis key targeted by the command. | -| `(EX \| PX \| EXAT \| PXAT \| PERSIST)` | No | No | Choose one form: `EX` (set a lifetime in seconds); `PX` (set a lifetime in milliseconds); `EXAT` (expire at a Unix timestamp in seconds); `PXAT` (expire at a Unix timestamp in milliseconds); `PERSIST` (remove the expiration). Left unchanged when omitted. | -| `FIELDS [ ...]` | Yes | No | Fields to target. Give the field count first, then that many field names. | +| `(NX \| XX)` | No | No | Choose one form: `NX` (only add new members, never update an existing one); `XX` (only update members that already exist). | +| `CH` | No | No | Count changed members rather than only new members. | +| ` ` | Yes | Yes | Longitude, latitude, and the member they belong to. Repeat to add several members. | + +## Important points + +* `NX` and `XX` are mutually exclusive. ## Response @@ -29929,8 +29854,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Array of bulk-string values or null values, one per field | -| RESP3 | Array of bulk-string values or null values, one per field | +| RESP2 | Integer | +| RESP3 | Integer | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -29945,7 +29870,7 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -HGETEX my-key FIELDS 1 field +GEOADD my-key 29.0 41.0 member ``` @@ -29956,12 +29881,12 @@ HGETEX my-key FIELDS 1 field import { Redis } from "@upstash/redis"; const redis = Redis.fromEnv(); - -await redis.hset("user:123", { name: "John", email: "john@example.com" }); - -// Get fields and set expiration to 60 seconds -const result = await redis.hgetex("user:123", { ex: 60 }, "name", "email"); -console.log(result); // { name: "John", email: "john@example.com" } +const result = await redis.geoadd("my-key", { + longitude: 29.0, + latitude: 41.0, + member: "member", +}); +console.log(result); ``` @@ -29972,7 +29897,7 @@ console.log(result); // { name: "John", email: "john@example.com" } from upstash_redis import Redis redis = Redis.from_env() -result = redis.hgetex("my-key", "field") +result = redis.geoadd("my-key", (29.0, 41.0, "member")) print(result) ``` @@ -29984,7 +29909,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.hgetex("my-key", "FIELDS", "1", "field"); +const result = await redis.geoadd("my-key", "29.0", "41.0", "member"); console.log(result); ``` @@ -29998,7 +29923,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.hGetEx("my-key", "field"); +const result = await client.geoAdd("my-key", { longitude: 29, latitude: 41, member: "member" }); console.log(result); ``` @@ -30011,7 +29936,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.hgetex("my-key", "field") +result = client.geoadd("my-key", (29.0, 41.0, "member")) print(result) ``` @@ -30036,7 +29961,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.HGetEX(context.Background(), "my-key", "1", "field").Result() + result, err := client.GeoAdd(context.Background(), "my-key", &redis.GeoLocation{Longitude: 29.0, Latitude: 41.0, Name: "member"}).Result() if err != nil { panic(err) } @@ -30054,7 +29979,7 @@ import java.net.URI; import redis.clients.jedis.Jedis; try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.hgetex("my-key", redis.clients.jedis.params.HGetExParams.hGetExParams(), "field"); + Object result = jedis.geoadd("my-key", 29.0, 41.0, "member"); System.out.println(result); } ``` @@ -30071,7 +29996,7 @@ fn main() -> redis::RedisResult<()> { let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let result = connection.hget_ex("my-key", &["field"], redis::Expiry::PERSIST)?; + let result = connection.geo_add("my-key", (29.0, 41.0, "member"))?; println!("{result:?}"); Ok(()) } @@ -30081,19 +30006,17 @@ fn main() -> redis::RedisResult<()> { -# HINCRBY -Source: https://upstash.com/docs/redis/commands/hash/hincrby - -Use `HINCRBY` to add an integer to the number stored in a hash field and get the result. +# GEODIST +Source: https://upstash.com/docs/redis/commands/geo/geodist -A missing field, or a missing key, is treated as `0`, so the first call creates the hash and the field. The increment may be negative to count down. The stored value must be the string form of a 64-bit signed integer; anything else returns an error, as does an operation that would overflow the range. +Use `GEODIST` to get the distance between two members of a geospatial index. -Reading, adding, and writing back happen as one atomic step, so concurrent callers each receive a distinct result and no update is lost. That makes hashes a compact way to keep many related counters, such as per-status counts for one entity, under a single key. +The unit defaults to meters and can be set to `m`, `km`, `ft`, or `mi`. The distance is a great-circle distance computed from the stored positions assuming the Earth is a sphere, so it carries the small error of the geohash encoding and, of course, says nothing about the distance actually travelled on roads. If either member is missing from the index the reply is null. ## Syntax ```redis -HINCRBY +GEODIST [m | km | ft | mi] ``` ## Arguments @@ -30101,8 +30024,13 @@ HINCRBY | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | | `` | Yes | No | Redis key targeted by the command. | -| `` | Yes | No | Hash field name. | -| `` | Yes | No | Integer amount to add to the field. | +| `` | Yes | No | First member. | +| `` | Yes | No | Second member. | +| `(m \| km \| ft \| mi)` | No | No | Distance unit: `m` (meters), `km` (kilometers), `ft` (feet), or `mi` (miles). Defaults to `m` when omitted. | + +## Important points + +* The distance is always returned as a bulk string, in both RESP2 and RESP3. Client libraries commonly decode it to a language number. ## Response @@ -30110,8 +30038,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Integer | -| RESP3 | Integer | +| RESP2 | Null bulk string or null array or Bulk string | +| RESP3 | Null or Bulk string | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -30126,7 +30054,7 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -HINCRBY my-key field 1 +GEODIST my-key member1 member2 ``` @@ -30137,12 +30065,8 @@ HINCRBY my-key field 1 import { Redis } from "@upstash/redis"; const redis = Redis.fromEnv(); - -await redis.hset("key", { - field: 20, - }); -const after = await redis.hincrby("key", "field", 2); -console.log(after); // 22 +const result = await redis.geodist("my-key", "member1", "member2"); +console.log(result); ``` @@ -30153,7 +30077,7 @@ console.log(after); // 22 from upstash_redis import Redis redis = Redis.from_env() -result = redis.hincrby("my-key", "field", 1) +result = redis.geodist("my-key", "member1", "member2") print(result) ``` @@ -30165,7 +30089,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.hincrby("my-key", "field", "1"); +const result = await redis.geodist("my-key", "member1", "member2"); console.log(result); ``` @@ -30179,7 +30103,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.hIncrBy("my-key", "field", 1); +const result = await client.geoDist("my-key", "member1", "member2"); console.log(result); ``` @@ -30192,7 +30116,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.hincrby("my-key", "field", 1) +result = client.geodist("my-key", "member1", "member2") print(result) ``` @@ -30217,7 +30141,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.HIncrBy(context.Background(), "my-key", "field", 1).Result() + result, err := client.GeoDist(context.Background(), "my-key", "member1", "member2", "m").Result() if err != nil { panic(err) } @@ -30235,7 +30159,7 @@ import java.net.URI; import redis.clients.jedis.Jedis; try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.hincrBy("my-key", "field", 1); + Object result = jedis.geodist("my-key", "member1", "member2"); System.out.println(result); } ``` @@ -30245,6 +30169,7 @@ try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { ```rust +use redis::geo::Unit; use redis::TypedCommands; fn main() -> redis::RedisResult<()> { @@ -30252,7 +30177,7 @@ fn main() -> redis::RedisResult<()> { let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let result = connection.hincr("my-key", "field", 1)?; + let result = connection.geo_dist("my-key", "member1", "member2", Unit::Meters)?; println!("{result:?}"); Ok(()) } @@ -30262,19 +30187,17 @@ fn main() -> redis::RedisResult<()> { -# HINCRBYFLOAT -Source: https://upstash.com/docs/redis/commands/hash/hincrbyfloat - -Use `HINCRBYFLOAT` to add a floating point number to the value of a hash field and get the result. +# GEOHASH +Source: https://upstash.com/docs/redis/commands/geo/geohash -The stored value and the increment are parsed as double precision floats, and a missing field or key counts as `0`. The increment may be negative, and there is no separate decrement command. A value that is not a valid number returns an error. +Use `GEOHASH` to get standard Geohash strings for members of a geospatial index. -The reply is the new value as a string, which client libraries usually decode into a native number. Note that the result is stored in the same textual form, so repeated increments of values that cannot be represented exactly in binary floating point accumulate the usual rounding error; keep money and similar quantities in integer units and use [`HINCRBY`](/docs/redis/commands/hash/hincrby). +The reply holds one 11-character string per requested member, in the order requested, with null for members that are not in the index. These are the strings used by geohash.org and by other geospatial tools, which makes the command the right way to export positions or to share them with systems that speak Geohash. They are derived from the stored 52-bit position, so they reflect the same rounding as [`GEOPOS`](/docs/redis/commands/geo/geopos). ## Syntax ```redis -HINCRBYFLOAT +GEOHASH [ [ ...]] ``` ## Arguments @@ -30282,12 +30205,7 @@ HINCRBYFLOAT | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | | `` | Yes | No | Redis key targeted by the command. | -| `` | Yes | No | Hash field name. | -| `` | Yes | No | Amount to add to the field; may be a floating-point number. | - -## Important points - -* The value is always returned as a bulk string, in both RESP2 and RESP3. Client libraries commonly decode it to a language number. +| `` | No | Yes | Member name. | ## Response @@ -30295,8 +30213,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Bulk string | -| RESP3 | Bulk string | +| RESP2 | Array of bulk-string geohashes or null values | +| RESP3 | Array of bulk-string geohashes or null values | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -30311,7 +30229,7 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -HINCRBYFLOAT my-key field 1.5 +GEOHASH my-key member ``` @@ -30322,12 +30240,8 @@ HINCRBYFLOAT my-key field 1.5 import { Redis } from "@upstash/redis"; const redis = Redis.fromEnv(); - -await redis.hset("key", { - field: 20, - }); -const after = await redis.hincrby("key", "field", 2.5); -console.log(after); // 22.5 +const result = await redis.geohash("my-key", "member"); +console.log(result); ``` @@ -30338,7 +30252,7 @@ console.log(after); // 22.5 from upstash_redis import Redis redis = Redis.from_env() -result = redis.hincrbyfloat("my-key", "field", 1.5) +result = redis.geohash("my-key", "member") print(result) ``` @@ -30350,7 +30264,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.hincrbyfloat("my-key", "field", "1.5"); +const result = await redis.geohash("my-key", "member"); console.log(result); ``` @@ -30364,7 +30278,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.hIncrByFloat("my-key", "field", 1.5); +const result = await client.geoHash("my-key", "member"); console.log(result); ``` @@ -30377,7 +30291,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.hincrbyfloat("my-key", "field", 1.5) +result = client.geohash("my-key", "member") print(result) ``` @@ -30402,7 +30316,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.HIncrByFloat(context.Background(), "my-key", "field", 1.5).Result() + result, err := client.GeoHash(context.Background(), "my-key", "member").Result() if err != nil { panic(err) } @@ -30420,7 +30334,7 @@ import java.net.URI; import redis.clients.jedis.Jedis; try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.hincrByFloat("my-key", "field", 1.5); + Object result = jedis.geohash("my-key", "member"); System.out.println(result); } ``` @@ -30437,7 +30351,7 @@ fn main() -> redis::RedisResult<()> { let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let result = connection.hincr("my-key", "field", 1.5)?; + let result = connection.geo_hash("my-key", "member")?; println!("{result:?}"); Ok(()) } @@ -30447,17 +30361,17 @@ fn main() -> redis::RedisResult<()> { -# HKEYS -Source: https://upstash.com/docs/redis/commands/hash/hkeys +# GEOPOS +Source: https://upstash.com/docs/redis/commands/geo/geopos -Use `HKEYS` to get the names of all the fields in a hash, without their values. +Use `GEOPOS` to get the longitude and latitude of members of a geospatial index. -A missing key returns an empty list. The whole field list is built and transferred in one reply, so on large hashes prefer [`HSCAN`](/docs/redis/commands/hash/hscan) with `NOVALUES`, which walks the field names in batches instead. +The reply holds one entry per requested member, in the order requested, each an array with longitude first and latitude second, and null for members that are not in the index. Positions are decoded from the stored geohash, so they are very close to but not bit-for-bit identical with the coordinates originally passed to [`GEOADD`](/docs/redis/commands/geo/geoadd). ## Syntax ```redis -HKEYS +GEOPOS [ [ ...]] ``` ## Arguments @@ -30465,6 +30379,7 @@ HKEYS | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | | `` | Yes | No | Redis key targeted by the command. | +| `` | No | Yes | Member name. | ## Response @@ -30472,8 +30387,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Array of bulk-string fields | -| RESP3 | Array of bulk-string fields | +| RESP2 | Array of coordinate-pair arrays or null values | +| RESP3 | Array of coordinate-pair arrays or null values | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -30488,7 +30403,7 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -HKEYS my-key +GEOPOS my-key member ``` @@ -30499,13 +30414,8 @@ HKEYS my-key import { Redis } from "@upstash/redis"; const redis = Redis.fromEnv(); - -await redis.hset("key", { - id: 1, - username: "chronark", - }); -const fields = await redis.hkeys("key"); -console.log(fields); // ["id", "username"] +const result = await redis.geopos("my-key", "member"); +console.log(result); ``` @@ -30516,7 +30426,7 @@ console.log(fields); // ["id", "username"] from upstash_redis import Redis redis = Redis.from_env() -result = redis.hkeys("my-key") +result = redis.geopos("my-key", "member") print(result) ``` @@ -30528,7 +30438,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.hkeys("my-key"); +const result = await redis.geopos("my-key", "member"); console.log(result); ``` @@ -30542,7 +30452,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.hKeys("my-key"); +const result = await client.geoPos("my-key", "member"); console.log(result); ``` @@ -30555,7 +30465,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.hkeys("my-key") +result = client.geopos("my-key", "member") print(result) ``` @@ -30580,7 +30490,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.HKeys(context.Background(), "my-key").Result() + result, err := client.GeoPos(context.Background(), "my-key", "member").Result() if err != nil { panic(err) } @@ -30598,7 +30508,7 @@ import java.net.URI; import redis.clients.jedis.Jedis; try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.hkeys("my-key"); + Object result = jedis.geopos("my-key", "member"); System.out.println(result); } ``` @@ -30615,7 +30525,7 @@ fn main() -> redis::RedisResult<()> { let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let result = connection.hkeys("my-key")?; + let result = connection.geo_pos("my-key", "member")?; println!("{result:?}"); Ok(()) } @@ -30625,17 +30535,31 @@ fn main() -> redis::RedisResult<()> { -# HLEN -Source: https://upstash.com/docs/redis/commands/hash/hlen +# GEORADIUS +Source: https://upstash.com/docs/redis/commands/geo/georadius -Use `HLEN` to get the number of fields in a hash. + + Prefer [`GEOSEARCH`](/docs/redis/commands/geo/geosearch) with `FROMLONLAT` and `BYRADIUS` in new code: `GEOSEARCH FROMLONLAT BYRADIUS (m | km | ft | mi)`. In place of `STORE` and `STOREDIST`, use [`GEOSEARCHSTORE`](/docs/redis/commands/geo/geosearchstore) with the same query. + -The reply is `0` when the key does not exist. The count is kept by Redis rather than computed, so it is cheap whatever the size of the hash, which makes it the right way to check how big a hash has grown before deciding to read or iterate it. +Use `GEORADIUS` to find the members of a geospatial index that lie within a given radius of a longitude and latitude point. + +By default only member names are returned. `WITHDIST` adds each member's distance from the center in the unit of the query, `WITHCOORD` adds its coordinates, and `WITHHASH` adds its raw 52-bit geohash score. `COUNT` caps the number of results and, combined with `ANY`, lets the server stop as soon as it has enough matches instead of examining the whole area, which is faster but returns an arbitrary subset rather than the nearest ones. `ASC` and `DESC` sort the results by distance from the center. + +`STORE` writes the matching members into a sorted set scored by geohash, so the result stays usable as a geospatial index, while `STOREDIST` scores them by their distance from the center, which makes the result easy to page through by proximity. + +[`GEOSEARCH`](/docs/redis/commands/geo/geosearch) and [`GEOSEARCHSTORE`](/docs/redis/commands/geo/geosearchstore) do the same work and additionally support rectangular areas. Use [`GEORADIUS_RO`](/docs/redis/commands/geo/georadius-ro) if you want a form that cannot write. ## Syntax ```redis -HLEN +GEORADIUS (m | km | ft | mi) + [WITHCOORD] + [WITHDIST] + [WITHHASH] + [COUNT [ANY]] + [ASC | DESC] + [STORE | STOREDIST ] ``` ## Arguments @@ -30643,6 +30567,16 @@ HLEN | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | | `` | Yes | No | Redis key targeted by the command. | +| `` | Yes | No | Longitude in degrees, from -180 to 180. | +| `` | Yes | No | Latitude in degrees, from -85.05112878 to 85.05112878. | +| `` | Yes | No | Search radius, in the unit given after it. | +| `(m \| km \| ft \| mi)` | Yes | No | Distance unit: `m` (meters), `km` (kilometers), `ft` (feet), or `mi` (miles). | +| `WITHCOORD` | No | No | Also return the longitude and latitude of each match. | +| `WITHDIST` | No | No | Also return the distance from the center, in the requested unit. | +| `WITHHASH` | No | No | Also return the raw 52-bit geohash score of each match. | +| `COUNT [ANY]` | No | No | Return at most `` matches. `ANY` returns as soon as enough matches are found, instead of sorting every match first. | +| `(ASC \| DESC)` | No | No | Sort the matches by distance from the center: `ASC` (nearest first) or `DESC` (farthest first). Unsorted when omitted. | +| `(STORE \| STOREDIST )` | No | No | Store the matches in a sorted set instead of returning them: `STORE` scores them by geohash, so the destination stays a geospatial index, and `STOREDIST` scores them by their distance from the center. | ## Response @@ -30650,8 +30584,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Integer | -| RESP3 | Integer | +| RESP2 | Array of bulk-string members or member-detail arrays, or Integer when storing | +| RESP3 | Array of bulk-string members or member-detail arrays, or Integer when storing | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -30666,25 +30600,16 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -HLEN my-key +GEORADIUS my-key 29.0 41.0 1.5 m ``` -```ts -import { Redis } from "@upstash/redis"; - -const redis = Redis.fromEnv(); - -await redis.hset("key", { - id: 1, - username: "chronark", - }); -const fields = await redis.hlen("key"); -console.log(fields); // 2 -``` + + This command is not supported yet in `@upstash/redis`. + @@ -30694,7 +30619,7 @@ console.log(fields); // 2 from upstash_redis import Redis redis = Redis.from_env() -result = redis.hlen("my-key") +result = redis.georadius("my-key", 29.0, 41.0, 1.5, "M") print(result) ``` @@ -30706,7 +30631,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.hlen("my-key"); +const result = await redis.georadius("my-key", "29.0", "41.0", "1.5", "m"); console.log(result); ``` @@ -30720,7 +30645,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.hLen("my-key"); +const result = await client.geoRadius("my-key", { longitude: 29, latitude: 41 }, 1.5, "m"); console.log(result); ``` @@ -30733,7 +30658,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.hlen("my-key") +result = client.georadius("my-key", 29.0, 41.0, 1.5, "m") print(result) ``` @@ -30758,7 +30683,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.HLen(context.Background(), "my-key").Result() + result, err := client.GeoRadius(context.Background(), "my-key", 29.0, 41.0, &redis.GeoRadiusQuery{Radius: 1.5, Unit: "m"}).Result() if err != nil { panic(err) } @@ -30776,7 +30701,7 @@ import java.net.URI; import redis.clients.jedis.Jedis; try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.hlen("my-key"); + Object result = jedis.georadius("my-key", 29.0, 41.0, 1.5, redis.clients.jedis.args.GeoUnit.M); System.out.println(result); } ``` @@ -30786,6 +30711,7 @@ try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { ```rust +use redis::geo::{RadiusOptions, Unit}; use redis::TypedCommands; fn main() -> redis::RedisResult<()> { @@ -30793,7 +30719,14 @@ fn main() -> redis::RedisResult<()> { let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let result = connection.hlen("my-key")?; + let result = connection.geo_radius( + "my-key", + 29.0, + 41.0, + 1.5, + Unit::Meters, + RadiusOptions::default(), + )?; println!("{result:?}"); Ok(()) } @@ -30803,19 +30736,28 @@ fn main() -> redis::RedisResult<()> { -# HMGET -Source: https://upstash.com/docs/redis/commands/hash/hmget +# GEORADIUS_RO +Source: https://upstash.com/docs/redis/commands/geo/georadius-ro -Use `HMGET` to read several fields of a hash in one call. + + Prefer [`GEOSEARCH`](/docs/redis/commands/geo/geosearch) with `FROMLONLAT` and `BYRADIUS` in new code: `GEOSEARCH FROMLONLAT BYRADIUS (m | km | ft | mi)`. + -The reply holds one entry per requested field, in the order requested, with null for fields that do not exist. Asking for fields of a key that does not exist returns a list of nulls rather than an error, so the shape of the reply is always predictable and can be zipped back onto your list of field names. +Use `GEORADIUS_RO` to find members within a radius of a point. It is the read-only form of [`GEORADIUS`](/docs/redis/commands/geo/georadius). -It saves the round trips of repeated [`HGET`](/docs/redis/commands/hash/hget) calls and transfers far less than [`HGETALL`](/docs/redis/commands/hash/hgetall) when you only need a few fields of a large hash. +It accepts the same query and the same `WITHCOORD`, `WITHDIST`, `WITHHASH`, `COUNT`, and sorting options, but it has no `STORE` or `STOREDIST` clause, so the server knows the call cannot write and can serve it on replicas and from read-only scripts. + +[`GEOSEARCH`](/docs/redis/commands/geo/geosearch) is read-only as well and also supports rectangular areas. ## Syntax ```redis -HMGET [ ...] +GEORADIUS_RO (m | km | ft | mi) + [WITHCOORD] + [WITHDIST] + [WITHHASH] + [COUNT [ANY]] + [ASC | DESC] ``` ## Arguments @@ -30823,7 +30765,15 @@ HMGET [ ...] | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | | `` | Yes | No | Redis key targeted by the command. | -| `` | Yes | Yes | Hash field name. | +| `` | Yes | No | Longitude in degrees, from -180 to 180. | +| `` | Yes | No | Latitude in degrees, from -85.05112878 to 85.05112878. | +| `` | Yes | No | Search radius, in the unit given after it. | +| `(m \| km \| ft \| mi)` | Yes | No | Distance unit: `m` (meters), `km` (kilometers), `ft` (feet), or `mi` (miles). | +| `WITHCOORD` | No | No | Also return the longitude and latitude of each match. | +| `WITHDIST` | No | No | Also return the distance from the center, in the requested unit. | +| `WITHHASH` | No | No | Also return the raw 52-bit geohash score of each match. | +| `COUNT [ANY]` | No | No | Return at most `` matches. `ANY` returns as soon as enough matches are found, instead of sorting every match first. | +| `(ASC \| DESC)` | No | No | Sort the matches by distance from the center: `ASC` (nearest first) or `DESC` (farthest first). Unsorted when omitted. | ## Response @@ -30831,8 +30781,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Array of bulk-string values or null values, one per field | -| RESP3 | Array of bulk-string values or null values, one per field | +| RESP2 | Array of bulk-string members or member-detail arrays | +| RESP3 | Array of bulk-string members or member-detail arrays | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -30847,26 +30797,16 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -HMGET my-key field +GEORADIUS_RO my-key 29.0 41.0 1.5 m ``` -```ts -import { Redis } from "@upstash/redis"; - -const redis = Redis.fromEnv(); - -await redis.hset("key", { - id: 1, - username: "chronark", - name: "andreas" - }); -const fields = await redis.hmget("key", "username", "name"); -console.log(fields); // { username: "chronark", name: "andreas" } -``` + + This command is not supported yet in `@upstash/redis`. + @@ -30876,7 +30816,7 @@ console.log(fields); // { username: "chronark", name: "andreas" } from upstash_redis import Redis redis = Redis.from_env() -result = redis.hmget("my-key", "field") +result = redis.georadius_ro("my-key", 29.0, 41.0, 1.5, "M") print(result) ``` @@ -30888,7 +30828,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.hmget("my-key", "field"); +const result = await redis.georadius_ro("my-key", "29.0", "41.0", "1.5", "m"); console.log(result); ``` @@ -30902,7 +30842,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.hmGet("my-key", "field"); +const result = await client.geoRadiusRo("my-key", { longitude: 29, latitude: 41 }, 1.5, "m"); console.log(result); ``` @@ -30915,7 +30855,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.hmget("my-key", ["field"]) +result = client.execute_command("GEORADIUS_RO", "my-key", "29.0", "41.0", "1.5", "m") print(result) ``` @@ -30940,7 +30880,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.HMGet(context.Background(), "my-key", "field").Result() + result, err := client.GeoRadius(context.Background(), "my-key", 29.0, 41.0, &redis.GeoRadiusQuery{Radius: 1.5, Unit: "m"}).Result() if err != nil { panic(err) } @@ -30958,7 +30898,7 @@ import java.net.URI; import redis.clients.jedis.Jedis; try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.hmget("my-key", "field"); + Object result = jedis.georadiusReadonly("my-key", 29.0, 41.0, 1.5, redis.clients.jedis.args.GeoUnit.M); System.out.println(result); } ``` @@ -30968,14 +30908,18 @@ try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { ```rust -use redis::TypedCommands; - fn main() -> redis::RedisResult<()> { let url = std::env::var("REDIS_URL").expect("REDIS_URL is not set"); let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let result = connection.hmget("my-key", &["field"])?; + let mut command = redis::cmd("GEORADIUS_RO"); + command.arg("my-key"); + command.arg("29.0"); + command.arg("41.0"); + command.arg("1.5"); + command.arg("m"); + let result: redis::Value = command.query(&mut connection)?; println!("{result:?}"); Ok(()) } @@ -30985,21 +30929,29 @@ fn main() -> redis::RedisResult<()> { -# HMSET -Source: https://upstash.com/docs/redis/commands/hash/hmset +# GEORADIUSBYMEMBER +Source: https://upstash.com/docs/redis/commands/geo/georadiusbymember - Prefer [`HSET`](/docs/redis/commands/hash/hset) with multiple field-value pairs in new code: `HSET [ ...]`. + Prefer [`GEOSEARCH`](/docs/redis/commands/geo/geosearch) with `FROMMEMBER` and `BYRADIUS` in new code: `GEOSEARCH FROMMEMBER BYRADIUS (m | km | ft | mi)`. In place of `STORE` and `STOREDIST`, use [`GEOSEARCHSTORE`](/docs/redis/commands/geo/geosearchstore) with the same query. -Use `HMSET` to set several field and value pairs of a hash in one call, creating the key if it does not exist. +Use `GEORADIUSBYMEMBER` to find the members of a geospatial index that lie within a given radius of another member. -Existing fields are overwritten and the reply is always `OK`, so it says nothing about what changed. [`HSET`](/docs/redis/commands/hash/hset) accepts multiple pairs as well and additionally reports how many fields were new, so prefer it in new code. +It works exactly like [`GEORADIUS`](/docs/redis/commands/geo/georadius) except that the center is the stored position of a member instead of an explicit coordinate pair, which saves a lookup when the reference point is already in the index (a store, a driver, a landmark). The reference member itself always matches, since its distance from itself is zero, so remember to filter it out or ask for one result more than you need. + +The same `WITHCOORD`, `WITHDIST`, `WITHHASH`, `COUNT`, sorting, and `STORE` or `STOREDIST` options apply. ## Syntax ```redis -HMSET [ ...] +GEORADIUSBYMEMBER (m | km | ft | mi) + [WITHCOORD] + [WITHDIST] + [WITHHASH] + [COUNT [ANY]] + [ASC | DESC] + [STORE | STOREDIST ] ``` ## Arguments @@ -31007,7 +30959,15 @@ HMSET [ ...] | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | | `` | Yes | No | Redis key targeted by the command. | -| ` ` | Yes | Yes | Field and the value to store in it. Repeat to set several fields in one call. | +| `` | Yes | No | Member name. | +| `` | Yes | No | Search radius, in the unit given after it. | +| `(m \| km \| ft \| mi)` | Yes | No | Distance unit: `m` (meters), `km` (kilometers), `ft` (feet), or `mi` (miles). | +| `WITHCOORD` | No | No | Also return the longitude and latitude of each match. | +| `WITHDIST` | No | No | Also return the distance from the center, in the requested unit. | +| `WITHHASH` | No | No | Also return the raw 52-bit geohash score of each match. | +| `COUNT [ANY]` | No | No | Return at most `` matches. `ANY` returns as soon as enough matches are found, instead of sorting every match first. | +| `(ASC \| DESC)` | No | No | Sort the matches by distance from the center: `ASC` (nearest first) or `DESC` (farthest first). Unsorted when omitted. | +| `(STORE \| STOREDIST )` | No | No | Store the matches in a sorted set instead of returning them: `STORE` scores them by geohash, so the destination stays a geospatial index, and `STOREDIST` scores them by their distance from the center. | ## Response @@ -31015,8 +30975,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Simple string `OK` | -| RESP3 | Simple string `OK` | +| RESP2 | Array of bulk-string members or member-detail arrays, or Integer when storing | +| RESP3 | Array of bulk-string members or member-detail arrays, or Integer when storing | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -31031,20 +30991,16 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -HMSET my-key field value +GEORADIUSBYMEMBER my-key member 1.5 m ``` -```ts -import { Redis } from "@upstash/redis"; - -const redis = Redis.fromEnv(); -const result = await redis.hmset("my-key", { field: "value" }); -console.log(result); -``` + + This command is not supported yet in `@upstash/redis`. + @@ -31054,7 +31010,7 @@ console.log(result); from upstash_redis import Redis redis = Redis.from_env() -result = redis.hmset("my-key", {"field": "value"}) +result = redis.georadiusbymember("my-key", "member", 1.5, "M") print(result) ``` @@ -31066,7 +31022,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.hmset("my-key", "field", "value"); +const result = await redis.georadiusbymember("my-key", "member", "1.5", "m"); console.log(result); ``` @@ -31080,7 +31036,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.hSet("my-key", { field: "value" }); +const result = await client.geoRadiusByMember("my-key", "member", 1.5, "m"); console.log(result); ``` @@ -31093,7 +31049,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.hset("my-key", mapping={"field": "value"}) +result = client.georadiusbymember("my-key", "member", 1.5, "m") print(result) ``` @@ -31118,7 +31074,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.HMSet(context.Background(), "my-key", "field", "value").Result() + result, err := client.GeoRadiusByMember(context.Background(), "my-key", "member", &redis.GeoRadiusQuery{Radius: 1.5, Unit: "m"}).Result() if err != nil { panic(err) } @@ -31136,7 +31092,7 @@ import java.net.URI; import redis.clients.jedis.Jedis; try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.hmset("my-key", java.util.Map.of("field", "value")); + Object result = jedis.georadiusByMember("my-key", "member", 1.5, redis.clients.jedis.args.GeoUnit.M); System.out.println(result); } ``` @@ -31146,6 +31102,7 @@ try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { ```rust +use redis::geo::{RadiusOptions, Unit}; use redis::TypedCommands; fn main() -> redis::RedisResult<()> { @@ -31153,7 +31110,13 @@ fn main() -> redis::RedisResult<()> { let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let result = connection.hset_multiple("my-key", &[("field", "value")])?; + let result = connection.geo_radius_by_member( + "my-key", + "member", + 1.5, + Unit::Meters, + RadiusOptions::default(), + )?; println!("{result:?}"); Ok(()) } @@ -31163,19 +31126,26 @@ fn main() -> redis::RedisResult<()> { -# HPERSIST -Source: https://upstash.com/docs/redis/commands/hash/hpersist +# GEORADIUSBYMEMBER_RO +Source: https://upstash.com/docs/redis/commands/geo/georadiusbymember-ro -Use `HPERSIST` to remove the expiration from hash fields so that they stop being deleted automatically. + + Prefer [`GEOSEARCH`](/docs/redis/commands/geo/geosearch) with `FROMMEMBER` and `BYRADIUS` in new code: `GEOSEARCH FROMMEMBER BYRADIUS (m | km | ft | mi)`. + -The reply holds one status code per requested field, in order: `1` when an expiration was removed, `-1` when the field exists but had no expiration, and `-2` when the field or the key does not exist. This is how a field set by [`HEXPIRE`](/docs/redis/commands/hash/hexpire) or [`HSETEX`](/docs/redis/commands/hash/hsetex) is promoted from temporary to permanent without rewriting its value. +Use `GEORADIUSBYMEMBER_RO` to find members within a radius of another member. It is the read-only form of [`GEORADIUSBYMEMBER`](/docs/redis/commands/geo/georadiusbymember). -`FIELDS ` introduces the field list and the count must match. +The center is the stored position of the given member, which always appears among the matches at distance zero. The command accepts the same `WITHCOORD`, `WITHDIST`, `WITHHASH`, `COUNT`, and sorting options but has no `STORE` or `STOREDIST` clause, so it can be served on replicas and used from read-only scripts. ## Syntax ```redis -HPERSIST FIELDS [ ...] +GEORADIUSBYMEMBER_RO (m | km | ft | mi) + [WITHCOORD] + [WITHDIST] + [WITHHASH] + [COUNT [ANY]] + [ASC | DESC] ``` ## Arguments @@ -31183,7 +31153,14 @@ HPERSIST FIELDS [ ...] | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | | `` | Yes | No | Redis key targeted by the command. | -| `FIELDS [ ...]` | Yes | No | Fields to target. Give the field count first, then that many field names. | +| `` | Yes | No | Member name. | +| `` | Yes | No | Search radius, in the unit given after it. | +| `(m \| km \| ft \| mi)` | Yes | No | Distance unit: `m` (meters), `km` (kilometers), `ft` (feet), or `mi` (miles). | +| `WITHCOORD` | No | No | Also return the longitude and latitude of each match. | +| `WITHDIST` | No | No | Also return the distance from the center, in the requested unit. | +| `WITHHASH` | No | No | Also return the raw 52-bit geohash score of each match. | +| `COUNT [ANY]` | No | No | Return at most `` matches. `ANY` returns as soon as enough matches are found, instead of sorting every match first. | +| `(ASC \| DESC)` | No | No | Sort the matches by distance from the center: `ASC` (nearest first) or `DESC` (farthest first). Unsorted when omitted. | ## Response @@ -31191,8 +31168,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Array of integer status codes, one per field | -| RESP3 | Array of integer status codes, one per field | +| RESP2 | Array of bulk-string members or member-detail arrays | +| RESP3 | Array of bulk-string members or member-detail arrays | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -31207,25 +31184,16 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -HPERSIST my-key FIELDS 1 field +GEORADIUSBYMEMBER_RO my-key member 1.5 m ``` -```ts -import { Redis } from "@upstash/redis"; - -const redis = Redis.fromEnv(); - -await redis.hset("my-key", "my-field", "my-value"); -await redis.hpexpire("my-key", "my-field", 1000); - -const expirationRemoved = await redis.hpersist("my-key", "my-field"); - -console.log(expirationRemoved); // [1] -``` + + This command is not supported yet in `@upstash/redis`. + @@ -31235,7 +31203,7 @@ console.log(expirationRemoved); // [1] from upstash_redis import Redis redis = Redis.from_env() -result = redis.hpersist("my-key", "field") +result = redis.georadiusbymember_ro("my-key", "member", 1.5, "M") print(result) ``` @@ -31247,7 +31215,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.hpersist("my-key", "FIELDS", "1", "field"); +const result = await redis.georadiusbymember_ro("my-key", "member", "1.5", "m"); console.log(result); ``` @@ -31261,7 +31229,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.hPersist("my-key", "field"); +const result = await client.geoRadiusByMemberRo("my-key", "member", 1.5, "m"); console.log(result); ``` @@ -31274,7 +31242,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.hpersist("my-key", "field") +result = client.execute_command("GEORADIUSBYMEMBER_RO", "my-key", "member", "1.5", "m") print(result) ``` @@ -31299,7 +31267,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.HPersist(context.Background(), "my-key", "1", "field").Result() + result, err := client.GeoRadiusByMember(context.Background(), "my-key", "member", &redis.GeoRadiusQuery{Radius: 1.5, Unit: "m"}).Result() if err != nil { panic(err) } @@ -31317,7 +31285,7 @@ import java.net.URI; import redis.clients.jedis.Jedis; try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.hpersist("my-key", "1", "field"); + Object result = jedis.georadiusByMemberReadonly("my-key", "member", 1.5, redis.clients.jedis.args.GeoUnit.M); System.out.println(result); } ``` @@ -31327,14 +31295,17 @@ try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { ```rust -use redis::TypedCommands; - fn main() -> redis::RedisResult<()> { let url = std::env::var("REDIS_URL").expect("REDIS_URL is not set"); let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let result = connection.hpersist("my-key", &["field"])?; + let mut command = redis::cmd("GEORADIUSBYMEMBER_RO"); + command.arg("my-key"); + command.arg("member"); + command.arg("1.5"); + command.arg("m"); + let result: redis::Value = command.query(&mut connection)?; println!("{result:?}"); Ok(()) } @@ -31344,21 +31315,29 @@ fn main() -> redis::RedisResult<()> { -# HPEXPIRE -Source: https://upstash.com/docs/redis/commands/hash/hpexpire +# GEOSEARCH +Source: https://upstash.com/docs/redis/commands/geo/geosearch -Use `HPEXPIRE` to give individual hash fields a lifetime in milliseconds, after which those fields are removed from the hash. +Use `GEOSEARCH` to find the members of a geospatial index that fall inside a circle or a rectangle. -It is the millisecond form of [`HEXPIRE`](/docs/redis/commands/hash/hexpire) and behaves identically otherwise: expiration is per field, the hash survives as long as it has fields, and the key disappears when the last field expires. The finer precision matters for short-lived fields such as per-field locks or rate limit windows. +The center is either an existing member of the index (`FROMMEMBER`) or an explicit coordinate pair (`FROMLONLAT`). The area is either a circle of a given radius (`BYRADIUS`) or an axis-aligned box of a given width and height centered on that point (`BYBOX`), which is the shape to use when you are covering a map viewport rather than a "within N km" question. -`FIELDS ` introduces the field list and the count must match. The optional condition applies the new lifetime only in certain cases: `NX` when the field has no expiration, `XX` when it already has one, `GT` when the new expiration is later than the current one, and `LT` when it is earlier. The reply holds one status code per field: `1` when set, `0` when the condition prevented it, `2` when the field was deleted immediately, and `-2` when it does not exist. +By default only member names come back. `WITHDIST` adds the distance from the center in the unit of the query, `WITHCOORD` the member's coordinates, and `WITHHASH` its raw geohash score. `ASC` and `DESC` sort by distance, and `COUNT` caps the number of results; adding `ANY` lets the server return as soon as it has enough matches, which is faster but no longer gives you the nearest ones. + +`GEOSEARCH` replaces the deprecated `GEORADIUS` and `GEORADIUSBYMEMBER` commands and is the command to use for new code. Use [`GEOSEARCHSTORE`](/docs/redis/commands/geo/geosearchstore) when the result should be stored instead of returned. ## Syntax ```redis -HPEXPIRE - [NX | XX | GT | LT] - FIELDS [ ...] +GEOSEARCH + (FROMMEMBER | FROMLONLAT ) + (BYRADIUS (m | km | ft | mi) | + BYBOX (m | km | ft | mi)) + [ASC | DESC] + [COUNT [ANY]] + [WITHCOORD] + [WITHDIST] + [WITHHASH] ``` ## Arguments @@ -31366,14 +31345,13 @@ HPEXPIRE | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | | `` | Yes | No | Redis key targeted by the command. | -| `` | Yes | No | Lifetime in milliseconds. | -| `(NX \| XX \| GT \| LT)` | No | No | Choose one form: `NX` (only when the field has no expiration); `XX` (only when the field already has one); `GT` (only when the new expiration is later than the current one); `LT` (only when it is earlier). | -| `FIELDS [ ...]` | Yes | No | Fields to target. Give the field count first, then that many field names. | - -## Important points - -* `NX` cannot be combined with `XX`, `GT`, or `LT`, and `GT` and `LT` cannot be used together. -* A field with no expiration counts as an infinite one, so `GT` never sets an expiration on such a field and `LT` always does. +| `(FROMMEMBER \| FROMLONLAT )` | Yes | No | Where to center the search: `FROMMEMBER` uses the stored position of an existing member, `FROMLONLAT` uses the given coordinates. | +| `(BYRADIUS (m \| km \| ft \| mi) \| BYBOX (m \| km \| ft \| mi))` | Yes | No | The area to search: `BYRADIUS` a circle of the given radius, `BYBOX` an axis-aligned box of the given width and height centered on the search point. The unit is `m` (meters), `km` (kilometers), `ft` (feet), or `mi` (miles). | +| `(ASC \| DESC)` | No | No | Sort the matches by distance from the center: `ASC` (nearest first) or `DESC` (farthest first). Unsorted when omitted. | +| `COUNT [ANY]` | No | No | Return at most `` matches. `ANY` returns as soon as enough matches are found, instead of sorting every match first. | +| `WITHCOORD` | No | No | Also return the longitude and latitude of each match. | +| `WITHDIST` | No | No | Also return the distance from the center, in the requested unit. | +| `WITHHASH` | No | No | Also return the raw 52-bit geohash score of each match. | ## Response @@ -31381,8 +31359,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Array of integer status codes, one per field | -| RESP3 | Array of integer status codes, one per field | +| RESP2 | Array of bulk-string members or member-detail arrays | +| RESP3 | Array of bulk-string members or member-detail arrays | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -31397,7 +31375,7 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -HPEXPIRE my-key 1000 FIELDS 1 field +GEOSEARCH my-key FROMMEMBER member BYRADIUS 1.5 m ``` @@ -31408,11 +31386,13 @@ HPEXPIRE my-key 1000 FIELDS 1 field import { Redis } from "@upstash/redis"; const redis = Redis.fromEnv(); - -await redis.hset("my-key", "my-field", "my-value"); -const expirationSet = await redis.hpexpire("my-key", "my-field", 1000); - -console.log(expirationSet); // [1] +const result = await redis.geosearch( + "my-key", + { type: "FROMMEMBER", member: "member" }, + { type: "BYRADIUS", radius: 1.5, radiusType: "M" }, + "ASC", +); +console.log(result); ``` @@ -31423,7 +31403,7 @@ console.log(expirationSet); // [1] from upstash_redis import Redis redis = Redis.from_env() -result = redis.hpexpire("my-key", "field", 1000) +result = redis.geosearch("my-key", member="member", radius=1.5, unit="M") print(result) ``` @@ -31435,7 +31415,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.hpexpire("my-key", "1000", "FIELDS", "1", "field"); +const result = await redis.geosearch("my-key", "FROMMEMBER", "member", "BYRADIUS", "1.5", "m"); console.log(result); ``` @@ -31449,7 +31429,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.hpExpire("my-key", "field", 1000); +const result = await client.geoSearch("my-key", "member", { radius: 1.5, unit: "m" }); console.log(result); ``` @@ -31462,7 +31442,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.hpexpire("my-key", 1000, "field") +result = client.geosearch("my-key", member="member", radius=1.5, unit="m") print(result) ``` @@ -31487,7 +31467,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.HPExpire(context.Background(), "my-key", time.Second, "field").Result() + result, err := client.GeoSearch(context.Background(), "my-key", &redis.GeoSearchQuery{Member: "member", Radius: 1.5, RadiusUnit: "m"}).Result() if err != nil { panic(err) } @@ -31505,7 +31485,7 @@ import java.net.URI; import redis.clients.jedis.Jedis; try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.hpexpire("my-key", 1000, "field"); + Object result = jedis.geosearch("my-key", "member", 1.5, redis.clients.jedis.args.GeoUnit.M); System.out.println(result); } ``` @@ -31515,14 +31495,17 @@ try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { ```rust -use redis::TypedCommands; - fn main() -> redis::RedisResult<()> { let url = std::env::var("REDIS_URL").expect("REDIS_URL is not set"); let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let result = connection.hpexpire("my-key", 1000, redis::ExpireOption::NONE, &["field"])?; + let mut command = redis::cmd("GEOSEARCH"); + command.arg("my-key"); + command.arg("member"); + command.arg("1.5"); + command.arg("m"); + let result: redis::Value = command.query(&mut connection)?; println!("{result:?}"); Ok(()) } @@ -31532,36 +31515,38 @@ fn main() -> redis::RedisResult<()> { -# HPEXPIREAT -Source: https://upstash.com/docs/redis/commands/hash/hpexpireat +# GEOSEARCHSTORE +Source: https://upstash.com/docs/redis/commands/geo/geosearchstore -Use `HPEXPIREAT` to schedule individual hash fields for deletion at a fixed point in time, given as a Unix timestamp in milliseconds. +Use `GEOSEARCHSTORE` to run the same query as [`GEOSEARCH`](/docs/redis/commands/geo/geosearch) and store the matching members in another key instead of returning them. -It combines the absolute deadline of [`HEXPIREAT`](/docs/redis/commands/hash/hexpireat) with millisecond precision, which is what you need when fields spread over several hashes have to expire at exactly the same instant. A timestamp in the past removes the fields right away, and the key is deleted when its last field expires. +The destination is a sorted set holding the matches. By default their scores are the raw geohash values, so the destination is itself a valid geospatial index that can be queried further; with `STOREDIST` the score is the distance from the center in the unit of the query, which turns the result into a proximity-ordered list you can page through with [`ZRANGE`](/docs/redis/commands/sorted-set/zrange). -`FIELDS ` introduces the field list and the count must match. The optional condition applies the deadline only when the field has no expiration (`NX`), already has one (`XX`), or when the new deadline is later (`GT`) or earlier (`LT`) than the current one. The reply holds one status code per field: `1` when set, `0` when the condition prevented it, `2` when the field was deleted immediately, and `-2` when it does not exist. +The destination is overwritten on every call, and it is deleted when the query matches nothing. The reply is the number of members stored. This is the usual way to materialize a "nearby" result once and then reuse it for pagination or further set operations. ## Syntax ```redis -HPEXPIREAT - [NX | XX | GT | LT] - FIELDS [ ...] +GEOSEARCHSTORE + (FROMMEMBER | FROMLONLAT ) + (BYRADIUS (m | km | ft | mi) | + BYBOX (m | km | ft | mi)) + [ASC | DESC] + [COUNT [ANY]] + [STOREDIST] ``` ## Arguments | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | -| `` | Yes | No | Redis key targeted by the command. | -| `` | Yes | No | Expiration time as a Unix timestamp in milliseconds. | -| `(NX \| XX \| GT \| LT)` | No | No | Choose one form: `NX` (only when the field has no expiration); `XX` (only when the field already has one); `GT` (only when the new expiration is later than the current one); `LT` (only when it is earlier). | -| `FIELDS [ ...]` | Yes | No | Fields to target. Give the field count first, then that many field names. | - -## Important points - -* `NX` cannot be combined with `XX`, `GT`, or `LT`, and `GT` and `LT` cannot be used together. -* A field with no expiration counts as an infinite one, so `GT` never sets an expiration on such a field and `LT` always does. +| `` | Yes | No | Redis key used as destination. | +| `` | Yes | No | Redis key used as source. | +| `(FROMMEMBER \| FROMLONLAT )` | Yes | No | Where to center the search: `FROMMEMBER` uses the stored position of an existing member, `FROMLONLAT` uses the given coordinates. | +| `(BYRADIUS (m \| km \| ft \| mi) \| BYBOX (m \| km \| ft \| mi))` | Yes | No | The area to search: `BYRADIUS` a circle of the given radius, `BYBOX` an axis-aligned box of the given width and height centered on the search point. The unit is `m` (meters), `km` (kilometers), `ft` (feet), or `mi` (miles). | +| `(ASC \| DESC)` | No | No | Sort the matches by distance from the center: `ASC` (nearest first) or `DESC` (farthest first). Unsorted when omitted. | +| `COUNT [ANY]` | No | No | Return at most `` matches. `ANY` returns as soon as enough matches are found, instead of sorting every match first. | +| `STOREDIST` | No | No | Store each match's distance from the center instead of its geohash score. | ## Response @@ -31569,8 +31554,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Array of integer status codes, one per field | -| RESP3 | Array of integer status codes, one per field | +| RESP2 | Integer | +| RESP3 | Integer | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -31585,7 +31570,7 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -HPEXPIREAT my-key 1735689600 FIELDS 1 field +GEOSEARCHSTORE destination-key source-key FROMMEMBER member BYRADIUS 1.5 m ``` @@ -31596,11 +31581,14 @@ HPEXPIREAT my-key 1735689600 FIELDS 1 field import { Redis } from "@upstash/redis"; const redis = Redis.fromEnv(); - -await redis.hset("my-key", "my-field", "my-value"); -const expirationSet = await redis.hpexpireat("my-key", "my-field", Date.now() + 1000); - -console.log(expirationSet); // [1] +const result = await redis.geosearchstore( + "destination-key", + "source-key", + { type: "FROMMEMBER", member: "member" }, + { type: "BYRADIUS", radius: 1.5, radiusType: "M" }, + "ASC", +); +console.log(result); ``` @@ -31611,7 +31599,7 @@ console.log(expirationSet); // [1] from upstash_redis import Redis redis = Redis.from_env() -result = redis.hpexpireat("my-key", "field", 1735689600) +result = redis.geosearchstore("destination-key", "source-key", member="member", radius=1.5, unit="M") print(result) ``` @@ -31623,7 +31611,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.hpexpireat("my-key", "1735689600", "FIELDS", "1", "field"); +const result = await redis.geosearchstore("destination-key", "source-key", "FROMMEMBER", "member", "BYRADIUS", "1.5", "m"); console.log(result); ``` @@ -31637,7 +31625,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.hpExpireAt("my-key", "field", 1735689600); +const result = await client.geoSearchStore("destination-key", "source-key", "member", { radius: 1.5, unit: "m" }); console.log(result); ``` @@ -31650,7 +31638,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.hpexpireat("my-key", 1735689600, "field") +result = client.geosearchstore("destination-key", "source-key", member="member", radius=1.5, unit="m") print(result) ``` @@ -31675,7 +31663,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.HPExpireAt(context.Background(), "my-key", time.UnixMilli(1735689600), "field").Result() + result, err := client.GeoSearchStore(context.Background(), "source-key", "destination-key", &redis.GeoSearchStoreQuery{GeoSearchQuery: redis.GeoSearchQuery{Member: "member", Radius: 1.5, RadiusUnit: "m"}}).Result() if err != nil { panic(err) } @@ -31693,7 +31681,7 @@ import java.net.URI; import redis.clients.jedis.Jedis; try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.hpexpireAt("my-key", 1735689600, "field"); + Object result = jedis.geosearchStore("destination-key", "source-key", "member", 1.5, redis.clients.jedis.args.GeoUnit.M); System.out.println(result); } ``` @@ -31703,14 +31691,18 @@ try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { ```rust -use redis::TypedCommands; - fn main() -> redis::RedisResult<()> { let url = std::env::var("REDIS_URL").expect("REDIS_URL is not set"); let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let result = connection.hpexpire_at("my-key", 1735689600, redis::ExpireOption::NONE, &["field"])?; + let mut command = redis::cmd("GEOSEARCHSTORE"); + command.arg("destination-key"); + command.arg("source-key"); + command.arg("member"); + command.arg("1.5"); + command.arg("m"); + let result: redis::Value = command.query(&mut connection)?; println!("{result:?}"); Ok(()) } @@ -31720,17 +31712,33 @@ fn main() -> redis::RedisResult<()> { -# HPEXPIRETIME -Source: https://upstash.com/docs/redis/commands/hash/hpexpiretime +# Geo commands +Source: https://upstash.com/docs/redis/commands/geo/overview -Use `HPEXPIRETIME` to read the absolute expiration time of hash fields, as Unix timestamps in milliseconds. + +Add geospatial items +Get distance between two members +Get geohash strings for members +Get coordinates of members +Find members within a radius of a point +Read-only radius query +Find members within a radius of another member +Read-only radius query by member +Search for members in an area +Store geosearch results + -The reply holds one value per requested field, in order: the timestamp at which it expires, `-1` when the field exists but has no expiration, and `-2` when the field or the key does not exist. It is the millisecond form of [`HEXPIRETIME`](/docs/redis/commands/hash/hexpiretime) and reports a deadline rather than the time left, which is the value to compare against a clock. +# HDEL +Source: https://upstash.com/docs/redis/commands/hash/hdel + +Use `HDEL` to remove one or more fields from a hash. + +The reply counts only the fields that were actually present, so deleting a field that is already gone is not an error. When the last field of a hash is removed the key itself is deleted, because Redis does not keep empty collections. ## Syntax ```redis -HPEXPIRETIME FIELDS [ ...] +HDEL [ ...] ``` ## Arguments @@ -31738,11 +31746,7 @@ HPEXPIRETIME FIELDS [ ...] | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | | `` | Yes | No | Redis key targeted by the command. | -| `FIELDS [ ...]` | Yes | No | Fields to target. Give the field count first, then that many field names. | - -## Important points - -* Negative integer replies are sentinel values, not durations or timestamps; see the response description for missing or persistent data. +| `` | Yes | Yes | Hash field name. | ## Response @@ -31750,8 +31754,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Array of expiration timestamps or negative integer status codes, one per field | -| RESP3 | Array of expiration timestamps or negative integer status codes, one per field | +| RESP2 | Integer | +| RESP3 | Integer | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -31766,7 +31770,7 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -HPEXPIRETIME my-key FIELDS 1 field +HDEL my-key field ``` @@ -31778,11 +31782,8 @@ import { Redis } from "@upstash/redis"; const redis = Redis.fromEnv(); -await redis.hset("my-key", "my-field", "my-value"); -await redis.hpexpireat("my-key", "my-field", Date.now() + 1000); -const expireTime = await redis.hpexpiretime("my-key", "my-field"); - -console.log(expireTime); // e.g., 1697059200000 +await redis.hdel(key, 'field1', 'field2'); +// returns 5 ``` @@ -31793,7 +31794,7 @@ console.log(expireTime); // e.g., 1697059200000 from upstash_redis import Redis redis = Redis.from_env() -result = redis.hpexpiretime("my-key", "field") +result = redis.hdel("my-key", "field") print(result) ``` @@ -31805,7 +31806,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.hpexpiretime("my-key", "FIELDS", "1", "field"); +const result = await redis.hdel("my-key", "field"); console.log(result); ``` @@ -31819,7 +31820,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.hpExpireTime("my-key", "field"); +const result = await client.hDel("my-key", "field"); console.log(result); ``` @@ -31832,7 +31833,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.hpexpiretime("my-key", "field") +result = client.hdel("my-key", "field") print(result) ``` @@ -31857,7 +31858,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.HPExpireTime(context.Background(), "my-key", "1", "field").Result() + result, err := client.HDel(context.Background(), "my-key", "field").Result() if err != nil { panic(err) } @@ -31875,7 +31876,7 @@ import java.net.URI; import redis.clients.jedis.Jedis; try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.hpexpireTime("my-key", "1", "field"); + Object result = jedis.hdel("my-key", "field"); System.out.println(result); } ``` @@ -31892,7 +31893,7 @@ fn main() -> redis::RedisResult<()> { let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let result = connection.hpexpire_time("my-key", &["field"])?; + let result = connection.hdel("my-key", "field")?; println!("{result:?}"); Ok(()) } @@ -31902,17 +31903,17 @@ fn main() -> redis::RedisResult<()> { -# HPTTL -Source: https://upstash.com/docs/redis/commands/hash/hpttl +# HEXISTS +Source: https://upstash.com/docs/redis/commands/hash/hexists -Use `HPTTL` to read how much longer hash fields will live, in milliseconds. +Use `HEXISTS` to check whether a field is present in a hash. -The reply holds one value per requested field, in order: the remaining lifetime, `-1` when the field exists but has no expiration, and `-2` when the field or the key does not exist. It is the millisecond form of [`HTTL`](/docs/redis/commands/hash/httl), and the extra precision matters for fields that live for less than a second. +The reply is `1` when the field exists and `0` when either the field or the key is missing. Since the value is never transferred, this is the cheap way to test for presence, and it is also how you tell a missing field apart from a field whose value happens to be empty, which [`HGET`](/docs/redis/commands/hash/hget) cannot do. ## Syntax ```redis -HPTTL FIELDS [ ...] +HEXISTS ``` ## Arguments @@ -31920,11 +31921,7 @@ HPTTL FIELDS [ ...] | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | | `` | Yes | No | Redis key targeted by the command. | -| `FIELDS [ ...]` | Yes | No | Fields to target. Give the field count first, then that many field names. | - -## Important points - -* Negative integer replies are sentinel values, not durations or timestamps; see the response description for missing or persistent data. +| `` | Yes | No | Hash field name. | ## Response @@ -31932,8 +31929,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Array of TTL values or negative integer status codes, one per field | -| RESP3 | Array of TTL values or negative integer status codes, one per field | +| RESP2 | Integer: `1` if the field exists, `0` otherwise | +| RESP3 | Integer: `1` if the field exists, `0` otherwise | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -31948,7 +31945,7 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -HPTTL my-key FIELDS 1 field +HEXISTS my-key field ``` @@ -31960,11 +31957,10 @@ import { Redis } from "@upstash/redis"; const redis = Redis.fromEnv(); -await redis.hset("my-key", "my-field", "my-value"); -await redis.hpexpire("my-key", "my-field", 1000); -const ttl = await redis.hpttl("my-key", "my-field"); +await redis.hset("key", "field", "value"); +const exists = await redis.hexists("key", "field"); -console.log(ttl); // e.g., [950] +console.log(exists); // 1 ``` @@ -31975,7 +31971,7 @@ console.log(ttl); // e.g., [950] from upstash_redis import Redis redis = Redis.from_env() -result = redis.hpttl("my-key", "field") +result = redis.hexists("my-key", "field") print(result) ``` @@ -31987,7 +31983,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.hpttl("my-key", "FIELDS", "1", "field"); +const result = await redis.hexists("my-key", "field"); console.log(result); ``` @@ -32001,7 +31997,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.hpTTL("my-key", "field"); +const result = await client.hExists("my-key", "field"); console.log(result); ``` @@ -32014,7 +32010,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.hpttl("my-key", "field") +result = client.hexists("my-key", "field") print(result) ``` @@ -32039,7 +32035,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.HPTTL(context.Background(), "my-key", "1", "field").Result() + result, err := client.HExists(context.Background(), "my-key", "field").Result() if err != nil { panic(err) } @@ -32057,7 +32053,7 @@ import java.net.URI; import redis.clients.jedis.Jedis; try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.hpttl("my-key", "1", "field"); + Object result = jedis.hexists("my-key", "field"); System.out.println(result); } ``` @@ -32074,7 +32070,7 @@ fn main() -> redis::RedisResult<()> { let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let result = connection.hpttl("my-key", &["field"])?; + let result = connection.hexists("my-key", "field")?; println!("{result:?}"); Ok(()) } @@ -32084,19 +32080,23 @@ fn main() -> redis::RedisResult<()> { -# HRANDFIELD -Source: https://upstash.com/docs/redis/commands/hash/hrandfield +# HEXPIRE +Source: https://upstash.com/docs/redis/commands/hash/hexpire -Use `HRANDFIELD` to get one or more random fields from a hash. +Use `HEXPIRE` to give individual hash fields a lifetime in seconds, after which those fields are removed from the hash. -Without a count a single field name is returned, or null when the key does not exist. A positive count returns up to that many distinct fields, capped at the size of the hash, while a negative count returns exactly that many fields chosen independently, so the same field can come up more than once. `WITHVALUES` returns each field together with its value. +Expiration here is per field, not per key: the hash itself stays alive as long as it still has fields, and the key is deleted automatically when the last surviving field expires. This makes it possible to keep short-lived and long-lived data in one hash, for example a user record whose verification code expires while the rest of the record stays. -Nothing is removed from the hash, which is the difference from [`HGETDEL`](/docs/redis/commands/hash/hgetdel): use this for sampling, random selection, and quick inspection of an unfamiliar hash. +`FIELDS ` introduces the list of fields and the count must match the number of names that follow. The optional condition works as it does on [`EXPIRE`](/docs/redis/commands/generic/expire): `NX` only when the field has no expiration, `XX` only when it already has one, `GT` only when the new expiration is later than the current one, and `LT` only when it is earlier. + +The reply holds one status code per field, in order: `1` when the expiration was set, `0` when the condition prevented it, `2` when the field was deleted immediately because the given lifetime was zero or negative, and `-2` when the field does not exist. ## Syntax ```redis -HRANDFIELD [ [WITHVALUES]] +HEXPIRE + [NX | XX | GT | LT] + FIELDS [ ...] ``` ## Arguments @@ -32104,11 +32104,14 @@ HRANDFIELD [ [WITHVALUES]] | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | | `` | Yes | No | Redis key targeted by the command. | -| ` [WITHVALUES]` | No | No | Number of fields to return; a negative count may repeat fields. `WITHVALUES` also returns each field's value. | +| `` | Yes | No | Lifetime in seconds. | +| `(NX \| XX \| GT \| LT)` | No | No | Choose one form: `NX` (only when the field has no expiration); `XX` (only when the field already has one); `GT` (only when the new expiration is later than the current one); `LT` (only when it is earlier). | +| `FIELDS [ ...]` | Yes | No | Fields to target. Give the field count first, then that many field names. | ## Important points -* Pair-based results may be flattened into one alternating array in RESP2 while RESP3 preserves nested pairs or a map. +* `NX` cannot be combined with `XX`, `GT`, or `LT`, and `GT` and `LT` cannot be used together. +* A field with no expiration counts as an infinite one, so `GT` never sets an expiration on such a field and `LT` always does. ## Response @@ -32116,8 +32119,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Null bulk string or null array, Bulk string, array of fields, or flat field/value array | -| RESP3 | Null, Bulk string, array of fields, or array of field/value pairs | +| RESP2 | Array of integer status codes, one per field | +| RESP3 | Array of integer status codes, one per field | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -32132,7 +32135,7 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -HRANDFIELD my-key +HEXPIRE my-key 1000 FIELDS 1 field ``` @@ -32144,13 +32147,10 @@ import { Redis } from "@upstash/redis"; const redis = Redis.fromEnv(); -await redis.hset("key", { - id: 1, - username: "chronark", - name: "andreas" - }); -const randomField = await redis.hrandfield("key"); -console.log(randomField); // one of "id", "username" or "name" +await redis.hset("my-key", "my-field", "my-value"); +const expirationSet = await redis.hexpire("my-key", "my-field", 1); + +console.log(expirationSet); // 1 ``` @@ -32161,7 +32161,7 @@ console.log(randomField); // one of "id", "username" or "name" from upstash_redis import Redis redis = Redis.from_env() -result = redis.hrandfield("my-key") +result = redis.hexpire("my-key", "field", 1000) print(result) ``` @@ -32173,7 +32173,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.hrandfield("my-key"); +const result = await redis.hexpire("my-key", "1000", "FIELDS", "1", "field"); console.log(result); ``` @@ -32187,7 +32187,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.hRandField("my-key"); +const result = await client.hExpire("my-key", "field", 1000); console.log(result); ``` @@ -32200,7 +32200,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.hrandfield("my-key") +result = client.hexpire("my-key", 1000, "field") print(result) ``` @@ -32225,7 +32225,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.HRandField(context.Background(), "my-key", 1).Result() + result, err := client.HExpire(context.Background(), "my-key", 1000*time.Second, "field").Result() if err != nil { panic(err) } @@ -32243,7 +32243,7 @@ import java.net.URI; import redis.clients.jedis.Jedis; try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.hrandfield("my-key"); + Object result = jedis.hexpire("my-key", 1000, "field"); System.out.println(result); } ``` @@ -32253,14 +32253,14 @@ try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { ```rust +use redis::TypedCommands; + fn main() -> redis::RedisResult<()> { let url = std::env::var("REDIS_URL").expect("REDIS_URL is not set"); let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let mut command = redis::cmd("HRANDFIELD"); - command.arg("my-key"); - let result: redis::Value = command.query(&mut connection)?; + let result = connection.hexpire("my-key", 1000, redis::ExpireOption::NONE, &["field"])?; println!("{result:?}"); Ok(()) } @@ -32270,19 +32270,23 @@ fn main() -> redis::RedisResult<()> { -# HSCAN -Source: https://upstash.com/docs/redis/commands/hash/hscan +# HEXPIREAT +Source: https://upstash.com/docs/redis/commands/hash/hexpireat -Use `HSCAN` to iterate the fields of a hash in batches instead of reading it all at once. +Use `HEXPIREAT` to schedule individual hash fields for deletion at a fixed point in time, given as a Unix timestamp in seconds. -Each call takes a cursor and returns the next cursor together with a batch of field and value pairs. Start at cursor `0` and keep calling with the cursor from the previous reply until the server returns `0`, which ends the iteration. Because each call does a bounded amount of work, this avoids the long single reply that [`HGETALL`](/docs/redis/commands/hash/hgetall) produces on a large hash. +It is the absolute-deadline form of [`HEXPIRE`](/docs/redis/commands/hash/hexpire), which is what you want when several fields, or fields across several hashes, must expire at the same moment. A timestamp in the past removes the fields right away. The key is deleted when its last field expires. -`MATCH` filters field names with a glob-style pattern, `COUNT` hints at how much work each call should do, and `NOVALUES` returns field names only, which is noticeably cheaper when values are large and you do not need them. Filtering is applied after a batch has been read, so a call can return nothing while the cursor is still non-zero: only a cursor of `0` means the iteration is over. Fields present for the whole iteration are returned at least once, and fields added or removed while it runs may or may not appear. +`FIELDS ` introduces the field list and the count must match. The optional condition applies the deadline only in certain cases: `NX` when the field has no expiration, `XX` when it already has one, `GT` when the new deadline is later than the current one, and `LT` when it is earlier. + +The reply holds one status code per field: `1` when the expiration was set, `0` when the condition prevented it, `2` when the field was deleted immediately, and `-2` when the field does not exist. ## Syntax ```redis -HSCAN [MATCH ] [COUNT ] [NOVALUES] +HEXPIREAT + [NX | XX | GT | LT] + FIELDS [ ...] ``` ## Arguments @@ -32290,15 +32294,14 @@ HSCAN [MATCH ] [COUNT ] [NOVALUES] | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | | `` | Yes | No | Redis key targeted by the command. | -| `` | Yes | No | Cursor returned by the previous call; start at `0`. | -| `MATCH ` | No | No | Return only elements matching this glob-style pattern. | -| `COUNT ` | No | No | Hint for how much work each iteration should do. | -| `NOVALUES` | No | No | Return only field names, without their values. | +| `` | Yes | No | Expiration time as a Unix timestamp in seconds. | +| `(NX \| XX \| GT \| LT)` | No | No | Choose one form: `NX` (only when the field has no expiration); `XX` (only when the field already has one); `GT` (only when the new expiration is later than the current one); `LT` (only when it is earlier). | +| `FIELDS [ ...]` | Yes | No | Fields to target. Give the field count first, then that many field names. | ## Important points -* This operation can inspect a large part of the database. Prefer cursor-based scans where possible and avoid unbounded use on hot paths. -* The cursor is opaque. Start with `0` and continue until the server returns cursor `0`; a single iteration may return no elements. +* `NX` cannot be combined with `XX`, `GT`, or `LT`, and `GT` and `LT` cannot be used together. +* A field with no expiration counts as an infinite one, so `GT` never sets an expiration on such a field and `LT` always does. ## Response @@ -32306,8 +32309,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Two-element array: cursor and flat field/value array, or field array with `NOVALUES` | -| RESP3 | Two-element array: cursor and flat field/value array, or field array with `NOVALUES` | +| RESP2 | Array of integer status codes, one per field | +| RESP3 | Array of integer status codes, one per field | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -32322,7 +32325,7 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -HSCAN my-key 0 +HEXPIREAT my-key 1735689600 FIELDS 1 field ``` @@ -32334,14 +32337,10 @@ import { Redis } from "@upstash/redis"; const redis = Redis.fromEnv(); -await redis.hset("key", { - id: 1, - username: "chronark", - name: "andreas" - }); -const [newCursor, fields] = await redis.hscan("key", 0); -console.log(newCursor); // likely `0` since this is a very small hash -console.log(fields); // ["id", 1, "username", "chronark", "name", "andreas"] +await redis.hset("my-key", "my-field", "my-value"); +const expirationSet = await redis.hexpireat("my-key", "my-field", Math.floor(Date.now() / 1000) + 10); + +console.log(expirationSet); // [1] ``` @@ -32352,7 +32351,7 @@ console.log(fields); // ["id", 1, "username", "chronark", "name", "andreas"] from upstash_redis import Redis redis = Redis.from_env() -result = redis.hscan("my-key", 0) +result = redis.hexpireat("my-key", "field", 1735689600) print(result) ``` @@ -32364,7 +32363,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.hscan("my-key", "0"); +const result = await redis.hexpireat("my-key", "1735689600", "FIELDS", "1", "field"); console.log(result); ``` @@ -32378,7 +32377,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.hScan("my-key", "0"); +const result = await client.hExpireAt("my-key", "field", 1735689600); console.log(result); ``` @@ -32391,7 +32390,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.hscan("my-key", 0) +result = client.hexpireat("my-key", 1735689600, "field") print(result) ``` @@ -32416,7 +32415,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, _, err := client.HScan(context.Background(), "my-key", 0, "*", 0).Result() + result, err := client.HExpireAt(context.Background(), "my-key", time.Unix(1735689600, 0), "field").Result() if err != nil { panic(err) } @@ -32434,7 +32433,7 @@ import java.net.URI; import redis.clients.jedis.Jedis; try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.hscan("my-key", "0"); + Object result = jedis.hexpireAt("my-key", 1735689600, "field"); System.out.println(result); } ``` @@ -32451,10 +32450,8 @@ fn main() -> redis::RedisResult<()> { let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let iter: redis::Iter<(String, String)> = connection.hscan("my-key")?; - for (field, value) in iter { - println!("{field}: {value}"); - } + let result = connection.hexpire_at("my-key", 1735689600, redis::ExpireOption::NONE, &["field"])?; + println!("{result:?}"); Ok(()) } ``` @@ -32463,19 +32460,17 @@ fn main() -> redis::RedisResult<()> { -# HSET -Source: https://upstash.com/docs/redis/commands/hash/hset - -Use `HSET` to set one or more fields of a hash to the given values, creating the key when it does not exist. +# HEXPIRETIME +Source: https://upstash.com/docs/redis/commands/hash/hexpiretime -Existing fields are overwritten, and the reply counts only the fields that were added, not those that were updated, which is how you tell an insert from an update. Setting fields does not touch the key's time to live, so a hash with an expiration keeps it as it is written to. A field's own expiration is a different matter: writing a field clears the TTL it may have been given by [`HEXPIRE`](/docs/redis/commands/hash/hexpire) or [`HSETEX`](/docs/redis/commands/hash/hsetex). +Use `HEXPIRETIME` to read the absolute expiration time of hash fields, as Unix timestamps in seconds. -Hashes are the compact way to store an object under one key: field access with `HSET` and [`HGET`](/docs/redis/commands/hash/hget) avoids reading and rewriting the whole value the way a serialized string would. +The reply holds one value per requested field, in order: the timestamp when the field expires, `-1` when the field exists but has no expiration, and `-2` when the field or the key does not exist. Use [`HTTL`](/docs/redis/commands/hash/httl) when you want the remaining lifetime instead of the deadline, and [`HPEXPIRETIME`](/docs/redis/commands/hash/hpexpiretime) for millisecond precision. ## Syntax ```redis -HSET [ ...] +HEXPIRETIME FIELDS [ ...] ``` ## Arguments @@ -32483,7 +32478,11 @@ HSET [ ...] | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | | `` | Yes | No | Redis key targeted by the command. | -| ` ` | Yes | Yes | Field and the value to store in it. Repeat to set several fields in one call. | +| `FIELDS [ ...]` | Yes | No | Fields to target. Give the field count first, then that many field names. | + +## Important points + +* Negative integer replies are sentinel values, not durations or timestamps; see the response description for missing or persistent data. ## Response @@ -32491,8 +32490,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Integer | -| RESP3 | Integer | +| RESP2 | Array of expiration timestamps or negative integer status codes, one per field | +| RESP3 | Array of expiration timestamps or negative integer status codes, one per field | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -32507,7 +32506,7 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -HSET my-key field value +HEXPIRETIME my-key FIELDS 1 field ``` @@ -32519,11 +32518,11 @@ import { Redis } from "@upstash/redis"; const redis = Redis.fromEnv(); -await redis.hset("key", { - id: 1, - username: "chronark", - name: "andreas" - }); +await redis.hset("my-key", "my-field", "my-value"); +await redis.hexpireat("my-key", "my-field", Math.floor(Date.now() / 1000) + 10); +const expireTime = await redis.hexpiretime("my-key", "my-field"); + +console.log(expireTime); // e.g., [1697059200] ``` @@ -32534,7 +32533,7 @@ await redis.hset("key", { from upstash_redis import Redis redis = Redis.from_env() -result = redis.hset("my-key", "field", "value") +result = redis.hexpiretime("my-key", "field") print(result) ``` @@ -32546,7 +32545,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.hset("my-key", "field", "value"); +const result = await redis.hexpiretime("my-key", "FIELDS", "1", "field"); console.log(result); ``` @@ -32560,7 +32559,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.hSet("my-key", "field", "value"); +const result = await client.hExpireTime("my-key", "field"); console.log(result); ``` @@ -32573,7 +32572,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.hset("my-key", "field", "value") +result = client.hexpiretime("my-key", "field") print(result) ``` @@ -32598,7 +32597,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.HSet(context.Background(), "my-key", "field", "value").Result() + result, err := client.HExpireTime(context.Background(), "my-key", "field").Result() if err != nil { panic(err) } @@ -32612,11 +32611,10 @@ func main() { ```java import java.net.URI; - import redis.clients.jedis.Jedis; try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.hset("my-key", "field", "value"); + Object result = jedis.hexpireTime("my-key", "field"); System.out.println(result); } ``` @@ -32633,7 +32631,7 @@ fn main() -> redis::RedisResult<()> { let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let result = connection.hset("my-key", "field", "value")?; + let result = connection.hexpire_time("my-key", &["field"])?; println!("{result:?}"); Ok(()) } @@ -32643,23 +32641,17 @@ fn main() -> redis::RedisResult<()> { -# HSETEX -Source: https://upstash.com/docs/redis/commands/hash/hsetex - -Use `HSETEX` to write hash fields and set their expiration in the same atomic call. +# HGET +Source: https://upstash.com/docs/redis/commands/hash/hget -`FIELDS ` introduces the field and value pairs and the count must match. `EX`, `PX`, `EXAT`, and `PXAT` give the written fields a lifetime or an absolute deadline, and `KEEPTTL` keeps whatever expiration those fields already had. Without any of these options the fields are written without an expiration, so a previously set one is dropped. +Use `HGET` to read the value of a single field of a hash. -`FNX` writes only when none of the given fields exist and `FXX` only when all of them do, which turns the command into an atomic conditional write: create-if-absent or update-if-present, with the expiration applied in the same step. The reply is `1` when the fields were written and `0` when the condition prevented it. +The reply is null when either the field or the whole key is missing, so the two cases cannot be told apart from the reply alone; use [`HEXISTS`](/docs/redis/commands/hash/hexists) when that difference matters. To read several fields use [`HMGET`](/docs/redis/commands/hash/hmget) rather than repeated calls, and to read all of them use [`HGETALL`](/docs/redis/commands/hash/hgetall). ## Syntax ```redis -HSETEX - [FNX | FXX] - [EX | PX | EXAT | - PXAT | KEEPTTL] - FIELDS [ ...] +HGET ``` ## Arguments @@ -32667,9 +32659,7 @@ HSETEX | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | | `` | Yes | No | Redis key targeted by the command. | -| `(FNX \| FXX)` | No | No | Write only when the condition holds: `FNX` when none of the given fields exist, `FXX` when all of them do. | -| `(EX \| PX \| EXAT \| PXAT \| KEEPTTL)` | No | No | Choose one form: `EX` (set a lifetime in seconds); `PX` (set a lifetime in milliseconds); `EXAT` (expire at a Unix timestamp in seconds); `PXAT` (expire at a Unix timestamp in milliseconds); `KEEPTTL` (preserve the existing key lifetime). | -| `FIELDS [ ...]` | Yes | No | Field-value pairs to set. Give the pair count first, then that many field and value pairs. | +| `` | Yes | No | Hash field name. | ## Response @@ -32677,8 +32667,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Integer: `1` if the fields were set, `0` otherwise | -| RESP3 | Integer: `1` if the fields were set, `0` otherwise | +| RESP2 | Bulk string or Null bulk string or null array | +| RESP3 | Bulk string or Null | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -32693,7 +32683,7 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -HSETEX my-key FIELDS 1 field value +HGET my-key field ``` @@ -32705,11 +32695,9 @@ import { Redis } from "@upstash/redis"; const redis = Redis.fromEnv(); -// Set fields with 1 hour expiration -await redis.hsetex("user:123", { expiration: { ex: 3600 } }, { - name: "John", - email: "john@example.com" -}); +await redis.hset("key", {field: "value"}); +const field = await redis.hget("key", "field"); +console.log(field); // "value" ``` @@ -32720,7 +32708,7 @@ await redis.hsetex("user:123", { expiration: { ex: 3600 } }, { from upstash_redis import Redis redis = Redis.from_env() -result = redis.hsetex("my-key", field="field", value="value") +result = redis.hget("my-key", "field") print(result) ``` @@ -32732,7 +32720,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.hsetex("my-key", "FIELDS", "1", "field", "value"); +const result = await redis.hget("my-key", "field"); console.log(result); ``` @@ -32746,7 +32734,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.hSetEx("my-key", { field: "value" }); +const result = await client.hGet("my-key", "field"); console.log(result); ``` @@ -32759,7 +32747,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.hsetex("my-key", mapping={"field": "value"}) +result = client.hget("my-key", "field") print(result) ``` @@ -32784,7 +32772,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.HSetEX(context.Background(), "my-key", "1", "field", "value").Result() + result, err := client.HGet(context.Background(), "my-key", "field").Result() if err != nil { panic(err) } @@ -32802,7 +32790,7 @@ import java.net.URI; import redis.clients.jedis.Jedis; try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.hsetex("my-key", redis.clients.jedis.params.HSetExParams.hSetExParams(), "field", "value"); + Object result = jedis.hget("my-key", "field"); System.out.println(result); } ``` @@ -32812,18 +32800,14 @@ try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { ```rust -use redis::{HashFieldExpirationOptions, TypedCommands}; +use redis::TypedCommands; fn main() -> redis::RedisResult<()> { let url = std::env::var("REDIS_URL").expect("REDIS_URL is not set"); let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let result = connection.hset_ex( - "my-key", - &HashFieldExpirationOptions::default(), - &[("field", "value")], - )?; + let result = connection.hget("my-key", "field")?; println!("{result:?}"); Ok(()) } @@ -32833,17 +32817,19 @@ fn main() -> redis::RedisResult<()> { -# HSETNX -Source: https://upstash.com/docs/redis/commands/hash/hsetnx +# HGETALL +Source: https://upstash.com/docs/redis/commands/hash/hgetall -Use `HSETNX` to set a hash field only when it does not already exist. +Use `HGETALL` to read every field and value of a hash in one call. -The reply is `1` when the field was created and `0` when it was already present and left untouched. The check and the write happen atomically, so of several clients racing to fill the same field exactly one succeeds, which makes the command a way to claim a slot inside a hash without overwriting whatever a concurrent writer put there. +The reply pairs each field with its value. RESP2 flattens it into a single alternating array while RESP3 returns a map, and client libraries normally decode either form into a native dictionary. A missing key returns an empty result rather than an error. + +The whole hash is transferred, so on hashes with many fields prefer [`HMGET`](/docs/redis/commands/hash/hmget) when you know which fields you need, or [`HSCAN`](/docs/redis/commands/hash/hscan) to walk the hash in batches. ## Syntax ```redis -HSETNX +HGETALL ``` ## Arguments @@ -32851,8 +32837,10 @@ HSETNX | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | | `` | Yes | No | Redis key targeted by the command. | -| `` | Yes | No | Hash field name. | -| `` | Yes | No | Value to store in the field. | + +## Important points + +* Pair-based results may be flattened into one alternating array in RESP2 while RESP3 preserves nested pairs or a map. ## Response @@ -32860,8 +32848,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Integer: `1` if the field was set, `0` if it already exists | -| RESP3 | Integer: `1` if the field was set, `0` if it already exists | +| RESP2 | Flat array of alternating keys and values | +| RESP3 | Map | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -32876,7 +32864,7 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -HSETNX my-key field value +HGETALL my-key ``` @@ -32888,7 +32876,12 @@ import { Redis } from "@upstash/redis"; const redis = Redis.fromEnv(); -await redis.hsetnx("key", "id", 1) +await redis.hset("key", { + field1: "value1", + field2: "value2", + }); +const hash = await redis.hgetall("key"); +console.log(hash); // { field1: "value1", field2: "value2" } ``` @@ -32899,7 +32892,7 @@ await redis.hsetnx("key", "id", 1) from upstash_redis import Redis redis = Redis.from_env() -result = redis.hsetnx("my-key", "field", "value") +result = redis.hgetall("my-key") print(result) ``` @@ -32911,7 +32904,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.hsetnx("my-key", "field", "value"); +const result = await redis.hgetall("my-key"); console.log(result); ``` @@ -32925,7 +32918,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.hSetNX("my-key", "field", "value"); +const result = await client.hGetAll("my-key"); console.log(result); ``` @@ -32938,7 +32931,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.hsetnx("my-key", "field", "value") +result = client.hgetall("my-key") print(result) ``` @@ -32963,7 +32956,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.HSetNX(context.Background(), "my-key", "field", "value").Result() + result, err := client.HGetAll(context.Background(), "my-key").Result() if err != nil { panic(err) } @@ -32981,7 +32974,7 @@ import java.net.URI; import redis.clients.jedis.Jedis; try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.hsetnx("my-key", "field", "value"); + Object result = jedis.hgetAll("my-key"); System.out.println(result); } ``` @@ -32998,7 +32991,7 @@ fn main() -> redis::RedisResult<()> { let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let result = connection.hset_nx("my-key", "field", "value")?; + let result = connection.hgetall("my-key")?; println!("{result:?}"); Ok(()) } @@ -33008,17 +33001,19 @@ fn main() -> redis::RedisResult<()> { -# HSTRLEN -Source: https://upstash.com/docs/redis/commands/hash/hstrlen +# HGETDEL +Source: https://upstash.com/docs/redis/commands/hash/hgetdel -Use `HSTRLEN` to get the length in bytes of the value stored in a hash field. +Use `HGETDEL` to read hash fields and delete them in the same atomic step. -The reply is `0` when either the field or the key is missing. Since the value itself is not transferred, this is how you check the size of a large field, or decide whether it is worth fetching, without paying for it. +The reply holds the previous value of each requested field, in the order requested, with null for fields that were not present. Reading and removing together removes the race that an [`HGET`](/docs/redis/commands/hash/hget) followed by an [`HDEL`](/docs/redis/commands/hash/hdel) would leave open, which makes the command a good fit for one-shot values such as one-time codes, claim tickets, or queued items keyed by name: exactly one caller gets the value. + +`FIELDS ` introduces the field list and the count must match. The key is deleted when its last field is removed. ## Syntax ```redis -HSTRLEN +HGETDEL FIELDS [ ...] ``` ## Arguments @@ -33026,7 +33021,7 @@ HSTRLEN | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | | `` | Yes | No | Redis key targeted by the command. | -| `` | Yes | No | Hash field name. | +| `FIELDS [ ...]` | Yes | No | Fields to target. Give the field count first, then that many field names. | ## Response @@ -33034,8 +33029,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Integer | -| RESP3 | Integer | +| RESP2 | Array of bulk-string values or null values, one per field | +| RESP3 | Array of bulk-string values or null values, one per field | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -33050,7 +33045,7 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -HSTRLEN my-key field +HGETDEL my-key FIELDS 1 field ``` @@ -33062,7 +33057,16 @@ import { Redis } from "@upstash/redis"; const redis = Redis.fromEnv(); -const length = await redis.hstrlen("key", "field") +// Set some hash fields +await redis.hset("user:123", { name: "John", age: "30", email: "john@example.com" }); + +// Get and delete specific fields +const result = await redis.hgetdel("user:123", "name", "email"); +console.log(result); // { name: "John", email: "john@example.com" } + +// Verify fields were deleted +const name = await redis.hget("user:123", "name"); +console.log(name); // null ``` @@ -33073,7 +33077,7 @@ const length = await redis.hstrlen("key", "field") from upstash_redis import Redis redis = Redis.from_env() -result = redis.hstrlen("my-key", "field") +result = redis.hgetdel("my-key", "field") print(result) ``` @@ -33085,7 +33089,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.hstrlen("my-key", "field"); +const result = await redis.hgetdel("my-key", "FIELDS", "1", "field"); console.log(result); ``` @@ -33099,7 +33103,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.hStrLen("my-key", "field"); +const result = await client.hGetDel("my-key", "field"); console.log(result); ``` @@ -33112,7 +33116,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.hstrlen("my-key", "field") +result = client.hgetdel("my-key", "field") print(result) ``` @@ -33137,7 +33141,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.HStrLen(context.Background(), "my-key", "field").Result() + result, err := client.HGetDel(context.Background(), "my-key", "1", "field").Result() if err != nil { panic(err) } @@ -33155,7 +33159,7 @@ import java.net.URI; import redis.clients.jedis.Jedis; try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.hstrlen("my-key", "field"); + Object result = jedis.hgetdel("my-key", "1", "field"); System.out.println(result); } ``` @@ -33165,15 +33169,14 @@ try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { ```rust +use redis::TypedCommands; + fn main() -> redis::RedisResult<()> { let url = std::env::var("REDIS_URL").expect("REDIS_URL is not set"); let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let mut command = redis::cmd("HSTRLEN"); - command.arg("my-key"); - command.arg("field"); - let result: redis::Value = command.query(&mut connection)?; + let result = connection.hget_del("my-key", &["field"])?; println!("{result:?}"); Ok(()) } @@ -33183,17 +33186,22 @@ fn main() -> redis::RedisResult<()> { -# HTTL -Source: https://upstash.com/docs/redis/commands/hash/httl +# HGETEX +Source: https://upstash.com/docs/redis/commands/hash/hgetex -Use `HTTL` to read how much longer hash fields will live, in seconds. +Use `HGETEX` to read hash fields and change their expiration in the same call. -The reply holds one value per requested field, in order: the remaining lifetime, `-1` when the field exists but has no expiration, and `-2` when the field or the key does not exist, so a missing field is never confused with a permanent one. Use [`HPTTL`](/docs/redis/commands/hash/hpttl) for millisecond precision and [`HEXPIRETIME`](/docs/redis/commands/hash/hexpiretime) when you want the absolute deadline instead of the time left. +Without an expiration option it simply returns the values, like [`HMGET`](/docs/redis/commands/hash/hmget). `EX`, `PX`, `EXAT`, and `PXAT` give every requested field a new lifetime or deadline, and `PERSIST` removes the expiration so the fields stop expiring altogether. + +Doing both in one command is what makes sliding expirations possible per field: reading a session attribute can extend it, with no window in which another client sees the field without its refreshed lifetime. `FIELDS ` introduces the field list and the count must match. ## Syntax ```redis -HTTL FIELDS [ ...] +HGETEX + [EX | PX | EXAT | + PXAT | PERSIST] + FIELDS [ ...] ``` ## Arguments @@ -33201,20 +33209,17 @@ HTTL FIELDS [ ...] | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | | `` | Yes | No | Redis key targeted by the command. | +| `(EX \| PX \| EXAT \| PXAT \| PERSIST)` | No | No | Choose one form: `EX` (set a lifetime in seconds); `PX` (set a lifetime in milliseconds); `EXAT` (expire at a Unix timestamp in seconds); `PXAT` (expire at a Unix timestamp in milliseconds); `PERSIST` (remove the expiration). Left unchanged when omitted. | | `FIELDS [ ...]` | Yes | No | Fields to target. Give the field count first, then that many field names. | -## Important points - -* Negative integer replies are sentinel values, not durations or timestamps; see the response description for missing or persistent data. - ## Response The reply reports the result of the operation. Error replies have the same shape in RESP2 and RESP3 and are surfaced as exceptions by the SDKs below. | Protocol | Reply | | --- | --- | -| RESP2 | Array of TTL values or negative integer status codes, one per field | -| RESP3 | Array of TTL values or negative integer status codes, one per field | +| RESP2 | Array of bulk-string values or null values, one per field | +| RESP3 | Array of bulk-string values or null values, one per field | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -33229,7 +33234,7 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -HTTL my-key FIELDS 1 field +HGETEX my-key FIELDS 1 field ``` @@ -33241,11 +33246,11 @@ import { Redis } from "@upstash/redis"; const redis = Redis.fromEnv(); -await redis.hset("my-key", "my-field", "my-value"); -await redis.hexpire("my-key", "my-field", 10); -const ttl = await redis.httl("my-key", "my-field"); +await redis.hset("user:123", { name: "John", email: "john@example.com" }); -console.log(ttl); // e.g., [9] +// Get fields and set expiration to 60 seconds +const result = await redis.hgetex("user:123", { ex: 60 }, "name", "email"); +console.log(result); // { name: "John", email: "john@example.com" } ``` @@ -33256,7 +33261,7 @@ console.log(ttl); // e.g., [9] from upstash_redis import Redis redis = Redis.from_env() -result = redis.httl("my-key", "field") +result = redis.hgetex("my-key", "field") print(result) ``` @@ -33268,7 +33273,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.httl("my-key", "FIELDS", "1", "field"); +const result = await redis.hgetex("my-key", "FIELDS", "1", "field"); console.log(result); ``` @@ -33282,7 +33287,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.hTTL("my-key", "field"); +const result = await client.hGetEx("my-key", "field"); console.log(result); ``` @@ -33295,7 +33300,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.httl("my-key", "field") +result = client.hgetex("my-key", "field") print(result) ``` @@ -33320,7 +33325,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.HTTL(context.Background(), "my-key", "1", "field").Result() + result, err := client.HGetEX(context.Background(), "my-key", "1", "field").Result() if err != nil { panic(err) } @@ -33338,7 +33343,7 @@ import java.net.URI; import redis.clients.jedis.Jedis; try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.httl("my-key", "1", "field"); + Object result = jedis.hgetex("my-key", redis.clients.jedis.params.HGetExParams.hGetExParams(), "field"); System.out.println(result); } ``` @@ -33355,7 +33360,7 @@ fn main() -> redis::RedisResult<()> { let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let result = connection.httl("my-key", &["field"])?; + let result = connection.hget_ex("my-key", &["field"], redis::Expiry::PERSIST)?; println!("{result:?}"); Ok(()) } @@ -33365,17 +33370,19 @@ fn main() -> redis::RedisResult<()> { -# HVALS -Source: https://upstash.com/docs/redis/commands/hash/hvals +# HINCRBY +Source: https://upstash.com/docs/redis/commands/hash/hincrby -Use `HVALS` to get all the values in a hash, without their field names. +Use `HINCRBY` to add an integer to the number stored in a hash field and get the result. -A missing key returns an empty list, and values come back in no particular order. The whole hash is transferred, so on large hashes prefer [`HSCAN`](/docs/redis/commands/hash/hscan) to walk it in batches, or [`HMGET`](/docs/redis/commands/hash/hmget) when you know which fields you need. +A missing field, or a missing key, is treated as `0`, so the first call creates the hash and the field. The increment may be negative to count down. The stored value must be the string form of a 64-bit signed integer; anything else returns an error, as does an operation that would overflow the range. + +Reading, adding, and writing back happen as one atomic step, so concurrent callers each receive a distinct result and no update is lost. That makes hashes a compact way to keep many related counters, such as per-status counts for one entity, under a single key. ## Syntax ```redis -HVALS +HINCRBY ``` ## Arguments @@ -33383,6 +33390,8 @@ HVALS | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | | `` | Yes | No | Redis key targeted by the command. | +| `` | Yes | No | Hash field name. | +| `` | Yes | No | Integer amount to add to the field. | ## Response @@ -33390,8 +33399,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Array of bulk-string values | -| RESP3 | Array of bulk-string values | +| RESP2 | Integer | +| RESP3 | Integer | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -33406,7 +33415,7 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -HVALS my-key +HINCRBY my-key field 1 ``` @@ -33419,11 +33428,10 @@ import { Redis } from "@upstash/redis"; const redis = Redis.fromEnv(); await redis.hset("key", { - field1: "Hello", - field2: "World", -}) -const values = await redis.hvals("key") -console.log(values) // ["Hello", "World"] + field: 20, + }); +const after = await redis.hincrby("key", "field", 2); +console.log(after); // 22 ``` @@ -33434,7 +33442,7 @@ console.log(values) // ["Hello", "World"] from upstash_redis import Redis redis = Redis.from_env() -result = redis.hvals("my-key") +result = redis.hincrby("my-key", "field", 1) print(result) ``` @@ -33446,7 +33454,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.hvals("my-key"); +const result = await redis.hincrby("my-key", "field", "1"); console.log(result); ``` @@ -33460,7 +33468,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.hVals("my-key"); +const result = await client.hIncrBy("my-key", "field", 1); console.log(result); ``` @@ -33473,7 +33481,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.hvals("my-key") +result = client.hincrby("my-key", "field", 1) print(result) ``` @@ -33498,7 +33506,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.HVals(context.Background(), "my-key").Result() + result, err := client.HIncrBy(context.Background(), "my-key", "field", 1).Result() if err != nil { panic(err) } @@ -33516,7 +33524,7 @@ import java.net.URI; import redis.clients.jedis.Jedis; try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.hvals("my-key"); + Object result = jedis.hincrBy("my-key", "field", 1); System.out.println(result); } ``` @@ -33533,7 +33541,7 @@ fn main() -> redis::RedisResult<()> { let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let result = connection.hvals("my-key")?; + let result = connection.hincr("my-key", "field", 1)?; println!("{result:?}"); Ok(()) } @@ -33543,62 +33551,19 @@ fn main() -> redis::RedisResult<()> { -# Hash commands -Source: https://upstash.com/docs/redis/commands/hash/overview - - -Delete one or more hash fields -Check if a hash field exists -Set field TTL in seconds -Set field expiry as timestamp -Get field expiry as timestamp -Get the value of a hash field -Get all fields and values -Get and delete hash fields -Get fields and set their expiry -Increment integer value of a field -Increment float value of a field -Get all fields in a hash -Get number of fields in a hash -Get values of multiple fields -Set multiple hash fields -Remove field expiration -Set field TTL in milliseconds -Set field expiry as ms timestamp -Get field expiry as ms timestamp -Get field TTL in milliseconds -Get random fields from a hash -Incrementally iterate hash fields -Set hash field values -Set fields with expiration -Set field only if it doesn't exist -Get length of a field's value -Get field TTL in seconds -Get all values in a hash - - -# HyperLogLog commands -Source: https://upstash.com/docs/redis/commands/hyperloglog/overview - - -Add elements to HyperLogLog -Get estimated cardinality -Merge multiple HyperLogLogs - - -# PFADD -Source: https://upstash.com/docs/redis/commands/hyperloglog/pfadd +# HINCRBYFLOAT +Source: https://upstash.com/docs/redis/commands/hash/hincrbyfloat -Use `PFADD` to add elements to a HyperLogLog. +Use `HINCRBYFLOAT` to add a floating point number to the value of a hash field and get the result. -A HyperLogLog estimates how many distinct items it has seen while using a small, fixed amount of memory (at most about 12 KB) no matter how many elements pass through it. That is the trade it makes: individual elements are not stored, so they cannot be listed, checked for membership, or removed, and the cardinality that comes back from [`PFCOUNT`](/docs/redis/commands/hyperloglog/pfcount) is an approximation with a standard error of about 0.81%. +The stored value and the increment are parsed as double precision floats, and a missing field or key counts as `0`. The increment may be negative, and there is no separate decrement command. A value that is not a valid number returns an error. -The key is created on first use, and the reply is `1` when the internal registers changed as a result of the call, which is a hint that at least one element was new, and `0` when they did not. Calling `PFADD` with no elements creates an empty HyperLogLog if the key does not exist yet. Use it for counts where the exact number does not matter, such as unique visitors per page or per day, and a set for cases where you must be able to look elements up. +The reply is the new value as a string, which client libraries usually decode into a native number. Note that the result is stored in the same textual form, so repeated increments of values that cannot be represented exactly in binary floating point accumulate the usual rounding error; keep money and similar quantities in integer units and use [`HINCRBY`](/docs/redis/commands/hash/hincrby). ## Syntax ```redis -PFADD [ [ ...]] +HINCRBYFLOAT ``` ## Arguments @@ -33606,7 +33571,12 @@ PFADD [ [ ...]] | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | | `` | Yes | No | Redis key targeted by the command. | -| `` | No | Yes | Element to add to the HyperLogLog. | +| `` | Yes | No | Hash field name. | +| `` | Yes | No | Amount to add to the field; may be a floating-point number. | + +## Important points + +* The value is always returned as a bulk string, in both RESP2 and RESP3. Client libraries commonly decode it to a language number. ## Response @@ -33614,8 +33584,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Integer: `1` if at least one internal register was altered, `0` otherwise | -| RESP3 | Integer: `1` if at least one internal register was altered, `0` otherwise | +| RESP2 | Bulk string | +| RESP3 | Bulk string | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -33630,7 +33600,7 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -PFADD my-key element +HINCRBYFLOAT my-key field 1.5 ``` @@ -33641,8 +33611,12 @@ PFADD my-key element import { Redis } from "@upstash/redis"; const redis = Redis.fromEnv(); -const result = await redis.pfadd("my-key", "element"); -console.log(result); + +await redis.hset("key", { + field: 20, + }); +const after = await redis.hincrby("key", "field", 2.5); +console.log(after); // 22.5 ``` @@ -33653,7 +33627,7 @@ console.log(result); from upstash_redis import Redis redis = Redis.from_env() -result = redis.pfadd("my-key", "member") +result = redis.hincrbyfloat("my-key", "field", 1.5) print(result) ``` @@ -33665,7 +33639,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.pfadd("my-key", "element"); +const result = await redis.hincrbyfloat("my-key", "field", "1.5"); console.log(result); ``` @@ -33679,7 +33653,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.pfAdd("my-key", "element"); +const result = await client.hIncrByFloat("my-key", "field", 1.5); console.log(result); ``` @@ -33692,7 +33666,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.pfadd("my-key", "member") +result = client.hincrbyfloat("my-key", "field", 1.5) print(result) ``` @@ -33717,7 +33691,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.PFAdd(context.Background(), "my-key").Result() + result, err := client.HIncrByFloat(context.Background(), "my-key", "field", 1.5).Result() if err != nil { panic(err) } @@ -33735,7 +33709,7 @@ import java.net.URI; import redis.clients.jedis.Jedis; try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.pfadd("my-key"); + Object result = jedis.hincrByFloat("my-key", "field", 1.5); System.out.println(result); } ``` @@ -33752,7 +33726,7 @@ fn main() -> redis::RedisResult<()> { let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let result = connection.pfadd("my-key", &["member"])?; + let result = connection.hincr("my-key", "field", 1.5)?; println!("{result:?}"); Ok(()) } @@ -33762,26 +33736,24 @@ fn main() -> redis::RedisResult<()> { -# PFCOUNT -Source: https://upstash.com/docs/redis/commands/hyperloglog/pfcount - -Use `PFCOUNT` to read the estimated number of distinct elements recorded in one or more HyperLogLogs. +# HKEYS +Source: https://upstash.com/docs/redis/commands/hash/hkeys -With a single key the stored estimate is returned. With several keys the structures are merged on the fly and the cardinality of their union is returned, without modifying any of them, which is how you answer "how many unique users across these seven days" without double counting. The result is an approximation with a standard error of about 0.81%. +Use `HKEYS` to get the names of all the fields in a hash, without their values. -The multi-key form does real work on every call, so when the same union is read often it is cheaper to roll the sources up into one key with [`PFMERGE`](/docs/redis/commands/hyperloglog/pfmerge) and count that. +A missing key returns an empty list. The whole field list is built and transferred in one reply, so on large hashes prefer [`HSCAN`](/docs/redis/commands/hash/hscan) with `NOVALUES`, which walks the field names in batches instead. ## Syntax ```redis -PFCOUNT [ ...] +HKEYS ``` ## Arguments | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | -| `` | Yes | Yes | Redis key targeted by the command. | +| `` | Yes | No | Redis key targeted by the command. | ## Response @@ -33789,8 +33761,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Integer | -| RESP3 | Integer | +| RESP2 | Array of bulk-string fields | +| RESP3 | Array of bulk-string fields | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -33805,7 +33777,7 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -PFCOUNT my-key +HKEYS my-key ``` @@ -33816,8 +33788,13 @@ PFCOUNT my-key import { Redis } from "@upstash/redis"; const redis = Redis.fromEnv(); -const result = await redis.pfcount("my-key"); -console.log(result); + +await redis.hset("key", { + id: 1, + username: "chronark", + }); +const fields = await redis.hkeys("key"); +console.log(fields); // ["id", "username"] ``` @@ -33828,7 +33805,7 @@ console.log(result); from upstash_redis import Redis redis = Redis.from_env() -result = redis.pfcount("my-key") +result = redis.hkeys("my-key") print(result) ``` @@ -33840,7 +33817,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.pfcount("my-key"); +const result = await redis.hkeys("my-key"); console.log(result); ``` @@ -33854,7 +33831,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.pfCount("my-key"); +const result = await client.hKeys("my-key"); console.log(result); ``` @@ -33867,7 +33844,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.pfcount("my-key") +result = client.hkeys("my-key") print(result) ``` @@ -33892,7 +33869,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.PFCount(context.Background(), "my-key").Result() + result, err := client.HKeys(context.Background(), "my-key").Result() if err != nil { panic(err) } @@ -33910,7 +33887,7 @@ import java.net.URI; import redis.clients.jedis.Jedis; try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.pfcount("my-key"); + Object result = jedis.hkeys("my-key"); System.out.println(result); } ``` @@ -33927,7 +33904,7 @@ fn main() -> redis::RedisResult<()> { let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let result = connection.pfcount(&["my-key"])?; + let result = connection.hkeys("my-key")?; println!("{result:?}"); Ok(()) } @@ -33937,27 +33914,24 @@ fn main() -> redis::RedisResult<()> { -# PFMERGE -Source: https://upstash.com/docs/redis/commands/hyperloglog/pfmerge - -Use `PFMERGE` to merge several HyperLogLogs into a single one. +# HLEN +Source: https://upstash.com/docs/redis/commands/hash/hlen -The destination ends up representing the union of the source structures and of whatever it already held, so merging the same sources again changes nothing and new data can be folded in as it arrives. The destination is created if it does not exist. +Use `HLEN` to get the number of fields in a hash. -Because the union is computed register by register and loses no accuracy compared with counting the raw data, rolling hourly keys into a daily key, or daily keys into a monthly one, gives the same estimate as if every element had been added to that key directly. That makes `PFMERGE` the building block for time-based rollups of unique counts. +The reply is `0` when the key does not exist. The count is kept by Redis rather than computed, so it is cheap whatever the size of the hash, which makes it the right way to check how big a hash has grown before deciding to read or iterate it. ## Syntax ```redis -PFMERGE [ [ ...]] +HLEN ``` ## Arguments | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | -| `` | Yes | No | Redis key used as destkey. | -| `` | No | Yes | Redis key used as sourcekey. | +| `` | Yes | No | Redis key targeted by the command. | ## Response @@ -33965,8 +33939,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Simple string `OK` | -| RESP3 | Simple string `OK` | +| RESP2 | Integer | +| RESP3 | Integer | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -33981,7 +33955,7 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -PFMERGE destination-key source-key +HLEN my-key ``` @@ -33992,8 +33966,13 @@ PFMERGE destination-key source-key import { Redis } from "@upstash/redis"; const redis = Redis.fromEnv(); -const result = await redis.pfmerge("destination-key", "source-key"); -console.log(result); + +await redis.hset("key", { + id: 1, + username: "chronark", + }); +const fields = await redis.hlen("key"); +console.log(fields); // 2 ``` @@ -34004,7 +33983,7 @@ console.log(result); from upstash_redis import Redis redis = Redis.from_env() -result = redis.pfmerge("destination-key", "source-key") +result = redis.hlen("my-key") print(result) ``` @@ -34016,7 +33995,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.pfmerge("destination-key", "source-key"); +const result = await redis.hlen("my-key"); console.log(result); ``` @@ -34030,7 +34009,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.pfMerge("destination-key", "source-key"); +const result = await client.hLen("my-key"); console.log(result); ``` @@ -34043,7 +34022,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.pfmerge("destination-key", "source-key") +result = client.hlen("my-key") print(result) ``` @@ -34068,7 +34047,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.PFMerge(context.Background(), "my-key").Result() + result, err := client.HLen(context.Background(), "my-key").Result() if err != nil { panic(err) } @@ -34086,7 +34065,7 @@ import java.net.URI; import redis.clients.jedis.Jedis; try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.pfmerge("my-key"); + Object result = jedis.hlen("my-key"); System.out.println(result); } ``` @@ -34103,7 +34082,7 @@ fn main() -> redis::RedisResult<()> { let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let result = connection.pfmerge("destination-key", &["source-key"])?; + let result = connection.hlen("my-key")?; println!("{result:?}"); Ok(()) } @@ -34113,31 +34092,27 @@ fn main() -> redis::RedisResult<()> { -# JSON.ARRAPPEND -Source: https://upstash.com/docs/redis/commands/json/json-arrappend +# HMGET +Source: https://upstash.com/docs/redis/commands/hash/hmget -Use `JSON.ARRAPPEND` to append one or more values to the end of the arrays a path selects. +Use `HMGET` to read several fields of a hash in one call. -Values are JSON text and each one is appended as a single element, so appending an array adds a nested array rather than merging its items. The reply is the new length of each array the path matched, with null for matches that are not arrays. +The reply holds one entry per requested field, in the order requested, with null for fields that do not exist. Asking for fields of a key that does not exist returns a list of nulls rather than an error, so the shape of the reply is always predictable and can be zipped back onto your list of field names. + +It saves the round trips of repeated [`HGET`](/docs/redis/commands/hash/hget) calls and transfers far less than [`HGETALL`](/docs/redis/commands/hash/hgetall) when you only need a few fields of a large hash. ## Syntax ```redis -JSON.ARRAPPEND [value ...] +HMGET [ ...] ``` ## Arguments | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | -| `key` | Yes | No | JSON document key. | -| `path` | Yes | No | Path selecting arrays. | -| `value` | Yes | Yes | Valid JSON value to append. | - -## Important points - -* Raw TCP examples pass JSON values as valid JSON text. Typed Upstash SDK helpers serialize native objects and values for you. -* Paths beginning with `$` use JSONPath and can match multiple values, so many JSON commands return an array of per-match results. +| `` | Yes | No | Redis key targeted by the command. | +| `` | Yes | Yes | Hash field name. | ## Response @@ -34145,8 +34120,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Array of integer lengths or null values, one per matched path | -| RESP3 | Array of integer lengths or null values, one per matched path | +| RESP2 | Array of bulk-string values or null values, one per field | +| RESP3 | Array of bulk-string values or null values, one per field | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -34161,7 +34136,7 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -JSON.ARRAPPEND profile $.tags '"new"' +HMGET my-key field ``` @@ -34173,7 +34148,13 @@ import { Redis } from "@upstash/redis"; const redis = Redis.fromEnv(); -await redis.json.arrappend("key", "$.path.to.array", "a"); +await redis.hset("key", { + id: 1, + username: "chronark", + name: "andreas" + }); +const fields = await redis.hmget("key", "username", "name"); +console.log(fields); // { username: "chronark", name: "andreas" } ``` @@ -34184,7 +34165,7 @@ await redis.json.arrappend("key", "$.path.to.array", "a"); from upstash_redis import Redis redis = Redis.from_env() -result = redis.json().arrappend("profile", "$.tags", "new") +result = redis.hmget("my-key", "field") print(result) ``` @@ -34196,7 +34177,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.call("JSON.ARRAPPEND", "profile", "$.tags", "\"new\""); +const result = await redis.hmget("my-key", "field"); console.log(result); ``` @@ -34210,7 +34191,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.json.arrAppend("profile", "$.tags", "new"); +const result = await client.hmGet("my-key", "field"); console.log(result); ``` @@ -34223,7 +34204,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.json().arrappend("profile", "$.tags", "new") +result = client.hmget("my-key", ["field"]) print(result) ``` @@ -34248,7 +34229,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.JSONArrAppend(context.Background(), "profile", "$.tags", "new").Result() + result, err := client.HMGet(context.Background(), "my-key", "field").Result() if err != nil { panic(err) } @@ -34263,10 +34244,10 @@ func main() { ```java import java.net.URI; -import redis.clients.jedis.JedisPooled; +import redis.clients.jedis.Jedis; -try (JedisPooled jedis = new JedisPooled(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.jsonArrAppend("profile", new redis.clients.jedis.json.Path("$.tags"), "new"); +try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { + Object result = jedis.hmget("my-key", "field"); System.out.println(result); } ``` @@ -34276,14 +34257,14 @@ try (JedisPooled jedis = new JedisPooled(new URI(System.getenv("REDIS_URL")))) { ```rust -use redis::JsonCommands; +use redis::TypedCommands; fn main() -> redis::RedisResult<()> { let url = std::env::var("REDIS_URL").expect("REDIS_URL is not set"); let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let result: redis::Value = connection.json_arr_append("profile", "$.tags", &"new")?; + let result = connection.hmget("my-key", &["field"])?; println!("{result:?}"); Ok(()) } @@ -34293,33 +34274,29 @@ fn main() -> redis::RedisResult<()> { -# JSON.ARRINDEX -Source: https://upstash.com/docs/redis/commands/json/json-arrindex +# HMSET +Source: https://upstash.com/docs/redis/commands/hash/hmset -Use `JSON.ARRINDEX` to find the first position of a value inside the arrays a path selects. + + Prefer [`HSET`](/docs/redis/commands/hash/hset) with multiple field-value pairs in new code: `HSET [ ...]`. + -The value is JSON text and is compared for exact equality, so `1` does not match `"1"`. The optional `start` and `stop` bound the search: `start` is inclusive, `stop` is exclusive, both may be negative to count from the end of the array, and `0` for `stop` means "to the end". The reply is the index of the first match or `-1` when the value is not present, with one result per array the path matched. +Use `HMSET` to set several field and value pairs of a hash in one call, creating the key if it does not exist. + +Existing fields are overwritten and the reply is always `OK`, so it says nothing about what changed. [`HSET`](/docs/redis/commands/hash/hset) accepts multiple pairs as well and additionally reports how many fields were new, so prefer it in new code. ## Syntax ```redis -JSON.ARRINDEX [start [stop]] +HMSET [ ...] ``` ## Arguments | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | -| `key` | Yes | No | JSON document key. | -| `path` | Yes | No | Path selecting arrays. | -| `value` | Yes | No | Valid JSON value to locate. | -| `start` | No | No | Inclusive starting index. | -| `stop` | No | No | Exclusive ending index. | - -## Important points - -* Raw TCP examples pass JSON values as valid JSON text. Typed Upstash SDK helpers serialize native objects and values for you. -* Paths beginning with `$` use JSONPath and can match multiple values, so many JSON commands return an array of per-match results. +| `` | Yes | No | Redis key targeted by the command. | +| ` ` | Yes | Yes | Field and the value to store in it. Repeat to set several fields in one call. | ## Response @@ -34327,8 +34304,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Array of integer indexes or null values, one per matched path | -| RESP3 | Array of integer indexes or null values, one per matched path | +| RESP2 | Simple string `OK` | +| RESP3 | Simple string `OK` | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -34343,7 +34320,7 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -JSON.ARRINDEX profile $.tags '"new"' +HMSET my-key field value ``` @@ -34354,8 +34331,8 @@ JSON.ARRINDEX profile $.tags '"new"' import { Redis } from "@upstash/redis"; const redis = Redis.fromEnv(); - -const index = await redis.json.arrindex("key", "$.path.to.array", "a"); +const result = await redis.hmset("my-key", { field: "value" }); +console.log(result); ``` @@ -34366,7 +34343,7 @@ const index = await redis.json.arrindex("key", "$.path.to.array", "a"); from upstash_redis import Redis redis = Redis.from_env() -result = redis.json().arrindex("profile", "$.tags", "new") +result = redis.hmset("my-key", {"field": "value"}) print(result) ``` @@ -34378,7 +34355,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.call("JSON.ARRINDEX", "profile", "$.tags", "\"new\""); +const result = await redis.hmset("my-key", "field", "value"); console.log(result); ``` @@ -34392,7 +34369,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.json.arrIndex("profile", "$.tags", "new"); +const result = await client.hSet("my-key", { field: "value" }); console.log(result); ``` @@ -34405,7 +34382,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.json().arrindex("profile", "$.tags", "new") +result = client.hset("my-key", mapping={"field": "value"}) print(result) ``` @@ -34430,7 +34407,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.JSONArrIndex(context.Background(), "profile", "$.tags", "new").Result() + result, err := client.HMSet(context.Background(), "my-key", "field", "value").Result() if err != nil { panic(err) } @@ -34445,10 +34422,10 @@ func main() { ```java import java.net.URI; -import redis.clients.jedis.JedisPooled; +import redis.clients.jedis.Jedis; -try (JedisPooled jedis = new JedisPooled(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.jsonArrIndex("profile", new redis.clients.jedis.json.Path("$.tags"), "new"); +try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { + Object result = jedis.hmset("my-key", java.util.Map.of("field", "value")); System.out.println(result); } ``` @@ -34458,14 +34435,14 @@ try (JedisPooled jedis = new JedisPooled(new URI(System.getenv("REDIS_URL")))) { ```rust -use redis::JsonCommands; +use redis::TypedCommands; fn main() -> redis::RedisResult<()> { let url = std::env::var("REDIS_URL").expect("REDIS_URL is not set"); let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let result: redis::Value = connection.json_arr_index("profile", "$.tags", &"new")?; + let result = connection.hset_multiple("my-key", &[("field", "value")])?; println!("{result:?}"); Ok(()) } @@ -34475,32 +34452,27 @@ fn main() -> redis::RedisResult<()> { -# JSON.ARRINSERT -Source: https://upstash.com/docs/redis/commands/json/json-arrinsert +# HPERSIST +Source: https://upstash.com/docs/redis/commands/hash/hpersist -Use `JSON.ARRINSERT` to insert one or more values into the arrays a path selects, before a given index. +Use `HPERSIST` to remove the expiration from hash fields so that they stop being deleted automatically. -Elements at and after that index shift to the right, keeping the rest of the array in order. A negative index counts from the end of the array and an index equal to the array's length appends, while an index outside the array returns an error. The reply is the new length of each array the path matched. +The reply holds one status code per requested field, in order: `1` when an expiration was removed, `-1` when the field exists but had no expiration, and `-2` when the field or the key does not exist. This is how a field set by [`HEXPIRE`](/docs/redis/commands/hash/hexpire) or [`HSETEX`](/docs/redis/commands/hash/hsetex) is promoted from temporary to permanent without rewriting its value. + +`FIELDS ` introduces the field list and the count must match. ## Syntax ```redis -JSON.ARRINSERT [value ...] +HPERSIST FIELDS [ ...] ``` ## Arguments | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | -| `key` | Yes | No | JSON document key. | -| `path` | Yes | No | Path selecting arrays. | -| `index` | Yes | No | Insertion index; negative indexes count from the end. | -| `value` | Yes | Yes | Valid JSON value to insert. | - -## Important points - -* Raw TCP examples pass JSON values as valid JSON text. Typed Upstash SDK helpers serialize native objects and values for you. -* Paths beginning with `$` use JSONPath and can match multiple values, so many JSON commands return an array of per-match results. +| `` | Yes | No | Redis key targeted by the command. | +| `FIELDS [ ...]` | Yes | No | Fields to target. Give the field count first, then that many field names. | ## Response @@ -34508,8 +34480,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Array of integer lengths or null values, one per matched path | -| RESP3 | Array of integer lengths or null values, one per matched path | +| RESP2 | Array of integer status codes, one per field | +| RESP3 | Array of integer status codes, one per field | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -34524,7 +34496,7 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -JSON.ARRINSERT profile $.tags 0 '"first"' +HPERSIST my-key FIELDS 1 field ``` @@ -34536,7 +34508,12 @@ import { Redis } from "@upstash/redis"; const redis = Redis.fromEnv(); -const length = await redis.json.arrinsert("key", "$.path.to.array", 2, "a", "b"); +await redis.hset("my-key", "my-field", "my-value"); +await redis.hpexpire("my-key", "my-field", 1000); + +const expirationRemoved = await redis.hpersist("my-key", "my-field"); + +console.log(expirationRemoved); // [1] ``` @@ -34547,7 +34524,7 @@ const length = await redis.json.arrinsert("key", "$.path.to.array", 2, "a", "b") from upstash_redis import Redis redis = Redis.from_env() -result = redis.json().arrinsert("profile", "$.tags", 0, "first") +result = redis.hpersist("my-key", "field") print(result) ``` @@ -34559,7 +34536,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.call("JSON.ARRINSERT", "profile", "$.tags", "0", "\"first\""); +const result = await redis.hpersist("my-key", "FIELDS", "1", "field"); console.log(result); ``` @@ -34573,7 +34550,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.json.arrInsert("profile", "$.tags", 0, "first"); +const result = await client.hPersist("my-key", "field"); console.log(result); ``` @@ -34586,7 +34563,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.json().arrinsert("profile", "$.tags", 0, "first") +result = client.hpersist("my-key", "field") print(result) ``` @@ -34611,7 +34588,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.JSONArrInsert(context.Background(), "profile", "$.tags", 0, "first").Result() + result, err := client.HPersist(context.Background(), "my-key", "1", "field").Result() if err != nil { panic(err) } @@ -34626,10 +34603,10 @@ func main() { ```java import java.net.URI; -import redis.clients.jedis.JedisPooled; +import redis.clients.jedis.Jedis; -try (JedisPooled jedis = new JedisPooled(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.jsonArrInsert("profile", new redis.clients.jedis.json.Path("$.tags"), 0, "first"); +try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { + Object result = jedis.hpersist("my-key", "1", "field"); System.out.println(result); } ``` @@ -34639,14 +34616,14 @@ try (JedisPooled jedis = new JedisPooled(new URI(System.getenv("REDIS_URL")))) { ```rust -use redis::JsonCommands; +use redis::TypedCommands; fn main() -> redis::RedisResult<()> { let url = std::env::var("REDIS_URL").expect("REDIS_URL is not set"); let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let result: redis::Value = connection.json_arr_insert("profile", "$.tags", 0, &"first")?; + let result = connection.hpersist("my-key", &["field"])?; println!("{result:?}"); Ok(()) } @@ -34656,30 +34633,36 @@ fn main() -> redis::RedisResult<()> { -# JSON.ARRLEN -Source: https://upstash.com/docs/redis/commands/json/json-arrlen +# HPEXPIRE +Source: https://upstash.com/docs/redis/commands/hash/hpexpire -Use `JSON.ARRLEN` to get the number of elements in the arrays a path selects. +Use `HPEXPIRE` to give individual hash fields a lifetime in milliseconds, after which those fields are removed from the hash. -Without a path the root value is used. The reply is one length per match, with null for matches that are not arrays, so it doubles as a cheap way to check that a branch of the document really is an array before working on it. +It is the millisecond form of [`HEXPIRE`](/docs/redis/commands/hash/hexpire) and behaves identically otherwise: expiration is per field, the hash survives as long as it has fields, and the key disappears when the last field expires. The finer precision matters for short-lived fields such as per-field locks or rate limit windows. + +`FIELDS ` introduces the field list and the count must match. The optional condition applies the new lifetime only in certain cases: `NX` when the field has no expiration, `XX` when it already has one, `GT` when the new expiration is later than the current one, and `LT` when it is earlier. The reply holds one status code per field: `1` when set, `0` when the condition prevented it, `2` when the field was deleted immediately, and `-2` when it does not exist. ## Syntax ```redis -JSON.ARRLEN [path] +HPEXPIRE + [NX | XX | GT | LT] + FIELDS [ ...] ``` ## Arguments | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | -| `key` | Yes | No | JSON document key. | -| `path` | No | No | Path selecting arrays; defaults to the root. | +| `` | Yes | No | Redis key targeted by the command. | +| `` | Yes | No | Lifetime in milliseconds. | +| `(NX \| XX \| GT \| LT)` | No | No | Choose one form: `NX` (only when the field has no expiration); `XX` (only when the field already has one); `GT` (only when the new expiration is later than the current one); `LT` (only when it is earlier). | +| `FIELDS [ ...]` | Yes | No | Fields to target. Give the field count first, then that many field names. | ## Important points -* Raw TCP examples pass JSON values as valid JSON text. Typed Upstash SDK helpers serialize native objects and values for you. -* Paths beginning with `$` use JSONPath and can match multiple values, so many JSON commands return an array of per-match results. +* `NX` cannot be combined with `XX`, `GT`, or `LT`, and `GT` and `LT` cannot be used together. +* A field with no expiration counts as an infinite one, so `GT` never sets an expiration on such a field and `LT` always does. ## Response @@ -34687,8 +34670,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Array of integer lengths or null values, one per matched path | -| RESP3 | Array of integer lengths or null values, one per matched path | +| RESP2 | Array of integer status codes, one per field | +| RESP3 | Array of integer status codes, one per field | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -34703,7 +34686,7 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -JSON.ARRLEN profile $.tags +HPEXPIRE my-key 1000 FIELDS 1 field ``` @@ -34715,7 +34698,10 @@ import { Redis } from "@upstash/redis"; const redis = Redis.fromEnv(); -const length = await redis.json.arrlen("key", "$.path.to.array"); +await redis.hset("my-key", "my-field", "my-value"); +const expirationSet = await redis.hpexpire("my-key", "my-field", 1000); + +console.log(expirationSet); // [1] ``` @@ -34726,7 +34712,7 @@ const length = await redis.json.arrlen("key", "$.path.to.array"); from upstash_redis import Redis redis = Redis.from_env() -result = redis.json().arrlen("profile", "$.tags") +result = redis.hpexpire("my-key", "field", 1000) print(result) ``` @@ -34738,7 +34724,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.call("JSON.ARRLEN", "profile", "$.tags"); +const result = await redis.hpexpire("my-key", "1000", "FIELDS", "1", "field"); console.log(result); ``` @@ -34752,7 +34738,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.json.arrLen("profile", { path: "$.tags" }); +const result = await client.hpExpire("my-key", "field", 1000); console.log(result); ``` @@ -34765,7 +34751,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.json().arrlen("profile", "$.tags") +result = client.hpexpire("my-key", 1000, "field") print(result) ``` @@ -34790,7 +34776,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.JSONArrLen(context.Background(), "profile", "$.tags").Result() + result, err := client.HPExpire(context.Background(), "my-key", time.Second, "field").Result() if err != nil { panic(err) } @@ -34805,10 +34791,10 @@ func main() { ```java import java.net.URI; -import redis.clients.jedis.JedisPooled; +import redis.clients.jedis.Jedis; -try (JedisPooled jedis = new JedisPooled(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.jsonArrLen("profile", new redis.clients.jedis.json.Path("$.tags")); +try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { + Object result = jedis.hpexpire("my-key", 1000, "field"); System.out.println(result); } ``` @@ -34818,14 +34804,14 @@ try (JedisPooled jedis = new JedisPooled(new URI(System.getenv("REDIS_URL")))) { ```rust -use redis::JsonCommands; +use redis::TypedCommands; fn main() -> redis::RedisResult<()> { let url = std::env::var("REDIS_URL").expect("REDIS_URL is not set"); let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let result: redis::Value = connection.json_arr_len("profile", "$.tags")?; + let result = connection.hpexpire("my-key", 1000, redis::ExpireOption::NONE, &["field"])?; println!("{result:?}"); Ok(()) } @@ -34835,33 +34821,36 @@ fn main() -> redis::RedisResult<()> { -# JSON.ARRPOP -Source: https://upstash.com/docs/redis/commands/json/json-arrpop +# HPEXPIREAT +Source: https://upstash.com/docs/redis/commands/hash/hpexpireat -Use `JSON.ARRPOP` to remove an element from the arrays a path selects and return it. +Use `HPEXPIREAT` to schedule individual hash fields for deletion at a fixed point in time, given as a Unix timestamp in milliseconds. -Without an index the last element is popped, which makes the command a stack pop; index `0` pops the first element, and negative indexes count from the end. An index past the end of the array is clamped to the last element. The reply is the removed element as JSON text, or null when the array is empty. +It combines the absolute deadline of [`HEXPIREAT`](/docs/redis/commands/hash/hexpireat) with millisecond precision, which is what you need when fields spread over several hashes have to expire at exactly the same instant. A timestamp in the past removes the fields right away, and the key is deleted when its last field expires. -Because the read and the removal happen in one atomic step, a JSON array can be used as a small work queue without the risk of two clients taking the same element. +`FIELDS ` introduces the field list and the count must match. The optional condition applies the deadline only when the field has no expiration (`NX`), already has one (`XX`), or when the new deadline is later (`GT`) or earlier (`LT`) than the current one. The reply holds one status code per field: `1` when set, `0` when the condition prevented it, `2` when the field was deleted immediately, and `-2` when it does not exist. ## Syntax ```redis -JSON.ARRPOP [path [index]] +HPEXPIREAT + [NX | XX | GT | LT] + FIELDS [ ...] ``` ## Arguments | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | -| `key` | Yes | No | JSON document key. | -| `path` | No | No | Path selecting arrays; defaults to the root. | -| `index` | No | No | Element index; defaults to the last element. | +| `` | Yes | No | Redis key targeted by the command. | +| `` | Yes | No | Expiration time as a Unix timestamp in milliseconds. | +| `(NX \| XX \| GT \| LT)` | No | No | Choose one form: `NX` (only when the field has no expiration); `XX` (only when the field already has one); `GT` (only when the new expiration is later than the current one); `LT` (only when it is earlier). | +| `FIELDS [ ...]` | Yes | No | Fields to target. Give the field count first, then that many field names. | ## Important points -* Raw TCP examples pass JSON values as valid JSON text. Typed Upstash SDK helpers serialize native objects and values for you. -* Paths beginning with `$` use JSONPath and can match multiple values, so many JSON commands return an array of per-match results. +* `NX` cannot be combined with `XX`, `GT`, or `LT`, and `GT` and `LT` cannot be used together. +* A field with no expiration counts as an infinite one, so `GT` never sets an expiration on such a field and `LT` always does. ## Response @@ -34869,8 +34858,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Array of bulk-string JSON values or null values, one per matched path | -| RESP3 | Array of bulk-string JSON values or null values, one per matched path | +| RESP2 | Array of integer status codes, one per field | +| RESP3 | Array of integer status codes, one per field | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -34885,7 +34874,7 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -JSON.ARRPOP profile $.tags -1 +HPEXPIREAT my-key 1735689600 FIELDS 1 field ``` @@ -34897,7 +34886,10 @@ import { Redis } from "@upstash/redis"; const redis = Redis.fromEnv(); -const element = await redis.json.arrpop("key", "$.path.to.array"); +await redis.hset("my-key", "my-field", "my-value"); +const expirationSet = await redis.hpexpireat("my-key", "my-field", Date.now() + 1000); + +console.log(expirationSet); // [1] ``` @@ -34908,7 +34900,7 @@ const element = await redis.json.arrpop("key", "$.path.to.array"); from upstash_redis import Redis redis = Redis.from_env() -result = redis.json().arrpop("profile", "$.tags", -1) +result = redis.hpexpireat("my-key", "field", 1735689600) print(result) ``` @@ -34920,7 +34912,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.call("JSON.ARRPOP", "profile", "$.tags", "-1"); +const result = await redis.hpexpireat("my-key", "1735689600", "FIELDS", "1", "field"); console.log(result); ``` @@ -34934,7 +34926,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.json.arrPop("profile", { path: "$.tags", index: -1 }); +const result = await client.hpExpireAt("my-key", "field", 1735689600); console.log(result); ``` @@ -34947,7 +34939,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.json().arrpop("profile", "$.tags", -1) +result = client.hpexpireat("my-key", 1735689600, "field") print(result) ``` @@ -34972,7 +34964,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.JSONArrPop(context.Background(), "profile", "$.tags", -1).Result() + result, err := client.HPExpireAt(context.Background(), "my-key", time.UnixMilli(1735689600), "field").Result() if err != nil { panic(err) } @@ -34987,10 +34979,10 @@ func main() { ```java import java.net.URI; -import redis.clients.jedis.JedisPooled; +import redis.clients.jedis.Jedis; -try (JedisPooled jedis = new JedisPooled(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.jsonArrPop("profile", new redis.clients.jedis.json.Path("$.tags"), -1); +try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { + Object result = jedis.hpexpireAt("my-key", 1735689600, "field"); System.out.println(result); } ``` @@ -35000,14 +34992,14 @@ try (JedisPooled jedis = new JedisPooled(new URI(System.getenv("REDIS_URL")))) { ```rust -use redis::JsonCommands; +use redis::TypedCommands; fn main() -> redis::RedisResult<()> { let url = std::env::var("REDIS_URL").expect("REDIS_URL is not set"); let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let result: redis::Value = connection.json_arr_pop("profile", "$.tags", -1)?; + let result = connection.hpexpire_at("my-key", 1735689600, redis::ExpireOption::NONE, &["field"])?; println!("{result:?}"); Ok(()) } @@ -35017,34 +35009,29 @@ fn main() -> redis::RedisResult<()> { -# JSON.ARRTRIM -Source: https://upstash.com/docs/redis/commands/json/json-arrtrim - -Use `JSON.ARRTRIM` to keep only a range of elements in the arrays a path selects and discard the rest. +# HPEXPIRETIME +Source: https://upstash.com/docs/redis/commands/hash/hpexpiretime -Both `start` and `stop` are inclusive indexes and may be negative to count from the end of the array. Indexes outside the array are clamped, and a range that selects nothing leaves an empty array. The reply is the new length of each array the path matched. +Use `HPEXPIRETIME` to read the absolute expiration time of hash fields, as Unix timestamps in milliseconds. -It is the JSON counterpart of [`LTRIM`](/docs/redis/commands/list/ltrim): combine it with [`JSON.ARRAPPEND`](/docs/redis/commands/json/json-arrappend) to keep a capped list, such as the last N events, inside a document. +The reply holds one value per requested field, in order: the timestamp at which it expires, `-1` when the field exists but has no expiration, and `-2` when the field or the key does not exist. It is the millisecond form of [`HEXPIRETIME`](/docs/redis/commands/hash/hexpiretime) and reports a deadline rather than the time left, which is the value to compare against a clock. ## Syntax ```redis -JSON.ARRTRIM +HPEXPIRETIME FIELDS [ ...] ``` ## Arguments | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | -| `key` | Yes | No | JSON document key. | -| `path` | Yes | No | Path selecting arrays. | -| `start` | Yes | No | Inclusive first index to keep. | -| `stop` | Yes | No | Inclusive last index to keep. | +| `` | Yes | No | Redis key targeted by the command. | +| `FIELDS [ ...]` | Yes | No | Fields to target. Give the field count first, then that many field names. | ## Important points -* Raw TCP examples pass JSON values as valid JSON text. Typed Upstash SDK helpers serialize native objects and values for you. -* Paths beginning with `$` use JSONPath and can match multiple values, so many JSON commands return an array of per-match results. +* Negative integer replies are sentinel values, not durations or timestamps; see the response description for missing or persistent data. ## Response @@ -35052,8 +35039,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Array of integer lengths or null values, one per matched path | -| RESP3 | Array of integer lengths or null values, one per matched path | +| RESP2 | Array of expiration timestamps or negative integer status codes, one per field | +| RESP3 | Array of expiration timestamps or negative integer status codes, one per field | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -35068,7 +35055,7 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -JSON.ARRTRIM profile $.tags 0 9 +HPEXPIRETIME my-key FIELDS 1 field ``` @@ -35080,7 +35067,11 @@ import { Redis } from "@upstash/redis"; const redis = Redis.fromEnv(); -const length = await redis.json.arrtrim("key", "$.path.to.array", 2, 10); +await redis.hset("my-key", "my-field", "my-value"); +await redis.hpexpireat("my-key", "my-field", Date.now() + 1000); +const expireTime = await redis.hpexpiretime("my-key", "my-field"); + +console.log(expireTime); // e.g., 1697059200000 ``` @@ -35091,7 +35082,7 @@ const length = await redis.json.arrtrim("key", "$.path.to.array", 2, 10); from upstash_redis import Redis redis = Redis.from_env() -result = redis.json().arrtrim("profile", "$.tags", 0, 9) +result = redis.hpexpiretime("my-key", "field") print(result) ``` @@ -35103,7 +35094,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.call("JSON.ARRTRIM", "profile", "$.tags", "0", "9"); +const result = await redis.hpexpiretime("my-key", "FIELDS", "1", "field"); console.log(result); ``` @@ -35117,7 +35108,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.json.arrTrim("profile", "$.tags", 0, 9); +const result = await client.hpExpireTime("my-key", "field"); console.log(result); ``` @@ -35130,7 +35121,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.json().arrtrim("profile", "$.tags", 0, 9) +result = client.hpexpiretime("my-key", "field") print(result) ``` @@ -35155,7 +35146,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.JSONArrTrimWithArgs(context.Background(), "profile", "$.tags", &redis.JSONArrTrimArgs{Start: 0, Stop: func() *int { stop := 9; return &stop }()}).Result() + result, err := client.HPExpireTime(context.Background(), "my-key", "1", "field").Result() if err != nil { panic(err) } @@ -35170,10 +35161,10 @@ func main() { ```java import java.net.URI; -import redis.clients.jedis.JedisPooled; +import redis.clients.jedis.Jedis; -try (JedisPooled jedis = new JedisPooled(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.jsonArrTrim("profile", new redis.clients.jedis.json.Path("$.tags"), 0, 9); +try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { + Object result = jedis.hpexpireTime("my-key", "1", "field"); System.out.println(result); } ``` @@ -35183,14 +35174,14 @@ try (JedisPooled jedis = new JedisPooled(new URI(System.getenv("REDIS_URL")))) { ```rust -use redis::JsonCommands; +use redis::TypedCommands; fn main() -> redis::RedisResult<()> { let url = std::env::var("REDIS_URL").expect("REDIS_URL is not set"); let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let result: redis::Value = connection.json_arr_trim("profile", "$.tags", 0, 9)?; + let result = connection.hpexpire_time("my-key", &["field"])?; println!("{result:?}"); Ok(()) } @@ -35200,32 +35191,29 @@ fn main() -> redis::RedisResult<()> { -# JSON.CLEAR -Source: https://upstash.com/docs/redis/commands/json/json-clear - -Use `JSON.CLEAR` to empty the values a path selects without removing them from the document. +# HPTTL +Source: https://upstash.com/docs/redis/commands/hash/hpttl -Objects lose all their keys, arrays lose all their elements, and numbers are reset to `0`. Values of other types, such as strings and booleans, are left as they are. The reply is the number of values that were cleared. +Use `HPTTL` to read how much longer hash fields will live, in milliseconds. -The difference from [`JSON.DEL`](/docs/redis/commands/json/json-del) is that the selected keys and slots stay in the document as empty containers, so the shape of the document is preserved and consumers that expect a field to exist keep working. +The reply holds one value per requested field, in order: the remaining lifetime, `-1` when the field exists but has no expiration, and `-2` when the field or the key does not exist. It is the millisecond form of [`HTTL`](/docs/redis/commands/hash/httl), and the extra precision matters for fields that live for less than a second. ## Syntax ```redis -JSON.CLEAR [path] +HPTTL FIELDS [ ...] ``` ## Arguments | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | -| `key` | Yes | No | JSON document key. | -| `path` | No | No | Path to containers or numbers; defaults to the root. | +| `` | Yes | No | Redis key targeted by the command. | +| `FIELDS [ ...]` | Yes | No | Fields to target. Give the field count first, then that many field names. | ## Important points -* Raw TCP examples pass JSON values as valid JSON text. Typed Upstash SDK helpers serialize native objects and values for you. -* Paths beginning with `$` use JSONPath and can match multiple values, so many JSON commands return an array of per-match results. +* Negative integer replies are sentinel values, not durations or timestamps; see the response description for missing or persistent data. ## Response @@ -35233,8 +35221,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Integer | -| RESP3 | Integer | +| RESP2 | Array of TTL values or negative integer status codes, one per field | +| RESP3 | Array of TTL values or negative integer status codes, one per field | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -35249,7 +35237,7 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -JSON.CLEAR profile $.stats +HPTTL my-key FIELDS 1 field ``` @@ -35261,7 +35249,11 @@ import { Redis } from "@upstash/redis"; const redis = Redis.fromEnv(); -await redis.json.clear("key"); +await redis.hset("my-key", "my-field", "my-value"); +await redis.hpexpire("my-key", "my-field", 1000); +const ttl = await redis.hpttl("my-key", "my-field"); + +console.log(ttl); // e.g., [950] ``` @@ -35272,7 +35264,7 @@ await redis.json.clear("key"); from upstash_redis import Redis redis = Redis.from_env() -result = redis.json().clear("profile", "$.stats") +result = redis.hpttl("my-key", "field") print(result) ``` @@ -35284,7 +35276,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.call("JSON.CLEAR", "profile", "$.stats"); +const result = await redis.hpttl("my-key", "FIELDS", "1", "field"); console.log(result); ``` @@ -35298,7 +35290,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.json.clear("profile", { path: "$.stats" }); +const result = await client.hpTTL("my-key", "field"); console.log(result); ``` @@ -35311,7 +35303,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.json().clear("profile", "$.stats") +result = client.hpttl("my-key", "field") print(result) ``` @@ -35336,7 +35328,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.JSONClear(context.Background(), "profile", "$.stats").Result() + result, err := client.HPTTL(context.Background(), "my-key", "1", "field").Result() if err != nil { panic(err) } @@ -35351,10 +35343,10 @@ func main() { ```java import java.net.URI; -import redis.clients.jedis.JedisPooled; +import redis.clients.jedis.Jedis; -try (JedisPooled jedis = new JedisPooled(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.jsonClear("profile", new redis.clients.jedis.json.Path("$.stats")); +try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { + Object result = jedis.hpttl("my-key", "1", "field"); System.out.println(result); } ``` @@ -35364,14 +35356,14 @@ try (JedisPooled jedis = new JedisPooled(new URI(System.getenv("REDIS_URL")))) { ```rust -use redis::JsonCommands; +use redis::TypedCommands; fn main() -> redis::RedisResult<()> { let url = std::env::var("REDIS_URL").expect("REDIS_URL is not set"); let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let result: redis::Value = connection.json_clear("profile", "$.stats")?; + let result = connection.hpttl("my-key", &["field"])?; println!("{result:?}"); Ok(()) } @@ -35381,35 +35373,31 @@ fn main() -> redis::RedisResult<()> { -# JSON.DEBUG -Source: https://upstash.com/docs/redis/commands/json/json-debug +# HRANDFIELD +Source: https://upstash.com/docs/redis/commands/hash/hrandfield -Use `JSON.DEBUG` to inspect internal details of stored JSON values. +Use `HRANDFIELD` to get one or more random fields from a hash. -`JSON.DEBUG MEMORY` reports the approximate number of bytes used by the value a key and optional path select, which is how you find out which documents, or which branches of a document, are responsible for memory growth. The figure includes internal overhead and is an estimate meant for comparison rather than exact accounting. `JSON.DEBUG HELP` lists the supported forms. +Without a count a single field name is returned, or null when the key does not exist. A positive count returns up to that many distinct fields, capped at the size of the hash, while a negative count returns exactly that many fields chosen independently, so the same field can come up more than once. `WITHVALUES` returns each field together with its value. -It is a diagnostic aid: the details it exposes are implementation-specific and can change, so do not build application logic on them. +Nothing is removed from the hash, which is the difference from [`HGETDEL`](/docs/redis/commands/hash/hgetdel): use this for sampling, random selection, and quick inspection of an unfamiliar hash. ## Syntax ```redis -JSON.DEBUG MEMORY [path] -JSON.DEBUG HELP +HRANDFIELD [ [WITHVALUES]] ``` ## Arguments | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | -| `MEMORY` | One form | No | Report the approximate memory used by the JSON value selected by key and optional path. | -| `key` | For MEMORY | No | JSON document key. | -| `path` | No | No | JSONPath to inspect; defaults to the root. | -| `HELP` | One form | No | Return the supported JSON.DEBUG forms. | +| `` | Yes | No | Redis key targeted by the command. | +| ` [WITHVALUES]` | No | No | Number of fields to return; a negative count may repeat fields. `WITHVALUES` also returns each field's value. | ## Important points -* Raw TCP examples pass JSON values as valid JSON text. Typed Upstash SDK helpers serialize native objects and values for you. -* Paths beginning with `$` use JSONPath and can match multiple values, so many JSON commands return an array of per-match results. +* Pair-based results may be flattened into one alternating array in RESP2 while RESP3 preserves nested pairs or a map. ## Response @@ -35417,8 +35405,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Integer, array of integers, or array of help strings | -| RESP3 | Integer, array of integers, or array of help strings | +| RESP2 | Null bulk string or null array, Bulk string, array of fields, or flat field/value array | +| RESP3 | Null, Bulk string, array of fields, or array of field/value pairs | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -35433,24 +35421,38 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -JSON.DEBUG MEMORY profile $.stats +HRANDFIELD my-key ``` - - This command is not supported yet in `@upstash/redis`. - +```ts +import { Redis } from "@upstash/redis"; + +const redis = Redis.fromEnv(); + +await redis.hset("key", { + id: 1, + username: "chronark", + name: "andreas" + }); +const randomField = await redis.hrandfield("key"); +console.log(randomField); // one of "id", "username" or "name" +``` - - This command is not supported yet in `upstash_redis`. - +```python +from upstash_redis import Redis + +redis = Redis.from_env() +result = redis.hrandfield("my-key") +print(result) +``` @@ -35460,7 +35462,7 @@ JSON.DEBUG MEMORY profile $.stats import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.call("JSON.DEBUG", "MEMORY", "profile", "$.stats"); +const result = await redis.hrandfield("my-key"); console.log(result); ``` @@ -35474,7 +35476,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.json.debugMemory("profile", { path: "$.stats" }); +const result = await client.hRandField("my-key"); console.log(result); ``` @@ -35487,7 +35489,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.json().debug("MEMORY", "profile", "$.stats") +result = client.hrandfield("my-key") print(result) ``` @@ -35512,7 +35514,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.JSONDebugMemory(context.Background(), "profile", "$.stats").Result() + result, err := client.HRandField(context.Background(), "my-key", 1).Result() if err != nil { panic(err) } @@ -35527,10 +35529,10 @@ func main() { ```java import java.net.URI; -import redis.clients.jedis.JedisPooled; +import redis.clients.jedis.Jedis; -try (JedisPooled jedis = new JedisPooled(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.jsonDebugMemory("profile", new redis.clients.jedis.json.Path("$.stats")); +try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { + Object result = jedis.hrandfield("my-key"); System.out.println(result); } ``` @@ -35545,10 +35547,8 @@ fn main() -> redis::RedisResult<()> { let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let mut command = redis::cmd("JSON.DEBUG"); - command.arg("MEMORY"); - command.arg("profile"); - command.arg("$.stats"); + let mut command = redis::cmd("HRANDFIELD"); + command.arg("my-key"); let result: redis::Value = command.query(&mut connection)?; println!("{result:?}"); Ok(()) @@ -35559,32 +35559,35 @@ fn main() -> redis::RedisResult<()> { -# JSON.DEL -Source: https://upstash.com/docs/redis/commands/json/json-del +# HSCAN +Source: https://upstash.com/docs/redis/commands/hash/hscan -Use `JSON.DEL` to delete the value at a path in a JSON document. +Use `HSCAN` to iterate the fields of a hash in batches instead of reading it all at once. -Without a path the entire key is deleted. The reply is the number of values that were deleted, which is `0` when the path matched nothing, and deleting the root of a document removes the key itself. With a JSONPath that matches several places, every match is removed in the same call. +Each call takes a cursor and returns the next cursor together with a batch of field and value pairs. Start at cursor `0` and keep calling with the cursor from the previous reply until the server returns `0`, which ends the iteration. Because each call does a bounded amount of work, this avoids the long single reply that [`HGETALL`](/docs/redis/commands/hash/hgetall) produces on a large hash. -[`JSON.FORGET`](/docs/redis/commands/json/json-forget) is an alias with identical behavior. +`MATCH` filters field names with a glob-style pattern, `COUNT` hints at how much work each call should do, and `NOVALUES` returns field names only, which is noticeably cheaper when values are large and you do not need them. Filtering is applied after a batch has been read, so a call can return nothing while the cursor is still non-zero: only a cursor of `0` means the iteration is over. Fields present for the whole iteration are returned at least once, and fields added or removed while it runs may or may not appear. ## Syntax ```redis -JSON.DEL [path] +HSCAN [MATCH ] [COUNT ] [NOVALUES] ``` ## Arguments | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | -| `key` | Yes | No | JSON document key. | -| `path` | No | No | Path to delete; omitting it deletes the whole key. | +| `` | Yes | No | Redis key targeted by the command. | +| `` | Yes | No | Cursor returned by the previous call; start at `0`. | +| `MATCH ` | No | No | Return only elements matching this glob-style pattern. | +| `COUNT ` | No | No | Hint for how much work each iteration should do. | +| `NOVALUES` | No | No | Return only field names, without their values. | ## Important points -* Raw TCP examples pass JSON values as valid JSON text. Typed Upstash SDK helpers serialize native objects and values for you. -* Paths beginning with `$` use JSONPath and can match multiple values, so many JSON commands return an array of per-match results. +* This operation can inspect a large part of the database. Prefer cursor-based scans where possible and avoid unbounded use on hot paths. +* The cursor is opaque. Start with `0` and continue until the server returns cursor `0`; a single iteration may return no elements. ## Response @@ -35592,8 +35595,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Integer | -| RESP3 | Integer | +| RESP2 | Two-element array: cursor and flat field/value array, or field array with `NOVALUES` | +| RESP3 | Two-element array: cursor and flat field/value array, or field array with `NOVALUES` | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -35608,7 +35611,7 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -JSON.DEL profile $.temporary +HSCAN my-key 0 ``` @@ -35620,7 +35623,14 @@ import { Redis } from "@upstash/redis"; const redis = Redis.fromEnv(); -await redis.json.del("key", "$.path.to.value"); +await redis.hset("key", { + id: 1, + username: "chronark", + name: "andreas" + }); +const [newCursor, fields] = await redis.hscan("key", 0); +console.log(newCursor); // likely `0` since this is a very small hash +console.log(fields); // ["id", 1, "username", "chronark", "name", "andreas"] ``` @@ -35631,7 +35641,7 @@ await redis.json.del("key", "$.path.to.value"); from upstash_redis import Redis redis = Redis.from_env() -result = redis.json().delete("profile", "$.temporary") +result = redis.hscan("my-key", 0) print(result) ``` @@ -35643,7 +35653,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.call("JSON.DEL", "profile", "$.temporary"); +const result = await redis.hscan("my-key", "0"); console.log(result); ``` @@ -35657,7 +35667,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.json.del("profile", { path: "$.temporary" }); +const result = await client.hScan("my-key", "0"); console.log(result); ``` @@ -35670,7 +35680,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.json().delete("profile", "$.temporary") +result = client.hscan("my-key", 0) print(result) ``` @@ -35695,7 +35705,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.JSONDel(context.Background(), "profile", "$.temporary").Result() + result, _, err := client.HScan(context.Background(), "my-key", 0, "*", 0).Result() if err != nil { panic(err) } @@ -35710,10 +35720,10 @@ func main() { ```java import java.net.URI; -import redis.clients.jedis.JedisPooled; +import redis.clients.jedis.Jedis; -try (JedisPooled jedis = new JedisPooled(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.jsonDel("profile", new redis.clients.jedis.json.Path("$.temporary")); +try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { + Object result = jedis.hscan("my-key", "0"); System.out.println(result); } ``` @@ -35723,15 +35733,17 @@ try (JedisPooled jedis = new JedisPooled(new URI(System.getenv("REDIS_URL")))) { ```rust -use redis::JsonCommands; +use redis::TypedCommands; fn main() -> redis::RedisResult<()> { let url = std::env::var("REDIS_URL").expect("REDIS_URL is not set"); let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let result: redis::Value = connection.json_del("profile", "$.temporary")?; - println!("{result:?}"); + let iter: redis::Iter<(String, String)> = connection.hscan("my-key")?; + for (field, value) in iter { + println!("{field}: {value}"); + } Ok(()) } ``` @@ -35740,30 +35752,27 @@ fn main() -> redis::RedisResult<()> { -# JSON.FORGET -Source: https://upstash.com/docs/redis/commands/json/json-forget +# HSET +Source: https://upstash.com/docs/redis/commands/hash/hset -Use `JSON.FORGET` to delete the value at a path in a JSON document. It is an alias of [`JSON.DEL`](/docs/redis/commands/json/json-del) with identical behavior, kept for compatibility with clients and code that use the older name. +Use `HSET` to set one or more fields of a hash to the given values, creating the key when it does not exist. -Without a path the entire key is deleted, and the reply is the number of values that were deleted, which is `0` when the path matched nothing. +Existing fields are overwritten, and the reply counts only the fields that were added, not those that were updated, which is how you tell an insert from an update. Setting fields does not touch the key's time to live, so a hash with an expiration keeps it as it is written to. A field's own expiration is a different matter: writing a field clears the TTL it may have been given by [`HEXPIRE`](/docs/redis/commands/hash/hexpire) or [`HSETEX`](/docs/redis/commands/hash/hsetex). + +Hashes are the compact way to store an object under one key: field access with `HSET` and [`HGET`](/docs/redis/commands/hash/hget) avoids reading and rewriting the whole value the way a serialized string would. ## Syntax ```redis -JSON.FORGET [path] +HSET [ ...] ``` ## Arguments | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | -| `key` | Yes | No | JSON document key. | -| `path` | No | No | Path to delete; omitting it deletes the whole key. | - -## Important points - -* Raw TCP examples pass JSON values as valid JSON text. Typed Upstash SDK helpers serialize native objects and values for you. -* Paths beginning with `$` use JSONPath and can match multiple values, so many JSON commands return an array of per-match results. +| `` | Yes | No | Redis key targeted by the command. | +| ` ` | Yes | Yes | Field and the value to store in it. Repeat to set several fields in one call. | ## Response @@ -35787,7 +35796,7 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -JSON.FORGET profile $.temporary +HSET my-key field value ``` @@ -35799,7 +35808,11 @@ import { Redis } from "@upstash/redis"; const redis = Redis.fromEnv(); -await redis.json.forget("key", "$.path.to.value"); +await redis.hset("key", { + id: 1, + username: "chronark", + name: "andreas" + }); ``` @@ -35810,7 +35823,7 @@ await redis.json.forget("key", "$.path.to.value"); from upstash_redis import Redis redis = Redis.from_env() -result = redis.json().forget("profile", "$.temporary") +result = redis.hset("my-key", "field", "value") print(result) ``` @@ -35822,7 +35835,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.call("JSON.FORGET", "profile", "$.temporary"); +const result = await redis.hset("my-key", "field", "value"); console.log(result); ``` @@ -35836,7 +35849,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.json.forget("profile", { path: "$.temporary" }); +const result = await client.hSet("my-key", "field", "value"); console.log(result); ``` @@ -35849,7 +35862,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.json().forget("profile", "$.temporary") +result = client.hset("my-key", "field", "value") print(result) ``` @@ -35874,7 +35887,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.JSONForget(context.Background(), "profile", "$.temporary").Result() + result, err := client.HSet(context.Background(), "my-key", "field", "value").Result() if err != nil { panic(err) } @@ -35889,10 +35902,10 @@ func main() { ```java import java.net.URI; -import redis.clients.jedis.JedisPooled; +import redis.clients.jedis.Jedis; -try (JedisPooled jedis = new JedisPooled(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.jsonDel("profile", new redis.clients.jedis.json.Path("$.temporary")); +try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { + Object result = jedis.hset("my-key", "field", "value"); System.out.println(result); } ``` @@ -35902,14 +35915,14 @@ try (JedisPooled jedis = new JedisPooled(new URI(System.getenv("REDIS_URL")))) { ```rust -use redis::JsonCommands; +use redis::TypedCommands; fn main() -> redis::RedisResult<()> { let url = std::env::var("REDIS_URL").expect("REDIS_URL is not set"); let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let result: redis::Value = connection.json_del("profile", "$.temporary")?; + let result = connection.hset("my-key", "field", "value")?; println!("{result:?}"); Ok(()) } @@ -35919,39 +35932,33 @@ fn main() -> redis::RedisResult<()> { -# JSON.GET -Source: https://upstash.com/docs/redis/commands/json/json-get +# HSETEX +Source: https://upstash.com/docs/redis/commands/hash/hsetex -Use `JSON.GET` to read one or more values from a JSON document. +Use `HSETEX` to write hash fields and set their expiration in the same atomic call. -Without a path the whole document is returned. The reply shape depends on the path syntax: a path starting with `$` is a JSONPath and always returns an array with one entry per match, so an empty array means nothing matched, while the legacy dot syntax returns the value itself and reports an error when the path does not exist. Passing several paths returns an object keyed by the path expressions, which is a cheap way to pull a few unrelated branches of a large document in one call. +`FIELDS ` introduces the field and value pairs and the count must match. `EX`, `PX`, `EXAT`, and `PXAT` give the written fields a lifetime or an absolute deadline, and `KEEPTTL` keeps whatever expiration those fields already had. Without any of these options the fields are written without an expiration, so a previously set one is dropped. -`INDENT`, `NEWLINE`, and `SPACE` control the formatting of the returned JSON text, which is otherwise compact. They are meant for human-readable output; leave them out when a program parses the reply. +`FNX` writes only when none of the given fields exist and `FXX` only when all of them do, which turns the command into an atomic conditional write: create-if-absent or update-if-present, with the expiration applied in the same step. The reply is `1` when the fields were written and `0` when the condition prevented it. ## Syntax ```redis -JSON.GET - [INDENT indent] - [NEWLINE newline] - [SPACE space] - [path [path ...]] +HSETEX + [FNX | FXX] + [EX | PX | EXAT | + PXAT | KEEPTTL] + FIELDS [ ...] ``` ## Arguments | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | -| `key` | Yes | No | JSON document key. | -| `INDENT indent` | No | No | Indentation characters for formatted JSON. | -| `NEWLINE newline` | No | No | Line-separator characters for formatted JSON. | -| `SPACE space` | No | No | Characters placed after JSON separators. | -| `path` | No | Yes | One or more paths; defaults to the root. | - -## Important points - -* Raw TCP examples pass JSON values as valid JSON text. Typed Upstash SDK helpers serialize native objects and values for you. -* Paths beginning with `$` use JSONPath and can match multiple values, so many JSON commands return an array of per-match results. +| `` | Yes | No | Redis key targeted by the command. | +| `(FNX \| FXX)` | No | No | Write only when the condition holds: `FNX` when none of the given fields exist, `FXX` when all of them do. | +| `(EX \| PX \| EXAT \| PXAT \| KEEPTTL)` | No | No | Choose one form: `EX` (set a lifetime in seconds); `PX` (set a lifetime in milliseconds); `EXAT` (expire at a Unix timestamp in seconds); `PXAT` (expire at a Unix timestamp in milliseconds); `KEEPTTL` (preserve the existing key lifetime). | +| `FIELDS [ ...]` | Yes | No | Field-value pairs to set. Give the pair count first, then that many field and value pairs. | ## Response @@ -35959,8 +35966,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Bulk string or Null bulk string or null array | -| RESP3 | Bulk string or Null | +| RESP2 | Integer: `1` if the fields were set, `0` otherwise | +| RESP3 | Integer: `1` if the fields were set, `0` otherwise | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -35975,7 +35982,7 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -JSON.GET profile $.name +HSETEX my-key FIELDS 1 field value ``` @@ -35987,7 +35994,11 @@ import { Redis } from "@upstash/redis"; const redis = Redis.fromEnv(); -const value = await redis.json.get("key", "$.path.to.somewhere"); +// Set fields with 1 hour expiration +await redis.hsetex("user:123", { expiration: { ex: 3600 } }, { + name: "John", + email: "john@example.com" +}); ``` @@ -35998,7 +36009,7 @@ const value = await redis.json.get("key", "$.path.to.somewhere"); from upstash_redis import Redis redis = Redis.from_env() -result = redis.json().get("profile", "$.name") +result = redis.hsetex("my-key", field="field", value="value") print(result) ``` @@ -36010,7 +36021,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.call("JSON.GET", "profile", "$.name"); +const result = await redis.hsetex("my-key", "FIELDS", "1", "field", "value"); console.log(result); ``` @@ -36024,7 +36035,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.json.get("profile", { path: "$.name" }); +const result = await client.hSetEx("my-key", { field: "value" }); console.log(result); ``` @@ -36037,7 +36048,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.json().get("profile", "$.name") +result = client.hsetex("my-key", mapping={"field": "value"}) print(result) ``` @@ -36062,7 +36073,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.JSONGet(context.Background(), "profile", "$.name").Result() + result, err := client.HSetEX(context.Background(), "my-key", "1", "field", "value").Result() if err != nil { panic(err) } @@ -36077,10 +36088,10 @@ func main() { ```java import java.net.URI; -import redis.clients.jedis.JedisPooled; +import redis.clients.jedis.Jedis; -try (JedisPooled jedis = new JedisPooled(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.jsonGet("profile", new redis.clients.jedis.json.Path("$.name")); +try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { + Object result = jedis.hsetex("my-key", redis.clients.jedis.params.HSetExParams.hSetExParams(), "field", "value"); System.out.println(result); } ``` @@ -36090,14 +36101,18 @@ try (JedisPooled jedis = new JedisPooled(new URI(System.getenv("REDIS_URL")))) { ```rust -use redis::JsonCommands; +use redis::{HashFieldExpirationOptions, TypedCommands}; fn main() -> redis::RedisResult<()> { let url = std::env::var("REDIS_URL").expect("REDIS_URL is not set"); let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let result: redis::Value = connection.json_get("profile", &["$.name"])?; + let result = connection.hset_ex( + "my-key", + &HashFieldExpirationOptions::default(), + &[("field", "value")], + )?; println!("{result:?}"); Ok(()) } @@ -36107,33 +36122,26 @@ fn main() -> redis::RedisResult<()> { -# JSON.MERGE -Source: https://upstash.com/docs/redis/commands/json/json-merge - -Use `JSON.MERGE` to merge a JSON value into a document at a path, following the JSON Merge Patch semantics of RFC 7386. +# HSETNX +Source: https://upstash.com/docs/redis/commands/hash/hsetnx -Objects are merged recursively: keys in the patch replace or create the matching keys in the target, keys set to `null` delete them, and any value that is not an object, arrays included, replaces the target outright instead of being merged element by element. The parent must already exist: merging into a missing child of an existing object creates it, but intermediate levels are not built along the way, and a key that does not exist yet can only be created by merging at the root. +Use `HSETNX` to set a hash field only when it does not already exist. -This is the command for partial updates of an object, where [`JSON.SET`](/docs/redis/commands/json/json-set) would replace the whole branch: one call can change a few fields, delete another, and leave the rest of the document untouched. +The reply is `1` when the field was created and `0` when it was already present and left untouched. The check and the write happen atomically, so of several clients racing to fill the same field exactly one succeeds, which makes the command a way to claim a slot inside a hash without overwriting whatever a concurrent writer put there. ## Syntax ```redis -JSON.MERGE +HSETNX ``` ## Arguments | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | -| `key` | Yes | No | JSON document key. | -| `path` | Yes | No | Path to merge into. | -| `value` | Yes | No | Valid JSON value containing the merge patch. | - -## Important points - -* Raw TCP examples pass JSON values as valid JSON text. Typed Upstash SDK helpers serialize native objects and values for you. -* Paths beginning with `$` use JSONPath and can match multiple values, so many JSON commands return an array of per-match results. +| `` | Yes | No | Redis key targeted by the command. | +| `` | Yes | No | Hash field name. | +| `` | Yes | No | Value to store in the field. | ## Response @@ -36141,8 +36149,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Simple string `OK` | -| RESP3 | Simple string `OK` | +| RESP2 | Integer: `1` if the field was set, `0` if it already exists | +| RESP3 | Integer: `1` if the field was set, `0` if it already exists | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -36157,7 +36165,7 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -JSON.MERGE profile $ '{"active":true}' +HSETNX my-key field value ``` @@ -36169,7 +36177,7 @@ import { Redis } from "@upstash/redis"; const redis = Redis.fromEnv(); -await redis.json.merge("key", "$.path.to.value", {"new": "value"}) +await redis.hsetnx("key", "id", 1) ``` @@ -36180,7 +36188,7 @@ await redis.json.merge("key", "$.path.to.value", {"new": "value"}) from upstash_redis import Redis redis = Redis.from_env() -result = redis.json().merge("profile", "$", {"active": True}) +result = redis.hsetnx("my-key", "field", "value") print(result) ``` @@ -36192,7 +36200,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.call("JSON.MERGE", "profile", "$", "{\"active\":true}"); +const result = await redis.hsetnx("my-key", "field", "value"); console.log(result); ``` @@ -36206,7 +36214,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.json.merge("profile", "$", { active: true }); +const result = await client.hSetNX("my-key", "field", "value"); console.log(result); ``` @@ -36219,7 +36227,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.json().merge("profile", "$", {"active": True}) +result = client.hsetnx("my-key", "field", "value") print(result) ``` @@ -36244,7 +36252,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.JSONMerge(context.Background(), "profile", "$", `{"active":true}`).Result() + result, err := client.HSetNX(context.Background(), "my-key", "field", "value").Result() if err != nil { panic(err) } @@ -36259,10 +36267,10 @@ func main() { ```java import java.net.URI; -import redis.clients.jedis.JedisPooled; +import redis.clients.jedis.Jedis; -try (JedisPooled jedis = new JedisPooled(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.jsonMerge("profile", new redis.clients.jedis.json.Path("$"), java.util.Map.of("active", true)); +try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { + Object result = jedis.hsetnx("my-key", "field", "value"); System.out.println(result); } ``` @@ -36272,16 +36280,14 @@ try (JedisPooled jedis = new JedisPooled(new URI(System.getenv("REDIS_URL")))) { ```rust +use redis::TypedCommands; + fn main() -> redis::RedisResult<()> { let url = std::env::var("REDIS_URL").expect("REDIS_URL is not set"); let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let mut command = redis::cmd("JSON.MERGE"); - command.arg("profile"); - command.arg("$"); - command.arg("{\"active\":true}"); - let result: redis::Value = command.query(&mut connection)?; + let result = connection.hset_nx("my-key", "field", "value")?; println!("{result:?}"); Ok(()) } @@ -36291,30 +36297,25 @@ fn main() -> redis::RedisResult<()> { -# JSON.MGET -Source: https://upstash.com/docs/redis/commands/json/json-mget +# HSTRLEN +Source: https://upstash.com/docs/redis/commands/hash/hstrlen -Use `JSON.MGET` to read the same path from several JSON documents in one call. +Use `HSTRLEN` to get the length in bytes of the value stored in a hash field. -The reply holds one entry per key, in the order requested, containing what the path selected in that document, or null when the key does not exist or the path matches nothing. It replaces one [`JSON.GET`](/docs/redis/commands/json/json-get) per key when you are gathering the same field across many documents, for example the price of every product in a cart. +The reply is `0` when either the field or the key is missing. Since the value itself is not transferred, this is how you check the size of a large field, or decide whether it is worth fetching, without paying for it. ## Syntax ```redis -JSON.MGET [key ...] +HSTRLEN ``` ## Arguments | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | -| `key` | Yes | Yes | One or more JSON document keys. | -| `path` | Yes | No | Path read from every key. | - -## Important points - -* Raw TCP examples pass JSON values as valid JSON text. Typed Upstash SDK helpers serialize native objects and values for you. -* Paths beginning with `$` use JSONPath and can match multiple values, so many JSON commands return an array of per-match results. +| `` | Yes | No | Redis key targeted by the command. | +| `` | Yes | No | Hash field name. | ## Response @@ -36322,8 +36323,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Array of bulk-string JSON values or null values, one per key | -| RESP3 | Array of bulk-string JSON values or null values, one per key | +| RESP2 | Integer | +| RESP3 | Integer | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -36338,7 +36339,7 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -JSON.MGET profile:1 profile:2 $.name +HSTRLEN my-key field ``` @@ -36350,7 +36351,7 @@ import { Redis } from "@upstash/redis"; const redis = Redis.fromEnv(); -const values = await redis.json.mget(["key1", "key2"], "$.path.to.somewhere"); +const length = await redis.hstrlen("key", "field") ``` @@ -36361,7 +36362,7 @@ const values = await redis.json.mget(["key1", "key2"], "$.path.to.somewhere"); from upstash_redis import Redis redis = Redis.from_env() -result = redis.json().mget(["profile:1", "profile:2"], "$.name") +result = redis.hstrlen("my-key", "field") print(result) ``` @@ -36373,7 +36374,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.call("JSON.MGET", "profile:1", "profile:2", "$.name"); +const result = await redis.hstrlen("my-key", "field"); console.log(result); ``` @@ -36387,7 +36388,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.json.mGet(["profile:1", "profile:2"], "$.name"); +const result = await client.hStrLen("my-key", "field"); console.log(result); ``` @@ -36400,7 +36401,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.json().mget(["profile:1", "profile:2"], "$.name") +result = client.hstrlen("my-key", "field") print(result) ``` @@ -36425,7 +36426,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.JSONMGet(context.Background(), "$.name", "profile:1", "profile:2").Result() + result, err := client.HStrLen(context.Background(), "my-key", "field").Result() if err != nil { panic(err) } @@ -36440,10 +36441,10 @@ func main() { ```java import java.net.URI; -import redis.clients.jedis.JedisPooled; +import redis.clients.jedis.Jedis; -try (JedisPooled jedis = new JedisPooled(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.jsonMGet(new redis.clients.jedis.json.Path2("$.name"), "profile:1", "profile:2"); +try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { + Object result = jedis.hstrlen("my-key", "field"); System.out.println(result); } ``` @@ -36453,14 +36454,15 @@ try (JedisPooled jedis = new JedisPooled(new URI(System.getenv("REDIS_URL")))) { ```rust -use redis::JsonCommands; - fn main() -> redis::RedisResult<()> { let url = std::env::var("REDIS_URL").expect("REDIS_URL is not set"); let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let result: redis::Value = connection.json_mget(&["profile:1", "profile:2"], "$.name")?; + let mut command = redis::cmd("HSTRLEN"); + command.arg("my-key"); + command.arg("field"); + let result: redis::Value = command.query(&mut connection)?; println!("{result:?}"); Ok(()) } @@ -36470,29 +36472,29 @@ fn main() -> redis::RedisResult<()> { -# JSON.MSET -Source: https://upstash.com/docs/redis/commands/json/json-mset +# HTTL +Source: https://upstash.com/docs/redis/commands/hash/httl -Use `JSON.MSET` to set values at paths in several JSON documents in one atomic call. +Use `HTTL` to read how much longer hash fields will live, in seconds. -Each triple gives a key, a path, and a value, and missing keys are created. Either every write is applied or none is, with no other command running in between, which is what makes it the right tool for documents that must stay consistent with each other. The reply is `OK`. +The reply holds one value per requested field, in order: the remaining lifetime, `-1` when the field exists but has no expiration, and `-2` when the field or the key does not exist, so a missing field is never confused with a permanent one. Use [`HPTTL`](/docs/redis/commands/hash/hpttl) for millisecond precision and [`HEXPIRETIME`](/docs/redis/commands/hash/hexpiretime) when you want the absolute deadline instead of the time left. ## Syntax ```redis -JSON.MSET [key path value ...] +HTTL FIELDS [ ...] ``` ## Arguments | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | -| `key path value` | Yes | Yes | One or more key, path, and valid-JSON-value triples. | +| `` | Yes | No | Redis key targeted by the command. | +| `FIELDS [ ...]` | Yes | No | Fields to target. Give the field count first, then that many field names. | ## Important points -* Raw TCP examples pass JSON values as valid JSON text. Typed Upstash SDK helpers serialize native objects and values for you. -* Paths beginning with `$` use JSONPath and can match multiple values, so many JSON commands return an array of per-match results. +* Negative integer replies are sentinel values, not durations or timestamps; see the response description for missing or persistent data. ## Response @@ -36500,8 +36502,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Simple string `OK` | -| RESP3 | Simple string `OK` | +| RESP2 | Array of TTL values or negative integer status codes, one per field | +| RESP3 | Array of TTL values or negative integer status codes, one per field | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -36516,7 +36518,7 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -JSON.MSET profile:1 $ '{"name":"Ada"}' +HTTL my-key FIELDS 1 field ``` @@ -36527,10 +36529,12 @@ JSON.MSET profile:1 $ '{"name":"Ada"}' import { Redis } from "@upstash/redis"; const redis = Redis.fromEnv(); -const result = await redis.json.mset([ - { key: "profile:1", path: "$", value: { name: "Ada" } }, -]); -console.log(result); + +await redis.hset("my-key", "my-field", "my-value"); +await redis.hexpire("my-key", "my-field", 10); +const ttl = await redis.httl("my-key", "my-field"); + +console.log(ttl); // e.g., [9] ``` @@ -36541,7 +36545,7 @@ console.log(result); from upstash_redis import Redis redis = Redis.from_env() -result = redis.json().mset([("profile:1", "$", {"name": "Ada"})]) +result = redis.httl("my-key", "field") print(result) ``` @@ -36553,7 +36557,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.call("JSON.MSET", "profile:1", "$", "{\"name\":\"Ada\"}"); +const result = await redis.httl("my-key", "FIELDS", "1", "field"); console.log(result); ``` @@ -36567,7 +36571,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.json.mSet([{ key: "profile:1", path: "$", value: { name: "Ada" } }]); +const result = await client.hTTL("my-key", "field"); console.log(result); ``` @@ -36580,7 +36584,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.json().mset([("profile:1", "$", {"name": "Ada"})]) +result = client.httl("my-key", "field") print(result) ``` @@ -36605,7 +36609,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.JSONMSet(context.Background(), "profile:1", "$", `{"name":"Ada"}`).Result() + result, err := client.HTTL(context.Background(), "my-key", "1", "field").Result() if err != nil { panic(err) } @@ -36619,13 +36623,11 @@ func main() { ```java import java.net.URI; -import java.nio.charset.StandardCharsets; + import redis.clients.jedis.Jedis; -import redis.clients.jedis.commands.ProtocolCommand; -ProtocolCommand command = () -> "JSON.MSET".getBytes(StandardCharsets.UTF_8); try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.sendCommand(command, "profile:1", "$", "{\"name\":\"Ada\"}"); + Object result = jedis.httl("my-key", "1", "field"); System.out.println(result); } ``` @@ -36635,16 +36637,14 @@ try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { ```rust +use redis::TypedCommands; + fn main() -> redis::RedisResult<()> { let url = std::env::var("REDIS_URL").expect("REDIS_URL is not set"); let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let mut command = redis::cmd("JSON.MSET"); - command.arg("profile:1"); - command.arg("$"); - command.arg("{\"name\":\"Ada\"}"); - let result: redis::Value = command.query(&mut connection)?; + let result = connection.httl("my-key", &["field"])?; println!("{result:?}"); Ok(()) } @@ -36654,31 +36654,24 @@ fn main() -> redis::RedisResult<()> { -# JSON.NUMINCRBY -Source: https://upstash.com/docs/redis/commands/json/json-numincrby +# HVALS +Source: https://upstash.com/docs/redis/commands/hash/hvals -Use `JSON.NUMINCRBY` to add a number to the numeric values a path selects. +Use `HVALS` to get all the values in a hash, without their field names. -The increment may be negative to count down, and it is applied atomically, so concurrent callers cannot lose an update. The reply is the new value of each match; a match that is not a number returns an error. This is how a counter kept inside a document is updated without reading and rewriting the whole document. +A missing key returns an empty list, and values come back in no particular order. The whole hash is transferred, so on large hashes prefer [`HSCAN`](/docs/redis/commands/hash/hscan) to walk it in batches, or [`HMGET`](/docs/redis/commands/hash/hmget) when you know which fields you need. ## Syntax ```redis -JSON.NUMINCRBY +HVALS ``` ## Arguments | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | -| `key` | Yes | No | JSON document key. | -| `path` | Yes | No | Path selecting numbers. | -| `value` | Yes | No | Numeric amount to add. | - -## Important points - -* Raw TCP examples pass JSON values as valid JSON text. Typed Upstash SDK helpers serialize native objects and values for you. -* Paths beginning with `$` use JSONPath and can match multiple values, so many JSON commands return an array of per-match results. +| `` | Yes | No | Redis key targeted by the command. | ## Response @@ -36686,8 +36679,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Bulk string containing a JSON number or array | -| RESP3 | Bulk string containing a JSON number or array | +| RESP2 | Array of bulk-string values | +| RESP3 | Array of bulk-string values | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -36702,7 +36695,7 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -JSON.NUMINCRBY profile $.visits 1 +HVALS my-key ``` @@ -36714,7 +36707,12 @@ import { Redis } from "@upstash/redis"; const redis = Redis.fromEnv(); -const newValue = await redis.json.numincrby("key", "$.path.to.value", 2); +await redis.hset("key", { + field1: "Hello", + field2: "World", +}) +const values = await redis.hvals("key") +console.log(values) // ["Hello", "World"] ``` @@ -36725,7 +36723,7 @@ const newValue = await redis.json.numincrby("key", "$.path.to.value", 2); from upstash_redis import Redis redis = Redis.from_env() -result = redis.json().numincrby("profile", "$.visits", 1) +result = redis.hvals("my-key") print(result) ``` @@ -36737,7 +36735,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.call("JSON.NUMINCRBY", "profile", "$.visits", "1"); +const result = await redis.hvals("my-key"); console.log(result); ``` @@ -36751,7 +36749,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.json.numIncrBy("profile", "$.visits", 1); +const result = await client.hVals("my-key"); console.log(result); ``` @@ -36764,7 +36762,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.json().numincrby("profile", "$.visits", 1) +result = client.hvals("my-key") print(result) ``` @@ -36789,7 +36787,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.JSONNumIncrBy(context.Background(), "profile", "$.visits", 1).Result() + result, err := client.HVals(context.Background(), "my-key").Result() if err != nil { panic(err) } @@ -36804,10 +36802,10 @@ func main() { ```java import java.net.URI; -import redis.clients.jedis.JedisPooled; +import redis.clients.jedis.Jedis; -try (JedisPooled jedis = new JedisPooled(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.jsonNumIncrBy("profile", new redis.clients.jedis.json.Path("$.visits"), 1); +try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { + Object result = jedis.hvals("my-key"); System.out.println(result); } ``` @@ -36817,14 +36815,14 @@ try (JedisPooled jedis = new JedisPooled(new URI(System.getenv("REDIS_URL")))) { ```rust -use redis::JsonCommands; +use redis::TypedCommands; fn main() -> redis::RedisResult<()> { let url = std::env::var("REDIS_URL").expect("REDIS_URL is not set"); let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let result: redis::Value = connection.json_num_incr_by("profile", "$.visits", 1)?; + let result = connection.hvals("my-key")?; println!("{result:?}"); Ok(()) } @@ -36834,31 +36832,70 @@ fn main() -> redis::RedisResult<()> { -# JSON.NUMMULTBY -Source: https://upstash.com/docs/redis/commands/json/json-nummultby +# Hash commands +Source: https://upstash.com/docs/redis/commands/hash/overview -Use `JSON.NUMMULTBY` to multiply the numeric values a path selects by a number. + +Delete one or more hash fields +Check if a hash field exists +Set field TTL in seconds +Set field expiry as timestamp +Get field expiry as timestamp +Get the value of a hash field +Get all fields and values +Get and delete hash fields +Get fields and set their expiry +Increment integer value of a field +Increment float value of a field +Get all fields in a hash +Get number of fields in a hash +Get values of multiple fields +Set multiple hash fields +Remove field expiration +Set field TTL in milliseconds +Set field expiry as ms timestamp +Get field expiry as ms timestamp +Get field TTL in milliseconds +Get random fields from a hash +Incrementally iterate hash fields +Set hash field values +Set fields with expiration +Set field only if it doesn't exist +Get length of a field's value +Get field TTL in seconds +Get all values in a hash + -The multiplier may be a fraction to scale values down, and the update is atomic. The reply is the new value of each match, and a match that is not a number returns an error. It is the multiplicative counterpart of [`JSON.NUMINCRBY`](/docs/redis/commands/json/json-numincrby), useful for applying percentage changes such as a discount to every price in a document. +# HyperLogLog commands +Source: https://upstash.com/docs/redis/commands/hyperloglog/overview + + +Add elements to HyperLogLog +Get estimated cardinality +Merge multiple HyperLogLogs + + +# PFADD +Source: https://upstash.com/docs/redis/commands/hyperloglog/pfadd + +Use `PFADD` to add elements to a HyperLogLog. + +A HyperLogLog estimates how many distinct items it has seen while using a small, fixed amount of memory (at most about 12 KB) no matter how many elements pass through it. That is the trade it makes: individual elements are not stored, so they cannot be listed, checked for membership, or removed, and the cardinality that comes back from [`PFCOUNT`](/docs/redis/commands/hyperloglog/pfcount) is an approximation with a standard error of about 0.81%. + +The key is created on first use, and the reply is `1` when the internal registers changed as a result of the call, which is a hint that at least one element was new, and `0` when they did not. Calling `PFADD` with no elements creates an empty HyperLogLog if the key does not exist yet. Use it for counts where the exact number does not matter, such as unique visitors per page or per day, and a set for cases where you must be able to look elements up. ## Syntax ```redis -JSON.NUMMULTBY +PFADD [ [ ...]] ``` ## Arguments | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | -| `key` | Yes | No | JSON document key. | -| `path` | Yes | No | Path selecting numbers. | -| `value` | Yes | No | Numeric multiplier. | - -## Important points - -* Raw TCP examples pass JSON values as valid JSON text. Typed Upstash SDK helpers serialize native objects and values for you. -* Paths beginning with `$` use JSONPath and can match multiple values, so many JSON commands return an array of per-match results. +| `` | Yes | No | Redis key targeted by the command. | +| `` | No | Yes | Element to add to the HyperLogLog. | ## Response @@ -36866,8 +36903,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Bulk string containing a JSON number or array | -| RESP3 | Bulk string containing a JSON number or array | +| RESP2 | Integer: `1` if at least one internal register was altered, `0` otherwise | +| RESP3 | Integer: `1` if at least one internal register was altered, `0` otherwise | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -36882,7 +36919,7 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -JSON.NUMMULTBY profile $.score 2 +PFADD my-key element ``` @@ -36893,8 +36930,8 @@ JSON.NUMMULTBY profile $.score 2 import { Redis } from "@upstash/redis"; const redis = Redis.fromEnv(); - -const newValue = await redis.json.nummultby("key", "$.path.to.value", 2); +const result = await redis.pfadd("my-key", "element"); +console.log(result); ``` @@ -36905,7 +36942,7 @@ const newValue = await redis.json.nummultby("key", "$.path.to.value", 2); from upstash_redis import Redis redis = Redis.from_env() -result = redis.json().nummultby("profile", "$.score", 2) +result = redis.pfadd("my-key", "member") print(result) ``` @@ -36917,7 +36954,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.call("JSON.NUMMULTBY", "profile", "$.score", "2"); +const result = await redis.pfadd("my-key", "element"); console.log(result); ``` @@ -36931,7 +36968,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.json.numMultBy("profile", "$.score", 2); +const result = await client.pfAdd("my-key", "element"); console.log(result); ``` @@ -36944,7 +36981,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.json().nummultby("profile", "$.score", 2) +result = client.pfadd("my-key", "member") print(result) ``` @@ -36969,7 +37006,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.Do(context.Background(), "JSON.NUMMULTBY", "profile", "$.score", "2").Result() + result, err := client.PFAdd(context.Background(), "my-key").Result() if err != nil { panic(err) } @@ -36983,13 +37020,11 @@ func main() { ```java import java.net.URI; -import java.nio.charset.StandardCharsets; + import redis.clients.jedis.Jedis; -import redis.clients.jedis.commands.ProtocolCommand; -ProtocolCommand command = () -> "JSON.NUMMULTBY".getBytes(StandardCharsets.UTF_8); try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.sendCommand(command, "profile", "$.score", "2"); + Object result = jedis.pfadd("my-key"); System.out.println(result); } ``` @@ -36999,16 +37034,14 @@ try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { ```rust +use redis::TypedCommands; + fn main() -> redis::RedisResult<()> { let url = std::env::var("REDIS_URL").expect("REDIS_URL is not set"); let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let mut command = redis::cmd("JSON.NUMMULTBY"); - command.arg("profile"); - command.arg("$.score"); - command.arg("2"); - let result: redis::Value = command.query(&mut connection)?; + let result = connection.pfadd("my-key", &["member"])?; println!("{result:?}"); Ok(()) } @@ -37018,30 +37051,26 @@ fn main() -> redis::RedisResult<()> { -# JSON.OBJKEYS -Source: https://upstash.com/docs/redis/commands/json/json-objkeys +# PFCOUNT +Source: https://upstash.com/docs/redis/commands/hyperloglog/pfcount -Use `JSON.OBJKEYS` to list the field names of the objects a path selects. +Use `PFCOUNT` to read the estimated number of distinct elements recorded in one or more HyperLogLogs. -Without a path the root value is used. The reply holds one list of keys per match, with null for matches that are not objects. Only the field names come back, not the values, which makes it a cheap way to inspect the shape of a document before reading it. +With a single key the stored estimate is returned. With several keys the structures are merged on the fly and the cardinality of their union is returned, without modifying any of them, which is how you answer "how many unique users across these seven days" without double counting. The result is an approximation with a standard error of about 0.81%. + +The multi-key form does real work on every call, so when the same union is read often it is cheaper to roll the sources up into one key with [`PFMERGE`](/docs/redis/commands/hyperloglog/pfmerge) and count that. ## Syntax ```redis -JSON.OBJKEYS [path] +PFCOUNT [ ...] ``` ## Arguments | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | -| `key` | Yes | No | JSON document key. | -| `path` | No | No | Path selecting objects; defaults to the root. | - -## Important points - -* Raw TCP examples pass JSON values as valid JSON text. Typed Upstash SDK helpers serialize native objects and values for you. -* Paths beginning with `$` use JSONPath and can match multiple values, so many JSON commands return an array of per-match results. +| `` | Yes | Yes | Redis key targeted by the command. | ## Response @@ -37049,8 +37078,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Array of arrays of bulk-string object keys or null values, or Null bulk string or null array | -| RESP3 | Array of arrays of bulk-string object keys or null values, or Null | +| RESP2 | Integer | +| RESP3 | Integer | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -37065,7 +37094,7 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -JSON.OBJKEYS profile $ +PFCOUNT my-key ``` @@ -37076,8 +37105,8 @@ JSON.OBJKEYS profile $ import { Redis } from "@upstash/redis"; const redis = Redis.fromEnv(); - -const keys = await redis.json.objkeys("key", "$.path"); +const result = await redis.pfcount("my-key"); +console.log(result); ``` @@ -37088,7 +37117,7 @@ const keys = await redis.json.objkeys("key", "$.path"); from upstash_redis import Redis redis = Redis.from_env() -result = redis.json().objkeys("profile", "$") +result = redis.pfcount("my-key") print(result) ``` @@ -37100,7 +37129,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.call("JSON.OBJKEYS", "profile", "$"); +const result = await redis.pfcount("my-key"); console.log(result); ``` @@ -37114,7 +37143,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.json.objKeys("profile", { path: "$" }); +const result = await client.pfCount("my-key"); console.log(result); ``` @@ -37127,7 +37156,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.json().objkeys("profile", "$") +result = client.pfcount("my-key") print(result) ``` @@ -37152,7 +37181,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.JSONObjKeys(context.Background(), "profile", "$").Result() + result, err := client.PFCount(context.Background(), "my-key").Result() if err != nil { panic(err) } @@ -37167,10 +37196,10 @@ func main() { ```java import java.net.URI; -import redis.clients.jedis.JedisPooled; +import redis.clients.jedis.Jedis; -try (JedisPooled jedis = new JedisPooled(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.jsonObjKeys("profile", new redis.clients.jedis.json.Path("$")); +try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { + Object result = jedis.pfcount("my-key"); System.out.println(result); } ``` @@ -37180,14 +37209,14 @@ try (JedisPooled jedis = new JedisPooled(new URI(System.getenv("REDIS_URL")))) { ```rust -use redis::JsonCommands; +use redis::TypedCommands; fn main() -> redis::RedisResult<()> { let url = std::env::var("REDIS_URL").expect("REDIS_URL is not set"); let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let result: redis::Value = connection.json_obj_keys("profile", "$")?; + let result = connection.pfcount(&["my-key"])?; println!("{result:?}"); Ok(()) } @@ -37197,30 +37226,27 @@ fn main() -> redis::RedisResult<()> { -# JSON.OBJLEN -Source: https://upstash.com/docs/redis/commands/json/json-objlen +# PFMERGE +Source: https://upstash.com/docs/redis/commands/hyperloglog/pfmerge -Use `JSON.OBJLEN` to get the number of fields in the objects a path selects. +Use `PFMERGE` to merge several HyperLogLogs into a single one. -Without a path the root value is used. The reply holds one count per match, with null for matches that are not objects. It counts only the object's own fields, not the fields of nested objects. +The destination ends up representing the union of the source structures and of whatever it already held, so merging the same sources again changes nothing and new data can be folded in as it arrives. The destination is created if it does not exist. + +Because the union is computed register by register and loses no accuracy compared with counting the raw data, rolling hourly keys into a daily key, or daily keys into a monthly one, gives the same estimate as if every element had been added to that key directly. That makes `PFMERGE` the building block for time-based rollups of unique counts. ## Syntax ```redis -JSON.OBJLEN [path] +PFMERGE [ [ ...]] ``` ## Arguments | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | -| `key` | Yes | No | JSON document key. | -| `path` | No | No | Path selecting objects; defaults to the root. | - -## Important points - -* Raw TCP examples pass JSON values as valid JSON text. Typed Upstash SDK helpers serialize native objects and values for you. -* Paths beginning with `$` use JSONPath and can match multiple values, so many JSON commands return an array of per-match results. +| `` | Yes | No | Redis key used as destkey. | +| `` | No | Yes | Redis key used as sourcekey. | ## Response @@ -37228,8 +37254,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Integer, array of integer lengths or null values, or Null bulk string or null array | -| RESP3 | Integer, array of integer lengths or null values, or Null | +| RESP2 | Simple string `OK` | +| RESP3 | Simple string `OK` | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -37244,7 +37270,7 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -JSON.OBJLEN profile $ +PFMERGE destination-key source-key ``` @@ -37255,8 +37281,8 @@ JSON.OBJLEN profile $ import { Redis } from "@upstash/redis"; const redis = Redis.fromEnv(); - -const lengths = await redis.json.objlen("key", "$.path"); +const result = await redis.pfmerge("destination-key", "source-key"); +console.log(result); ``` @@ -37267,7 +37293,7 @@ const lengths = await redis.json.objlen("key", "$.path"); from upstash_redis import Redis redis = Redis.from_env() -result = redis.json().objlen("profile", "$") +result = redis.pfmerge("destination-key", "source-key") print(result) ``` @@ -37279,7 +37305,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.call("JSON.OBJLEN", "profile", "$"); +const result = await redis.pfmerge("destination-key", "source-key"); console.log(result); ``` @@ -37293,7 +37319,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.json.objLen("profile", { path: "$" }); +const result = await client.pfMerge("destination-key", "source-key"); console.log(result); ``` @@ -37306,7 +37332,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.json().objlen("profile", "$") +result = client.pfmerge("destination-key", "source-key") print(result) ``` @@ -37331,7 +37357,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.JSONObjLen(context.Background(), "profile", "$").Result() + result, err := client.PFMerge(context.Background(), "my-key").Result() if err != nil { panic(err) } @@ -37346,10 +37372,10 @@ func main() { ```java import java.net.URI; -import redis.clients.jedis.JedisPooled; +import redis.clients.jedis.Jedis; -try (JedisPooled jedis = new JedisPooled(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.jsonObjLen("profile", new redis.clients.jedis.json.Path("$")); +try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { + Object result = jedis.pfmerge("my-key"); System.out.println(result); } ``` @@ -37359,14 +37385,14 @@ try (JedisPooled jedis = new JedisPooled(new URI(System.getenv("REDIS_URL")))) { ```rust -use redis::JsonCommands; +use redis::TypedCommands; fn main() -> redis::RedisResult<()> { let url = std::env::var("REDIS_URL").expect("REDIS_URL is not set"); let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let result: redis::Value = connection.json_obj_len("profile", "$")?; + let result = connection.pfmerge("destination-key", &["source-key"])?; println!("{result:?}"); Ok(()) } @@ -37376,19 +37402,17 @@ fn main() -> redis::RedisResult<()> { -# JSON.RESP -Source: https://upstash.com/docs/redis/commands/json/json-resp - -Use `JSON.RESP` to get a JSON value in RESP form instead of as JSON text. +# JSON.ARRAPPEND +Source: https://upstash.com/docs/redis/commands/json/json-arrappend -The document is translated structurally: an object becomes an array whose first element is `{` followed by alternating field names and values, an array becomes an array whose first element is `[` followed by its elements, and scalars become the corresponding RESP types. Nested values are translated the same way, recursively. +Use `JSON.ARRAPPEND` to append one or more values to the end of the arrays a path selects. -This lets a client walk the structure using the protocol types it already decodes, without running a JSON parser on the reply. For ordinary use, [`JSON.GET`](/docs/redis/commands/json/json-get) is the more convenient command. +Values are JSON text and each one is appended as a single element, so appending an array adds a nested array rather than merging its items. The reply is the new length of each array the path matched, with null for matches that are not arrays. ## Syntax ```redis -JSON.RESP [path] +JSON.ARRAPPEND [value ...] ``` ## Arguments @@ -37396,7 +37420,8 @@ JSON.RESP [path] | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | | `key` | Yes | No | JSON document key. | -| `path` | No | No | Path to convert; defaults to the root. | +| `path` | Yes | No | Path selecting arrays. | +| `value` | Yes | Yes | Valid JSON value to append. | ## Important points @@ -37409,8 +37434,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Nested array, Integer, Bulk string, Simple string (`true` or `false` for JSON booleans), or Null bulk string or null array | -| RESP3 | Nested array, Integer, Bulk string, Simple string (`true` or `false` for JSON booleans), or Null | +| RESP2 | Array of integer lengths or null values, one per matched path | +| RESP3 | Array of integer lengths or null values, one per matched path | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -37425,7 +37450,7 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -JSON.RESP profile $ +JSON.ARRAPPEND profile $.tags '"new"' ``` @@ -37436,8 +37461,8 @@ JSON.RESP profile $ import { Redis } from "@upstash/redis"; const redis = Redis.fromEnv(); -const result = await redis.json.resp("profile", "$"); -console.log(result); + +await redis.json.arrappend("key", "$.path.to.array", "a"); ``` @@ -37448,7 +37473,7 @@ console.log(result); from upstash_redis import Redis redis = Redis.from_env() -result = redis.json().resp("profile", "$") +result = redis.json().arrappend("profile", "$.tags", "new") print(result) ``` @@ -37460,7 +37485,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.call("JSON.RESP", "profile", "$"); +const result = await redis.call("JSON.ARRAPPEND", "profile", "$.tags", "\"new\""); console.log(result); ``` @@ -37474,7 +37499,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.sendCommand(["JSON.RESP", "profile", "$"]); +const result = await client.json.arrAppend("profile", "$.tags", "new"); console.log(result); ``` @@ -37487,7 +37512,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.json().resp("profile", "$") +result = client.json().arrappend("profile", "$.tags", "new") print(result) ``` @@ -37512,193 +37537,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.Do(context.Background(), "JSON.RESP", "profile", "$").Result() - if err != nil { - panic(err) - } - fmt.Println(result) -} -``` - - - - - -```java -import java.net.URI; -import java.nio.charset.StandardCharsets; -import redis.clients.jedis.Jedis; -import redis.clients.jedis.commands.ProtocolCommand; - -ProtocolCommand command = () -> "JSON.RESP".getBytes(StandardCharsets.UTF_8); -try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.sendCommand(command, "profile", "$"); - System.out.println(result); -} -``` - - - - - -```rust -fn main() -> redis::RedisResult<()> { - let url = std::env::var("REDIS_URL").expect("REDIS_URL is not set"); - let client = redis::Client::open(url)?; - let mut connection = client.get_connection()?; - - let mut command = redis::cmd("JSON.RESP"); - command.arg("profile"); - command.arg("$"); - let result: redis::Value = command.query(&mut connection)?; - println!("{result:?}"); - Ok(()) -} -``` - - - - - -# JSON.SET -Source: https://upstash.com/docs/redis/commands/json/json-set - -Use `JSON.SET` to set a JSON value at a path inside a document, creating the key when it does not exist. - -The value is JSON text; the Upstash SDK helpers serialize native objects for you. With the root path (`$`) the whole document is replaced, which is also how a new document is created. For a nested path the parent must already exist: the command adds one missing child to an existing object or appends to an existing array, but it does not create intermediate levels along the way. - -`NX` writes only when the path does not exist yet and `XX` only when it does, which makes conditional updates atomic. When the path is a JSONPath that matches several places, every match is updated in the same call, so a single command can update all elements of an array. - -## Syntax - -```redis -JSON.SET [NX | XX] -``` - -## Arguments - -| Argument | Required | Repeatable | Description | -| --- | --- | --- | --- | -| `key` | Yes | No | JSON document key. | -| `path` | Yes | No | Path to create or replace. | -| `value` | Yes | No | Valid JSON value. | -| `NX \| XX` | No | No | Write only when the path is absent (`NX`) or present (`XX`). | - -## Important points - -* Raw TCP examples pass JSON values as valid JSON text. Typed Upstash SDK helpers serialize native objects and values for you. -* Paths beginning with `$` use JSONPath and can match multiple values, so many JSON commands return an array of per-match results. - -## Response - -The reply reports the result of the operation. Error replies have the same shape in RESP2 and RESP3 and are surfaced as exceptions by the SDKs below. - -| Protocol | Reply | -| --- | --- | -| RESP2 | Simple string `OK`, or Null bulk string or null array when `NX` or `XX` prevented the write | -| RESP3 | Simple string `OK`, or Null when `NX` or `XX` prevented the write | - - - Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. - - -## Examples - -TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use `UPSTASH_REDIS_REST_URL` and `UPSTASH_REDIS_REST_TOKEN`. - - - - - -```bash -JSON.SET profile $ '{"name":"Ada"}' -``` - - - - - -```ts -import { Redis } from "@upstash/redis"; - -const redis = Redis.fromEnv(); - -redis.json.set(key, "$.path", value); -``` - - - - - -```python -from upstash_redis import Redis - -redis = Redis.from_env() -result = redis.json().set("profile", "$", {"name": "Ada"}) -print(result) -``` - - - - - -```ts -import Redis from "ioredis"; - -const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.call("JSON.SET", "profile", "$", "{\"name\":\"Ada\"}"); -console.log(result); -``` - - - - - -```ts -import { createClient } from "redis"; - -const client = await createClient({ url: process.env.REDIS_URL }) - .on("error", console.error) - .connect(); -const result = await client.json.set("profile", "$", { name: "Ada" }); -console.log(result); -``` - - - - - -```python -import os -import redis - -client = redis.from_url(os.environ["REDIS_URL"]) -result = client.json().set("profile", "$", {"name": "Ada"}) -print(result) -``` - - - - - -```go -package main - -import ( - "context" - "fmt" - "os" - - "github.com/redis/go-redis/v9" -) - -func main() { - opts, err := redis.ParseURL(os.Getenv("REDIS_URL")) - if err != nil { - panic(err) - } - client := redis.NewClient(opts) - result, err := client.JSONSet(context.Background(), "profile", "$", map[string]interface{}{"name": "Ada"}).Result() + result, err := client.JSONArrAppend(context.Background(), "profile", "$.tags", "new").Result() if err != nil { panic(err) } @@ -37716,7 +37555,7 @@ import java.net.URI; import redis.clients.jedis.JedisPooled; try (JedisPooled jedis = new JedisPooled(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.jsonSet("profile", new redis.clients.jedis.json.Path("$"), java.util.Map.of("name", "Ada")); + Object result = jedis.jsonArrAppend("profile", new redis.clients.jedis.json.Path("$.tags"), "new"); System.out.println(result); } ``` @@ -37733,7 +37572,7 @@ fn main() -> redis::RedisResult<()> { let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let result: redis::Value = connection.json_set("profile", "$", &serde_json::json!({"name": "Ada"}))?; + let result: redis::Value = connection.json_arr_append("profile", "$.tags", &"new")?; println!("{result:?}"); Ok(()) } @@ -37743,17 +37582,17 @@ fn main() -> redis::RedisResult<()> { -# JSON.STRAPPEND -Source: https://upstash.com/docs/redis/commands/json/json-strappend +# JSON.ARRINDEX +Source: https://upstash.com/docs/redis/commands/json/json-arrindex -Use `JSON.STRAPPEND` to append text to the string values a path selects. +Use `JSON.ARRINDEX` to find the first position of a value inside the arrays a path selects. -The value is JSON text, so the appended string must be quoted, as in `'"suffix"'`. The reply is the new length of each string the path matched, with an error for matches that are not strings. Appending in place avoids reading and rewriting the whole document just to extend one field. +The value is JSON text and is compared for exact equality, so `1` does not match `"1"`. The optional `start` and `stop` bound the search: `start` is inclusive, `stop` is exclusive, both may be negative to count from the end of the array, and `0` for `stop` means "to the end". The reply is the index of the first match or `-1` when the value is not present, with one result per array the path matched. ## Syntax ```redis -JSON.STRAPPEND +JSON.ARRINDEX [start [stop]] ``` ## Arguments @@ -37761,8 +37600,10 @@ JSON.STRAPPEND | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | | `key` | Yes | No | JSON document key. | -| `path` | Yes | No | Path selecting strings. | -| `value` | Yes | No | JSON-encoded string to append. | +| `path` | Yes | No | Path selecting arrays. | +| `value` | Yes | No | Valid JSON value to locate. | +| `start` | No | No | Inclusive starting index. | +| `stop` | No | No | Exclusive ending index. | ## Important points @@ -37775,8 +37616,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Integer, array of integer lengths or null values, or Null bulk string or null array | -| RESP3 | Integer, array of integer lengths or null values, or Null | +| RESP2 | Array of integer indexes or null values, one per matched path | +| RESP3 | Array of integer indexes or null values, one per matched path | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -37791,7 +37632,7 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -JSON.STRAPPEND profile $.name '" Lovelace"' +JSON.ARRINDEX profile $.tags '"new"' ``` @@ -37803,7 +37644,7 @@ import { Redis } from "@upstash/redis"; const redis = Redis.fromEnv(); -await redis.json.strappend("key", "$.path.to.str", "abc"); +const index = await redis.json.arrindex("key", "$.path.to.array", "a"); ``` @@ -37814,7 +37655,7 @@ await redis.json.strappend("key", "$.path.to.str", "abc"); from upstash_redis import Redis redis = Redis.from_env() -result = redis.json().strappend("profile", "$.name", " Lovelace") +result = redis.json().arrindex("profile", "$.tags", "new") print(result) ``` @@ -37826,7 +37667,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.call("JSON.STRAPPEND", "profile", "$.name", "\" Lovelace\""); +const result = await redis.call("JSON.ARRINDEX", "profile", "$.tags", "\"new\""); console.log(result); ``` @@ -37840,7 +37681,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.json.strAppend("profile", " Lovelace", { path: "$.name" }); +const result = await client.json.arrIndex("profile", "$.tags", "new"); console.log(result); ``` @@ -37853,7 +37694,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.json().strappend("profile", " Lovelace", "$.name") +result = client.json().arrindex("profile", "$.tags", "new") print(result) ``` @@ -37878,7 +37719,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.JSONStrAppend(context.Background(), "profile", "$.name", `" Lovelace"`).Result() + result, err := client.JSONArrIndex(context.Background(), "profile", "$.tags", "new").Result() if err != nil { panic(err) } @@ -37896,7 +37737,7 @@ import java.net.URI; import redis.clients.jedis.JedisPooled; try (JedisPooled jedis = new JedisPooled(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.jsonStrAppend("profile", new redis.clients.jedis.json.Path("$.name"), " Lovelace"); + Object result = jedis.jsonArrIndex("profile", new redis.clients.jedis.json.Path("$.tags"), "new"); System.out.println(result); } ``` @@ -37913,7 +37754,7 @@ fn main() -> redis::RedisResult<()> { let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let result: redis::Value = connection.json_str_append("profile", "$.name", " Lovelace")?; + let result: redis::Value = connection.json_arr_index("profile", "$.tags", &"new")?; println!("{result:?}"); Ok(()) } @@ -37923,17 +37764,17 @@ fn main() -> redis::RedisResult<()> { -# JSON.STRLEN -Source: https://upstash.com/docs/redis/commands/json/json-strlen +# JSON.ARRINSERT +Source: https://upstash.com/docs/redis/commands/json/json-arrinsert -Use `JSON.STRLEN` to get the length of the string values a path selects. +Use `JSON.ARRINSERT` to insert one or more values into the arrays a path selects, before a given index. -Without a path the root value is used. The reply holds one length per match, with null for matches that are not strings, so it is also a quick way to check that a field is a string. The value itself is not transferred, which makes it cheap even for long strings. +Elements at and after that index shift to the right, keeping the rest of the array in order. A negative index counts from the end of the array and an index equal to the array's length appends, while an index outside the array returns an error. The reply is the new length of each array the path matched. ## Syntax ```redis -JSON.STRLEN [path] +JSON.ARRINSERT [value ...] ``` ## Arguments @@ -37941,7 +37782,9 @@ JSON.STRLEN [path] | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | | `key` | Yes | No | JSON document key. | -| `path` | No | No | Path selecting strings; defaults to the root. | +| `path` | Yes | No | Path selecting arrays. | +| `index` | Yes | No | Insertion index; negative indexes count from the end. | +| `value` | Yes | Yes | Valid JSON value to insert. | ## Important points @@ -37954,8 +37797,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Integer, array of integer lengths or null values, or Null bulk string or null array | -| RESP3 | Integer, array of integer lengths or null values, or Null | +| RESP2 | Array of integer lengths or null values, one per matched path | +| RESP3 | Array of integer lengths or null values, one per matched path | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -37970,7 +37813,7 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -JSON.STRLEN profile $.name +JSON.ARRINSERT profile $.tags 0 '"first"' ``` @@ -37982,7 +37825,7 @@ import { Redis } from "@upstash/redis"; const redis = Redis.fromEnv(); -await redis.json.strlen("key", "$.path.to.str", "a"); +const length = await redis.json.arrinsert("key", "$.path.to.array", 2, "a", "b"); ``` @@ -37993,7 +37836,7 @@ await redis.json.strlen("key", "$.path.to.str", "a"); from upstash_redis import Redis redis = Redis.from_env() -result = redis.json().strlen("profile", "$.name") +result = redis.json().arrinsert("profile", "$.tags", 0, "first") print(result) ``` @@ -38005,7 +37848,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.call("JSON.STRLEN", "profile", "$.name"); +const result = await redis.call("JSON.ARRINSERT", "profile", "$.tags", "0", "\"first\""); console.log(result); ``` @@ -38019,7 +37862,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.json.strLen("profile", { path: "$.name" }); +const result = await client.json.arrInsert("profile", "$.tags", 0, "first"); console.log(result); ``` @@ -38032,7 +37875,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.json().strlen("profile", "$.name") +result = client.json().arrinsert("profile", "$.tags", 0, "first") print(result) ``` @@ -38057,7 +37900,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.JSONStrLen(context.Background(), "profile", "$.name").Result() + result, err := client.JSONArrInsert(context.Background(), "profile", "$.tags", 0, "first").Result() if err != nil { panic(err) } @@ -38075,7 +37918,7 @@ import java.net.URI; import redis.clients.jedis.JedisPooled; try (JedisPooled jedis = new JedisPooled(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.jsonStrLen("profile", new redis.clients.jedis.json.Path("$.name")); + Object result = jedis.jsonArrInsert("profile", new redis.clients.jedis.json.Path("$.tags"), 0, "first"); System.out.println(result); } ``` @@ -38092,7 +37935,7 @@ fn main() -> redis::RedisResult<()> { let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let result: redis::Value = connection.json_str_len("profile", "$.name")?; + let result: redis::Value = connection.json_arr_insert("profile", "$.tags", 0, &"first")?; println!("{result:?}"); Ok(()) } @@ -38102,17 +37945,17 @@ fn main() -> redis::RedisResult<()> { -# JSON.TOGGLE -Source: https://upstash.com/docs/redis/commands/json/json-toggle +# JSON.ARRLEN +Source: https://upstash.com/docs/redis/commands/json/json-arrlen -Use `JSON.TOGGLE` to flip the boolean values a path selects, turning `true` into `false` and back. +Use `JSON.ARRLEN` to get the number of elements in the arrays a path selects. -The reply is the new value of each match, and a match that is not a boolean returns an error. Because the read and the write are one atomic step, this is the safe way to flip a flag inside a document, where reading it and writing the opposite value back would race with other clients. +Without a path the root value is used. The reply is one length per match, with null for matches that are not arrays, so it doubles as a cheap way to check that a branch of the document really is an array before working on it. ## Syntax ```redis -JSON.TOGGLE +JSON.ARRLEN [path] ``` ## Arguments @@ -38120,7 +37963,7 @@ JSON.TOGGLE | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | | `key` | Yes | No | JSON document key. | -| `path` | Yes | No | Path selecting Boolean values. | +| `path` | No | No | Path selecting arrays; defaults to the root. | ## Important points @@ -38133,8 +37976,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Array of integer Boolean values or null values, one per matched path | -| RESP3 | Array of integer Boolean values or null values, one per matched path | +| RESP2 | Array of integer lengths or null values, one per matched path | +| RESP3 | Array of integer lengths or null values, one per matched path | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -38149,7 +37992,7 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -JSON.TOGGLE profile $.active +JSON.ARRLEN profile $.tags ``` @@ -38161,7 +38004,7 @@ import { Redis } from "@upstash/redis"; const redis = Redis.fromEnv(); -const bool = await redis.json.toggle("key", "$.path.to.bool"); +const length = await redis.json.arrlen("key", "$.path.to.array"); ``` @@ -38172,7 +38015,7 @@ const bool = await redis.json.toggle("key", "$.path.to.bool"); from upstash_redis import Redis redis = Redis.from_env() -result = redis.json().toggle("profile", "$.active") +result = redis.json().arrlen("profile", "$.tags") print(result) ``` @@ -38184,7 +38027,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.call("JSON.TOGGLE", "profile", "$.active"); +const result = await redis.call("JSON.ARRLEN", "profile", "$.tags"); console.log(result); ``` @@ -38198,7 +38041,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.json.toggle("profile", "$.active"); +const result = await client.json.arrLen("profile", { path: "$.tags" }); console.log(result); ``` @@ -38211,7 +38054,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.json().toggle("profile", "$.active") +result = client.json().arrlen("profile", "$.tags") print(result) ``` @@ -38236,7 +38079,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.JSONToggle(context.Background(), "profile", "$.active").Result() + result, err := client.JSONArrLen(context.Background(), "profile", "$.tags").Result() if err != nil { panic(err) } @@ -38254,7 +38097,7 @@ import java.net.URI; import redis.clients.jedis.JedisPooled; try (JedisPooled jedis = new JedisPooled(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.jsonToggle("profile", new redis.clients.jedis.json.Path("$.active")); + Object result = jedis.jsonArrLen("profile", new redis.clients.jedis.json.Path("$.tags")); System.out.println(result); } ``` @@ -38271,7 +38114,7 @@ fn main() -> redis::RedisResult<()> { let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let result: redis::Value = connection.json_toggle("profile", "$.active")?; + let result: redis::Value = connection.json_arr_len("profile", "$.tags")?; println!("{result:?}"); Ok(()) } @@ -38281,17 +38124,19 @@ fn main() -> redis::RedisResult<()> { -# JSON.TYPE -Source: https://upstash.com/docs/redis/commands/json/json-type +# JSON.ARRPOP +Source: https://upstash.com/docs/redis/commands/json/json-arrpop -Use `JSON.TYPE` to find out the JSON type of the values a path selects. +Use `JSON.ARRPOP` to remove an element from the arrays a path selects and return it. -The reply names one type per match, one of `object`, `array`, `string`, `integer`, `number`, `boolean`, or `null`, and is empty when the path matches nothing. Whole numbers report as `integer` and fractional ones as `number`. It is the way to inspect documents whose shape you do not control before applying type-specific commands, which would otherwise fail. +Without an index the last element is popped, which makes the command a stack pop; index `0` pops the first element, and negative indexes count from the end. An index past the end of the array is clamped to the last element. The reply is the removed element as JSON text, or null when the array is empty. + +Because the read and the removal happen in one atomic step, a JSON array can be used as a small work queue without the risk of two clients taking the same element. ## Syntax ```redis -JSON.TYPE [path] +JSON.ARRPOP [path [index]] ``` ## Arguments @@ -38299,7 +38144,8 @@ JSON.TYPE [path] | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | | `key` | Yes | No | JSON document key. | -| `path` | No | No | Path to inspect; defaults to the root. | +| `path` | No | No | Path selecting arrays; defaults to the root. | +| `index` | No | No | Element index; defaults to the last element. | ## Important points @@ -38312,8 +38158,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Bulk string, array of bulk-string type names, or Null bulk string or null array | -| RESP3 | Bulk string, array of bulk-string type names, or Null | +| RESP2 | Array of bulk-string JSON values or null values, one per matched path | +| RESP3 | Array of bulk-string JSON values or null values, one per matched path | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -38328,7 +38174,7 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -JSON.TYPE profile $.name +JSON.ARRPOP profile $.tags -1 ``` @@ -38340,7 +38186,7 @@ import { Redis } from "@upstash/redis"; const redis = Redis.fromEnv(); -const myType = await redis.json.type("key", "$.path.to.value"); +const element = await redis.json.arrpop("key", "$.path.to.array"); ``` @@ -38351,7 +38197,7 @@ const myType = await redis.json.type("key", "$.path.to.value"); from upstash_redis import Redis redis = Redis.from_env() -result = redis.json().type("profile", "$.name") +result = redis.json().arrpop("profile", "$.tags", -1) print(result) ``` @@ -38363,7 +38209,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.call("JSON.TYPE", "profile", "$.name"); +const result = await redis.call("JSON.ARRPOP", "profile", "$.tags", "-1"); console.log(result); ``` @@ -38377,7 +38223,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.json.type("profile", { path: "$.name" }); +const result = await client.json.arrPop("profile", { path: "$.tags", index: -1 }); console.log(result); ``` @@ -38390,7 +38236,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.json().type("profile", "$.name") +result = client.json().arrpop("profile", "$.tags", -1) print(result) ``` @@ -38415,7 +38261,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.JSONType(context.Background(), "profile", "$.name").Result() + result, err := client.JSONArrPop(context.Background(), "profile", "$.tags", -1).Result() if err != nil { panic(err) } @@ -38433,7 +38279,7 @@ import java.net.URI; import redis.clients.jedis.JedisPooled; try (JedisPooled jedis = new JedisPooled(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.jsonType("profile", new redis.clients.jedis.json.Path("$.name")); + Object result = jedis.jsonArrPop("profile", new redis.clients.jedis.json.Path("$.tags"), -1); System.out.println(result); } ``` @@ -38450,7 +38296,7 @@ fn main() -> redis::RedisResult<()> { let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let result: redis::Value = connection.json_type("profile", "$.name")?; + let result: redis::Value = connection.json_arr_pop("profile", "$.tags", -1)?; println!("{result:?}"); Ok(()) } @@ -38460,66 +38306,34 @@ fn main() -> redis::RedisResult<()> { -# JSON commands -Source: https://upstash.com/docs/redis/commands/json/overview - -To query inside JSON values (full-text, fuzzy, phrase, regex), see [Upstash Redis Search](/docs/redis/search/introduction). - - -Append values to JSON array -Find index of value in array -Insert values into JSON array -Get JSON array length -Pop value from JSON array -Trim JSON array to range -Clear JSON values -Delete JSON values -Inspect JSON memory usage -Delete JSON values (alias of JSON.DEL) -Get JSON values -Merge JSON values -Get values from multiple keys -Set values in multiple keys -Increment JSON number -Multiply JSON number -Get JSON object keys -Get JSON object size -Get JSON in RESP format -Set JSON value -Append to JSON string -Get JSON string length -Toggle JSON boolean -Get JSON value type - - -# BLMOVE -Source: https://upstash.com/docs/redis/commands/list/blmove +# JSON.ARRTRIM +Source: https://upstash.com/docs/redis/commands/json/json-arrtrim -Use `BLMOVE` to move an element from one list to another, blocking until the source has an element or the timeout expires. +Use `JSON.ARRTRIM` to keep only a range of elements in the arrays a path selects and discard the rest. -It is the blocking form of [`LMOVE`](/docs/redis/commands/list/lmove): when the source list is not empty it behaves identically and returns immediately, and when it is empty the connection waits instead of returning null. The timeout is given in seconds, may be fractional, and `0` waits indefinitely. If several clients are waiting on the same key, the one that has been waiting longest is served first. +Both `start` and `stop` are inclusive indexes and may be negative to count from the end of the array. Indexes outside the array are clamped, and a range that selects nothing leaves an empty array. The reply is the new length of each array the path matched. -Because the element is never outside a list, this is the standard way to build a reliable queue: a worker blocks until work appears, atomically moves it to a processing list, and deletes it from there when done, so an interrupted job can be recovered instead of lost. +It is the JSON counterpart of [`LTRIM`](/docs/redis/commands/list/ltrim): combine it with [`JSON.ARRAPPEND`](/docs/redis/commands/json/json-arrappend) to keep a capped list, such as the last N events, inside a document. ## Syntax ```redis -BLMOVE (LEFT | RIGHT) (LEFT | RIGHT) +JSON.ARRTRIM ``` ## Arguments | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | -| `` | Yes | No | Redis key used as source. | -| `` | Yes | No | Redis key used as destination. | -| `(LEFT \| RIGHT)` | Yes | No | Which end of the source list the element is taken from: `LEFT` (head) or `RIGHT` (tail). | -| `(LEFT \| RIGHT)` | Yes | No | Which end of the destination list the element is pushed onto: `LEFT` (head) or `RIGHT` (tail). | -| `` | Yes | No | Seconds to block; `0` blocks indefinitely. | +| `key` | Yes | No | JSON document key. | +| `path` | Yes | No | Path selecting arrays. | +| `start` | Yes | No | Inclusive first index to keep. | +| `stop` | Yes | No | Inclusive last index to keep. | ## Important points -* A blocking form holds the request until data arrives or its timeout expires. Set the client/network timeout longer than the command timeout. +* Raw TCP examples pass JSON values as valid JSON text. Typed Upstash SDK helpers serialize native objects and values for you. +* Paths beginning with `$` use JSONPath and can match multiple values, so many JSON commands return an array of per-match results. ## Response @@ -38527,8 +38341,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Bulk string or Null bulk string or null array | -| RESP3 | Bulk string or Null | +| RESP2 | Array of integer lengths or null values, one per matched path | +| RESP3 | Array of integer lengths or null values, one per matched path | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -38543,24 +38357,32 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -BLMOVE source-key destination-key LEFT LEFT 1.5 +JSON.ARRTRIM profile $.tags 0 9 ``` - - This command is not supported yet in `@upstash/redis`. - +```ts +import { Redis } from "@upstash/redis"; + +const redis = Redis.fromEnv(); + +const length = await redis.json.arrtrim("key", "$.path.to.array", 2, 10); +``` - - This command is not supported yet in `upstash_redis`. - +```python +from upstash_redis import Redis + +redis = Redis.from_env() +result = redis.json().arrtrim("profile", "$.tags", 0, 9) +print(result) +``` @@ -38570,7 +38392,7 @@ BLMOVE source-key destination-key LEFT LEFT 1.5 import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.blmove("source-key", "destination-key", "LEFT", "LEFT", "1.5"); +const result = await redis.call("JSON.ARRTRIM", "profile", "$.tags", "0", "9"); console.log(result); ``` @@ -38584,7 +38406,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.blMove("source-key", "destination-key", "LEFT", "LEFT", 1.5); +const result = await client.json.arrTrim("profile", "$.tags", 0, 9); console.log(result); ``` @@ -38597,7 +38419,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.blmove("source-key", "destination-key", 1.5, "LEFT", "LEFT") +result = client.json().arrtrim("profile", "$.tags", 0, 9) print(result) ``` @@ -38612,7 +38434,6 @@ import ( "context" "fmt" "os" - "time" "github.com/redis/go-redis/v9" ) @@ -38623,7 +38444,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.BLMove(context.Background(), "source-key", "destination-key", "LEFT", "LEFT", 1500*time.Millisecond).Result() + result, err := client.JSONArrTrimWithArgs(context.Background(), "profile", "$.tags", &redis.JSONArrTrimArgs{Start: 0, Stop: func() *int { stop := 9; return &stop }()}).Result() if err != nil { panic(err) } @@ -38638,10 +38459,10 @@ func main() { ```java import java.net.URI; -import redis.clients.jedis.Jedis; +import redis.clients.jedis.JedisPooled; -try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.blmove("source-key", "destination-key", redis.clients.jedis.args.ListDirection.LEFT, redis.clients.jedis.args.ListDirection.LEFT, 1.5); +try (JedisPooled jedis = new JedisPooled(new URI(System.getenv("REDIS_URL")))) { + Object result = jedis.jsonArrTrim("profile", new redis.clients.jedis.json.Path("$.tags"), 0, 9); System.out.println(result); } ``` @@ -38651,20 +38472,14 @@ try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { ```rust -use redis::{Direction, TypedCommands}; +use redis::JsonCommands; fn main() -> redis::RedisResult<()> { let url = std::env::var("REDIS_URL").expect("REDIS_URL is not set"); let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let result = connection.blmove( - "source-key", - "destination-key", - Direction::Left, - Direction::Left, - 1.5, - )?; + let result: redis::Value = connection.json_arr_trim("profile", "$.tags", 0, 9)?; println!("{result:?}"); Ok(()) } @@ -38674,34 +38489,32 @@ fn main() -> redis::RedisResult<()> { -# BLMPOP -Source: https://upstash.com/docs/redis/commands/list/blmpop +# JSON.CLEAR +Source: https://upstash.com/docs/redis/commands/json/json-clear -Use `BLMPOP` to pop elements from the first non-empty list among several, blocking until one has elements or the timeout expires. +Use `JSON.CLEAR` to empty the values a path selects without removing them from the document. -It is the blocking form of [`LMPOP`](/docs/redis/commands/list/lmpop): keys are examined in the order given, so listing a high priority queue first drains it before the others are considered, `LEFT` or `RIGHT` chooses the end, and `COUNT` sets how many elements to take. The reply names the key that was popped from along with the elements. +Objects lose all their keys, arrays lose all their elements, and numbers are reset to `0`. Values of other types, such as strings and booleans, are left as they are. The reply is the number of values that were cleared. -The timeout is in seconds, may be fractional, and `0` waits indefinitely; when it expires the reply is null. This is the command to reach for when one worker serves several queues of differing priority. +The difference from [`JSON.DEL`](/docs/redis/commands/json/json-del) is that the selected keys and slots stay in the document as empty containers, so the shape of the document is preserved and consumers that expect a field to exist keep working. ## Syntax ```redis -BLMPOP [ ...] (LEFT | RIGHT) [COUNT ] +JSON.CLEAR [path] ``` ## Arguments | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | -| `` | Yes | No | Seconds to block; `0` blocks indefinitely. | -| `` | Yes | No | Number of key arguments that follow. | -| `` | Yes | Yes | Redis key targeted by the command. | -| `(LEFT \| RIGHT)` | Yes | No | Which end to pop from: `LEFT` (head) or `RIGHT` (tail). | -| `COUNT ` | No | No | Maximum number of elements to pop. | +| `key` | Yes | No | JSON document key. | +| `path` | No | No | Path to containers or numbers; defaults to the root. | ## Important points -* A blocking form holds the request until data arrives or its timeout expires. Set the client/network timeout longer than the command timeout. +* Raw TCP examples pass JSON values as valid JSON text. Typed Upstash SDK helpers serialize native objects and values for you. +* Paths beginning with `$` use JSONPath and can match multiple values, so many JSON commands return an array of per-match results. ## Response @@ -38709,8 +38522,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Null bulk string or null array, or two-element array: key and array of values | -| RESP3 | Null, or two-element array: key and array of values | +| RESP2 | Integer | +| RESP3 | Integer | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -38725,24 +38538,32 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -BLMPOP 1.5 1 my-key LEFT +JSON.CLEAR profile $.stats ``` - - This command is not supported yet in `@upstash/redis`. - +```ts +import { Redis } from "@upstash/redis"; + +const redis = Redis.fromEnv(); + +await redis.json.clear("key"); +``` - - This command is not supported yet in `upstash_redis`. - +```python +from upstash_redis import Redis + +redis = Redis.from_env() +result = redis.json().clear("profile", "$.stats") +print(result) +``` @@ -38752,7 +38573,7 @@ BLMPOP 1.5 1 my-key LEFT import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.blmpop("1.5", "1", "my-key", "LEFT"); +const result = await redis.call("JSON.CLEAR", "profile", "$.stats"); console.log(result); ``` @@ -38766,7 +38587,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.blmPop(1.5, "my-key", "LEFT"); +const result = await client.json.clear("profile", { path: "$.stats" }); console.log(result); ``` @@ -38779,7 +38600,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.blmpop(1.5, 1, "my-key", direction="LEFT") +result = client.json().clear("profile", "$.stats") print(result) ``` @@ -38794,7 +38615,6 @@ import ( "context" "fmt" "os" - "time" "github.com/redis/go-redis/v9" ) @@ -38805,7 +38625,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - _, result, err := client.BLMPop(context.Background(), 1500*time.Millisecond, "LEFT", 0, "my-key").Result() + result, err := client.JSONClear(context.Background(), "profile", "$.stats").Result() if err != nil { panic(err) } @@ -38820,10 +38640,10 @@ func main() { ```java import java.net.URI; -import redis.clients.jedis.Jedis; +import redis.clients.jedis.JedisPooled; -try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.blmpop(1.5, redis.clients.jedis.args.ListDirection.LEFT, "my-key"); +try (JedisPooled jedis = new JedisPooled(new URI(System.getenv("REDIS_URL")))) { + Object result = jedis.jsonClear("profile", new redis.clients.jedis.json.Path("$.stats")); System.out.println(result); } ``` @@ -38833,14 +38653,14 @@ try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { ```rust -use redis::{Direction, TypedCommands}; +use redis::JsonCommands; fn main() -> redis::RedisResult<()> { let url = std::env::var("REDIS_URL").expect("REDIS_URL is not set"); let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let result = connection.blmpop(1.5, 1, "my-key", Direction::Left, 1)?; + let result: redis::Value = connection.json_clear("profile", "$.stats")?; println!("{result:?}"); Ok(()) } @@ -38850,31 +38670,35 @@ fn main() -> redis::RedisResult<()> { -# BLPOP -Source: https://upstash.com/docs/redis/commands/list/blpop +# JSON.DEBUG +Source: https://upstash.com/docs/redis/commands/json/json-debug -Use `BLPOP` to pop an element from the head of the first non-empty list, blocking until one has an element or the timeout expires. +Use `JSON.DEBUG` to inspect internal details of stored JSON values. -It is the blocking form of [`LPOP`](/docs/redis/commands/list/lpop) and it accepts several keys, which are checked in the order given, so earlier keys act as higher priority queues. The reply names the key the element came from together with the element itself, which matters when you are waiting on more than one queue. +`JSON.DEBUG MEMORY` reports the approximate number of bytes used by the value a key and optional path select, which is how you find out which documents, or which branches of a document, are responsible for memory growth. The figure includes internal overhead and is an estimate meant for comparison rather than exact accounting. `JSON.DEBUG HELP` lists the supported forms. -The timeout is in seconds, may be fractional, and `0` waits indefinitely; when it expires the reply is null. Blocking lets a worker wait for work without polling, which cuts both latency and wasted commands. When several clients are blocked on the same key they are served in the order they started waiting. +It is a diagnostic aid: the details it exposes are implementation-specific and can change, so do not build application logic on them. ## Syntax ```redis -BLPOP [ ...] +JSON.DEBUG MEMORY [path] +JSON.DEBUG HELP ``` ## Arguments | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | -| `` | Yes | Yes | Redis key targeted by the command. | -| `` | Yes | No | Seconds to block; `0` blocks indefinitely. | +| `MEMORY` | One form | No | Report the approximate memory used by the JSON value selected by key and optional path. | +| `key` | For MEMORY | No | JSON document key. | +| `path` | No | No | JSONPath to inspect; defaults to the root. | +| `HELP` | One form | No | Return the supported JSON.DEBUG forms. | ## Important points -* A blocking form holds the request until data arrives or its timeout expires. Set the client/network timeout longer than the command timeout. +* Raw TCP examples pass JSON values as valid JSON text. Typed Upstash SDK helpers serialize native objects and values for you. +* Paths beginning with `$` use JSONPath and can match multiple values, so many JSON commands return an array of per-match results. ## Response @@ -38882,8 +38706,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Null bulk string or null array, or two-element key/value array | -| RESP3 | Null, or two-element key/value array | +| RESP2 | Integer, array of integers, or array of help strings | +| RESP3 | Integer, array of integers, or array of help strings | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -38898,7 +38722,7 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -BLPOP my-key 1.5 +JSON.DEBUG MEMORY profile $.stats ``` @@ -38925,7 +38749,7 @@ BLPOP my-key 1.5 import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.blpop("my-key", "1.5"); +const result = await redis.call("JSON.DEBUG", "MEMORY", "profile", "$.stats"); console.log(result); ``` @@ -38939,7 +38763,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.blPop("my-key", 1.5); +const result = await client.json.debugMemory("profile", { path: "$.stats" }); console.log(result); ``` @@ -38952,7 +38776,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.blpop(["my-key"], timeout=1.5) +result = client.json().debug("MEMORY", "profile", "$.stats") print(result) ``` @@ -38967,7 +38791,6 @@ import ( "context" "fmt" "os" - "time" "github.com/redis/go-redis/v9" ) @@ -38978,7 +38801,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.BLPop(context.Background(), 1500*time.Millisecond, "my-key").Result() + result, err := client.JSONDebugMemory(context.Background(), "profile", "$.stats").Result() if err != nil { panic(err) } @@ -38993,10 +38816,10 @@ func main() { ```java import java.net.URI; -import redis.clients.jedis.Jedis; +import redis.clients.jedis.JedisPooled; -try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.blpop(1.5, "my-key"); +try (JedisPooled jedis = new JedisPooled(new URI(System.getenv("REDIS_URL")))) { + Object result = jedis.jsonDebugMemory("profile", new redis.clients.jedis.json.Path("$.stats")); System.out.println(result); } ``` @@ -39006,14 +38829,16 @@ try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { ```rust -use redis::TypedCommands; - fn main() -> redis::RedisResult<()> { let url = std::env::var("REDIS_URL").expect("REDIS_URL is not set"); let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let result = connection.blpop("my-key", 1.5)?; + let mut command = redis::cmd("JSON.DEBUG"); + command.arg("MEMORY"); + command.arg("profile"); + command.arg("$.stats"); + let result: redis::Value = command.query(&mut connection)?; println!("{result:?}"); Ok(()) } @@ -39023,31 +38848,32 @@ fn main() -> redis::RedisResult<()> { -# BRPOP -Source: https://upstash.com/docs/redis/commands/list/brpop +# JSON.DEL +Source: https://upstash.com/docs/redis/commands/json/json-del -Use `BRPOP` to pop an element from the tail of the first non-empty list, blocking until one has an element or the timeout expires. +Use `JSON.DEL` to delete the value at a path in a JSON document. -It is the blocking form of [`RPOP`](/docs/redis/commands/list/rpop) and behaves like [`BLPOP`](/docs/redis/commands/list/blpop) in every other respect: several keys are checked in the order given, the reply names the key the element came from, the timeout is in seconds and may be fractional with `0` meaning wait forever, and clients blocked on the same key are served in the order they started waiting. +Without a path the entire key is deleted. The reply is the number of values that were deleted, which is `0` when the path matched nothing, and deleting the root of a document removes the key itself. With a JSONPath that matches several places, every match is removed in the same call. -Producers pushing with [`LPUSH`](/docs/redis/commands/list/lpush) and consumers waiting with `BRPOP` form the classic first-in, first-out worker queue. +[`JSON.FORGET`](/docs/redis/commands/json/json-forget) is an alias with identical behavior. ## Syntax ```redis -BRPOP [ ...] +JSON.DEL [path] ``` ## Arguments | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | -| `` | Yes | Yes | Redis key targeted by the command. | -| `` | Yes | No | Seconds to block; `0` blocks indefinitely. | +| `key` | Yes | No | JSON document key. | +| `path` | No | No | Path to delete; omitting it deletes the whole key. | ## Important points -* A blocking form holds the request until data arrives or its timeout expires. Set the client/network timeout longer than the command timeout. +* Raw TCP examples pass JSON values as valid JSON text. Typed Upstash SDK helpers serialize native objects and values for you. +* Paths beginning with `$` use JSONPath and can match multiple values, so many JSON commands return an array of per-match results. ## Response @@ -39055,8 +38881,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Null bulk string or null array, or two-element key/value array | -| RESP3 | Null, or two-element key/value array | +| RESP2 | Integer | +| RESP3 | Integer | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -39071,24 +38897,32 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -BRPOP my-key 1.5 +JSON.DEL profile $.temporary ``` - - This command is not supported yet in `@upstash/redis`. - +```ts +import { Redis } from "@upstash/redis"; + +const redis = Redis.fromEnv(); + +await redis.json.del("key", "$.path.to.value"); +``` - - This command is not supported yet in `upstash_redis`. - +```python +from upstash_redis import Redis + +redis = Redis.from_env() +result = redis.json().delete("profile", "$.temporary") +print(result) +``` @@ -39098,7 +38932,7 @@ BRPOP my-key 1.5 import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.brpop("my-key", "1.5"); +const result = await redis.call("JSON.DEL", "profile", "$.temporary"); console.log(result); ``` @@ -39112,7 +38946,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.brPop("my-key", 1.5); +const result = await client.json.del("profile", { path: "$.temporary" }); console.log(result); ``` @@ -39125,7 +38959,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.brpop(["my-key"], timeout=1.5) +result = client.json().delete("profile", "$.temporary") print(result) ``` @@ -39140,7 +38974,6 @@ import ( "context" "fmt" "os" - "time" "github.com/redis/go-redis/v9" ) @@ -39151,7 +38984,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.BRPop(context.Background(), 1500*time.Millisecond, "my-key").Result() + result, err := client.JSONDel(context.Background(), "profile", "$.temporary").Result() if err != nil { panic(err) } @@ -39166,10 +38999,10 @@ func main() { ```java import java.net.URI; -import redis.clients.jedis.Jedis; +import redis.clients.jedis.JedisPooled; -try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.brpop(1.5, "my-key"); +try (JedisPooled jedis = new JedisPooled(new URI(System.getenv("REDIS_URL")))) { + Object result = jedis.jsonDel("profile", new redis.clients.jedis.json.Path("$.temporary")); System.out.println(result); } ``` @@ -39179,14 +39012,14 @@ try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { ```rust -use redis::TypedCommands; +use redis::JsonCommands; fn main() -> redis::RedisResult<()> { let url = std::env::var("REDIS_URL").expect("REDIS_URL is not set"); let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let result = connection.brpop("my-key", 1.5)?; + let result: redis::Value = connection.json_del("profile", "$.temporary")?; println!("{result:?}"); Ok(()) } @@ -39196,36 +39029,30 @@ fn main() -> redis::RedisResult<()> { -# BRPOPLPUSH -Source: https://upstash.com/docs/redis/commands/list/brpoplpush - - - Prefer [`BLMOVE`](/docs/redis/commands/list/blmove) with `RIGHT` and `LEFT` in new code: `BLMOVE RIGHT LEFT `. - - -Use `BRPOPLPUSH` to pop an element from the tail of one list and push it to the head of another, blocking until the source has an element or the timeout expires. +# JSON.FORGET +Source: https://upstash.com/docs/redis/commands/json/json-forget -It is the blocking form of [`RPOPLPUSH`](/docs/redis/commands/list/rpoplpush). The timeout is in seconds, may be fractional, and `0` waits indefinitely; when it expires the reply is null. Since the element moves atomically into the destination, a worker that crashes after taking an item leaves it visible in the processing list, where it can be recovered. +Use `JSON.FORGET` to delete the value at a path in a JSON document. It is an alias of [`JSON.DEL`](/docs/redis/commands/json/json-del) with identical behavior, kept for compatibility with clients and code that use the older name. -[`BLMOVE`](/docs/redis/commands/list/blmove) does the same thing and additionally lets you choose which end of each list to use. +Without a path the entire key is deleted, and the reply is the number of values that were deleted, which is `0` when the path matched nothing. ## Syntax ```redis -BRPOPLPUSH +JSON.FORGET [path] ``` ## Arguments | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | -| `` | Yes | No | Redis key used as source. | -| `` | Yes | No | Redis key used as destination. | -| `` | Yes | No | Seconds to block; `0` blocks indefinitely. | +| `key` | Yes | No | JSON document key. | +| `path` | No | No | Path to delete; omitting it deletes the whole key. | ## Important points -* A blocking form holds the request until data arrives or its timeout expires. Set the client/network timeout longer than the command timeout. +* Raw TCP examples pass JSON values as valid JSON text. Typed Upstash SDK helpers serialize native objects and values for you. +* Paths beginning with `$` use JSONPath and can match multiple values, so many JSON commands return an array of per-match results. ## Response @@ -39233,8 +39060,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Bulk string or Null bulk string or null array | -| RESP3 | Bulk string or Null | +| RESP2 | Integer | +| RESP3 | Integer | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -39249,24 +39076,32 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -BRPOPLPUSH source-key destination-key 1.5 +JSON.FORGET profile $.temporary ``` - - This command is not supported yet in `@upstash/redis`. - +```ts +import { Redis } from "@upstash/redis"; + +const redis = Redis.fromEnv(); + +await redis.json.forget("key", "$.path.to.value"); +``` - - This command is not supported yet in `upstash_redis`. - +```python +from upstash_redis import Redis + +redis = Redis.from_env() +result = redis.json().forget("profile", "$.temporary") +print(result) +``` @@ -39276,7 +39111,7 @@ BRPOPLPUSH source-key destination-key 1.5 import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.brpoplpush("source-key", "destination-key", "1.5"); +const result = await redis.call("JSON.FORGET", "profile", "$.temporary"); console.log(result); ``` @@ -39290,7 +39125,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.brPopLPush("source-key", "destination-key", 1.5); +const result = await client.json.forget("profile", { path: "$.temporary" }); console.log(result); ``` @@ -39303,7 +39138,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.brpoplpush("source-key", "destination-key", timeout=1.5) +result = client.json().forget("profile", "$.temporary") print(result) ``` @@ -39318,7 +39153,6 @@ import ( "context" "fmt" "os" - "time" "github.com/redis/go-redis/v9" ) @@ -39329,7 +39163,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.BRPopLPush(context.Background(), "source-key", "destination-key", 1500*time.Millisecond).Result() + result, err := client.JSONForget(context.Background(), "profile", "$.temporary").Result() if err != nil { panic(err) } @@ -39344,10 +39178,10 @@ func main() { ```java import java.net.URI; -import redis.clients.jedis.Jedis; +import redis.clients.jedis.JedisPooled; -try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.brpoplpush("source-key", "destination-key", 1); +try (JedisPooled jedis = new JedisPooled(new URI(System.getenv("REDIS_URL")))) { + Object result = jedis.jsonDel("profile", new redis.clients.jedis.json.Path("$.temporary")); System.out.println(result); } ``` @@ -39357,14 +39191,14 @@ try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { ```rust -use redis::TypedCommands; +use redis::JsonCommands; fn main() -> redis::RedisResult<()> { let url = std::env::var("REDIS_URL").expect("REDIS_URL is not set"); let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let result = connection.brpoplpush("source-key", "destination-key", 1.5)?; + let result: redis::Value = connection.json_del("profile", "$.temporary")?; println!("{result:?}"); Ok(()) } @@ -39374,25 +39208,39 @@ fn main() -> redis::RedisResult<()> { -# LINDEX -Source: https://upstash.com/docs/redis/commands/list/lindex +# JSON.GET +Source: https://upstash.com/docs/redis/commands/json/json-get -Use `LINDEX` to read the element at a given position in a list. +Use `JSON.GET` to read one or more values from a JSON document. -Indexes are zero-based from the head, and negative indexes count from the tail, so `-1` is the last element. The reply is null when the key does not exist or the index is out of range. Redis walks the list from the nearer end to reach the index, so access is fast near the head and tail and gets more expensive towards the middle of a long list. +Without a path the whole document is returned. The reply shape depends on the path syntax: a path starting with `$` is a JSONPath and always returns an array with one entry per match, so an empty array means nothing matched, while the legacy dot syntax returns the value itself and reports an error when the path does not exist. Passing several paths returns an object keyed by the path expressions, which is a cheap way to pull a few unrelated branches of a large document in one call. + +`INDENT`, `NEWLINE`, and `SPACE` control the formatting of the returned JSON text, which is otherwise compact. They are meant for human-readable output; leave them out when a program parses the reply. ## Syntax ```redis -LINDEX +JSON.GET + [INDENT indent] + [NEWLINE newline] + [SPACE space] + [path [path ...]] ``` ## Arguments | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | -| `` | Yes | No | Redis key targeted by the command. | -| `` | Yes | No | Zero-based index; negative values count from the end. | +| `key` | Yes | No | JSON document key. | +| `INDENT indent` | No | No | Indentation characters for formatted JSON. | +| `NEWLINE newline` | No | No | Line-separator characters for formatted JSON. | +| `SPACE space` | No | No | Characters placed after JSON separators. | +| `path` | No | Yes | One or more paths; defaults to the root. | + +## Important points + +* Raw TCP examples pass JSON values as valid JSON text. Typed Upstash SDK helpers serialize native objects and values for you. +* Paths beginning with `$` use JSONPath and can match multiple values, so many JSON commands return an array of per-match results. ## Response @@ -39400,8 +39248,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Null bulk string or null array or Bulk string | -| RESP3 | Null or Bulk string | +| RESP2 | Bulk string or Null bulk string or null array | +| RESP3 | Bulk string or Null | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -39416,7 +39264,7 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -LINDEX my-key 0 +JSON.GET profile $.name ``` @@ -39428,9 +39276,7 @@ import { Redis } from "@upstash/redis"; const redis = Redis.fromEnv(); -await redis.rpush("key", "a", "b", "c"); -const element = await redis.lindex("key", 0); -console.log(element); // "a" +const value = await redis.json.get("key", "$.path.to.somewhere"); ``` @@ -39441,7 +39287,7 @@ console.log(element); // "a" from upstash_redis import Redis redis = Redis.from_env() -result = redis.lindex("my-key", 0) +result = redis.json().get("profile", "$.name") print(result) ``` @@ -39453,7 +39299,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.lindex("my-key", "0"); +const result = await redis.call("JSON.GET", "profile", "$.name"); console.log(result); ``` @@ -39467,7 +39313,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.lIndex("my-key", 0); +const result = await client.json.get("profile", { path: "$.name" }); console.log(result); ``` @@ -39480,7 +39326,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.lindex("my-key", 0) +result = client.json().get("profile", "$.name") print(result) ``` @@ -39505,7 +39351,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.LIndex(context.Background(), "my-key", 0).Result() + result, err := client.JSONGet(context.Background(), "profile", "$.name").Result() if err != nil { panic(err) } @@ -39520,10 +39366,10 @@ func main() { ```java import java.net.URI; -import redis.clients.jedis.Jedis; +import redis.clients.jedis.JedisPooled; -try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.lindex("my-key", 0); +try (JedisPooled jedis = new JedisPooled(new URI(System.getenv("REDIS_URL")))) { + Object result = jedis.jsonGet("profile", new redis.clients.jedis.json.Path("$.name")); System.out.println(result); } ``` @@ -39533,14 +39379,14 @@ try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { ```rust -use redis::TypedCommands; +use redis::JsonCommands; fn main() -> redis::RedisResult<()> { let url = std::env::var("REDIS_URL").expect("REDIS_URL is not set"); let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let result = connection.lindex("my-key", 0)?; + let result: redis::Value = connection.json_get("profile", &["$.name"])?; println!("{result:?}"); Ok(()) } @@ -39550,29 +39396,33 @@ fn main() -> redis::RedisResult<()> { -# LINSERT -Source: https://upstash.com/docs/redis/commands/list/linsert +# JSON.MERGE +Source: https://upstash.com/docs/redis/commands/json/json-merge -Use `LINSERT` to insert an element immediately before or after another element of a list. +Use `JSON.MERGE` to merge a JSON value into a document at a path, following the JSON Merge Patch semantics of RFC 7386. -The pivot is matched by value, and only its first occurrence starting from the head is used. The reply is the new length of the list, `0` when the key does not exist, and `-1` when the pivot value was not found, which is how you tell a failed insert from a successful one. +Objects are merged recursively: keys in the patch replace or create the matching keys in the target, keys set to `null` delete them, and any value that is not an object, arrays included, replaces the target outright instead of being merged element by element. The parent must already exist: merging into a missing child of an existing object creates it, but intermediate levels are not built along the way, and a key that does not exist yet can only be created by merging at the root. -Finding the pivot means scanning the list, so this is a linear operation; on long lists it is worth keeping an index elsewhere or using a sorted set instead. +This is the command for partial updates of an object, where [`JSON.SET`](/docs/redis/commands/json/json-set) would replace the whole branch: one call can change a few fields, delete another, and leave the rest of the document untouched. ## Syntax ```redis -LINSERT (BEFORE | AFTER) +JSON.MERGE ``` ## Arguments | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | -| `` | Yes | No | Redis key targeted by the command. | -| `(BEFORE \| AFTER)` | Yes | No | Where to place the new element relative to the pivot: `BEFORE` or `AFTER`. | -| `` | Yes | No | Existing element to insert next to. | -| `` | Yes | No | Element to insert. | +| `key` | Yes | No | JSON document key. | +| `path` | Yes | No | Path to merge into. | +| `value` | Yes | No | Valid JSON value containing the merge patch. | + +## Important points + +* Raw TCP examples pass JSON values as valid JSON text. Typed Upstash SDK helpers serialize native objects and values for you. +* Paths beginning with `$` use JSONPath and can match multiple values, so many JSON commands return an array of per-match results. ## Response @@ -39580,8 +39430,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Integer: the list length after insertion, `-1` if the pivot was not found, `0` if the key does not exist | -| RESP3 | Integer: the list length after insertion, `-1` if the pivot was not found, `0` if the key does not exist | +| RESP2 | Simple string `OK` | +| RESP3 | Simple string `OK` | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -39596,7 +39446,7 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -LINSERT my-key BEFORE pivot element +JSON.MERGE profile $ '{"active":true}' ``` @@ -39608,8 +39458,7 @@ import { Redis } from "@upstash/redis"; const redis = Redis.fromEnv(); -await redis.rpush("key", "a", "b", "c"); -await redis.linsert("key", "before", "b", "x"); +await redis.json.merge("key", "$.path.to.value", {"new": "value"}) ``` @@ -39620,7 +39469,7 @@ await redis.linsert("key", "before", "b", "x"); from upstash_redis import Redis redis = Redis.from_env() -result = redis.linsert("my-key", "BEFORE", "pivot", "element") +result = redis.json().merge("profile", "$", {"active": True}) print(result) ``` @@ -39632,7 +39481,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.linsert("my-key", "BEFORE", "pivot", "element"); +const result = await redis.call("JSON.MERGE", "profile", "$", "{\"active\":true}"); console.log(result); ``` @@ -39646,7 +39495,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.lInsert("my-key", "BEFORE", "pivot", "element"); +const result = await client.json.merge("profile", "$", { active: true }); console.log(result); ``` @@ -39659,7 +39508,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.linsert("my-key", "BEFORE", "pivot", "element") +result = client.json().merge("profile", "$", {"active": True}) print(result) ``` @@ -39684,7 +39533,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.LInsertBefore(context.Background(), "my-key", "pivot", "element").Result() + result, err := client.JSONMerge(context.Background(), "profile", "$", `{"active":true}`).Result() if err != nil { panic(err) } @@ -39699,10 +39548,10 @@ func main() { ```java import java.net.URI; -import redis.clients.jedis.Jedis; +import redis.clients.jedis.JedisPooled; -try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.linsert("my-key", redis.clients.jedis.args.ListPosition.BEFORE, "pivot", "element"); +try (JedisPooled jedis = new JedisPooled(new URI(System.getenv("REDIS_URL")))) { + Object result = jedis.jsonMerge("profile", new redis.clients.jedis.json.Path("$"), java.util.Map.of("active", true)); System.out.println(result); } ``` @@ -39712,14 +39561,16 @@ try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { ```rust -use redis::TypedCommands; - fn main() -> redis::RedisResult<()> { let url = std::env::var("REDIS_URL").expect("REDIS_URL is not set"); let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let result = connection.linsert_before("my-key", "pivot", "element")?; + let mut command = redis::cmd("JSON.MERGE"); + command.arg("profile"); + command.arg("$"); + command.arg("{\"active\":true}"); + let result: redis::Value = command.query(&mut connection)?; println!("{result:?}"); Ok(()) } @@ -39729,24 +39580,30 @@ fn main() -> redis::RedisResult<()> { -# LLEN -Source: https://upstash.com/docs/redis/commands/list/llen +# JSON.MGET +Source: https://upstash.com/docs/redis/commands/json/json-mget -Use `LLEN` to get the number of elements in a list. +Use `JSON.MGET` to read the same path from several JSON documents in one call. -The reply is `0` when the key does not exist. The length is maintained by Redis rather than computed on demand, so the command is cheap whatever the size of the list, which makes it the usual way to monitor a queue's backlog. +The reply holds one entry per key, in the order requested, containing what the path selected in that document, or null when the key does not exist or the path matches nothing. It replaces one [`JSON.GET`](/docs/redis/commands/json/json-get) per key when you are gathering the same field across many documents, for example the price of every product in a cart. ## Syntax ```redis -LLEN +JSON.MGET [key ...] ``` ## Arguments | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | -| `` | Yes | No | Redis key targeted by the command. | +| `key` | Yes | Yes | One or more JSON document keys. | +| `path` | Yes | No | Path read from every key. | + +## Important points + +* Raw TCP examples pass JSON values as valid JSON text. Typed Upstash SDK helpers serialize native objects and values for you. +* Paths beginning with `$` use JSONPath and can match multiple values, so many JSON commands return an array of per-match results. ## Response @@ -39754,8 +39611,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Integer | -| RESP3 | Integer | +| RESP2 | Array of bulk-string JSON values or null values, one per key | +| RESP3 | Array of bulk-string JSON values or null values, one per key | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -39770,7 +39627,7 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -LLEN my-key +JSON.MGET profile:1 profile:2 $.name ``` @@ -39782,9 +39639,7 @@ import { Redis } from "@upstash/redis"; const redis = Redis.fromEnv(); -await redis.rpush("key", "a", "b", "c"); -const length = await redis.llen("key"); -console.log(length); // 3 +const values = await redis.json.mget(["key1", "key2"], "$.path.to.somewhere"); ``` @@ -39795,7 +39650,7 @@ console.log(length); // 3 from upstash_redis import Redis redis = Redis.from_env() -result = redis.llen("my-key") +result = redis.json().mget(["profile:1", "profile:2"], "$.name") print(result) ``` @@ -39807,7 +39662,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.llen("my-key"); +const result = await redis.call("JSON.MGET", "profile:1", "profile:2", "$.name"); console.log(result); ``` @@ -39821,7 +39676,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.lLen("my-key"); +const result = await client.json.mGet(["profile:1", "profile:2"], "$.name"); console.log(result); ``` @@ -39834,7 +39689,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.llen("my-key") +result = client.json().mget(["profile:1", "profile:2"], "$.name") print(result) ``` @@ -39859,7 +39714,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.LLen(context.Background(), "my-key").Result() + result, err := client.JSONMGet(context.Background(), "$.name", "profile:1", "profile:2").Result() if err != nil { panic(err) } @@ -39874,10 +39729,10 @@ func main() { ```java import java.net.URI; -import redis.clients.jedis.Jedis; +import redis.clients.jedis.JedisPooled; -try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.llen("my-key"); +try (JedisPooled jedis = new JedisPooled(new URI(System.getenv("REDIS_URL")))) { + Object result = jedis.jsonMGet(new redis.clients.jedis.json.Path2("$.name"), "profile:1", "profile:2"); System.out.println(result); } ``` @@ -39887,14 +39742,14 @@ try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { ```rust -use redis::TypedCommands; +use redis::JsonCommands; fn main() -> redis::RedisResult<()> { let url = std::env::var("REDIS_URL").expect("REDIS_URL is not set"); let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let result = connection.llen("my-key")?; + let result: redis::Value = connection.json_mget(&["profile:1", "profile:2"], "$.name")?; println!("{result:?}"); Ok(()) } @@ -39904,29 +39759,29 @@ fn main() -> redis::RedisResult<()> { -# LMOVE -Source: https://upstash.com/docs/redis/commands/list/lmove - -Use `LMOVE` to atomically take an element from one end of a list and push it onto one end of another list, returning the element. +# JSON.MSET +Source: https://upstash.com/docs/redis/commands/json/json-mset -The two directions are chosen independently: `LEFT RIGHT` takes from the head of the source and appends to the tail of the destination, which preserves order when transferring between queues, while `LEFT LEFT` behaves like moving between stacks. If the source is empty nothing happens and the reply is null. Source and destination may be the same key, in which case the list is rotated. +Use `JSON.MSET` to set values at paths in several JSON documents in one atomic call. -Because the element is never outside a list, `LMOVE` is the building block for reliable queues: a worker moves an item into a processing list, does the work, and removes it from there, so a crash leaves the item recoverable instead of lost. It replaces the deprecated [`RPOPLPUSH`](/docs/redis/commands/list/rpoplpush), and [`BLMOVE`](/docs/redis/commands/list/blmove) is the blocking form. +Each triple gives a key, a path, and a value, and missing keys are created. Either every write is applied or none is, with no other command running in between, which is what makes it the right tool for documents that must stay consistent with each other. The reply is `OK`. ## Syntax ```redis -LMOVE (LEFT | RIGHT) (LEFT | RIGHT) +JSON.MSET [key path value ...] ``` ## Arguments | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | -| `` | Yes | No | Redis key used as source. | -| `` | Yes | No | Redis key used as destination. | -| `(LEFT \| RIGHT)` | Yes | No | Which end of the source list the element is taken from: `LEFT` (head) or `RIGHT` (tail). | -| `(LEFT \| RIGHT)` | Yes | No | Which end of the destination list the element is pushed onto: `LEFT` (head) or `RIGHT` (tail). | +| `key path value` | Yes | Yes | One or more key, path, and valid-JSON-value triples. | + +## Important points + +* Raw TCP examples pass JSON values as valid JSON text. Typed Upstash SDK helpers serialize native objects and values for you. +* Paths beginning with `$` use JSONPath and can match multiple values, so many JSON commands return an array of per-match results. ## Response @@ -39934,8 +39789,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Bulk string | -| RESP3 | Bulk string | +| RESP2 | Simple string `OK` | +| RESP3 | Simple string `OK` | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -39950,7 +39805,7 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -LMOVE source-key destination-key LEFT LEFT +JSON.MSET profile:1 $ '{"name":"Ada"}' ``` @@ -39961,9 +39816,10 @@ LMOVE source-key destination-key LEFT LEFT import { Redis } from "@upstash/redis"; const redis = Redis.fromEnv(); - -await redis.rpush("source", "a", "b", "c"); -const element = await redis.lmove("source", "destination", "left", "left"); +const result = await redis.json.mset([ + { key: "profile:1", path: "$", value: { name: "Ada" } }, +]); +console.log(result); ``` @@ -39974,7 +39830,7 @@ const element = await redis.lmove("source", "destination", "left", "left"); from upstash_redis import Redis redis = Redis.from_env() -result = redis.lmove("source-key", "destination-key", "LEFT", "LEFT") +result = redis.json().mset([("profile:1", "$", {"name": "Ada"})]) print(result) ``` @@ -39986,7 +39842,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.lmove("source-key", "destination-key", "LEFT", "LEFT"); +const result = await redis.call("JSON.MSET", "profile:1", "$", "{\"name\":\"Ada\"}"); console.log(result); ``` @@ -40000,7 +39856,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.lMove("source-key", "destination-key", "LEFT", "LEFT"); +const result = await client.json.mSet([{ key: "profile:1", path: "$", value: { name: "Ada" } }]); console.log(result); ``` @@ -40013,7 +39869,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.lmove("source-key", "destination-key", "LEFT", "LEFT") +result = client.json().mset([("profile:1", "$", {"name": "Ada"})]) print(result) ``` @@ -40038,7 +39894,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.LMove(context.Background(), "source-key", "destination-key", "LEFT", "LEFT").Result() + result, err := client.JSONMSet(context.Background(), "profile:1", "$", `{"name":"Ada"}`).Result() if err != nil { panic(err) } @@ -40052,11 +39908,13 @@ func main() { ```java import java.net.URI; - +import java.nio.charset.StandardCharsets; import redis.clients.jedis.Jedis; +import redis.clients.jedis.commands.ProtocolCommand; +ProtocolCommand command = () -> "JSON.MSET".getBytes(StandardCharsets.UTF_8); try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.lmove("source-key", "destination-key", redis.clients.jedis.args.ListDirection.LEFT, redis.clients.jedis.args.ListDirection.LEFT); + Object result = jedis.sendCommand(command, "profile:1", "$", "{\"name\":\"Ada\"}"); System.out.println(result); } ``` @@ -40066,14 +39924,14 @@ try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { ```rust -use redis::TypedCommands; +use redis::JsonCommands; fn main() -> redis::RedisResult<()> { let url = std::env::var("REDIS_URL").expect("REDIS_URL is not set"); let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let result = connection.lmove("source-key", "destination-key", redis::Direction::Left, redis::Direction::Left)?; + let result: redis::Value = connection.json_mset(&[("profile:1", "$", serde_json::json!({"name": "Ada"}))])?; println!("{result:?}"); Ok(()) } @@ -40083,29 +39941,31 @@ fn main() -> redis::RedisResult<()> { -# LMPOP -Source: https://upstash.com/docs/redis/commands/list/lmpop - -Use `LMPOP` to pop elements from the first of several lists that is not empty. +# JSON.NUMINCRBY +Source: https://upstash.com/docs/redis/commands/json/json-numincrby -`` states how many keys follow, `LEFT` or `RIGHT` chooses the end to pop from, and `COUNT` sets how many elements to take, defaulting to one. Keys are examined in the order given and only the first non-empty one is touched, which is exactly what a priority queue needs: list the high priority queue first and it is drained before the others are looked at. +Use `JSON.NUMINCRBY` to add a number to the numeric values a path selects. -The reply names the key that was popped from together with the elements, so a caller working with several queues knows where the work came from. When every key is empty the reply is null; use [`BLMPOP`](/docs/redis/commands/list/blmpop) to wait instead. +The increment may be negative to count down, and it is applied atomically, so concurrent callers cannot lose an update. The reply is the new value of each match; a match that is not a number returns an error. This is how a counter kept inside a document is updated without reading and rewriting the whole document. ## Syntax ```redis -LMPOP [ ...] (LEFT | RIGHT) [COUNT ] +JSON.NUMINCRBY ``` ## Arguments | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | -| `` | Yes | No | Number of key arguments that follow. | -| `` | Yes | Yes | Redis key targeted by the command. | -| `(LEFT \| RIGHT)` | Yes | No | Which end to pop from: `LEFT` (head) or `RIGHT` (tail). | -| `COUNT ` | No | No | Maximum number of elements to pop. | +| `key` | Yes | No | JSON document key. | +| `path` | Yes | No | Path selecting numbers. | +| `value` | Yes | No | Numeric amount to add. | + +## Important points + +* Raw TCP examples pass JSON values as valid JSON text. Typed Upstash SDK helpers serialize native objects and values for you. +* Paths beginning with `$` use JSONPath and can match multiple values, so many JSON commands return an array of per-match results. ## Response @@ -40113,8 +39973,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Null bulk string or null array, or two-element array: key and array of values | -| RESP3 | Null, or two-element array: key and array of values | +| RESP2 | Bulk string containing a JSON number or array | +| RESP3 | Bulk string containing a JSON number or array | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -40129,7 +39989,7 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -LMPOP 1 my-key LEFT +JSON.NUMINCRBY profile $.visits 1 ``` @@ -40140,17 +40000,21 @@ LMPOP 1 my-key LEFT import { Redis } from "@upstash/redis"; const redis = Redis.fromEnv(); -const result = await redis.lmpop(1, ["my-key"], "LEFT"); -console.log(result); + +const newValue = await redis.json.numincrby("key", "$.path.to.value", 2); ``` - - This command is not supported yet in `upstash_redis`. - +```python +from upstash_redis import Redis + +redis = Redis.from_env() +result = redis.json().numincrby("profile", "$.visits", 1) +print(result) +``` @@ -40160,7 +40024,7 @@ console.log(result); import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.lmpop("1", "my-key", "LEFT"); +const result = await redis.call("JSON.NUMINCRBY", "profile", "$.visits", "1"); console.log(result); ``` @@ -40174,7 +40038,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.lmPop("my-key", "LEFT"); +const result = await client.json.numIncrBy("profile", "$.visits", 1); console.log(result); ``` @@ -40187,7 +40051,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.lmpop(1, "my-key", direction="LEFT") +result = client.json().numincrby("profile", "$.visits", 1) print(result) ``` @@ -40212,7 +40076,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - _, result, err := client.LMPop(context.Background(), "LEFT", 0, "my-key").Result() + result, err := client.JSONNumIncrBy(context.Background(), "profile", "$.visits", 1).Result() if err != nil { panic(err) } @@ -40227,10 +40091,10 @@ func main() { ```java import java.net.URI; -import redis.clients.jedis.Jedis; +import redis.clients.jedis.JedisPooled; -try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.lmpop(redis.clients.jedis.args.ListDirection.LEFT, "my-key"); +try (JedisPooled jedis = new JedisPooled(new URI(System.getenv("REDIS_URL")))) { + Object result = jedis.jsonNumIncrBy("profile", new redis.clients.jedis.json.Path("$.visits"), 1); System.out.println(result); } ``` @@ -40240,14 +40104,14 @@ try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { ```rust -use redis::{Direction, TypedCommands}; +use redis::JsonCommands; fn main() -> redis::RedisResult<()> { let url = std::env::var("REDIS_URL").expect("REDIS_URL is not set"); let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let result = connection.lmpop(1, "my-key", Direction::Left, 1)?; + let result: redis::Value = connection.json_num_incr_by("profile", "$.visits", 1)?; println!("{result:?}"); Ok(()) } @@ -40257,27 +40121,31 @@ fn main() -> redis::RedisResult<()> { -# LPOP -Source: https://upstash.com/docs/redis/commands/list/lpop - -Use `LPOP` to remove and return elements from the head of a list. +# JSON.NUMMULTBY +Source: https://upstash.com/docs/redis/commands/json/json-nummultby -Without a count a single element is returned, or null when the key does not exist. With a count, up to that many elements are removed and returned in the order they were popped, and the reply is an empty array or null when the list is empty. The key is deleted once the last element is removed. +Use `JSON.NUMMULTBY` to multiply the numeric values a path selects by a number. -Paired with [`RPUSH`](/docs/redis/commands/list/rpush) this gives a first-in, first-out queue; paired with [`LPUSH`](/docs/redis/commands/list/lpush) it gives a stack. Use [`BLPOP`](/docs/redis/commands/list/blpop) when a consumer should wait for work rather than poll. +The multiplier may be a fraction to scale values down, and the update is atomic. The reply is the new value of each match, and a match that is not a number returns an error. It is the multiplicative counterpart of [`JSON.NUMINCRBY`](/docs/redis/commands/json/json-numincrby), useful for applying percentage changes such as a discount to every price in a document. ## Syntax ```redis -LPOP [] +JSON.NUMMULTBY ``` ## Arguments | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | -| `` | Yes | No | Redis key targeted by the command. | -| `` | No | No | Number of elements to pop. | +| `key` | Yes | No | JSON document key. | +| `path` | Yes | No | Path selecting numbers. | +| `value` | Yes | No | Numeric multiplier. | + +## Important points + +* Raw TCP examples pass JSON values as valid JSON text. Typed Upstash SDK helpers serialize native objects and values for you. +* Paths beginning with `$` use JSONPath and can match multiple values, so many JSON commands return an array of per-match results. ## Response @@ -40285,8 +40153,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Null bulk string or null array, Bulk string, or array of bulk-string values | -| RESP3 | Null, Bulk string, or array of bulk-string values | +| RESP2 | Bulk string containing a JSON number or array | +| RESP3 | Bulk string containing a JSON number or array | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -40301,7 +40169,7 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -LPOP my-key +JSON.NUMMULTBY profile $.score 2 ``` @@ -40313,9 +40181,7 @@ import { Redis } from "@upstash/redis"; const redis = Redis.fromEnv(); -await redis.rpush("key", "a", "b", "c"); -const element = await redis.lpop("key"); -console.log(element); // "a" +const newValue = await redis.json.nummultby("key", "$.path.to.value", 2); ``` @@ -40326,7 +40192,7 @@ console.log(element); // "a" from upstash_redis import Redis redis = Redis.from_env() -result = redis.lpop("my-key") +result = redis.json().nummultby("profile", "$.score", 2) print(result) ``` @@ -40338,7 +40204,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.lpop("my-key"); +const result = await redis.call("JSON.NUMMULTBY", "profile", "$.score", "2"); console.log(result); ``` @@ -40352,7 +40218,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.lPop("my-key"); +const result = await client.json.numMultBy("profile", "$.score", 2); console.log(result); ``` @@ -40365,7 +40231,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.lpop("my-key") +result = client.json().nummultby("profile", "$.score", 2) print(result) ``` @@ -40390,7 +40256,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.LPop(context.Background(), "my-key").Result() + result, err := client.Do(context.Background(), "JSON.NUMMULTBY", "profile", "$.score", "2").Result() if err != nil { panic(err) } @@ -40404,11 +40270,13 @@ func main() { ```java import java.net.URI; - +import java.nio.charset.StandardCharsets; import redis.clients.jedis.Jedis; +import redis.clients.jedis.commands.ProtocolCommand; +ProtocolCommand command = () -> "JSON.NUMMULTBY".getBytes(StandardCharsets.UTF_8); try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.lpop("my-key"); + Object result = jedis.sendCommand(command, "profile", "$.score", "2"); System.out.println(result); } ``` @@ -40418,14 +40286,16 @@ try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { ```rust -use redis::TypedCommands; - fn main() -> redis::RedisResult<()> { let url = std::env::var("REDIS_URL").expect("REDIS_URL is not set"); let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let result: Option = connection.lpop("my-key", None)?; + let mut command = redis::cmd("JSON.NUMMULTBY"); + command.arg("profile"); + command.arg("$.score"); + command.arg("2"); + let result: redis::Value = command.query(&mut connection)?; println!("{result:?}"); Ok(()) } @@ -40435,30 +40305,30 @@ fn main() -> redis::RedisResult<()> { -# LPOS -Source: https://upstash.com/docs/redis/commands/list/lpos - -Use `LPOS` to find the position of an element in a list. +# JSON.OBJKEYS +Source: https://upstash.com/docs/redis/commands/json/json-objkeys -The scan starts at the head and, by default, reports the index of the first match or null when there is none. `RANK` selects which match to report: `RANK 2` skips to the second occurrence, and a negative rank searches backwards from the tail, so `RANK -1` finds the last occurrence. `COUNT` returns that many matching indexes instead of just one, and `COUNT 0` returns all of them. `MAXLEN` limits how many elements are compared, which bounds the cost of the search on a long list at the price of possibly missing matches beyond that point. +Use `JSON.OBJKEYS` to list the field names of the objects a path selects. -It is the read-only way to locate a value before acting on it with [`LSET`](/docs/redis/commands/list/lset) or [`LREM`](/docs/redis/commands/list/lrem). +Without a path the root value is used. The reply holds one list of keys per match, with null for matches that are not objects. Only the field names come back, not the values, which makes it a cheap way to inspect the shape of a document before reading it. ## Syntax ```redis -LPOS [RANK ] [COUNT ] [MAXLEN ] +JSON.OBJKEYS [path] ``` ## Arguments | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | -| `` | Yes | No | Redis key targeted by the command. | -| `` | Yes | No | Element value to search for. | -| `RANK ` | No | No | Which match to return; negative values search from the tail. | -| `COUNT ` | No | No | Number of matches to return; `0` returns every match. | -| `MAXLEN ` | No | No | Maximum number of entries to keep in the stream. | +| `key` | Yes | No | JSON document key. | +| `path` | No | No | Path selecting objects; defaults to the root. | + +## Important points + +* Raw TCP examples pass JSON values as valid JSON text. Typed Upstash SDK helpers serialize native objects and values for you. +* Paths beginning with `$` use JSONPath and can match multiple values, so many JSON commands return an array of per-match results. ## Response @@ -40466,8 +40336,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Null bulk string or null array, Integer, or array of integer positions | -| RESP3 | Null, Integer, or array of integer positions | +| RESP2 | Array of arrays of bulk-string object keys or null values, or Null bulk string or null array | +| RESP3 | Array of arrays of bulk-string object keys or null values, or Null | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -40482,7 +40352,7 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -LPOS my-key element +JSON.OBJKEYS profile $ ``` @@ -40494,9 +40364,7 @@ import { Redis } from "@upstash/redis"; const redis = Redis.fromEnv(); -await redis.rpush("key", "a", "b", "c"); -const index = await redis.lpos("key", "b"); -console.log(index); // 1 +const keys = await redis.json.objkeys("key", "$.path"); ``` @@ -40507,7 +40375,7 @@ console.log(index); // 1 from upstash_redis import Redis redis = Redis.from_env() -result = redis.lpos("my-key", "element") +result = redis.json().objkeys("profile", "$") print(result) ``` @@ -40519,7 +40387,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.lpos("my-key", "element"); +const result = await redis.call("JSON.OBJKEYS", "profile", "$"); console.log(result); ``` @@ -40533,7 +40401,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.lPos("my-key", "element"); +const result = await client.json.objKeys("profile", { path: "$" }); console.log(result); ``` @@ -40546,7 +40414,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.lpos("my-key", "element") +result = client.json().objkeys("profile", "$") print(result) ``` @@ -40571,7 +40439,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.LPos(context.Background(), "my-key", "element", redis.LPosArgs{}).Result() + result, err := client.JSONObjKeys(context.Background(), "profile", "$").Result() if err != nil { panic(err) } @@ -40586,10 +40454,10 @@ func main() { ```java import java.net.URI; -import redis.clients.jedis.Jedis; +import redis.clients.jedis.JedisPooled; -try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.lpos("my-key", "element"); +try (JedisPooled jedis = new JedisPooled(new URI(System.getenv("REDIS_URL")))) { + Object result = jedis.jsonObjKeys("profile", new redis.clients.jedis.json.Path("$")); System.out.println(result); } ``` @@ -40599,14 +40467,14 @@ try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { ```rust -use redis::{LposOptions, TypedCommands}; +use redis::JsonCommands; fn main() -> redis::RedisResult<()> { let url = std::env::var("REDIS_URL").expect("REDIS_URL is not set"); let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let result: Option = connection.lpos("my-key", "element", LposOptions::default())?; + let result: redis::Value = connection.json_obj_keys("profile", "$")?; println!("{result:?}"); Ok(()) } @@ -40616,27 +40484,30 @@ fn main() -> redis::RedisResult<()> { -# LPUSH -Source: https://upstash.com/docs/redis/commands/list/lpush - -Use `LPUSH` to add one or more elements to the head of a list, creating the list when the key does not exist. +# JSON.OBJLEN +Source: https://upstash.com/docs/redis/commands/json/json-objlen -Elements are inserted one after another, so they end up in reverse order relative to the argument list: `LPUSH key a b c` leaves the list as `c`, `b`, `a`. The reply is the length of the list after the push. If the key holds a value of another type the command returns an error. +Use `JSON.OBJLEN` to get the number of fields in the objects a path selects. -Pushing to the head and popping from the tail with [`RPOP`](/docs/redis/commands/list/rpop) gives a first-in, first-out queue, while popping from the head with [`LPOP`](/docs/redis/commands/list/lpop) gives a stack. Combine with [`LTRIM`](/docs/redis/commands/list/ltrim) to keep a capped list of recent items. +Without a path the root value is used. The reply holds one count per match, with null for matches that are not objects. It counts only the object's own fields, not the fields of nested objects. ## Syntax ```redis -LPUSH [ ...] +JSON.OBJLEN [path] ``` ## Arguments | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | -| `` | Yes | No | Redis key targeted by the command. | -| `` | Yes | Yes | Element to push. | +| `key` | Yes | No | JSON document key. | +| `path` | No | No | Path selecting objects; defaults to the root. | + +## Important points + +* Raw TCP examples pass JSON values as valid JSON text. Typed Upstash SDK helpers serialize native objects and values for you. +* Paths beginning with `$` use JSONPath and can match multiple values, so many JSON commands return an array of per-match results. ## Response @@ -40644,8 +40515,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Integer | -| RESP3 | Integer | +| RESP2 | Integer, array of integer lengths or null values, or Null bulk string or null array | +| RESP3 | Integer, array of integer lengths or null values, or Null | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -40660,7 +40531,7 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -LPUSH my-key element +JSON.OBJLEN profile $ ``` @@ -40672,10 +40543,7 @@ import { Redis } from "@upstash/redis"; const redis = Redis.fromEnv(); -const length1 = await redis.lpush("key", "a", "b", "c"); -console.log(length1); // 3 -const length2 = await redis.lpush("key", "d"); -console.log(length2); // 4 +const lengths = await redis.json.objlen("key", "$.path"); ``` @@ -40686,7 +40554,7 @@ console.log(length2); // 4 from upstash_redis import Redis redis = Redis.from_env() -result = redis.lpush("my-key", "element") +result = redis.json().objlen("profile", "$") print(result) ``` @@ -40698,7 +40566,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.lpush("my-key", "element"); +const result = await redis.call("JSON.OBJLEN", "profile", "$"); console.log(result); ``` @@ -40712,7 +40580,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.lPush("my-key", "element"); +const result = await client.json.objLen("profile", { path: "$" }); console.log(result); ``` @@ -40725,7 +40593,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.lpush("my-key", "element") +result = client.json().objlen("profile", "$") print(result) ``` @@ -40750,7 +40618,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.LPush(context.Background(), "my-key", "element").Result() + result, err := client.JSONObjLen(context.Background(), "profile", "$").Result() if err != nil { panic(err) } @@ -40765,10 +40633,10 @@ func main() { ```java import java.net.URI; -import redis.clients.jedis.Jedis; +import redis.clients.jedis.JedisPooled; -try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.lpush("my-key", "element"); +try (JedisPooled jedis = new JedisPooled(new URI(System.getenv("REDIS_URL")))) { + Object result = jedis.jsonObjLen("profile", new redis.clients.jedis.json.Path("$")); System.out.println(result); } ``` @@ -40778,14 +40646,14 @@ try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { ```rust -use redis::TypedCommands; +use redis::JsonCommands; fn main() -> redis::RedisResult<()> { let url = std::env::var("REDIS_URL").expect("REDIS_URL is not set"); let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let result = connection.lpush("my-key", "element")?; + let result: redis::Value = connection.json_obj_len("profile", "$")?; println!("{result:?}"); Ok(()) } @@ -40795,25 +40663,32 @@ fn main() -> redis::RedisResult<()> { -# LPUSHX -Source: https://upstash.com/docs/redis/commands/list/lpushx +# JSON.RESP +Source: https://upstash.com/docs/redis/commands/json/json-resp -Use `LPUSHX` to add elements to the head of a list only when the list already exists. +Use `JSON.RESP` to get a JSON value in RESP form instead of as JSON text. -Nothing happens and the reply is `0` when the key does not exist, and no key is created. This is the difference from [`LPUSH`](/docs/redis/commands/list/lpush), and it is what you want when a list should only be fed while a consumer is holding it open, so that stale producers do not resurrect a queue that was already drained and deleted. +The document is translated structurally: an object becomes an array whose first element is `{` followed by alternating field names and values, an array becomes an array whose first element is `[` followed by its elements, and scalars become the corresponding RESP types. Nested values are translated the same way, recursively. + +This lets a client walk the structure using the protocol types it already decodes, without running a JSON parser on the reply. For ordinary use, [`JSON.GET`](/docs/redis/commands/json/json-get) is the more convenient command. ## Syntax ```redis -LPUSHX [ ...] +JSON.RESP [path] ``` ## Arguments | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | -| `` | Yes | No | Redis key targeted by the command. | -| `` | Yes | Yes | Element to push. | +| `key` | Yes | No | JSON document key. | +| `path` | No | No | Path to convert; defaults to the root. | + +## Important points + +* Raw TCP examples pass JSON values as valid JSON text. Typed Upstash SDK helpers serialize native objects and values for you. +* Paths beginning with `$` use JSONPath and can match multiple values, so many JSON commands return an array of per-match results. ## Response @@ -40821,8 +40696,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Integer | -| RESP3 | Integer | +| RESP2 | Nested array, Integer, Bulk string, Simple string (`true` or `false` for JSON booleans), or Null bulk string or null array | +| RESP3 | Nested array, Integer, Bulk string, Simple string (`true` or `false` for JSON booleans), or Null | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -40837,7 +40712,7 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -LPUSHX my-key element +JSON.RESP profile $ ``` @@ -40848,10 +40723,8 @@ LPUSHX my-key element import { Redis } from "@upstash/redis"; const redis = Redis.fromEnv(); - -await redis.lpush("key", "a", "b", "c"); -const length = await redis.lpushx("key", "d"); -console.log(length); // 4 +const result = await redis.json.resp("profile", "$"); +console.log(result); ``` @@ -40862,7 +40735,7 @@ console.log(length); // 4 from upstash_redis import Redis redis = Redis.from_env() -result = redis.lpushx("my-key", "element") +result = redis.json().resp("profile", "$") print(result) ``` @@ -40874,7 +40747,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.lpushx("my-key", "element"); +const result = await redis.call("JSON.RESP", "profile", "$"); console.log(result); ``` @@ -40888,7 +40761,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.lPushX("my-key", "element"); +const result = await client.sendCommand(["JSON.RESP", "profile", "$"]); console.log(result); ``` @@ -40901,7 +40774,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.lpushx("my-key", "element") +result = client.json().resp("profile", "$") print(result) ``` @@ -40926,7 +40799,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.LPushX(context.Background(), "my-key", "element").Result() + result, err := client.Do(context.Background(), "JSON.RESP", "profile", "$").Result() if err != nil { panic(err) } @@ -40940,11 +40813,13 @@ func main() { ```java import java.net.URI; - +import java.nio.charset.StandardCharsets; import redis.clients.jedis.Jedis; +import redis.clients.jedis.commands.ProtocolCommand; +ProtocolCommand command = () -> "JSON.RESP".getBytes(StandardCharsets.UTF_8); try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.lpushx("my-key", "element"); + Object result = jedis.sendCommand(command, "profile", "$"); System.out.println(result); } ``` @@ -40954,14 +40829,15 @@ try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { ```rust -use redis::TypedCommands; - fn main() -> redis::RedisResult<()> { let url = std::env::var("REDIS_URL").expect("REDIS_URL is not set"); let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let result = connection.lpush_exists("my-key", "element")?; + let mut command = redis::cmd("JSON.RESP"); + command.arg("profile"); + command.arg("$"); + let result: redis::Value = command.query(&mut connection)?; println!("{result:?}"); Ok(()) } @@ -40971,28 +40847,34 @@ fn main() -> redis::RedisResult<()> { -# LRANGE -Source: https://upstash.com/docs/redis/commands/list/lrange +# JSON.SET +Source: https://upstash.com/docs/redis/commands/json/json-set -Use `LRANGE` to read a range of elements from a list. +Use `JSON.SET` to set a JSON value at a path inside a document, creating the key when it does not exist. -Both `` and `` are zero-based, inclusive, and may be negative to count from the tail, so `LRANGE key 0 -1` returns the whole list and `LRANGE key 0 9` returns the first ten elements. Out-of-range indexes are clamped instead of producing an error, and a range that selects nothing, or a missing key, returns an empty list. +The value is JSON text; the Upstash SDK helpers serialize native objects for you. With the root path (`$`) the whole document is replaced, which is also how a new document is created. For a nested path the parent must already exist: the command adds one missing child to an existing object or appends to an existing array, but it does not create intermediate levels along the way. -The command copies the requested range into the reply, so reading a large list in one call is expensive: page through it with successive ranges when the list is big. +`NX` writes only when the path does not exist yet and `XX` only when it does, which makes conditional updates atomic. When the path is a JSONPath that matches several places, every match is updated in the same call, so a single command can update all elements of an array. ## Syntax ```redis -LRANGE +JSON.SET [NX | XX] ``` ## Arguments | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | -| `` | Yes | No | Redis key targeted by the command. | -| `` | Yes | No | Start index; negative values count from the end. | -| `` | Yes | No | Stop index, inclusive; negative values count from the end. | +| `key` | Yes | No | JSON document key. | +| `path` | Yes | No | Path to create or replace. | +| `value` | Yes | No | Valid JSON value. | +| `NX \| XX` | No | No | Write only when the path is absent (`NX`) or present (`XX`). | + +## Important points + +* Raw TCP examples pass JSON values as valid JSON text. Typed Upstash SDK helpers serialize native objects and values for you. +* Paths beginning with `$` use JSONPath and can match multiple values, so many JSON commands return an array of per-match results. ## Response @@ -41000,8 +40882,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Array of bulk-string values | -| RESP3 | Array of bulk-string values | +| RESP2 | Simple string `OK`, or Null bulk string or null array when `NX` or `XX` prevented the write | +| RESP3 | Simple string `OK`, or Null when `NX` or `XX` prevented the write | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -41016,7 +40898,7 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -LRANGE my-key 0 0 +JSON.SET profile $ '{"name":"Ada"}' ``` @@ -41028,9 +40910,7 @@ import { Redis } from "@upstash/redis"; const redis = Redis.fromEnv(); -await redis.lpush("key", "a", "b", "c"); -const elements = await redis.lrange("key", 1, 2); -console.log(elements) // ["b", "c"] +redis.json.set(key, "$.path", value); ``` @@ -41041,7 +40921,7 @@ console.log(elements) // ["b", "c"] from upstash_redis import Redis redis = Redis.from_env() -result = redis.lrange("my-key", 0, 0) +result = redis.json().set("profile", "$", {"name": "Ada"}) print(result) ``` @@ -41053,7 +40933,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.lrange("my-key", "0", "0"); +const result = await redis.call("JSON.SET", "profile", "$", "{\"name\":\"Ada\"}"); console.log(result); ``` @@ -41067,7 +40947,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.lRange("my-key", 0, 0); +const result = await client.json.set("profile", "$", { name: "Ada" }); console.log(result); ``` @@ -41080,7 +40960,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.lrange("my-key", 0, 0) +result = client.json().set("profile", "$", {"name": "Ada"}) print(result) ``` @@ -41105,7 +40985,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.LRange(context.Background(), "my-key", 0, 0).Result() + result, err := client.JSONSet(context.Background(), "profile", "$", map[string]interface{}{"name": "Ada"}).Result() if err != nil { panic(err) } @@ -41120,10 +41000,10 @@ func main() { ```java import java.net.URI; -import redis.clients.jedis.Jedis; +import redis.clients.jedis.JedisPooled; -try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.lrange("my-key", 0, 0); +try (JedisPooled jedis = new JedisPooled(new URI(System.getenv("REDIS_URL")))) { + Object result = jedis.jsonSet("profile", new redis.clients.jedis.json.Path("$"), java.util.Map.of("name", "Ada")); System.out.println(result); } ``` @@ -41133,14 +41013,14 @@ try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { ```rust -use redis::TypedCommands; +use redis::JsonCommands; fn main() -> redis::RedisResult<()> { let url = std::env::var("REDIS_URL").expect("REDIS_URL is not set"); let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let result = connection.lrange("my-key", 0, 0)?; + let result: redis::Value = connection.json_set("profile", "$", &serde_json::json!({"name": "Ada"}))?; println!("{result:?}"); Ok(()) } @@ -41150,28 +41030,31 @@ fn main() -> redis::RedisResult<()> { -# LREM -Source: https://upstash.com/docs/redis/commands/list/lrem - -Use `LREM` to remove elements equal to a given value from a list. +# JSON.STRAPPEND +Source: https://upstash.com/docs/redis/commands/json/json-strappend -The count decides how many occurrences are removed and in which direction: a positive count removes that many starting from the head, a negative count removes that many starting from the tail, and `0` removes every occurrence. The reply is the number of elements actually removed, and the key is deleted when the list becomes empty. +Use `JSON.STRAPPEND` to append text to the string values a path selects. -Removing by value means scanning the list, so on long lists prefer a set or a sorted set when you frequently need to delete arbitrary items. +The value is JSON text, so the appended string must be quoted, as in `'"suffix"'`. The reply is the new length of each string the path matched, with an error for matches that are not strings. Appending in place avoids reading and rewriting the whole document just to extend one field. ## Syntax ```redis -LREM +JSON.STRAPPEND ``` ## Arguments | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | -| `` | Yes | No | Redis key targeted by the command. | -| `` | Yes | No | How many matches to remove: positive scans head to tail, negative tail to head, `0` removes every match. | -| `` | Yes | No | Element value to remove. | +| `key` | Yes | No | JSON document key. | +| `path` | Yes | No | Path selecting strings. | +| `value` | Yes | No | JSON-encoded string to append. | + +## Important points + +* Raw TCP examples pass JSON values as valid JSON text. Typed Upstash SDK helpers serialize native objects and values for you. +* Paths beginning with `$` use JSONPath and can match multiple values, so many JSON commands return an array of per-match results. ## Response @@ -41179,8 +41062,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Integer | -| RESP3 | Integer | +| RESP2 | Integer, array of integer lengths or null values, or Null bulk string or null array | +| RESP3 | Integer, array of integer lengths or null values, or Null | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -41195,7 +41078,7 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -LREM my-key 1 element +JSON.STRAPPEND profile $.name '" Lovelace"' ``` @@ -41207,9 +41090,7 @@ import { Redis } from "@upstash/redis"; const redis = Redis.fromEnv(); -await redis.lpush("key", "a", "a", "b", "b", "c"); -const removed = await redis.lrem("key", 4, "b"); -console.log(removed) // 2 +await redis.json.strappend("key", "$.path.to.str", "abc"); ``` @@ -41220,7 +41101,7 @@ console.log(removed) // 2 from upstash_redis import Redis redis = Redis.from_env() -result = redis.lrem("my-key", 1, "element") +result = redis.json().strappend("profile", "$.name", " Lovelace") print(result) ``` @@ -41232,7 +41113,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.lrem("my-key", "1", "element"); +const result = await redis.call("JSON.STRAPPEND", "profile", "$.name", "\" Lovelace\""); console.log(result); ``` @@ -41246,7 +41127,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.lRem("my-key", 1, "element"); +const result = await client.json.strAppend("profile", " Lovelace", { path: "$.name" }); console.log(result); ``` @@ -41259,7 +41140,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.lrem("my-key", 1, "element") +result = client.json().strappend("profile", " Lovelace", "$.name") print(result) ``` @@ -41284,7 +41165,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.LRem(context.Background(), "my-key", 1, "element").Result() + result, err := client.JSONStrAppend(context.Background(), "profile", "$.name", `" Lovelace"`).Result() if err != nil { panic(err) } @@ -41299,10 +41180,10 @@ func main() { ```java import java.net.URI; -import redis.clients.jedis.Jedis; +import redis.clients.jedis.JedisPooled; -try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.lrem("my-key", 1, "element"); +try (JedisPooled jedis = new JedisPooled(new URI(System.getenv("REDIS_URL")))) { + Object result = jedis.jsonStrAppend("profile", new redis.clients.jedis.json.Path("$.name"), " Lovelace"); System.out.println(result); } ``` @@ -41312,14 +41193,14 @@ try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { ```rust -use redis::TypedCommands; +use redis::JsonCommands; fn main() -> redis::RedisResult<()> { let url = std::env::var("REDIS_URL").expect("REDIS_URL is not set"); let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let result = connection.lrem("my-key", 1, "element")?; + let result: redis::Value = connection.json_str_append("profile", "$.name", " Lovelace")?; println!("{result:?}"); Ok(()) } @@ -41329,26 +41210,30 @@ fn main() -> redis::RedisResult<()> { -# LSET -Source: https://upstash.com/docs/redis/commands/list/lset +# JSON.STRLEN +Source: https://upstash.com/docs/redis/commands/json/json-strlen -Use `LSET` to overwrite the element at a given position in a list. +Use `JSON.STRLEN` to get the length of the string values a path selects. -Indexes are zero-based from the head and negative indexes count from the tail. The command returns an error when the key does not exist or the index is out of range, since it never grows the list; use [`LPUSH`](/docs/redis/commands/list/lpush) or [`RPUSH`](/docs/redis/commands/list/rpush) to add elements. Locate the position first with [`LPOS`](/docs/redis/commands/list/lpos) when you know the value but not the index. +Without a path the root value is used. The reply holds one length per match, with null for matches that are not strings, so it is also a quick way to check that a field is a string. The value itself is not transferred, which makes it cheap even for long strings. ## Syntax ```redis -LSET +JSON.STRLEN [path] ``` ## Arguments | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | -| `` | Yes | No | Redis key targeted by the command. | -| `` | Yes | No | Zero-based index; negative values count from the end. | -| `` | Yes | No | Element to store at the index. | +| `key` | Yes | No | JSON document key. | +| `path` | No | No | Path selecting strings; defaults to the root. | + +## Important points + +* Raw TCP examples pass JSON values as valid JSON text. Typed Upstash SDK helpers serialize native objects and values for you. +* Paths beginning with `$` use JSONPath and can match multiple values, so many JSON commands return an array of per-match results. ## Response @@ -41356,8 +41241,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Simple string `OK` | -| RESP3 | Simple string `OK` | +| RESP2 | Integer, array of integer lengths or null values, or Null bulk string or null array | +| RESP3 | Integer, array of integer lengths or null values, or Null | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -41372,7 +41257,7 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -LSET my-key 0 element +JSON.STRLEN profile $.name ``` @@ -41384,10 +41269,7 @@ import { Redis } from "@upstash/redis"; const redis = Redis.fromEnv(); -await redis.lpush("key", "a", "b", "c"); -await redis.lset("key", 1, "d"); - -// list is now ["a", "d", "c"] +await redis.json.strlen("key", "$.path.to.str", "a"); ``` @@ -41398,7 +41280,7 @@ await redis.lset("key", 1, "d"); from upstash_redis import Redis redis = Redis.from_env() -result = redis.lset("my-key", 0, "element") +result = redis.json().strlen("profile", "$.name") print(result) ``` @@ -41410,7 +41292,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.lset("my-key", "0", "element"); +const result = await redis.call("JSON.STRLEN", "profile", "$.name"); console.log(result); ``` @@ -41424,7 +41306,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.lSet("my-key", 0, "element"); +const result = await client.json.strLen("profile", { path: "$.name" }); console.log(result); ``` @@ -41437,7 +41319,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.lset("my-key", 0, "element") +result = client.json().strlen("profile", "$.name") print(result) ``` @@ -41462,7 +41344,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.LSet(context.Background(), "my-key", 0, "element").Result() + result, err := client.JSONStrLen(context.Background(), "profile", "$.name").Result() if err != nil { panic(err) } @@ -41477,10 +41359,10 @@ func main() { ```java import java.net.URI; -import redis.clients.jedis.Jedis; +import redis.clients.jedis.JedisPooled; -try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.lset("my-key", 0, "element"); +try (JedisPooled jedis = new JedisPooled(new URI(System.getenv("REDIS_URL")))) { + Object result = jedis.jsonStrLen("profile", new redis.clients.jedis.json.Path("$.name")); System.out.println(result); } ``` @@ -41490,14 +41372,14 @@ try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { ```rust -use redis::TypedCommands; +use redis::JsonCommands; fn main() -> redis::RedisResult<()> { let url = std::env::var("REDIS_URL").expect("REDIS_URL is not set"); let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let result = connection.lset("my-key", 0, "element")?; + let result: redis::Value = connection.json_str_len("profile", "$.name")?; println!("{result:?}"); Ok(()) } @@ -41507,28 +41389,30 @@ fn main() -> redis::RedisResult<()> { -# LTRIM -Source: https://upstash.com/docs/redis/commands/list/ltrim - -Use `LTRIM` to keep only a range of elements in a list and delete everything outside it. +# JSON.TOGGLE +Source: https://upstash.com/docs/redis/commands/json/json-toggle -Both ends are inclusive, zero-based, and may be negative to count from the tail. If `` is greater than `` or lies past the end of the list, every element is removed and the key is deleted. The reply is always `OK`. +Use `JSON.TOGGLE` to flip the boolean values a path selects, turning `true` into `false` and back. -The classic use is a capped list: [`LPUSH`](/docs/redis/commands/list/lpush) a new item and then `LTRIM key 0 99` to keep the hundred most recent ones, which bounds memory without any separate cleanup job. +The reply is the new value of each match, and a match that is not a boolean returns an error. Because the read and the write are one atomic step, this is the safe way to flip a flag inside a document, where reading it and writing the opposite value back would race with other clients. ## Syntax ```redis -LTRIM +JSON.TOGGLE ``` ## Arguments | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | -| `` | Yes | No | Redis key targeted by the command. | -| `` | Yes | No | Start index; negative values count from the end. | -| `` | Yes | No | Stop index, inclusive; negative values count from the end. | +| `key` | Yes | No | JSON document key. | +| `path` | Yes | No | Path selecting Boolean values. | + +## Important points + +* Raw TCP examples pass JSON values as valid JSON text. Typed Upstash SDK helpers serialize native objects and values for you. +* Paths beginning with `$` use JSONPath and can match multiple values, so many JSON commands return an array of per-match results. ## Response @@ -41536,8 +41420,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Simple string `OK` | -| RESP3 | Simple string `OK` | +| RESP2 | Array of integer Boolean values or null values, one per matched path | +| RESP3 | Array of integer Boolean values or null values, one per matched path | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -41552,7 +41436,7 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -LTRIM my-key 0 0 +JSON.TOGGLE profile $.active ``` @@ -41564,9 +41448,7 @@ import { Redis } from "@upstash/redis"; const redis = Redis.fromEnv(); -await redis.lpush("key", "a", "b", "c", "d"); -await redis.ltrim("key", 1, 2); -// the list is now ["b", "c"] +const bool = await redis.json.toggle("key", "$.path.to.bool"); ``` @@ -41577,7 +41459,7 @@ await redis.ltrim("key", 1, 2); from upstash_redis import Redis redis = Redis.from_env() -result = redis.ltrim("my-key", 0, 0) +result = redis.json().toggle("profile", "$.active") print(result) ``` @@ -41589,7 +41471,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.ltrim("my-key", "0", "0"); +const result = await redis.call("JSON.TOGGLE", "profile", "$.active"); console.log(result); ``` @@ -41603,7 +41485,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.lTrim("my-key", 0, 0); +const result = await client.json.toggle("profile", "$.active"); console.log(result); ``` @@ -41616,7 +41498,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.ltrim("my-key", 0, 0) +result = client.json().toggle("profile", "$.active") print(result) ``` @@ -41641,7 +41523,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.LTrim(context.Background(), "my-key", 0, 0).Result() + result, err := client.JSONToggle(context.Background(), "profile", "$.active").Result() if err != nil { panic(err) } @@ -41656,10 +41538,10 @@ func main() { ```java import java.net.URI; -import redis.clients.jedis.Jedis; +import redis.clients.jedis.JedisPooled; -try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.ltrim("my-key", 0, 0); +try (JedisPooled jedis = new JedisPooled(new URI(System.getenv("REDIS_URL")))) { + Object result = jedis.jsonToggle("profile", new redis.clients.jedis.json.Path("$.active")); System.out.println(result); } ``` @@ -41669,14 +41551,14 @@ try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { ```rust -use redis::TypedCommands; +use redis::JsonCommands; fn main() -> redis::RedisResult<()> { let url = std::env::var("REDIS_URL").expect("REDIS_URL is not set"); let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let result = connection.ltrim("my-key", 0, 0)?; + let result: redis::Value = connection.json_toggle("profile", "$.active")?; println!("{result:?}"); Ok(()) } @@ -41686,55 +41568,30 @@ fn main() -> redis::RedisResult<()> { -# List commands -Source: https://upstash.com/docs/redis/commands/list/overview - - -Blocking list move -Blocking pop from multiple lists -Blocking left pop -Blocking right pop -Blocking pop and push -Get element by index -Insert before or after pivot -Get list length -Move element between lists -Pop from the first non-empty list -Pop from list head -Find the position of an element -Push to list head -Push to head if list exists -Get range of elements -Remove elements by value -Set element at index -Trim list to range -Pop from list tail -Pop from tail and push to head -Push to list tail -Push to tail if list exists - - -# RPOP -Source: https://upstash.com/docs/redis/commands/list/rpop - -Use `RPOP` to remove and return elements from the tail of a list. +# JSON.TYPE +Source: https://upstash.com/docs/redis/commands/json/json-type -Without a count a single element is returned, or null when the key does not exist. With a count, up to that many elements are removed and returned in the order they were popped, and the key is deleted once the last element is gone. +Use `JSON.TYPE` to find out the JSON type of the values a path selects. -Combined with [`LPUSH`](/docs/redis/commands/list/lpush) this gives a first-in, first-out queue, since producers add at the head and consumers take from the tail. Use [`BRPOP`](/docs/redis/commands/list/brpop) when a consumer should wait for work instead of polling. +The reply names one type per match, one of `object`, `array`, `string`, `integer`, `number`, `boolean`, or `null`, and is empty when the path matches nothing. Whole numbers report as `integer` and fractional ones as `number`. It is the way to inspect documents whose shape you do not control before applying type-specific commands, which would otherwise fail. ## Syntax ```redis -RPOP [] +JSON.TYPE [path] ``` ## Arguments | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | -| `` | Yes | No | Redis key targeted by the command. | -| `` | No | No | Number of elements to pop. | +| `key` | Yes | No | JSON document key. | +| `path` | No | No | Path to inspect; defaults to the root. | + +## Important points + +* Raw TCP examples pass JSON values as valid JSON text. Typed Upstash SDK helpers serialize native objects and values for you. +* Paths beginning with `$` use JSONPath and can match multiple values, so many JSON commands return an array of per-match results. ## Response @@ -41742,8 +41599,10 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Null bulk string or null array, Bulk string, or array of bulk-string values | -| RESP3 | Null, Bulk string, or array of bulk-string values | +| RESP2 | Bulk string, array of bulk-string type names, or Null bulk string or null array | +| RESP3 | One-element array wrapping the bulk string, the array of bulk-string type names, or Null | + +RESP3 adds one array level around the whole reply, matching upstream Redis. Where `JSON.TYPE doc $.a` replies `["string"]` under RESP2, it replies `[["string"]]` under RESP3, and the no-path form replies `"object"` under RESP2 and `["object"]` under RESP3. Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -41758,7 +41617,7 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -RPOP my-key +JSON.TYPE profile $.name ``` @@ -41770,9 +41629,7 @@ import { Redis } from "@upstash/redis"; const redis = Redis.fromEnv(); -await redis.rpush("key", "a", "b", "c"); -const element = await redis.rpop("key"); -console.log(element); // "c" +const myType = await redis.json.type("key", "$.path.to.value"); ``` @@ -41783,7 +41640,7 @@ console.log(element); // "c" from upstash_redis import Redis redis = Redis.from_env() -result = redis.rpop("my-key") +result = redis.json().type("profile", "$.name") print(result) ``` @@ -41795,7 +41652,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.rpop("my-key"); +const result = await redis.call("JSON.TYPE", "profile", "$.name"); console.log(result); ``` @@ -41809,7 +41666,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.rPop("my-key"); +const result = await client.json.type("profile", { path: "$.name" }); console.log(result); ``` @@ -41822,7 +41679,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.rpop("my-key") +result = client.json().type("profile", "$.name") print(result) ``` @@ -41847,7 +41704,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.RPop(context.Background(), "my-key").Result() + result, err := client.JSONType(context.Background(), "profile", "$.name").Result() if err != nil { panic(err) } @@ -41862,10 +41719,10 @@ func main() { ```java import java.net.URI; -import redis.clients.jedis.Jedis; +import redis.clients.jedis.JedisPooled; -try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.rpop("my-key"); +try (JedisPooled jedis = new JedisPooled(new URI(System.getenv("REDIS_URL")))) { + Object result = jedis.jsonType("profile", new redis.clients.jedis.json.Path("$.name")); System.out.println(result); } ``` @@ -41875,14 +41732,14 @@ try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { ```rust -use redis::TypedCommands; +use redis::JsonCommands; fn main() -> redis::RedisResult<()> { let url = std::env::var("REDIS_URL").expect("REDIS_URL is not set"); let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let result: Option = connection.rpop("my-key", None)?; + let result: redis::Value = connection.json_type("profile", "$.name")?; println!("{result:?}"); Ok(()) } @@ -41892,23 +41749,51 @@ fn main() -> redis::RedisResult<()> { -# RPOPLPUSH -Source: https://upstash.com/docs/redis/commands/list/rpoplpush +# JSON commands +Source: https://upstash.com/docs/redis/commands/json/overview - - Prefer [`LMOVE`](/docs/redis/commands/list/lmove) with `RIGHT` and `LEFT` in new code: `LMOVE RIGHT LEFT`. - +To query inside JSON values (full-text, fuzzy, phrase, regex), see [Upstash Redis Search](/docs/redis/search/introduction). -Use `RPOPLPUSH` to atomically move an element from the tail of one list to the head of another, returning the element. + +Append values to JSON array +Find index of value in array +Insert values into JSON array +Get JSON array length +Pop value from JSON array +Trim JSON array to range +Clear JSON values +Delete JSON values +Inspect JSON memory usage +Delete JSON values (alias of JSON.DEL) +Get JSON values +Merge JSON values +Get values from multiple keys +Set values in multiple keys +Increment JSON number +Multiply JSON number +Get JSON object keys +Get JSON object size +Get JSON in RESP format +Set JSON value +Append to JSON string +Get JSON string length +Toggle JSON boolean +Get JSON value type + -If the source is empty nothing happens and the reply is null. Source and destination may be the same key, which rotates the list: the tail element becomes the new head, so repeated calls cycle through a list of items forever, a handy pattern for round-robin scheduling. +# BLMOVE +Source: https://upstash.com/docs/redis/commands/list/blmove -[`LMOVE`](/docs/redis/commands/list/lmove) does the same thing but lets you choose both ends, and [`BRPOPLPUSH`](/docs/redis/commands/list/brpoplpush) is the blocking form. +Use `BLMOVE` to move an element from one list to another, blocking until the source has an element or the timeout expires. + +It is the blocking form of [`LMOVE`](/docs/redis/commands/list/lmove): when the source list is not empty it behaves identically and returns immediately, and when it is empty the connection waits instead of returning null. The timeout is given in seconds, may be fractional, and `0` waits indefinitely. If several clients are waiting on the same key, the one that has been waiting longest is served first. + +Because the element is never outside a list, this is the standard way to build a reliable queue: a worker blocks until work appears, atomically moves it to a processing list, and deletes it from there when done, so an interrupted job can be recovered instead of lost. ## Syntax ```redis -RPOPLPUSH +BLMOVE (LEFT | RIGHT) (LEFT | RIGHT) ``` ## Arguments @@ -41917,6 +41802,13 @@ RPOPLPUSH | --- | --- | --- | --- | | `` | Yes | No | Redis key used as source. | | `` | Yes | No | Redis key used as destination. | +| `(LEFT \| RIGHT)` | Yes | No | Which end of the source list the element is taken from: `LEFT` (head) or `RIGHT` (tail). | +| `(LEFT \| RIGHT)` | Yes | No | Which end of the destination list the element is pushed onto: `LEFT` (head) or `RIGHT` (tail). | +| `` | Yes | No | Seconds to block; `0` blocks indefinitely. | + +## Important points + +* A blocking form holds the request until data arrives or its timeout expires. Set the client/network timeout longer than the command timeout. ## Response @@ -41940,7 +41832,7 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -RPOPLPUSH source-key destination-key +BLMOVE source-key destination-key LEFT LEFT 1.5 ``` @@ -41955,13 +41847,9 @@ RPOPLPUSH source-key destination-key -```python -from upstash_redis import Redis - -redis = Redis.from_env() -result = redis.rpoplpush("source-key", "destination-key") -print(result) -``` + + This command is not supported yet in `upstash_redis`. + @@ -41971,7 +41859,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.rpoplpush("source-key", "destination-key"); +const result = await redis.blmove("source-key", "destination-key", "LEFT", "LEFT", "1.5"); console.log(result); ``` @@ -41985,7 +41873,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.rPopLPush("source-key", "destination-key"); +const result = await client.blMove("source-key", "destination-key", "LEFT", "LEFT", 1.5); console.log(result); ``` @@ -41998,7 +41886,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.rpoplpush("source-key", "destination-key") +result = client.blmove("source-key", "destination-key", 1.5, "LEFT", "LEFT") print(result) ``` @@ -42013,6 +41901,7 @@ import ( "context" "fmt" "os" + "time" "github.com/redis/go-redis/v9" ) @@ -42023,7 +41912,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.RPopLPush(context.Background(), "source-key", "destination-key").Result() + result, err := client.BLMove(context.Background(), "source-key", "destination-key", "LEFT", "LEFT", 1500*time.Millisecond).Result() if err != nil { panic(err) } @@ -42041,7 +41930,7 @@ import java.net.URI; import redis.clients.jedis.Jedis; try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.rpoplpush("source-key", "destination-key"); + Object result = jedis.blmove("source-key", "destination-key", redis.clients.jedis.args.ListDirection.LEFT, redis.clients.jedis.args.ListDirection.LEFT, 1.5); System.out.println(result); } ``` @@ -42051,14 +41940,20 @@ try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { ```rust -use redis::TypedCommands; +use redis::{Direction, TypedCommands}; fn main() -> redis::RedisResult<()> { let url = std::env::var("REDIS_URL").expect("REDIS_URL is not set"); let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let result = connection.rpoplpush("source-key", "destination-key")?; + let result = connection.blmove( + "source-key", + "destination-key", + Direction::Left, + Direction::Left, + 1.5, + )?; println!("{result:?}"); Ok(()) } @@ -42068,27 +41963,34 @@ fn main() -> redis::RedisResult<()> { -# RPUSH -Source: https://upstash.com/docs/redis/commands/list/rpush +# BLMPOP +Source: https://upstash.com/docs/redis/commands/list/blmpop -Use `RPUSH` to add one or more elements to the tail of a list, creating the list when the key does not exist. +Use `BLMPOP` to pop elements from the first non-empty list among several, blocking until one has elements or the timeout expires. -Elements are appended in the order given, so `RPUSH key a b c` leaves the list as `a`, `b`, `c`. The reply is the length of the list after the push, and a key holding another type returns an error. +It is the blocking form of [`LMPOP`](/docs/redis/commands/list/lmpop): keys are examined in the order given, so listing a high priority queue first drains it before the others are considered, `LEFT` or `RIGHT` chooses the end, and `COUNT` sets how many elements to take. The reply names the key that was popped from along with the elements. -Appending with `RPUSH` and consuming from the head with [`LPOP`](/docs/redis/commands/list/lpop) is the standard first-in, first-out queue, and it is the form most job queues use because the natural reading order with [`LRANGE`](/docs/redis/commands/list/lrange) then matches the order of insertion. +The timeout is in seconds, may be fractional, and `0` waits indefinitely; when it expires the reply is null. This is the command to reach for when one worker serves several queues of differing priority. ## Syntax ```redis -RPUSH [ ...] +BLMPOP [ ...] (LEFT | RIGHT) [COUNT ] ``` ## Arguments | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | -| `` | Yes | No | Redis key targeted by the command. | -| `` | Yes | Yes | Element to push. | +| `` | Yes | No | Seconds to block; `0` blocks indefinitely. | +| `` | Yes | No | Number of key arguments that follow. | +| `` | Yes | Yes | Redis key targeted by the command. | +| `(LEFT \| RIGHT)` | Yes | No | Which end to pop from: `LEFT` (head) or `RIGHT` (tail). | +| `COUNT ` | No | No | Maximum number of elements to pop. | + +## Important points + +* A blocking form holds the request until data arrives or its timeout expires. Set the client/network timeout longer than the command timeout. ## Response @@ -42096,8 +41998,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Integer | -| RESP3 | Integer | +| RESP2 | Null bulk string or null array, or two-element array: key and array of values | +| RESP3 | Null, or two-element array: key and array of values | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -42112,35 +42014,24 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -RPUSH my-key element +BLMPOP 1.5 1 my-key LEFT ``` -```ts -import { Redis } from "@upstash/redis"; - -const redis = Redis.fromEnv(); - -const length1 = await redis.rpush("key", "a", "b", "c"); -console.log(length1); // 3 -const length2 = await redis.rpush("key", "d"); -console.log(length2); // 4 -``` + + This command is not supported yet in `@upstash/redis`. + -```python -from upstash_redis import Redis - -redis = Redis.from_env() -result = redis.rpush("my-key", "element") -print(result) -``` + + This command is not supported yet in `upstash_redis`. + @@ -42150,7 +42041,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.rpush("my-key", "element"); +const result = await redis.blmpop("1.5", "1", "my-key", "LEFT"); console.log(result); ``` @@ -42164,7 +42055,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.rPush("my-key", "element"); +const result = await client.blmPop(1.5, "my-key", "LEFT"); console.log(result); ``` @@ -42177,7 +42068,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.rpush("my-key", "element") +result = client.blmpop(1.5, 1, "my-key", direction="LEFT") print(result) ``` @@ -42192,6 +42083,7 @@ import ( "context" "fmt" "os" + "time" "github.com/redis/go-redis/v9" ) @@ -42202,7 +42094,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.RPush(context.Background(), "my-key", "element").Result() + _, result, err := client.BLMPop(context.Background(), 1500*time.Millisecond, "LEFT", 0, "my-key").Result() if err != nil { panic(err) } @@ -42220,7 +42112,7 @@ import java.net.URI; import redis.clients.jedis.Jedis; try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.rpush("my-key", "element"); + Object result = jedis.blmpop(1.5, redis.clients.jedis.args.ListDirection.LEFT, "my-key"); System.out.println(result); } ``` @@ -42230,14 +42122,14 @@ try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { ```rust -use redis::TypedCommands; +use redis::{Direction, TypedCommands}; fn main() -> redis::RedisResult<()> { let url = std::env::var("REDIS_URL").expect("REDIS_URL is not set"); let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let result = connection.rpush("my-key", "element")?; + let result = connection.blmpop(1.5, 1, "my-key", Direction::Left, 1)?; println!("{result:?}"); Ok(()) } @@ -42247,25 +42139,31 @@ fn main() -> redis::RedisResult<()> { -# RPUSHX -Source: https://upstash.com/docs/redis/commands/list/rpushx +# BLPOP +Source: https://upstash.com/docs/redis/commands/list/blpop -Use `RPUSHX` to add elements to the tail of a list only when the list already exists. +Use `BLPOP` to pop an element from the head of the first non-empty list, blocking until one has an element or the timeout expires. -Nothing happens and the reply is `0` when the key does not exist, and no key is created. Use it when producers should append to a queue only while it is alive, for example when the list is created by a consumer that is currently attached, so that writes to a queue nobody is reading do not silently pile up. +It is the blocking form of [`LPOP`](/docs/redis/commands/list/lpop) and it accepts several keys, which are checked in the order given, so earlier keys act as higher priority queues. The reply names the key the element came from together with the element itself, which matters when you are waiting on more than one queue. + +The timeout is in seconds, may be fractional, and `0` waits indefinitely; when it expires the reply is null. Blocking lets a worker wait for work without polling, which cuts both latency and wasted commands. When several clients are blocked on the same key they are served in the order they started waiting. ## Syntax ```redis -RPUSHX [ ...] +BLPOP [ ...] ``` ## Arguments | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | -| `` | Yes | No | Redis key targeted by the command. | -| `` | Yes | Yes | Element to push. | +| `` | Yes | Yes | Redis key targeted by the command. | +| `` | Yes | No | Seconds to block; `0` blocks indefinitely. | + +## Important points + +* A blocking form holds the request until data arrives or its timeout expires. Set the client/network timeout longer than the command timeout. ## Response @@ -42273,8 +42171,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Integer | -| RESP3 | Integer | +| RESP2 | Null bulk string or null array, or two-element key/value array | +| RESP3 | Null, or two-element key/value array | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -42289,34 +42187,24 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -RPUSHX my-key element +BLPOP my-key 1.5 ``` -```ts -import { Redis } from "@upstash/redis"; - -const redis = Redis.fromEnv(); - -await redis.lpush("key", "a", "b", "c"); -const length = await redis.rpushx("key", "d"); -console.log(length); // 4 -``` + + This command is not supported yet in `@upstash/redis`. + -```python -from upstash_redis import Redis - -redis = Redis.from_env() -result = redis.rpushx("my-key", "element") -print(result) -``` + + This command is not supported yet in `upstash_redis`. + @@ -42326,7 +42214,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.rpushx("my-key", "element"); +const result = await redis.blpop("my-key", "1.5"); console.log(result); ``` @@ -42340,7 +42228,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.rPushX("my-key", "element"); +const result = await client.blPop("my-key", 1.5); console.log(result); ``` @@ -42353,7 +42241,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.rpushx("my-key", "element") +result = client.blpop(["my-key"], timeout=1.5) print(result) ``` @@ -42368,6 +42256,7 @@ import ( "context" "fmt" "os" + "time" "github.com/redis/go-redis/v9" ) @@ -42378,7 +42267,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.RPushX(context.Background(), "my-key", "element").Result() + result, err := client.BLPop(context.Background(), 1500*time.Millisecond, "my-key").Result() if err != nil { panic(err) } @@ -42396,7 +42285,7 @@ import java.net.URI; import redis.clients.jedis.Jedis; try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.rpushx("my-key", "element"); + Object result = jedis.blpop(1.5, "my-key"); System.out.println(result); } ``` @@ -42413,7 +42302,7 @@ fn main() -> redis::RedisResult<()> { let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let result = connection.rpush_exists("my-key", "element")?; + let result = connection.blpop("my-key", 1.5)?; println!("{result:?}"); Ok(()) } @@ -42423,69 +42312,31 @@ fn main() -> redis::RedisResult<()> { -# Redis command reference -Source: https://upstash.com/docs/redis/commands/overview - -Upstash supports Redis commands over both native Redis TCP and HTTPS REST. Choose a category to browse its supported commands. - - -Manipulate individual bits and bit fields -Authenticate and manage client connections -Load, manage, and invoke Redis functions -Manage keys, expiration, and serialization -Store and query geospatial data -Work with field-value collections -Estimate cardinality -Store and manipulate JSON values -Work with ordered collections -Publish messages and manage subscriptions -Load and execute Lua scripts -Create, query, and manage search indexes -Inspect and manage the Redis server -Work with unique unordered members -Work with scored collections -Process append-only logs and consumer groups -Store and manipulate string values -Group operations into atomic transactions - - -# Pub/Sub commands -Source: https://upstash.com/docs/redis/commands/pub-sub/overview - - -Subscribe to pattern channels -Publish message to channel -Inspect pub/sub state -Unsubscribe from patterns -Subscribe to channels -Unsubscribe from channels - - -# PSUBSCRIBE -Source: https://upstash.com/docs/redis/commands/pub-sub/psubscribe +# BRPOP +Source: https://upstash.com/docs/redis/commands/list/brpop -Use `PSUBSCRIBE` to subscribe the current connection to channels by glob-style pattern. +Use `BRPOP` to pop an element from the tail of the first non-empty list, blocking until one has an element or the timeout expires. -A pattern such as `news.*` matches every channel that starts with `news.`, including channels created after the subscription, which is what makes patterns useful for topic hierarchies. `?` matches a single character and `[...]` a character class. +It is the blocking form of [`RPOP`](/docs/redis/commands/list/rpop) and behaves like [`BLPOP`](/docs/redis/commands/list/blpop) in every other respect: several keys are checked in the order given, the reply names the key the element came from, the timeout is in seconds and may be fractional with `0` meaning wait forever, and clients blocked on the same key are served in the order they started waiting. -Pattern subscriptions are tracked separately from the exact-channel subscriptions made with [`SUBSCRIBE`](/docs/redis/commands/pub-sub/subscribe), and a message that matches several of a connection's patterns is delivered once per matching pattern, so overlapping patterns produce duplicates. Cancel a pattern with [`PUNSUBSCRIBE`](/docs/redis/commands/pub-sub/punsubscribe), passing exactly the same pattern string. +Producers pushing with [`LPUSH`](/docs/redis/commands/list/lpush) and consumers waiting with `BRPOP` form the classic first-in, first-out worker queue. ## Syntax ```redis -PSUBSCRIBE [ ...] +BRPOP [ ...] ``` ## Arguments | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | -| `` | Yes | Yes | Glob-style channel pattern. | +| `` | Yes | Yes | Redis key targeted by the command. | +| `` | Yes | No | Seconds to block; `0` blocks indefinitely. | ## Important points -* This is a connection-oriented command and is available over native Redis TCP, not the stateless REST endpoint. -* Subscription commands require a dedicated TCP connection. In RESP3, subscription events use push replies. +* A blocking form holds the request until data arrives or its timeout expires. Set the client/network timeout longer than the command timeout. ## Response @@ -42493,8 +42344,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Three-element subscription-state array per pattern | -| RESP3 | Three-element subscription-state push reply per pattern | +| RESP2 | Null bulk string or null array, or two-element key/value array | +| RESP3 | Null, or two-element key/value array | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -42509,19 +42360,35 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -PSUBSCRIBE events:* +BRPOP my-key 1.5 ``` + + + + This command is not supported yet in `@upstash/redis`. + + + + + + + + This command is not supported yet in `upstash_redis`. + + + + ```ts import Redis from "ioredis"; -const subscriber = new Redis(process.env.REDIS_URL!); -await subscriber.psubscribe("events:*"); -subscriber.on("message", (channel, message) => console.log(channel, message)); +const redis = new Redis(process.env.REDIS_URL!); +const result = await redis.brpop("my-key", "1.5"); +console.log(result); ``` @@ -42531,10 +42398,11 @@ subscriber.on("message", (channel, message) => console.log(channel, message)); ```ts import { createClient } from "redis"; -const subscriber = await createClient({ url: process.env.REDIS_URL }).connect(); -await subscriber.pSubscribe("events:*", (message, channel) => { - console.log(channel, message); -}); +const client = await createClient({ url: process.env.REDIS_URL }) + .on("error", console.error) + .connect(); +const result = await client.brPop("my-key", 1.5); +console.log(result); ``` @@ -42546,10 +42414,8 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -pubsub = client.pubsub() -pubsub.psubscribe("events:*") -for message in pubsub.listen(): - print(message) +result = client.brpop(["my-key"], timeout=1.5) +print(result) ``` @@ -42563,21 +42429,22 @@ import ( "context" "fmt" "os" + "time" "github.com/redis/go-redis/v9" ) func main() { - ctx := context.Background() opts, err := redis.ParseURL(os.Getenv("REDIS_URL")) if err != nil { panic(err) } client := redis.NewClient(opts) - pubsub := client.PSubscribe(ctx, "events:*") - for message := range pubsub.Channel() { - fmt.Println(message.Channel, message.Payload) + result, err := client.BRPop(context.Background(), 1500*time.Millisecond, "my-key").Result() + if err != nil { + panic(err) } + fmt.Println(result) } ``` @@ -42587,16 +42454,12 @@ func main() { ```java import java.net.URI; + import redis.clients.jedis.Jedis; -import redis.clients.jedis.JedisPubSub; try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - jedis.psubscribe(new JedisPubSub() { - @Override - public void onPMessage(String pattern, String channel, String message) { - System.out.println(channel + ": " + message); - } - }, "events:*"); + Object result = jedis.brpop(1.5, "my-key"); + System.out.println(result); } ``` @@ -42605,18 +42468,15 @@ try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { ```rust +use redis::TypedCommands; + fn main() -> redis::RedisResult<()> { let url = std::env::var("REDIS_URL").expect("REDIS_URL is not set"); let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let mut pubsub = connection.as_pubsub(); - pubsub.psubscribe("events:*")?; - loop { - let message = pubsub.get_message()?; - let payload: String = message.get_payload()?; - println!("{payload}"); - } - #[allow(unreachable_code)] + + let result = connection.brpop("my-key", 1.5)?; + println!("{result:?}"); Ok(()) } ``` @@ -42625,27 +42485,36 @@ fn main() -> redis::RedisResult<()> { -# PUBLISH -Source: https://upstash.com/docs/redis/commands/pub-sub/publish +# BRPOPLPUSH +Source: https://upstash.com/docs/redis/commands/list/brpoplpush -Use `PUBLISH` to send a message to a channel. + + Prefer [`BLMOVE`](/docs/redis/commands/list/blmove) with `RIGHT` and `LEFT` in new code: `BLMOVE RIGHT LEFT `. + -The reply is the number of subscribers the message was delivered to, counting both channel and pattern subscribers, so a reply of `0` means nobody was listening. Delivery is fire and forget: messages are not stored and a client that is not connected at that moment never sees them, so use [streams](/docs/redis/commands/streams/overview) when messages must survive a disconnect or be replayed. +Use `BRPOPLPUSH` to pop an element from the tail of one list and push it to the head of another, blocking until the source has an element or the timeout expires. -Publishing works over both the REST API and a TCP connection, while subscribing requires a TCP connection. +It is the blocking form of [`RPOPLPUSH`](/docs/redis/commands/list/rpoplpush). The timeout is in seconds, may be fractional, and `0` waits indefinitely; when it expires the reply is null. Since the element moves atomically into the destination, a worker that crashes after taking an item leaves it visible in the processing list, where it can be recovered. + +[`BLMOVE`](/docs/redis/commands/list/blmove) does the same thing and additionally lets you choose which end of each list to use. ## Syntax ```redis -PUBLISH +BRPOPLPUSH ``` ## Arguments | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | -| `` | Yes | No | Channel name. | -| `` | Yes | No | Message payload. | +| `` | Yes | No | Redis key used as source. | +| `` | Yes | No | Redis key used as destination. | +| `` | Yes | No | Seconds to block; `0` blocks indefinitely. | + +## Important points + +* A blocking form holds the request until data arrives or its timeout expires. Set the client/network timeout longer than the command timeout. ## Response @@ -42653,8 +42522,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Integer | -| RESP3 | Integer | +| RESP2 | Bulk string or Null bulk string or null array | +| RESP3 | Bulk string or Null | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -42669,32 +42538,24 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -PUBLISH events hello +BRPOPLPUSH source-key destination-key 1.5 ``` -```ts -import { Redis } from "@upstash/redis"; - -const redis = Redis.fromEnv(); - -const listeners = await redis.publish("my-channel", "my-message"); -``` + + This command is not supported yet in `@upstash/redis`. + -```python -from upstash_redis import Redis - -redis = Redis.from_env() -result = redis.publish("events", "hello") -print(result) -``` + + This command is not supported yet in `upstash_redis`. + @@ -42704,7 +42565,7 @@ print(result) import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.publish("events", "hello"); +const result = await redis.brpoplpush("source-key", "destination-key", "1.5"); console.log(result); ``` @@ -42718,7 +42579,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.publish("events", "hello"); +const result = await client.brPopLPush("source-key", "destination-key", 1.5); console.log(result); ``` @@ -42731,7 +42592,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.publish("events", "hello") +result = client.brpoplpush("source-key", "destination-key", timeout=1.5) print(result) ``` @@ -42746,6 +42607,7 @@ import ( "context" "fmt" "os" + "time" "github.com/redis/go-redis/v9" ) @@ -42756,7 +42618,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.Publish(context.Background(), "events", "hello").Result() + result, err := client.BRPopLPush(context.Background(), "source-key", "destination-key", 1500*time.Millisecond).Result() if err != nil { panic(err) } @@ -42774,7 +42636,7 @@ import java.net.URI; import redis.clients.jedis.Jedis; try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.publish("events", "hello"); + Object result = jedis.brpoplpush("source-key", "destination-key", 1); System.out.println(result); } ``` @@ -42791,7 +42653,7 @@ fn main() -> redis::RedisResult<()> { let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let result = connection.publish("events", "hello")?; + let result = connection.brpoplpush("source-key", "destination-key", 1.5)?; println!("{result:?}"); Ok(()) } @@ -42801,30 +42663,25 @@ fn main() -> redis::RedisResult<()> { -# PUBSUB -Source: https://upstash.com/docs/redis/commands/pub-sub/pubsub - -Use `PUBSUB` to inspect the state of the pub/sub system without subscribing to anything. +# LINDEX +Source: https://upstash.com/docs/redis/commands/list/lindex -`CHANNELS` lists the channels that currently have at least one subscriber, optionally filtered by a glob-style pattern. `NUMSUB` reports the subscriber count for each channel you name, and `NUMPAT` reports how many distinct patterns are subscribed to across all clients. +Use `LINDEX` to read the element at a given position in a list. -Only exact-channel subscriptions are counted by `CHANNELS` and `NUMSUB`: a client subscribed with [`PSUBSCRIBE`](/docs/redis/commands/pub-sub/psubscribe) will receive matching messages but does not make a channel appear as active. Use these forms for monitoring and debugging, for instance to confirm that a consumer is really attached before publishing. +Indexes are zero-based from the head, and negative indexes count from the tail, so `-1` is the last element. The reply is null when the key does not exist or the index is out of range. Redis walks the list from the nearer end to reach the index, so access is fast near the head and tail and gets more expensive towards the middle of a long list. ## Syntax ```redis -PUBSUB CHANNELS [pattern] -PUBSUB NUMSUB [channel ...] -PUBSUB NUMPAT +LINDEX ``` ## Arguments | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | -| `CHANNELS [pattern]` | One form | No | List active channels, optionally filtered by a glob-style pattern. | -| `NUMSUB [channel ...]` | One form | Yes | Return subscriber counts for the supplied channels. | -| `NUMPAT` | One form | No | Return the number of active pattern subscriptions. | +| `` | Yes | No | Redis key targeted by the command. | +| `` | Yes | No | Zero-based index; negative values count from the end. | ## Response @@ -42832,8 +42689,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Array of channel names, flat channel/count array, or Integer | -| RESP3 | Array of channel names, flat channel/count array, or Integer | +| RESP2 | Null bulk string or null array or Bulk string | +| RESP3 | Null or Bulk string | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -42848,24 +42705,34 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -PUBSUB CHANNELS events:* +LINDEX my-key 0 ``` - - This command is not supported yet in `@upstash/redis`. - +```ts +import { Redis } from "@upstash/redis"; + +const redis = Redis.fromEnv(); + +await redis.rpush("key", "a", "b", "c"); +const element = await redis.lindex("key", 0); +console.log(element); // "a" +``` - - This command is not supported yet in `upstash_redis`. - +```python +from upstash_redis import Redis + +redis = Redis.from_env() +result = redis.lindex("my-key", 0) +print(result) +``` @@ -42875,7 +42742,7 @@ PUBSUB CHANNELS events:* import Redis from "ioredis"; const redis = new Redis(process.env.REDIS_URL!); -const result = await redis.pubsub("CHANNELS", "events:*"); +const result = await redis.lindex("my-key", "0"); console.log(result); ``` @@ -42889,7 +42756,7 @@ import { createClient } from "redis"; const client = await createClient({ url: process.env.REDIS_URL }) .on("error", console.error) .connect(); -const result = await client.pubSubChannels("events:*"); +const result = await client.lIndex("my-key", 0); console.log(result); ``` @@ -42902,7 +42769,7 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -result = client.pubsub_channels("events:*") +result = client.lindex("my-key", 0) print(result) ``` @@ -42927,7 +42794,7 @@ func main() { panic(err) } client := redis.NewClient(opts) - result, err := client.PubSubChannels(context.Background(), "events:*").Result() + result, err := client.LIndex(context.Background(), "my-key", 0).Result() if err != nil { panic(err) } @@ -42945,7 +42812,7 @@ import java.net.URI; import redis.clients.jedis.Jedis; try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - Object result = jedis.pubsubChannels("events:*"); + Object result = jedis.lindex("my-key", 0); System.out.println(result); } ``` @@ -42955,15 +42822,14 @@ try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { ```rust +use redis::TypedCommands; + fn main() -> redis::RedisResult<()> { let url = std::env::var("REDIS_URL").expect("REDIS_URL is not set"); let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let mut command = redis::cmd("PUBSUB"); - command.arg("CHANNELS"); - command.arg("events:*"); - let result: redis::Value = command.query(&mut connection)?; + let result = connection.lindex("my-key", 0)?; println!("{result:?}"); Ok(()) } @@ -42973,31 +42839,29 @@ fn main() -> redis::RedisResult<()> { -# PUNSUBSCRIBE -Source: https://upstash.com/docs/redis/commands/pub-sub/punsubscribe +# LINSERT +Source: https://upstash.com/docs/redis/commands/list/linsert -Use `PUNSUBSCRIBE` to cancel pattern subscriptions of the current connection. +Use `LINSERT` to insert an element immediately before or after another element of a list. -With no arguments the connection unsubscribes from every pattern it registered, otherwise only from the patterns named, which must be given exactly as they were passed to [`PSUBSCRIBE`](/docs/redis/commands/pub-sub/psubscribe), since patterns are matched literally here and not expanded. The server sends one confirmation per pattern with the number of subscriptions still active. +The pivot is matched by value, and only its first occurrence starting from the head is used. The reply is the new length of the list, `0` when the key does not exist, and `-1` when the pivot value was not found, which is how you tell a failed insert from a successful one. -Exact-channel subscriptions are not affected; cancel those with [`UNSUBSCRIBE`](/docs/redis/commands/pub-sub/unsubscribe). +Finding the pivot means scanning the list, so this is a linear operation; on long lists it is worth keeping an index elsewhere or using a sorted set instead. ## Syntax ```redis -PUNSUBSCRIBE [ [ ...]] +LINSERT (BEFORE | AFTER) ``` ## Arguments | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | -| `` | No | Yes | Glob-style channel pattern; omit to unsubscribe from all patterns. | - -## Important points - -* This is a connection-oriented command and is available over native Redis TCP, not the stateless REST endpoint. -* Subscription commands require a dedicated TCP connection. In RESP3, subscription events use push replies. +| `` | Yes | No | Redis key targeted by the command. | +| `(BEFORE \| AFTER)` | Yes | No | Where to place the new element relative to the pivot: `BEFORE` or `AFTER`. | +| `` | Yes | No | Existing element to insert next to. | +| `` | Yes | No | Element to insert. | ## Response @@ -43005,8 +42869,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Three-element subscription-state array per pattern | -| RESP3 | Three-element subscription-state push reply per pattern | +| RESP2 | Integer: the list length after insertion, `-1` if the pivot was not found, `0` if the key does not exist | +| RESP3 | Integer: the list length after insertion, `-1` if the pivot was not found, `0` if the key does not exist | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -43021,7 +42885,32 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -PUNSUBSCRIBE events:* +LINSERT my-key BEFORE pivot element +``` + + + + + +```ts +import { Redis } from "@upstash/redis"; + +const redis = Redis.fromEnv(); + +await redis.rpush("key", "a", "b", "c"); +await redis.linsert("key", "before", "b", "x"); +``` + + + + + +```python +from upstash_redis import Redis + +redis = Redis.from_env() +result = redis.linsert("my-key", "BEFORE", "pivot", "element") +print(result) ``` @@ -43031,9 +42920,9 @@ PUNSUBSCRIBE events:* ```ts import Redis from "ioredis"; -const subscriber = new Redis(process.env.REDIS_URL!); -await subscriber.psubscribe("events:*"); -await subscriber.punsubscribe("events:*"); +const redis = new Redis(process.env.REDIS_URL!); +const result = await redis.linsert("my-key", "BEFORE", "pivot", "element"); +console.log(result); ``` @@ -43043,9 +42932,11 @@ await subscriber.punsubscribe("events:*"); ```ts import { createClient } from "redis"; -const subscriber = await createClient({ url: process.env.REDIS_URL }).connect(); -await subscriber.pSubscribe("events:*", console.log); -await subscriber.pUnsubscribe("events:*"); +const client = await createClient({ url: process.env.REDIS_URL }) + .on("error", console.error) + .connect(); +const result = await client.lInsert("my-key", "BEFORE", "pivot", "element"); +console.log(result); ``` @@ -43057,9 +42948,8 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -pubsub = client.pubsub() -pubsub.psubscribe("events:*") -pubsub.punsubscribe("events:*") +result = client.linsert("my-key", "BEFORE", "pivot", "element") +print(result) ``` @@ -43078,16 +42968,16 @@ import ( ) func main() { - ctx := context.Background() opts, err := redis.ParseURL(os.Getenv("REDIS_URL")) if err != nil { panic(err) } client := redis.NewClient(opts) - pubsub := client.PSubscribe(ctx, "events:*") - if err := pubsub.PUnsubscribe(ctx, "events:*"); err != nil { + result, err := client.LInsertBefore(context.Background(), "my-key", "pivot", "element").Result() + if err != nil { panic(err) } + fmt.Println(result) } ``` @@ -43097,16 +42987,12 @@ func main() { ```java import java.net.URI; + import redis.clients.jedis.Jedis; -import redis.clients.jedis.JedisPubSub; try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - jedis.psubscribe(new JedisPubSub() { - @Override - public void onPMessage(String pattern, String channel, String message) { - punsubscribe(); - } - }, "events:*"); + Object result = jedis.linsert("my-key", redis.clients.jedis.args.ListPosition.BEFORE, "pivot", "element"); + System.out.println(result); } ``` @@ -43115,14 +43001,15 @@ try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { ```rust +use redis::TypedCommands; + fn main() -> redis::RedisResult<()> { let url = std::env::var("REDIS_URL").expect("REDIS_URL is not set"); let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let mut pubsub = connection.as_pubsub(); - pubsub.psubscribe("events:*")?; - pubsub.punsubscribe("events:*")?; - #[allow(unreachable_code)] + + let result = connection.linsert_before("my-key", "pivot", "element")?; + println!("{result:?}"); Ok(()) } ``` @@ -43131,31 +43018,24 @@ fn main() -> redis::RedisResult<()> { -# SUBSCRIBE -Source: https://upstash.com/docs/redis/commands/pub-sub/subscribe - -Use `SUBSCRIBE` to subscribe the current connection to one or more channels. +# LLEN +Source: https://upstash.com/docs/redis/commands/list/llen -The server confirms each channel with its own reply carrying the running number of subscriptions this connection holds, and from then on messages published to those channels arrive on the connection as they are sent. +Use `LLEN` to get the number of elements in a list. -Under RESP2 a subscribed connection may only run subscription commands plus `PING`, `RESET`, and `QUIT`, which is why subscribers normally use a dedicated connection. Under RESP3 messages arrive as push replies and ordinary commands remain usable on the same connection. Messages published while the connection is not subscribed are not delivered later, since pub/sub keeps no history. +The reply is `0` when the key does not exist. The length is maintained by Redis rather than computed on demand, so the command is cheap whatever the size of the list, which makes it the usual way to monitor a queue's backlog. ## Syntax ```redis -SUBSCRIBE [ ...] +LLEN ``` ## Arguments | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | -| `` | Yes | Yes | Channel name. | - -## Important points - -* This is a connection-oriented command and is available over native Redis TCP, not the stateless REST endpoint. -* Subscription commands require a dedicated TCP connection. In RESP3, subscription events use push replies. +| `` | Yes | No | Redis key targeted by the command. | ## Response @@ -43163,8 +43043,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Three-element subscription-state array per channel | -| RESP3 | Three-element subscription-state push reply per channel | +| RESP2 | Integer | +| RESP3 | Integer | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -43179,7 +43059,33 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -SUBSCRIBE events +LLEN my-key +``` + + + + + +```ts +import { Redis } from "@upstash/redis"; + +const redis = Redis.fromEnv(); + +await redis.rpush("key", "a", "b", "c"); +const length = await redis.llen("key"); +console.log(length); // 3 +``` + + + + + +```python +from upstash_redis import Redis + +redis = Redis.from_env() +result = redis.llen("my-key") +print(result) ``` @@ -43189,9 +43095,9 @@ SUBSCRIBE events ```ts import Redis from "ioredis"; -const subscriber = new Redis(process.env.REDIS_URL!); -await subscriber.subscribe("events"); -subscriber.on("message", (channel, message) => console.log(channel, message)); +const redis = new Redis(process.env.REDIS_URL!); +const result = await redis.llen("my-key"); +console.log(result); ``` @@ -43201,10 +43107,11 @@ subscriber.on("message", (channel, message) => console.log(channel, message)); ```ts import { createClient } from "redis"; -const subscriber = await createClient({ url: process.env.REDIS_URL }).connect(); -await subscriber.subscribe("events", (message, channel) => { - console.log(channel, message); -}); +const client = await createClient({ url: process.env.REDIS_URL }) + .on("error", console.error) + .connect(); +const result = await client.lLen("my-key"); +console.log(result); ``` @@ -43216,10 +43123,8 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -pubsub = client.pubsub() -pubsub.subscribe("events") -for message in pubsub.listen(): - print(message) +result = client.llen("my-key") +print(result) ``` @@ -43238,16 +43143,16 @@ import ( ) func main() { - ctx := context.Background() opts, err := redis.ParseURL(os.Getenv("REDIS_URL")) if err != nil { panic(err) } client := redis.NewClient(opts) - pubsub := client.Subscribe(ctx, "events") - for message := range pubsub.Channel() { - fmt.Println(message.Channel, message.Payload) + result, err := client.LLen(context.Background(), "my-key").Result() + if err != nil { + panic(err) } + fmt.Println(result) } ``` @@ -43257,16 +43162,12 @@ func main() { ```java import java.net.URI; + import redis.clients.jedis.Jedis; -import redis.clients.jedis.JedisPubSub; try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - jedis.subscribe(new JedisPubSub() { - @Override - public void onMessage(String channel, String message) { - System.out.println(channel + ": " + message); - } - }, "events"); + Object result = jedis.llen("my-key"); + System.out.println(result); } ``` @@ -43275,18 +43176,15 @@ try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { ```rust +use redis::TypedCommands; + fn main() -> redis::RedisResult<()> { let url = std::env::var("REDIS_URL").expect("REDIS_URL is not set"); let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let mut pubsub = connection.as_pubsub(); - pubsub.subscribe("events")?; - loop { - let message = pubsub.get_message()?; - let payload: String = message.get_payload()?; - println!("{payload}"); - } - #[allow(unreachable_code)] + + let result = connection.llen("my-key")?; + println!("{result:?}"); Ok(()) } ``` @@ -43295,31 +43193,29 @@ fn main() -> redis::RedisResult<()> { -# UNSUBSCRIBE -Source: https://upstash.com/docs/redis/commands/pub-sub/unsubscribe +# LMOVE +Source: https://upstash.com/docs/redis/commands/list/lmove -Use `UNSUBSCRIBE` to cancel channel subscriptions of the current connection. +Use `LMOVE` to atomically take an element from one end of a list and push it onto one end of another list, returning the element. -With no arguments the connection unsubscribes from every channel it is subscribed to, otherwise only from the ones named. The server sends one confirmation per channel, each carrying the number of subscriptions still active, and the connection leaves subscriber mode once that count reaches zero. +The two directions are chosen independently: `LEFT RIGHT` takes from the head of the source and appends to the tail of the destination, which preserves order when transferring between queues, while `LEFT LEFT` behaves like moving between stacks. If the source is empty nothing happens and the reply is null. Source and destination may be the same key, in which case the list is rotated. -Pattern subscriptions are not affected; cancel those with [`PUNSUBSCRIBE`](/docs/redis/commands/pub-sub/punsubscribe). +Because the element is never outside a list, `LMOVE` is the building block for reliable queues: a worker moves an item into a processing list, does the work, and removes it from there, so a crash leaves the item recoverable instead of lost. It replaces the deprecated [`RPOPLPUSH`](/docs/redis/commands/list/rpoplpush), and [`BLMOVE`](/docs/redis/commands/list/blmove) is the blocking form. ## Syntax ```redis -UNSUBSCRIBE [ [ ...]] +LMOVE (LEFT | RIGHT) (LEFT | RIGHT) ``` ## Arguments | Argument | Required | Repeatable | Description | | --- | --- | --- | --- | -| `` | No | Yes | Channel name. | - -## Important points - -* This is a connection-oriented command and is available over native Redis TCP, not the stateless REST endpoint. -* Subscription commands require a dedicated TCP connection. In RESP3, subscription events use push replies. +| `` | Yes | No | Redis key used as source. | +| `` | Yes | No | Redis key used as destination. | +| `(LEFT \| RIGHT)` | Yes | No | Which end of the source list the element is taken from: `LEFT` (head) or `RIGHT` (tail). | +| `(LEFT \| RIGHT)` | Yes | No | Which end of the destination list the element is pushed onto: `LEFT` (head) or `RIGHT` (tail). | ## Response @@ -43327,8 +43223,8 @@ The reply reports the result of the operation. Error replies have the same shape | Protocol | Reply | | --- | --- | -| RESP2 | Three-element subscription-state array per channel | -| RESP3 | Three-element subscription-state push reply per channel | +| RESP2 | Bulk string | +| RESP3 | Bulk string | Client libraries often decode bulk strings, maps, sets, and numeric strings into language-native values. The table describes the Redis wire reply. @@ -43343,7 +43239,32 @@ TCP examples use the TLS `REDIS_URL` from the Upstash console. REST examples use ```bash -UNSUBSCRIBE events +LMOVE source-key destination-key LEFT LEFT +``` + + + + + +```ts +import { Redis } from "@upstash/redis"; + +const redis = Redis.fromEnv(); + +await redis.rpush("source", "a", "b", "c"); +const element = await redis.lmove("source", "destination", "left", "left"); +``` + + + + + +```python +from upstash_redis import Redis + +redis = Redis.from_env() +result = redis.lmove("source-key", "destination-key", "LEFT", "LEFT") +print(result) ``` @@ -43353,9 +43274,9 @@ UNSUBSCRIBE events ```ts import Redis from "ioredis"; -const subscriber = new Redis(process.env.REDIS_URL!); -await subscriber.subscribe("events"); -await subscriber.unsubscribe("events"); +const redis = new Redis(process.env.REDIS_URL!); +const result = await redis.lmove("source-key", "destination-key", "LEFT", "LEFT"); +console.log(result); ``` @@ -43365,9 +43286,11 @@ await subscriber.unsubscribe("events"); ```ts import { createClient } from "redis"; -const subscriber = await createClient({ url: process.env.REDIS_URL }).connect(); -await subscriber.subscribe("events", console.log); -await subscriber.unsubscribe("events"); +const client = await createClient({ url: process.env.REDIS_URL }) + .on("error", console.error) + .connect(); +const result = await client.lMove("source-key", "destination-key", "LEFT", "LEFT"); +console.log(result); ``` @@ -43379,9 +43302,8 @@ import os import redis client = redis.from_url(os.environ["REDIS_URL"]) -pubsub = client.pubsub() -pubsub.subscribe("events") -pubsub.unsubscribe("events") +result = client.lmove("source-key", "destination-key", "LEFT", "LEFT") +print(result) ``` @@ -43400,16 +43322,16 @@ import ( ) func main() { - ctx := context.Background() opts, err := redis.ParseURL(os.Getenv("REDIS_URL")) if err != nil { panic(err) } client := redis.NewClient(opts) - pubsub := client.Subscribe(ctx, "events") - if err := pubsub.Unsubscribe(ctx, "events"); err != nil { + result, err := client.LMove(context.Background(), "source-key", "destination-key", "LEFT", "LEFT").Result() + if err != nil { panic(err) } + fmt.Println(result) } ``` @@ -43419,16 +43341,12 @@ func main() { ```java import java.net.URI; + import redis.clients.jedis.Jedis; -import redis.clients.jedis.JedisPubSub; try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { - jedis.subscribe(new JedisPubSub() { - @Override - public void onMessage(String channel, String message) { - unsubscribe(); - } - }, "events"); + Object result = jedis.lmove("source-key", "destination-key", redis.clients.jedis.args.ListDirection.LEFT, redis.clients.jedis.args.ListDirection.LEFT); + System.out.println(result); } ``` @@ -43437,14 +43355,15 @@ try (Jedis jedis = new Jedis(new URI(System.getenv("REDIS_URL")))) { ```rust +use redis::TypedCommands; + fn main() -> redis::RedisResult<()> { let url = std::env::var("REDIS_URL").expect("REDIS_URL is not set"); let client = redis::Client::open(url)?; let mut connection = client.get_connection()?; - let mut pubsub = connection.as_pubsub(); - pubsub.subscribe("events")?; - pubsub.unsubscribe("events")?; - #[allow(unreachable_code)] + + let result = connection.lmove("source-key", "destination-key", redis::Direction::Left, redis::Direction::Left)?; + println!("{result:?}"); Ok(()) } ``` @@ -43453,59 +43372,29 @@ fn main() -> redis::RedisResult<()> { -# EVAL -Source: https://upstash.com/docs/redis/commands/scripting/eval - -Use `EVAL` to run a Lua script on the server. - -`` says how many of the arguments that follow are key names. The script receives those in the `KEYS` table and every remaining argument in `ARGV`. Passing key names as keys rather than hardcoding them in the script body matters, because Redis uses that list for routing and access checks. Inside the script, `redis.call` runs Redis commands and its return value is converted to a Lua value. - -The script runs as a single atomic step, which makes it the standard way to do read, decide, and write logic, such as a rate limiter or a compare-and-set update, in one round trip and without a transaction. Keep scripts short, since a script that holds the database blocks everything else, and keep them deterministic by deriving values from `KEYS`, `ARGV`, or data read inside the script rather than from clock or random sources. - -Upstash isolates a script with a lock. By default that is the global lock, because the engine cannot know in advance which keys the script will touch, so no other command runs while the script does. Adding the `allow-key-locking` flag to the script's shebang line makes it lock only the keys passed in `KEYS` instead, so calls that work on disjoint keys run in parallel: - -```lua -#!lua flags=allow-key-locking - -redis.call('INCR', KEYS[1]) -return 1 -``` +# LMPOP +Source: https://upstash.com/docs/redis/commands/list/lmpop -With the flag set, every key the script touches must appear in `KEYS`, and commands that need database-wide access, such as `FLUSHDB`, are rejected. See [Key-Based Locking](/docs/redis/features/key-locking) for the full rules. +Use `LMPOP` to pop elements from the first of several lists that is not empty. - - Pass every key the script touches through `KEYS`, even when the script runs - under the global lock. Upstash keeps idle entries - [on disk](/docs/redis/features/durability): declared keys are loaded before the - script starts and the lock is released during that read, but a key that the - script builds while it runs is read from disk with the lock held, stalling - every command waiting on it. See - [Dynamic Keys and Latency](/docs/redis/features/key-locking#dynamic-keys-and-latency). - +`` states how many keys follow, `LEFT` or `RIGHT` chooses the end to pop from, and `COUNT` sets how many elements to take, defaulting to one. Keys are examined in the order given and only the first non-empty one is touched, which is exactly what a priority queue needs: list the high priority queue first and it is drained before the others are looked at. -Sending a script also caches it under its SHA1 digest, so later calls can use [`EVALSHA`](/docs/redis/commands/scripting/evalsha) and avoid resending the body. Use [`EVAL_RO`](/docs/redis/commands/scripting/eval-ro) for scripts that only read. +The reply names the key that was popped from together with the elements, so a caller working with several queues knows where the work came from. When every key is empty the reply is null; use [`BLMPOP`](/docs/redis/commands/list/blmpop) to wait instead. ## Syntax ```redis -EVAL