Redis bitmaps

Summary: Introduction to Redis bitmaps

Bitmap/bitfield command summary

**7 commands in this group:**

[View all bitmap commands](https://redis.io/commands/?group=bitmap)

| Command | Summary | Complexity | Since |
|---------|---------|------------|-------|
| [BITCOUNT](https://redis.io/commands/bitcount/) | Counts the number of set bits (population counting) in a string. | O(N) | 2.6.0 |
| [BITFIELD](https://redis.io/commands/bitfield/) | Performs arbitrary bitfield integer operations on strings. | O(1) for each subcommand specified | 3.2.0 |
| [BITFIELD_RO](https://redis.io/commands/bitfield_ro/) | Performs arbitrary read-only bitfield integer operations on strings. | O(1) for each subcommand specified | 6.0.0 |
| [BITOP](https://redis.io/commands/bitop/) | Performs bitwise operations on multiple strings, and stores the result. | O(N) | 2.6.0 |
| [BITPOS](https://redis.io/commands/bitpos/) | Finds the first set (1) or clear (0) bit in a string. | O(N) | 2.8.7 |
| [GETBIT](https://redis.io/commands/getbit/) | Returns a bit value by offset. | O(1) | 2.2.0 |
| [SETBIT](https://redis.io/commands/setbit/) | Sets or clears the bit at offset of the string value. Creates the key if it doesn't exist. | O(1) | 2.2.0 |

Bitmaps are not an actual data type, but a set of bit-oriented operations
defined on the String type which is treated like a bit vector.
Since strings are binary safe blobs and their maximum length is 512 MB,
they are suitable to set up to 2^32 different bits.

You can perform bitwise operations on one or more strings.
Some examples of bitmap use cases include:

* Efficient set representations for cases where the members of a set correspond to the integers 0-N.
* Object permissions, where each bit represents a particular permission, similar to the way that file systems store permissions.

Example

Suppose you have 1000 cyclists racing through the country-side, with sensors on their bikes labeled 0-999.
You want to quickly determine whether a given sensor has pinged a tracking server within the hour to check in on a rider.

You can represent this scenario using a bitmap whose key references the current hour.

* Rider 123 pings the server on January 1, 2024 within the 00:00 hour. You can then confirm that rider 123 pinged the server. You can also check to see if rider 456 has pinged the server for that same hour.

#### Code Examples

Foundational: Set and get individual bits using SETBIT and GETBIT to track binary states

**Commands:** SETBIT, GETBIT

**Complexity:**
- SETBIT: O(1)
- GETBIT: O(1)

**Redis CLI:**

[code example]

**Available in:** Redis CLI, C# (Synchronous), Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python

**C# (Synchronous):**

[code example]

**Go:**

[code example]

**Java (Asynchronous - Lettuce):**

[code example]

**Java (Reactive - Lettuce):**

[code example]

**Java (Synchronous - Jedis):**

[code example]

**JavaScript (Node.js):**

[code example]

**PHP:**

[code example]

**Python:**

[code example]

Bit Operations

Bit operations are divided into two groups: constant-time single bit
operations, like setting a bit to 1 or 0, or getting its value, and
operations on groups of bits, for example counting the number of set
bits in a given range of bits (e.g., population counting).

One of the biggest advantages of bitmaps is that they often provide
extreme space savings when storing information. For example in a system
where different users are represented by incremental user IDs, it is possible
to remember a single bit information (for example, knowing whether
a user wants to receive a newsletter) of 4 billion users using just 512 MB of memory.

The [`SETBIT`]() command takes as its first argument the bit number, and as its second
argument the value to set the bit to, which is 1 or 0. The command
automatically enlarges the string if the addressed bit is outside the
current string length.

[`GETBIT`]() just returns the value of the bit at the specified index.
Out of range bits (addressing a bit that is outside the length of the string
stored into the target key) are always considered to be zero.

There are three commands operating on group of bits:

1. [`BITOP`]() performs bit-wise operations between different strings. The provided operators are `AND`, `OR`, `XOR`, `NOT`, `DIFF`, `DIFF1`, `ANDOR`, and `ONE`.
2. [`BITCOUNT`]() performs population counting, reporting the number of bits set to 1.
3. [`BITPOS`]() finds the first bit having the specified value of 0 or 1.

Both [`BITPOS`]() and [`BITCOUNT`]() are able to operate with byte ranges of the
string, instead of running for the whole length of the string. We can trivially see the number of bits that have been set in a bitmap.

#### Code Examples

Bit counting: Use BITCOUNT to count the number of set bits in a bitmap when you need to get population counts

**Builds upon:** ping

**Commands:** BITCOUNT

**Complexity:**
- BITCOUNT: O(N)

**Redis CLI:**

[code example]

**Available in:** Redis CLI, C# (Synchronous), Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python

**C# (Synchronous):**

[code example]

**Go:**

[code example]

**Java (Asynchronous - Lettuce):**

[code example]

**Java (Reactive - Lettuce):**

[code example]

**Java (Synchronous - Jedis):**

[code example]

**JavaScript (Node.js):**

[code example]

**PHP:**

[code example]

**Python:**

[code example]

For example imagine you want to know the longest streak of daily visits of
your web site users. You start counting days starting from zero, that is the
day you made your web site public, and set a bit with [`SETBIT`]() every time
the user visits the web site. As a bit index you simply take the current unix
time, subtract the initial offset, and divide by the number of seconds in a day
(normally, 3600\*24).

This way for each user you have a small string containing the visit
information for each day. With [`BITCOUNT`]() it is possible to easily get
the number of days a given user visited the web site, while with
a few [`BITPOS`]() calls, or simply fetching and analyzing the bitmap client-side,
it is possible to easily compute the longest streak.

Bitwise operations

The [`BITOP`]() command performs bitwise
operations over two or more source keys, storing the result in a destination key.

The examples below show the available operations using three keys: `A` (with bit pattern
`11011000`), `B` (`00011001`), and `C` (`01101100`).

Numbering the bits from left to right, starting at zero, the following `SETBIT` commands
will create these bitmaps:

#### Code Examples

Setup for bitwise operations: Create multiple bitmaps using SETBIT to prepare for demonstrating bitwise operations

**Difficulty:** Intermediate

**Builds upon:** ping

**Commands:** SETBIT, GET

**Complexity:**
- SETBIT: O(1)
- GET: O(1)

**Redis CLI:**

[code example]

**Available in:** Redis CLI, C# (Synchronous), Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python

**C# (Synchronous):**

[code example]

**Go:**

[code example]

**Java (Asynchronous - Lettuce):**

[code example]

**Java (Reactive - Lettuce):**

[code example]

**Java (Synchronous - Jedis):**

[code example]

**JavaScript (Node.js):**

[code example]

**PHP:**

[code example]

**Python:**

[code example]

#### `AND`

Set a bit in the destination key to 1 only if it is set in all the source keys.

#### Code Examples

AND operation: Use BITOP AND to find bits set in all source bitmaps when you need to find common bits across multiple sets

**Difficulty:** Intermediate

**Builds upon:** bitop_setup

**Commands:** BITOP, GET

**Complexity:**
- BITOP: O(N)
- GET: O(1)

**Redis CLI:**

[code example]

**Available in:** Redis CLI, C# (Synchronous), Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python

**C# (Synchronous):**

[code example]

**Go:**

[code example]

**Java (Asynchronous - Lettuce):**

[code example]

**Java (Reactive - Lettuce):**

[code example]

**Java (Synchronous - Jedis):**

[code example]

**JavaScript (Node.js):**

[code example]

**PHP:**

[code example]

**Python:**

[code example]

#### `OR`
Set a bit in the destination key to 1 if it is set in at least one of the source keys.

#### Code Examples

OR operation: Use BITOP OR to find bits set in at least one source bitmap when you need to combine multiple sets

**Difficulty:** Intermediate

**Builds upon:** bitop_setup

**Commands:** BITOP, GET

**Complexity:**
- BITOP: O(N)
- GET: O(1)

**Redis CLI:**

[code example]

**Available in:** Redis CLI, C# (Synchronous), Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python

**C# (Synchronous):**

[code example]

**Go:**

[code example]

**Java (Asynchronous - Lettuce):**

[code example]

**Java (Reactive - Lettuce):**

[code example]

**Java (Synchronous - Jedis):**

[code example]

**JavaScript (Node.js):**

[code example]

**PHP:**

[code example]

**Python:**

[code example]

#### `XOR`

For two source keys, set a bit in the destination key to 1 if the value of the bit is
different in the two keys. For three or more source keys, the result of XORing the first two
keys is then XORed with the next key, and so forth.

#### Code Examples

XOR operation: Use BITOP XOR to find bits that differ between bitmaps when you need to identify differences

**Difficulty:** Intermediate

**Builds upon:** bitop_setup

**Commands:** BITOP, GET

**Complexity:**
- BITOP: O(N)
- GET: O(1)

**Redis CLI:**

[code example]

**Available in:** Redis CLI, C# (Synchronous), Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python

**C# (Synchronous):**

[code example]

**Go:**

[code example]

**Java (Asynchronous - Lettuce):**

[code example]

**Java (Reactive - Lettuce):**

[code example]

**Java (Synchronous - Jedis):**

[code example]

**JavaScript (Node.js):**

[code example]

**PHP:**

[code example]

**Python:**

[code example]

#### `NOT`

Set a bit in the destination key to 1 if it is not set in the source key (this
is the only unary operator).

#### Code Examples

NOT operation: Use BITOP NOT to invert all bits in a bitmap when you need to negate a set

**Difficulty:** Intermediate

**Builds upon:** bitop_setup

**Commands:** BITOP, GET

**Complexity:**
- BITOP: O(N)
- GET: O(1)

**Redis CLI:**

[code example]

**Available in:** Redis CLI, C# (Synchronous), Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python

**C# (Synchronous):**

[code example]

**Go:**

[code example]

**Java (Asynchronous - Lettuce):**

[code example]

**Java (Reactive - Lettuce):**

[code example]

**Java (Synchronous - Jedis):**

[code example]

**JavaScript (Node.js):**

[code example]

**PHP:**

[code example]

**Python:**

[code example]

#### `DIFF`

Set a bit in the destination key to 1 if it is set in the first source key, but not in any
of the other source keys.

#### Code Examples

DIFF operation: Use BITOP DIFF to find bits set in the first bitmap but not in others when you need set difference

**Difficulty:** Advanced

**Builds upon:** bitop_setup

**Commands:** BITOP, GET

**Complexity:**
- BITOP: O(N)
- GET: O(1)

**Redis CLI:**

[code example]

**Available in:** Redis CLI, C# (Synchronous), Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python

**C# (Synchronous):**

[code example]

**Go:**

[code example]

**Java (Asynchronous - Lettuce):**

[code example]

**Java (Reactive - Lettuce):**

[code example]

**Java (Synchronous - Jedis):**

[code example]

**JavaScript (Node.js):**

[code example]

**PHP:**

[code example]

**Python:**

[code example]

#### `DIFF1`

Set a bit in the destination key to 1 if it is not set in the first source key,
but set in at least one of the other source keys.

#### Code Examples

DIFF1 operation: Use BITOP DIFF1 to find bits not in the first bitmap but in at least one other when you need inverse difference

**Difficulty:** Advanced

**Builds upon:** bitop_setup

**Commands:** BITOP, GET

**Complexity:**
- BITOP: O(N)
- GET: O(1)

**Redis CLI:**

[code example]

**Available in:** Redis CLI, C# (Synchronous), Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python

**C# (Synchronous):**

[code example]

**Go:**

[code example]

**Java (Asynchronous - Lettuce):**

[code example]

**Java (Reactive - Lettuce):**

[code example]

**Java (Synchronous - Jedis):**

[code example]

**JavaScript (Node.js):**

[code example]

**PHP:**

[code example]

**Python:**

[code example]

#### `ANDOR`

Set a bit in the destination key to 1 if it is set in the first source key and also in at least one of the other source keys.

#### Code Examples

ANDOR operation: Use BITOP ANDOR to find bits in the first bitmap and at least one other when you need intersection with union

**Difficulty:** Advanced

**Builds upon:** bitop_setup

**Commands:** BITOP, GET

**Complexity:**
- BITOP: O(N)
- GET: O(1)

**Redis CLI:**

[code example]

**Available in:** Redis CLI, C# (Synchronous), Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python

**C# (Synchronous):**

[code example]

**Go:**

[code example]

**Java (Asynchronous - Lettuce):**

[code example]

**Java (Reactive - Lettuce):**

[code example]

**Java (Synchronous - Jedis):**

[code example]

**JavaScript (Node.js):**

[code example]

**PHP:**

[code example]

**Python:**

[code example]

#### `ONE`

Set a bit in the destination key to 1 if it is set in exactly one of the source keys.

#### Code Examples

ONE operation: Use BITOP ONE to find bits set in exactly one bitmap when you need exclusive membership

**Difficulty:** Advanced

**Builds upon:** bitop_setup

**Commands:** BITOP, GET

**Complexity:**
- BITOP: O(N)
- GET: O(1)

**Redis CLI:**

[code example]

**Available in:** Redis CLI, C# (Synchronous), Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python

**C# (Synchronous):**

[code example]

**Go:**

[code example]

**Java (Asynchronous - Lettuce):**

[code example]

**Java (Reactive - Lettuce):**

[code example]

**Java (Synchronous - Jedis):**

[code example]

**JavaScript (Node.js):**

[code example]

**PHP:**

[code example]

**Python:**

[code example]

Split bitmaps into multiple keys

Bitmaps are trivial to split into multiple keys, for example for
the sake of sharding the data set and because in general it is better to
avoid working with huge keys. To split a bitmap across different keys
instead of setting all the bits into a key, a trivial strategy is just
to store M bits per key and obtain the key name with `bit-number/M` and
the Nth bit to address inside the key with `bit-number MOD M`.

Performance

[`SETBIT`]() and [`GETBIT`]() are O(1).
[`BITOP`]() is O(n), where _n_ is the length of the longest string in the comparison.

Learn more

* [Redis Bitmaps Explained](https://www.youtube.com/watch?v=oj8LdJQjhJo) teaches you how to use bitmaps for map exploration in an online game.
* [Redis University's RU101](https://university.redis.com/courses/ru101/) covers Redis bitmaps in detail.

bool res1 = db.StringSetBit("pings:2024-01-01-00:00", 123, true);
        Console.WriteLine(res1);    // >>> 0

        bool res2 = db.StringGetBit("pings:2024-01-01-00:00", 123);
        Console.WriteLine(res2);    // >>> True

        bool res3 = db.StringGetBit("pings:2024-01-01-00:00", 456);
        Console.WriteLine(res3);    // >>> False

res1, err := rdb.SetBit(ctx, "pings:2024-01-01-00:00", 123, 1).Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(res1) // >>> 0

	res2, err := rdb.GetBit(ctx, "pings:2024-01-01-00:00", 123).Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(res2) // >>> 1

	res3, err := rdb.GetBit(ctx, "pings:2024-01-01-00:00", 456).Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(res3) // >>> 0

CompletableFuture<Void> ping = async.setbit("pings:2024-01-01-00:00", 123, 1).thenCompose(res1 -> {
                System.out.println(res1); // >>> 0
                return async.getbit("pings:2024-01-01-00:00", 123);
            }).thenCompose(res2 -> {
                System.out.println(res2); // >>> 1
                return async.getbit("pings:2024-01-01-00:00", 456);
            }).thenAccept(res3 -> {
                System.out.println(res3); // >>> 0
            }).toCompletableFuture();

Mono<Void> ping = reactive.setbit("pings:2024-01-01-00:00", 123, 1).doOnNext(res1 -> {
                System.out.println(res1); // >>> 0
            }).flatMap(v -> reactive.getbit("pings:2024-01-01-00:00", 123)).doOnNext(res2 -> {
                System.out.println(res2); // >>> 1
            }).flatMap(v -> reactive.getbit("pings:2024-01-01-00:00", 456)).doOnNext(res3 -> {
                System.out.println(res3); // >>> 0
            }).then();

boolean res1 = jedis.setbit("pings:2024-01-01-00:00", 123, true);
        System.out.println(res1); // >>> false

        boolean res2 = jedis.getbit("pings:2024-01-01-00:00", 123);
        System.out.println(res2); // >>> true

        boolean res3 = jedis.getbit("pings:2024-01-01-00:00", 456);
        System.out.println(res3); // >>> false

const res1 = await client.setBit("pings:2024-01-01-00:00", 123, 1)
console.log(res1)  // >>> 0

const res2 = await client.getBit("pings:2024-01-01-00:00", 123)
console.log(res2)  // >>> 1

const res3 = await client.getBit("pings:2024-01-01-00:00", 456)
console.log(res3)  // >>> 0

bool res4 = db.StringSetBit("pings:2024-01-01-00:00", 123, true);
        long res5 = db.StringBitCount("pings:2024-01-01-00:00");
        Console.WriteLine(res5);    // >>> 1

res4, err := rdb.BitCount(ctx, "pings:2024-01-01-00:00",
		&redis.BitCount{
			Start: 0,
			End:   456,
		}).Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(res4) // >>> 1

CompletableFuture<Void> bitcount = async.bitcount("pings:2024-01-01-00:00").thenAccept(res4 -> {
                System.out.println(res4); // >>> 1
            }).toCompletableFuture();

Mono<Void> bitcount = reactive.bitcount("pings:2024-01-01-00:00").doOnNext(res4 -> {
                System.out.println(res4); // >>> 1
            }).then();

long res4 = jedis.bitcount("pings:2024-01-01-00:00");
        System.out.println(res4); // >>> 1

await client.setBit("pings:2024-01-01-00:00", 123, 1)
const res4 = await client.bitCount("pings:2024-01-01-00:00")
console.log(res4)  // >>> 1

> SETBIT A 0 1
(integer) 0
> SETBIT A 1 1
(integer) 0
> SETBIT A 3 1
(integer) 0
> SETBIT A 4 1
(integer) 0
> GET A
"\xd8"
# Hex value: 0xd8 = 0b11011000

> SETBIT B 3 1
(integer) 0
> SETBIT B 4 1
(integer) 0
> SETBIT B 7 1
(integer) 0
> GET B
"\x19"
# Hex value: 0x19 = 0b00011001

> SETBIT C 1 1
(integer) 0
> SETBIT C 2 1
(integer) 0
> SETBIT C 4 1
(integer) 0
> SETBIT C 5 1
(integer) 0
> GET C
"l"
# ASCII "l" = hex 0x6c = 0b01101100

// Bitwise operations are not supported in NRedisStack.

rdb.SetBit(ctx, "A", 0, 1)
	rdb.SetBit(ctx, "A", 1, 1)
	rdb.SetBit(ctx, "A", 3, 1)
	rdb.SetBit(ctx, "A", 4, 1)
	ba, _ := rdb.Get(ctx, "A").Bytes()
	fmt.Printf("%08b\n", ba[0])
	// >>> 11011000

	rdb.SetBit(ctx, "B", 3, 1)
	rdb.SetBit(ctx, "B", 4, 1)
	rdb.SetBit(ctx, "B", 7, 1)
	bb, _ := rdb.Get(ctx, "B").Bytes()
	fmt.Printf("%08b\n", bb[0])
	// >>> 00011001

	rdb.SetBit(ctx, "C", 1, 1)
	rdb.SetBit(ctx, "C", 2, 1)
	rdb.SetBit(ctx, "C", 4, 1)
	rdb.SetBit(ctx, "C", 5, 1)
	bc, _ := rdb.Get(ctx, "C").Bytes()
	fmt.Printf("%08b\n", bc[0])
	// >>> 01101100

CompletableFuture<Void> setup = async.setbit("A", 0, 1).thenCompose(v -> async.setbit("A", 1, 1))
                    .thenCompose(v -> async.setbit("A", 3, 1)).thenCompose(v -> async.setbit("A", 4, 1))
                    .thenCompose(v -> async.setbit("B", 3, 1)).thenCompose(v -> async.setbit("B", 4, 1))
                    .thenCompose(v -> async.setbit("B", 7, 1)).thenCompose(v -> async.setbit("C", 1, 1))
                    .thenCompose(v -> async.setbit("C", 2, 1)).thenCompose(v -> async.setbit("C", 4, 1))
                    .thenCompose(v -> async.setbit("C", 5, 1)).thenCompose(v -> asyncBytes.get("A".getBytes()))
                    .thenApply(res -> {
                        byte b = (res != null && res.length > 0) ? res[0] : 0;
                        String bitsA = String.format("%8s", Integer.toBinaryString(b & 0xFF)).replace(' ', '0');
                        System.out.println(bitsA); // >>> 11011000
                        return bitsA;
                    }).thenCompose(v -> asyncBytes.get("B".getBytes())).thenApply(res -> {
                        byte b = (res != null && res.length > 0) ? res[0] : 0;
                        String bitsB = String.format("%8s", Integer.toBinaryString(b & 0xFF)).replace(' ', '0');
                        System.out.println(bitsB); // >>> 00011001
                        return bitsB;
                    })
                    // Print C
                    .thenCompose(v -> asyncBytes.get("C".getBytes())).thenAccept(res -> {
                        byte b = (res != null && res.length > 0) ? res[0] : 0;
                        String bitsC = String.format("%8s", Integer.toBinaryString(b & 0xFF)).replace(' ', '0');
                        System.out.println(bitsC); // >>> 01101100
                    }).toCompletableFuture();

Mono<Void> setup = reactive.setbit("A", 0, 1).then(reactive.setbit("A", 1, 1)).then(reactive.setbit("A", 3, 1))
                    .then(reactive.setbit("A", 4, 1)).then(reactive.setbit("B", 3, 1)).then(reactive.setbit("B", 4, 1))
                    .then(reactive.setbit("B", 7, 1)).then(reactive.setbit("C", 1, 1)).then(reactive.setbit("C", 2, 1))
                    .then(reactive.setbit("C", 4, 1)).then(reactive.setbit("C", 5, 1)).then(reactiveBytes.get("A".getBytes()))
                    .doOnNext(res -> {
                        byte b = (res != null && res.length > 0) ? res[0] : 0;
                        String bitsA = String.format("%8s", Integer.toBinaryString(b & 0xFF)).replace(' ', '0');
                        System.out.println(bitsA); // >>> 11011000
                    }).then(reactiveBytes.get("B".getBytes())).doOnNext(res -> {
                        byte b = (res != null && res.length > 0) ? res[0] : 0;
                        String bitsB = String.format("%8s", Integer.toBinaryString(b & 0xFF)).replace(' ', '0');
                        System.out.println(bitsB); // >>> 00011001
                    }).then(reactiveBytes.get("C".getBytes())).doOnNext(res -> {
                        byte b = (res != null && res.length > 0) ? res[0] : 0;
                        String bitsC = String.format("%8s", Integer.toBinaryString(b & 0xFF)).replace(' ', '0');
                        System.out.println(bitsC); // >>> 01101100
                    }).then();

jedis.setbit("A", 0, true);
        jedis.setbit("A", 1, true);
        jedis.setbit("A", 3, true);
        jedis.setbit("A", 4, true);

        byte[] res5 = jedis.get("A".getBytes());
        System.out.println(String.format("%8s", Integer.toBinaryString(res5[0] & 0xFF)).replace(' ', '0'));
        // >>> 11011000

        jedis.setbit("B", 3, true);
        jedis.setbit("B", 4, true);
        jedis.setbit("B", 7, true);

        byte[] res6 = jedis.get("B".getBytes());
        System.out.println(String.format("%8s", Integer.toBinaryString(res6[0] & 0xFF)).replace(' ', '0'));
        // >>> 00011001

        jedis.setbit("C", 1, true);
        jedis.setbit("C", 2, true);
        jedis.setbit("C", 4, true);
        jedis.setbit("C", 5, true);

        byte[] res7 = jedis.get("C".getBytes());
        System.out.println(String.format("%8s", Integer.toBinaryString(res7[0] & 0xFF)).replace(' ', '0'));
        // >>> 01101100

await client.setBit("A", 0, 1)
await client.setBit("A", 1, 1)
await client.setBit("A", 3, 1)
await client.setBit("A", 4, 1)

const res5 = await client.get("A")
console.log(res5.readUInt8(0).toString(2).padStart(8, '0'))
// >>> 11011000

await client.setBit("B", 3, 1)
await client.setBit("B", 4, 1)
await client.setBit("B", 7, 1)

const res6 = await client.get("B")
console.log(res6.readUInt8(0).toString(2).padStart(8, '0'))
// >>> 00011001

await client.setBit("C", 1, 1)
await client.setBit("C", 2, 1)
await client.setBit("C", 4, 1)
await client.setBit("C", 5, 1)

const res7 = await client.get("C")
console.log(res7.readUInt8(0).toString(2).padStart(8, '0'))
// >>> 01101100

> BITOP AND R A B C
(integer) 1
> GET R
"\b"
# ASCII "\b" (backspace) = hex 0x08 = 0b00001000

CompletableFuture<Void> andOp = async.bitopAnd("R", "A", "B", "C")
                    .thenCompose(len -> asyncBytes.get("R".getBytes())).thenAccept(res -> {
                        byte b = (res != null && res.length > 0) ? res[0] : 0;
                        String bits = String.format("%8s", Integer.toBinaryString(b & 0xFF)).replace(' ', '0');
                        System.out.println(bits); // >>> 00001000
                    }).toCompletableFuture();

Mono<Void> andOp = reactive.bitopAnd("R", "A", "B", "C").then(reactiveBytes.get("R".getBytes())).doOnNext(res -> {
                byte b = (res != null && res.length > 0) ? res[0] : 0;
                String bits = String.format("%8s", Integer.toBinaryString(b & 0xFF)).replace(' ', '0');
                System.out.println(bits); // >>> 00001000
            }).then();

jedis.bitop(BitOP.AND, "R", "A", "B", "C");
        byte[] res8 = jedis.get("R".getBytes());
        System.out.println(String.format("%8s", Integer.toBinaryString(res8[0] & 0xFF)).replace(' ', '0'));
        // >>> 00001000

await client.bitOp("AND", "R", ["A", "B", "C"])
const res8 = await client.get("R")
console.log(res8.readUInt8(0).toString(2).padStart(8, '0'))
// >>> 00001000

> BITOP OR R A B C
(integer) 1
> GET R
"\xfd"
# Hex value: 0xfd = 0b11111101

CompletableFuture<Void> orOp = async.bitopOr("R", "A", "B", "C").thenCompose(len -> asyncBytes.get("R".getBytes()))
                    .thenAccept(res -> {
                        byte b = (res != null && res.length > 0) ? res[0] : 0;
                        String bits = String.format("%8s", Integer.toBinaryString(b & 0xFF)).replace(' ', '0');
                        System.out.println(bits); // >>> 11111101
                    }).toCompletableFuture();

Mono<Void> orOp = reactive.bitopOr("R", "A", "B", "C").then(reactiveBytes.get("R".getBytes())).doOnNext(res -> {
                byte b = (res != null && res.length > 0) ? res[0] : 0;
                String bits = String.format("%8s", Integer.toBinaryString(b & 0xFF)).replace(' ', '0');
                System.out.println(bits); // >>> 11111101
            }).then();

jedis.bitop(BitOP.OR, "R", "A", "B", "C");
        byte[] res9 = jedis.get("R".getBytes());
        System.out.println(String.format("%8s", Integer.toBinaryString(res9[0] & 0xFF)).replace(' ', '0'));
        // >>> 11111101

await client.bitOp("OR", "R", ["A", "B", "C"])
const res9 = await client.get("R")
console.log(res9.readUInt8(0).toString(2).padStart(8, '0'))
// >>> 11111101

> BITOP XOR R A B
(integer) 1
> GET R
"\xc1"
# Hex value: 0xc1 = 0b11000001

// Bitwise operations are not supported in NRedisStack.

CompletableFuture<Void> xorOp = async.bitopXor("R", "A", "B").thenCompose(len -> asyncBytes.get("R".getBytes()))
                    .thenAccept(res -> {
                        byte b = (res != null && res.length > 0) ? res[0] : 0;
                        String bits = String.format("%8s", Integer.toBinaryString(b & 0xFF)).replace(' ', '0');
                        System.out.println(bits); // >>> 11000001
                    }).toCompletableFuture();

Mono<Void> xorOp = reactive.bitopXor("R", "A", "B").then(reactiveBytes.get("R".getBytes())).doOnNext(res -> {
                byte b = (res != null && res.length > 0) ? res[0] : 0;
                String bits = String.format("%8s", Integer.toBinaryString(b & 0xFF)).replace(' ', '0');
                System.out.println(bits); // >>> 11000001
            }).then();

jedis.bitop(BitOP.XOR, "R", "A", "B");
        byte[] res10 = jedis.get("R".getBytes());
        System.out.println(String.format("%8s", Integer.toBinaryString(res10[0] & 0xFF)).replace(' ', '0'));
        // >>> 11000001

await client.bitOp("XOR", "R", ["A", "B"]) // XOR uses two keys here
const res10 = await client.get("R")
console.log(res10.readUInt8(0).toString(2).padStart(8, '0'))
// >>> 11000001

> BITOP NOT R A
(integer) 1
> GET R
"'"
# ASCII "'" (single quote) = hex 0x27 = 0b00100111

CompletableFuture<Void> notOp = async.bitopNot("R", "A").thenCompose(len -> asyncBytes.get("R".getBytes()))
                    .thenAccept(res -> {
                        byte b = (res != null && res.length > 0) ? res[0] : 0;
                        String bits = String.format("%8s", Integer.toBinaryString(b & 0xFF)).replace(' ', '0');
                        System.out.println(bits); // >>> 00100111
                    }).toCompletableFuture();

Mono<Void> notOp = reactive.bitopNot("R", "A").then(reactiveBytes.get("R".getBytes())).doOnNext(res -> {
                byte b = (res != null && res.length > 0) ? res[0] : 0;
                String bits = String.format("%8s", Integer.toBinaryString(b & 0xFF)).replace(' ', '0');
                System.out.println(bits); // >>> 00100111
            }).then();

jedis.bitop(BitOP.NOT, "R", "A");
        byte[] res11 = jedis.get("R".getBytes());
        System.out.println(String.format("%8s", Integer.toBinaryString(res11[0] & 0xFF)).replace(' ', '0'));
        // >>> 00100111

await client.bitOp("NOT", "R", "A")
const res11 = await client.get("R")
console.log(res11.readUInt8(0).toString(2).padStart(8, '0'))
// >>> 00100111

> BITOP DIFF R A B C
(integer) 1
> GET R
"\x80"
# Hex value: 0x80 = 0b10000000

CompletableFuture<Void> diffOp = async.bitopDiff("R", "A", "B", "C")
                    .thenCompose(len -> asyncBytes.get("R".getBytes())).thenAccept(res -> {
                        byte b = (res != null && res.length > 0) ? res[0] : 0;
                        String bits = String.format("%8s", Integer.toBinaryString(b & 0xFF)).replace(' ', '0');
                        System.out.println(bits); // >>> 10000000
                    }).toCompletableFuture();

Mono<Void> diffOp = reactive.bitopDiff("R", "A", "B", "C").then(reactiveBytes.get("R".getBytes())).doOnNext(res -> {
                byte b = (res != null && res.length > 0) ? res[0] : 0;
                String bits = String.format("%8s", Integer.toBinaryString(b & 0xFF)).replace(' ', '0');
                System.out.println(bits); // >>> 10000000
            }).then();

jedis.bitop(BitOP.DIFF, "R", "A", "B", "C");
        byte[] res12 = jedis.get("R".getBytes());
        System.out.println(String.format("%8s", Integer.toBinaryString(res12[0] & 0xFF)).replace(' ', '0'));
        // >>> 10000000

await client.bitOp("DIFF", "R", ["A", "B", "C"])
const res12 = await client.get("R")
console.log(res12.readUInt8(0).toString(2).padStart(8, '0'))
// >>> 10000000

> BITOP DIFF1 R A B C
(integer) 1
> GET R
"%"
# ASCII "%" (percent) = hex 0x25 = 0b00100101

CompletableFuture<Void> diff1Op = async.bitopDiff1("R", "A", "B", "C")
                    .thenCompose(len -> asyncBytes.get("R".getBytes())).thenAccept(res -> {
                        byte b = (res != null && res.length > 0) ? res[0] : 0;
                        String bits = String.format("%8s", Integer.toBinaryString(b & 0xFF)).replace(' ', '0');
                        System.out.println(bits); // >>> 00100101
                    }).toCompletableFuture();

Mono<Void> diff1Op = reactive.bitopDiff1("R", "A", "B", "C").then(reactiveBytes.get("R".getBytes()))
                    .doOnNext(res -> {
                        byte b = (res != null && res.length > 0) ? res[0] : 0;
                        String bits = String.format("%8s", Integer.toBinaryString(b & 0xFF)).replace(' ', '0');
                        System.out.println(bits); // >>> 00100101
                    }).then();

jedis.bitop(BitOP.DIFF1, "R", "A", "B", "C");
        byte[] res13 = jedis.get("R".getBytes());
        System.out.println(String.format("%8s", Integer.toBinaryString(res13[0] & 0xFF)).replace(' ', '0'));
        // >>> 00100101

await client.bitOp("DIFF1", "R", ["A", "B", "C"])
const res13 = await client.get("R")
console.log(res13.readUInt8(0).toString(2).padStart(8, '0'))
// >>> 00100101

> BITOP ANDOR R A B C
(integer) 1
> GET R
"X"
# ASCII "X" = hex 0x58 = 0b01011000

// Bitwise operations are not supported in NRedisStack.

CompletableFuture<Void> andorOp = async.bitopAndor("R", "A", "B", "C")
                    .thenCompose(len -> asyncBytes.get("R".getBytes())).thenAccept(res -> {
                        byte b = (res != null && res.length > 0) ? res[0] : 0;
                        String bits = String.format("%8s", Integer.toBinaryString(b & 0xFF)).replace(' ', '0');
                        System.out.println(bits); // >>> 01011000
                    }).toCompletableFuture();

Mono<Void> andorOp = reactive.bitopAndor("R", "A", "B", "C").then(reactiveBytes.get("R".getBytes()))
                    .doOnNext(res -> {
                        byte b = (res != null && res.length > 0) ? res[0] : 0;
                        String bits = String.format("%8s", Integer.toBinaryString(b & 0xFF)).replace(' ', '0');
                        System.out.println(bits); // >>> 01011000
                    }).then();

jedis.bitop(BitOP.ANDOR, "R", "A", "B", "C");
        byte[] res14 = jedis.get("R".getBytes());
        System.out.println(String.format("%8s", Integer.toBinaryString(res14[0] & 0xFF)).replace(' ', '0'));
        // >>> 01011000

await client.bitOp("ANDOR", "R", ["A", "B", "C"])
const res14 = await client.get("R")
console.log(res14.readUInt8(0).toString(2).padStart(8, '0'))
// >>> 01011000

> BITOP ONE R A B C
(integer) 1
> GET R
"\xa5"
# Hex value: 0xa5 = 0b10100101

CompletableFuture<Void> oneOp = async.bitopOne("R", "A", "B", "C")
                    .thenCompose(len -> asyncBytes.get("R".getBytes())).thenAccept(res -> {
                        byte b = (res != null && res.length > 0) ? res[0] : 0;
                        String bits = String.format("%8s", Integer.toBinaryString(b & 0xFF)).replace(' ', '0');
                        System.out.println(bits); // >>> 10100101
                    }).toCompletableFuture();

Mono<Void> oneOp = reactive.bitopOne("R", "A", "B", "C").then(reactiveBytes.get("R".getBytes())).doOnNext(res -> {
                byte b = (res != null && res.length > 0) ? res[0] : 0;
                String bits = String.format("%8s", Integer.toBinaryString(b & 0xFF)).replace(' ', '0');
                System.out.println(bits); // >>> 10100101
            }).then();

jedis.bitop(BitOP.ONE, "R", "A", "B", "C");
        byte[] res15 = jedis.get("R".getBytes());
        System.out.println(String.format("%8s", Integer.toBinaryString(res15[0] & 0xFF)).replace(' ', '0'));
        // >>> 10100101

await client.bitOp("ONE", "R", ["A", "B", "C"])
const res15 = await client.get("R")
console.log(res15.readUInt8(0).toString(2).padStart(8, '0'))
// >>> 10100101


Compare data types

Summary: Choose the best Redis data type for your task.

Overview

Redis provides a wide range of data types to store your data.
The following are highly specialized for precise purposes:

-   [Geospatial]():
    store strings with associated coordinates for geospatial queries.
-   [Vector sets]():
    store strings with associated vector data (and optional metadata)
    for vector similarity queries.
-   [Probabilistic data types]():
    keep approximate counts and other statistics for large datasets.
-   [Time series]():
    store real-valued data points along with the time they were collected.

The remaining data types are more general-purpose:

-   [Strings]():
    store text or binary data.
-   [Hashes]():
    store key-value pairs within a single key.
-   [JSON]():
    store structured, hierarchical arrays and key-value objects that match
    the popular [JSON](https://www.json.org/json-en.html) text file format.
-   [Lists]():
    store a simple sequence of strings.
-   [Sets]():
    store a collection of unique strings.
-   [Sorted sets]():
    store a collection of unique strings with associated scores.
-   [Streams]():
    store a sequence of entries, each with a set of field-value pairs.

The general-purpose data types have some overlap among their features
and indeed, you could probably emulate any of them using just strings
and a little creativity. However, each data type provides different
tradeoffs in terms of performance, memory usage, and functionality.
This guide helps you choose the best data type for your task.

Data type features

The sections below summarize the features of each data type.

Strings

-   **Structure**: unstructured text/binary data or simple counters,
    bit sets, or integer collections.
-   **Operations**: get, set, append, increment, decrement, bitwise operations.
-   **Suitable for**: Unstructured documents, counters, flags, bitmaps.

Strings are mainly useful for storing text or binary data chunks
whose internal structure will be managed by your own application.
However, they also support operations to access ranges of bits
in the string to use as bit sets, integers, or floating-point numbers.

Hashes

-   **Structure**: collection of key-value pairs.
-   **Operations**: get, set, delete, increment, decrement, query.
-   **Suitable for**: Simple objects with a small number of fields.

Hashes are mainly useful for storing objects with a small number of fields
that are not nested or intricately structured. However, there is
no real limit to the number of fields you can store in a hash, so you
can use hashes in many different ways inside your application.
The field values are strings, but hashes provide commands to treat
them as integers or floating-point numbers and perform simple arithmetic
operations on them. You can set expirations on individual hash fields
and you can also index and query hash documents using
[Redis Search]().

JSON

-   **Structure**: hierarchical arrays and key-value objects that match
    the popular [JSON](https://www.json.org/json-en.html) text file format.
-   **Operations**: get, set, update, delete, query.
-   **Suitable for**: Complex, nested objects with many fields.

JSON provides rich data modeling capabilities with nested fields and arrays.
You can use a simple path syntax to access any subset of the data within
a JSON document. JSON also has more powerful and flexible
[Redis Search]()
features compared to hashes.

Lists

-   **Structure**: simple sequence of strings.
-   **Operations**: push, pop, get, set, trim.
-   **Suitable for**: Queues, stacks, logs, and other linear data structures.

Lists store sequences of string values. They are optimized for
adding and removing small numbers of elements at the head or tail,
and so they are very efficient for implementing queues, stacks,
and deques.

Sets

-   **Structure**: collection of unique strings.
-   **Operations**: add, remove, test membership, intersect, union, difference.
-   **Suitable for**: Unique items with no associated data.

Sets store collections of unique strings. They provide efficient
operations for testing membership, adding and removing elements.
They also support set operations like intersection, union, and difference.

Sorted sets

-   **Structure**: collection of unique strings with associated scores.
-   **Operations**: add, remove, test membership, range by score or rank.
-   **Suitable for**: Unique items with a score, or ordered collections.

Sorted sets store collections of unique strings with associated scores.
They are optimized for efficient range queries based on the score,
and so they are useful for implementing priority queues and other ordered
collections.

Streams

-   **Structure**: sequence of entries, each with a set of field-value pairs.
-   **Operations**: add, read, trim.
-   **Suitable for**: Log data, time series, and other append-only structures.

Streams store sequences of entries, each with a set of field-value pairs.
They are optimized for appending new entries and reading them in order,
and so they are useful for implementing log data, time series, and other
append-only data structures. They also have built-in support for consumer groups
to manage multiple readers and ensure at-least-once delivery.

Choose a data type

The sections below explore the pros and cons of each data type for
particular tasks. Note that you should regard
the suggestions as "rules-of-thumb" rather than strict prescriptions, since
there are potentially many subtle reasons to prefer one data type over another.

Documents

You would normally store document data using the string, hash, or JSON
types. JSON generally has the highest requirements for memory and processing,
followed by hashes, and then strings. Use the decision tree below as a guide to
choosing the best data type for your task.

[code example]

Collections

You would normally store collection data using the set or sorted set
types and for very simple collections, you can even use strings. They all allow
basic membership tests, but have different additional features and tradeoffs.
Sorted sets have the highest memory overhead and processing requirements, followed
by sets, and then strings.
Use the decision tree below as a guide to choosing the best data type for your task.
Note that if you need to store extra information for the keys in a set
or sorted set, you can do so with an auxiliary hash or JSON object that has field
names matching the keys in the collection.

[code example]

Sequences

You would normally store sequences of string or binary data using sorted sets,
lists, or streams. They each have advantages and disadvantages for particular purposes.
Use the decision tree below as a guide to choosing the best data type for your task.

[code example]

id: documents-tree
scope: documents
rootQuestion: root
questions:
    root:
        text: |
            Do you need nested data structures (fields and arrays) or geospatial
            index/query with Redis Search?
        whyAsk: |
            JSON is the only document type that supports deeply nested structures and integrates with Redis Search for those structures
        answers:
            yes:
                value: "Yes"
                outcome:
                    label: "Use JSON"
                    id: jsonOutcome
            no:
                value: "No"
                nextQuestion: hashQuestion
    hashQuestion:
        text: |
            Do you need to index/query using Redis Search but can live
            without nested data structures and geospatial indexing?
        whyAsk: |
            Hashes support indexing and querying with lower memory overhead and faster field access than JSON
        answers:
            yes:
                value: "Yes"
                outcome:
                    label: "Use hashes"
                    id: hashOutcome
            no:
                value: "No"
                nextQuestion: expirationQuestion
    expirationQuestion:
        text: |
            Do you need to set expiration times on individual pieces of data
            within the document?
        whyAsk: "Only hashes support efficient field-level access and expiration"
        answers:
            yes:
                value: "Yes"
                outcome:
                    label: "Use hashes"
                    id: hashOutcome
            no:
                value: "No"
                nextQuestion: fieldAccessQuestion
    fieldAccessQuestion:
        text: |
            Do you need frequent access to individual data fields within the
            document, but the fields are simple integers or bits that you can easily
            refer to by an integer index?
        whyAsk: |
            Strings and hashes support efficient field access, but strings are more compact and efficient if you only need bit fields with integer indices
        answers:
            yes:
                value: "Yes"
                outcome:
                    label: "Use strings"
                    id: stringOutcome
            no:
                value: "No"
                nextQuestion: stringQuestion
    stringQuestion:
        text: |
            Do you need frequent access to individual data fields within the
            document that have string or binary data values?
        whyAsk: |
            Hashes support general field access, but strings are more compact and efficient if you don't need it
        answers:
            yes:
                value: "Yes"
                outcome:
                    label: "Use hashes"
                    id: hashOutcome
            no:
                value: "No"
                outcome:
                    label: "Use strings"
                    id: stringOutcome

id: collections-tree
scope: collections
rootQuestion: root
questions:
    root:
        text: |
            Do you need to store and retrieve the keys in an arbitrary order or in
            lexicographical order?
        whyAsk: |
            Sorted sets are the only collection type that supports ordered iteration,
            which is essential if you need to access elements in a specific order
        answers:
            yes:
                value: "Yes"
                outcome:
                    label: "Use sorted sets"
                    id: sortedSetsOutcome
            no:
                value: "No"
                nextQuestion: extraInfo
    extraInfo:
        text: |
            Do you need to store extra information for each key AND you don't need
            set operations (union, intersection, difference)?
        whyAsk: |
            Hashes allow you to associate data with each key, but they don't support
            set operations. If you need both extra data and set operations, sets are not suitable
        answers:
            yes:
                value: "Yes"
                outcome:
                    label: "Use hashes"
                    id: hashesOutcome
            no:
                value: "No"
                nextQuestion: integerIndices
    integerIndices:
        text: |
            Are the keys always simple integer indices in a known range?
        whyAsk: |
            String bitmaps provide minimum memory overhead and efficient random access
            for integer indices, with bitwise operations equivalent to set operations
        answers:
            yes:
                value: "Yes"
                outcome:
                    label: "Use strings (bitmaps)"
                    id: stringsOutcome
            no:
                value: "No"
                outcome:
                    label: "Use sets"
                    id: setsOutcome

id: sequences-tree
scope: sequences
rootQuestion: root
questions:
    root:
        text: |
            Do you need to maintain an arbitrary priority order, lexicographical order,
            frequently access elements by index, or perform set operations?
        whyAsk: |
            Sorted sets are the only sequence type that supports both ordering and set operations.
            While lists also support indexing, it is O(n) for lists but O(log n) for sorted sets,
            so sorted sets are more efficient if you need frequent index access
        answers:
            yes:
                value: "Yes"
                outcome:
                    label: "Use sorted sets"
                    id: sortedSetsOutcome
            no:
                value: "No"
                nextQuestion: timestampOrder
    timestampOrder:
        text: |
            Do you need to store and retrieve elements primarily in timestamp order
            or manage multiple consumers reading from the sequence?
        whyAsk: |
            Streams are the only sequence type that supports timestamp-based ordering
            and consumer groups for managing multiple readers with at-least-once delivery
        answers:
            yes:
                value: "Yes"
                outcome:
                    label: "Use streams"
                    id: streamsOutcome
            no:
                value: "No"
                outcome:
                    label: "Use lists"
                    id: listsOutcome


Redis geospatial

Summary: Introduction to the Redis Geospatial data type

Geospatial command summary

**10 commands in this group:**

[View all geo commands](https://redis.io/commands/?group=geo)

| Command | Summary | Complexity | Since |
|---------|---------|------------|-------|
| [GEOADD](https://redis.io/commands/geoadd/) | Adds one or more members to a geospatial index. The key is created if it doesn't exist. | O(log(N)) for each item added, where N is the n... | 3.2.0 |
| [GEODIST](https://redis.io/commands/geodist/) | Returns the distance between two members of a geospatial index. | O(1) | 3.2.0 |
| [GEOHASH](https://redis.io/commands/geohash/) | Returns members from a geospatial index as geohash strings. | O(1) for each member requested. | 3.2.0 |
| [GEOPOS](https://redis.io/commands/geopos/) | Returns the longitude and latitude of members from a geospatial index. | O(1) for each member requested. | 3.2.0 |
| [GEORADIUS](https://redis.io/commands/georadius/) | Queries a geospatial index for members within a distance from a coordinate, optionally stores the result. | O(N+log(M)) where N is the number of elements i... | 3.2.0 |
| [GEORADIUS_RO](https://redis.io/commands/georadius_ro/) | Returns members from a geospatial index that are within a distance from a coordinate. | O(N+log(M)) where N is the number of elements i... | 3.2.10 |
| [GEORADIUSBYMEMBER](https://redis.io/commands/georadiusbymember/) | Queries a geospatial index for members within a distance from a member, optionally stores the result. | O(N+log(M)) where N is the number of elements i... | 3.2.0 |
| [GEORADIUSBYMEMBER_RO](https://redis.io/commands/georadiusbymember_ro/) | Returns members from a geospatial index that are within a distance from a member. | O(N+log(M)) where N is the number of elements i... | 3.2.10 |
| [GEOSEARCH](https://redis.io/commands/geosearch/) | Queries a geospatial index for members inside an area of a box or a circle. | O(N+log(M)) where N is the number of elements i... | 6.2.0 |
| [GEOSEARCHSTORE](https://redis.io/commands/geosearchstore/) | Queries a geospatial index for members inside an area of a box or a circle, optionally stores the result. | O(N+log(M)) where N is the number of elements i... | 6.2.0 |

Redis geospatial indexes let you store coordinates and search for them.
This data structure is useful for finding nearby points within a given radius or bounding box.

Take care not to confuse the Geospatial data type with the
[Geospatial]()
features in [Redis Search]().
Although there are some similarities between these two features, the data type is intended
for simpler use cases and doesn't have the range of format options and queries
available in Redis Search.

Examples

Suppose you're building a mobile app that lets you find all of the bike rental stations closest to your current location.

Add several locations to a geospatial index:
#### Code Examples

Foundational: Add geographic coordinates to a geospatial index using GEOADD to store location data

**Commands:** GEOADD

**Complexity:**
- GEOADD: O(log(N)

**Redis CLI:**

[code example]

**Available in:** Redis CLI, C# (Synchronous), Go, Java (Synchronous - Jedis), JavaScript (Node.js), Python

**C# (Synchronous):**

[code example]

**Go:**

[code example]

**Java (Synchronous - Jedis):**

[code example]

**JavaScript (Node.js):**

[code example]

**Python:**

[code example]

Find all locations within a 5 kilometer radius of a given location, and return the distance to each location:
#### Code Examples

Proximity search: Use GEOSEARCH to find locations within a radius or bounding box when you need to discover nearby points

**Difficulty:** Intermediate

**Builds upon:** geoadd

**Commands:** GEOSEARCH

**Complexity:**
- GEOSEARCH: O(N+log(M)

**Redis CLI:**

[code example]

**Available in:** Redis CLI, C# (Synchronous), Go, Java (Synchronous - Jedis), JavaScript (Node.js), Python

**C# (Synchronous):**

[code example]

**Go:**

[code example]

**Java (Synchronous - Jedis):**

[code example]

**JavaScript (Node.js):**

[code example]

**Python:**

[code example]

Learn more

* [Redis Geospatial Explained](https://www.youtube.com/watch?v=qftiVQraxmI) introduces geospatial indexes by showing you how to build a map of local park attractions.
* [Redis University's RU101](https://university.redis.com/courses/ru101/) covers Redis geospatial indexes in detail.

> GEOADD bikes:rentable -122.27652 37.805186 station:1
(integer) 1
> GEOADD bikes:rentable -122.2674626 37.8062344 station:2
(integer) 1
> GEOADD bikes:rentable -122.2469854 37.8104049 station:3
(integer) 1

bool res1 = db.GeoAdd("bikes:rentable", -122.27652, 37.805186, "station:1");
        Console.WriteLine(res1);    // >>> True

        bool res2 = db.GeoAdd("bikes:rentable", -122.2674626, 37.8062344, "station:2");
        Console.WriteLine(res2);    // >>> True

        bool res3 = db.GeoAdd("bikes:rentable", -122.2469854, 37.8104049, "station:3");
        Console.WriteLine(res3);    // >>> True

res1, err := rdb.GeoAdd(ctx, "bikes:rentable",
		&redis.GeoLocation{
			Longitude: -122.27652,
			Latitude:  37.805186,
			Name:      "station:1",
		}).Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(res1) // >>> 1

	res2, err := rdb.GeoAdd(ctx, "bikes:rentable",
		&redis.GeoLocation{
			Longitude: -122.2674626,
			Latitude:  37.8062344,
			Name:      "station:2",
		}).Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(res2) // >>> 1

	res3, err := rdb.GeoAdd(ctx, "bikes:rentable",
		&redis.GeoLocation{
			Longitude: -122.2469854,
			Latitude:  37.8104049,
			Name:      "station:3",
		}).Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(res3) // >>> 1

long res1 = jedis.geoadd("bikes:rentable", -122.27652, 37.805186, "station:1");
      System.out.println(res1); // 1

      long res2 = jedis.geoadd("bikes:rentable", -122.2674626, 37.8062344, "station:2");
      System.out.println(res2); // 1

      long res3 = jedis.geoadd("bikes:rentable", -122.2469854, 37.8104049, "station:3");
      System.out.println(res2); // 1

const res1 = await client.geoAdd('bikes:rentable', {
  longitude: -122.27652,
  latitude: 37.805186,
  member: 'station:1'
});
console.log(res1)  // 1

const res2 = await client.geoAdd('bikes:rentable', {
  longitude: -122.2674626,
  latitude: 37.8062344,
  member: 'station:2'
});
console.log(res2)  // 1

const res3 = await client.geoAdd('bikes:rentable', {
  longitude: -122.2469854,
  latitude: 37.8104049,
  member: 'station:3'
})
console.log(res3)  // 1

GeoRadiusResult[] res4 = db.GeoSearch("bikes:rentable",
            -122.27652,
            37.805186,
            new GeoSearchCircle(5, GeoUnit.Kilometers)
        );

        foreach (GeoRadiusResult member in res4)
        {
            Console.WriteLine($"Member: '{member.Member}', distance: {member.Distance}, position: {member.Position}");
        }
        // >>> Member: 'station:1', distance: 0.0001, position: -122.27652043104172 37.80518485897756
        // >>> Member: 'station:2', distance: 0.8047, position: -122.26745992898941 37.80623423353753
        // >>> Member: 'station:3', distance: 2.6596, position: -122.24698394536972 37.81040384984464

res4, err := rdb.GeoSearch(ctx, "bikes:rentable",
		&redis.GeoSearchQuery{
			Longitude:  -122.27652,
			Latitude:   37.805186,
			Radius:     5,
			RadiusUnit: "km",
		},
	).Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(res4) // >>> [station:1 station:2 station:3]

List<GeoRadiusResponse> res4 = jedis.geosearch(
          "bikes:rentable",
          new GeoCoordinate(-122.27652, 37.805186),
          5,
          GeoUnit.KM
      );
      List<String> members = res4.stream() //
          .map(GeoRadiusResponse::getMemberByString) //
          .collect(Collectors.toList());
      System.out.println(members); // [station:1, station:2, station:3]

const res4 = await client.geoSearch(
  'bikes:rentable', {
    longitude: -122.27652,
    latitude: 37.805186,
  },
  { radius: 5,
    unit: 'km'
  }
);
console.log(res4)  // ['station:1', 'station:2', 'station:3']

res4 = r.geosearch(
    "bikes:rentable",
    longitude=-122.27652,
    latitude=37.805186,
    radius=5,
    unit="km",
)
print(res4)  # >>> ['station:1', 'station:2', 'station:3']


Redis hashes

Summary: Introduction to Redis hashes

Hash command summary

**28 commands in this group:**

[View all hash commands](https://redis.io/commands/?group=hash)

| Command | Summary | Complexity | Since |
|---------|---------|------------|-------|
| [HDEL](https://redis.io/commands/hdel/) | Deletes one or more fields and their values from a hash. Deletes the hash if no fields remain. | O(N) where N is the number of fields to be remo... | 2.0.0 |
| [HEXISTS](https://redis.io/commands/hexists/) | Determines whether a field exists in a hash. | O(1) | 2.0.0 |
| [HEXPIRE](https://redis.io/commands/hexpire/) | Set expiry for hash field using relative time to expire (seconds) | O(N) where N is the number of specified fields | 7.4.0 |
| [HEXPIREAT](https://redis.io/commands/hexpireat/) | Set expiry for hash field using an absolute Unix timestamp (seconds) | O(N) where N is the number of specified fields | 7.4.0 |
| [HEXPIRETIME](https://redis.io/commands/hexpiretime/) | Returns the expiration time of a hash field as a Unix timestamp, in seconds. | O(N) where N is the number of specified fields | 7.4.0 |
| [HGET](https://redis.io/commands/hget/) | Returns the value of a field in a hash. | O(1) | 2.0.0 |
| [HGETALL](https://redis.io/commands/hgetall/) | Returns all fields and values in a hash. | O(N) where N is the size of the hash. | 2.0.0 |
| [HGETDEL](https://redis.io/commands/hgetdel/) | Returns the value of a field and deletes it from the hash. | O(N) where N is the number of specified fields | 8.0.0 |
| [HGETEX](https://redis.io/commands/hgetex/) | Get the value of one or more fields of a given hash key, and optionally set their expiration. | O(N) where N is the number of specified fields | 8.0.0 |
| [HINCRBY](https://redis.io/commands/hincrby/) | Increments the integer value of a field in a hash by a number. Uses 0 as initial value if the field doesn't exist. | O(1) | 2.0.0 |
| [HINCRBYFLOAT](https://redis.io/commands/hincrbyfloat/) | Increments the floating point value of a field by a number. Uses 0 as initial value if the field doesn't exist. | O(1) | 2.6.0 |
| [HKEYS](https://redis.io/commands/hkeys/) | Returns all fields in a hash. | O(N) where N is the size of the hash. | 2.0.0 |
| [HLEN](https://redis.io/commands/hlen/) | Returns the number of fields in a hash. | O(1) | 2.0.0 |
| [HMGET](https://redis.io/commands/hmget/) | Returns the values of all fields in a hash. | O(N) where N is the number of fields being requ... | 2.0.0 |
| [HMSET](https://redis.io/commands/hmset/) | Sets the values of multiple fields. | O(N) where N is the number of fields being set. | 2.0.0 |
| [HPERSIST](https://redis.io/commands/hpersist/) | Removes the expiration time for each specified field | O(N) where N is the number of specified fields | 7.4.0 |
| [HPEXPIRE](https://redis.io/commands/hpexpire/) | Set expiry for hash field using relative time to expire (milliseconds) | O(N) where N is the number of specified fields | 7.4.0 |
| [HPEXPIREAT](https://redis.io/commands/hpexpireat/) | Set expiry for hash field using an absolute Unix timestamp (milliseconds) | O(N) where N is the number of specified fields | 7.4.0 |
| [HPEXPIRETIME](https://redis.io/commands/hpexpiretime/) | Returns the expiration time of a hash field as a Unix timestamp, in msec. | O(N) where N is the number of specified fields | 7.4.0 |
| [HPTTL](https://redis.io/commands/hpttl/) | Returns the TTL in milliseconds of a hash field. | O(N) where N is the number of specified fields | 7.4.0 |
| [HRANDFIELD](https://redis.io/commands/hrandfield/) | Returns one or more random fields from a hash. | O(N) where N is the number of fields returned | 6.2.0 |
| [HSCAN](https://redis.io/commands/hscan/) | Iterates over fields and values of a hash. | O(1) for every call. O(N) for a complete iterat... | 2.8.0 |
| [HSET](https://redis.io/commands/hset/) | Creates or modifies the value of a field in a hash. | O(1) for each field/value pair added, so O(N) t... | 2.0.0 |
| [HSETEX](https://redis.io/commands/hsetex/) | Set the value of one or more fields of a given hash key, and optionally set their expiration. | O(N) where N is the number of fields being set. | 8.0.0 |
| [HSETNX](https://redis.io/commands/hsetnx/) | Sets the value of a field in a hash only when the field doesn't exist. | O(1) | 2.0.0 |
| [HSTRLEN](https://redis.io/commands/hstrlen/) | Returns the length of the value of a field. | O(1) | 3.2.0 |
| [HTTL](https://redis.io/commands/httl/) | Returns the TTL in seconds of a hash field. | O(N) where N is the number of specified fields | 7.4.0 |
| [HVALS](https://redis.io/commands/hvals/) | Returns all values in a hash. | O(N) where N is the size of the hash. | 2.0.0 |

Redis hashes are record types structured as collections of field-value pairs.
You can use hashes to represent basic objects and to store groupings of counters, among other things.

#### Code Examples

Foundational: Set and retrieve hash fields using HSET and HGET (overwrites existing field values)

**Commands:** HSET, HGET, HGETALL

**Complexity:**
- HSET: O(1)
- HGET: O(1)
- HGETALL: O(N)

**Redis CLI:**

[code example]

**Available in:** Redis CLI, C# (Synchronous), Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python, Rust (Asynchronous), Rust (Synchronous)

**C# (Synchronous):**

[code example]

**Go:**

[code example]

**Java (Asynchronous - Lettuce):**

[code example]

**Java (Reactive - Lettuce):**

[code example]

**Java (Synchronous - Jedis):**

[code example]

**JavaScript (Node.js):**

[code example]

**PHP:**

[code example]

**Python:**

[code example]

**Rust (Asynchronous):**

[code example]

**Rust (Synchronous):**

[code example]

While hashes are handy to represent *objects*, actually the number of fields you can
put inside a hash has no practical limits (other than available memory), so you can use
hashes in many different ways inside your application.

The command [`HSET`]() sets multiple fields of the hash, while [`HGET`]() retrieves
a single field. [`HMGET`]() is similar to [`HGET`]() but returns an array of values:

#### Code Examples

Retrieve multiple field values from a hash using HMGET when you need to reduce round trips to the server

**Builds upon:** set_get_all

**Commands:** HMGET

**Complexity:**
- HMGET: O(N)

**Redis CLI:**

[code example]

**Available in:** Redis CLI, C# (Synchronous), Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python, Rust (Asynchronous), Rust (Synchronous)

**C# (Synchronous):**

[code example]

**Go:**

[code example]

**Java (Asynchronous - Lettuce):**

[code example]

**Java (Reactive - Lettuce):**

[code example]

**Java (Synchronous - Jedis):**

[code example]

**JavaScript (Node.js):**

[code example]

**PHP:**

[code example]

**Python:**

[code example]

**Rust (Asynchronous):**

[code example]

**Rust (Synchronous):**

[code example]

There are commands that are able to perform operations on individual fields
as well, like [`HINCRBY`]():

#### Code Examples

Increment hash field values for counters using HINCRBY (creates field if missing, initializes to 0)

**Builds upon:** set_get_all

**Commands:** HINCRBY

**Complexity:**
- HINCRBY: O(1)

**Redis CLI:**

[code example]

**Available in:** Redis CLI, C# (Synchronous), Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python, Rust (Asynchronous), Rust (Synchronous)

**C# (Synchronous):**

[code example]

**Go:**

[code example]

**Java (Asynchronous - Lettuce):**

[code example]

**Java (Reactive - Lettuce):**

[code example]

**Java (Synchronous - Jedis):**

[code example]

**JavaScript (Node.js):**

[code example]

**PHP:**

[code example]

**Python:**

[code example]

**Rust (Asynchronous):**

[code example]

**Rust (Synchronous):**

[code example]

You can find the [full list of hash commands in the documentation]().

It is worth noting that small hashes (i.e., a few elements with small values) are
encoded in special way in memory that make them very memory efficient.

Examples

* Store counters for the number of times bike:1 has been ridden, has crashed, or has changed owners:
#### Code Examples

Practical pattern: Combine HINCRBY and HMGET to track multiple counters when you need atomic updates across multiple fields

**Difficulty:** Intermediate

**Builds upon:** hincrby, hmget

**Commands:** HINCRBY, HGET, HMGET

**Complexity:**
- HINCRBY: O(1)
- HGET: O(1)
- HMGET: O(N)

**Redis CLI:**

[code example]

**Available in:** Redis CLI, C# (Synchronous), Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python, Rust (Asynchronous), Rust (Synchronous)

**C# (Synchronous):**

[code example]

**Go:**

[code example]

**Java (Asynchronous - Lettuce):**

[code example]

**Java (Reactive - Lettuce):**

[code example]

**Java (Synchronous - Jedis):**

[code example]

**JavaScript (Node.js):**

[code example]

**PHP:**

[code example]

**Python:**

[code example]

**Rust (Asynchronous):**

[code example]

**Rust (Synchronous):**

[code example]

Field expiration

Redis 7.4 introduced the ability to specify an expiration time or a time-to-live (TTL) value for individual hash fields.
This capability is comparable to [key expiration]() and includes a number of similar commands.

Use the following commands to set either an exact expiration time or a TTL value for specific fields:

* [`HEXPIRE`](): set the remaining TTL in seconds.
* [`HPEXPIRE`](): set the remaining TTL in milliseconds.
* [`HEXPIREAT`](): set the expiration time to a timestamp[^1] specified in seconds.
* [`HPEXPIREAT`](): set the expiration time to a timestamp specified in milliseconds.

[^1]: all timestamps are specified in seconds or milliseconds since the [Unix epoch](https://en.wikipedia.org/wiki/Unix_time).

Use the following commands to retrieve either the exact time when or the remaining TTL until specific fields will expire:

* [`HEXPIRETIME`](): get the expiration time as a timestamp in seconds.
* [`HPEXPIRETIME`](): get the expiration time as a timestamp in milliseconds.
* [`HTTL`](): get the remaining TTL in seconds.
* [`HPTTL`](): get the remaining TTL in milliseconds.

Use the following command to remove the expiration of specific fields:

* [`HPERSIST`](): remove the expiration.

Redis 8.0 introduced the following commands:

* [`HGETEX`](): Get the value of one or more fields of a given hash key and optionally set their expiration time or time-to-live (TTL).
* [`HSETEX`](): Set the value of one or more fields of a given hash key and optionally set their expiration time or time-to-live (TTL).

Common field expiration use cases

1. **Event Tracking**: Use a hash key to store events from the last hour. Set each event's TTL to one hour. Use `HLEN` to count events from the past hour.

1. **Fraud Detection**: Create a hash with hourly counters for events. Set each field's TTL to 48 hours. Query the hash to get the number of events per hour for the last 48 hours.

1. **Customer Session Management**: Store customer data in hash keys. Create a new hash key for each session and add a session field to the customer’s hash key. Expire both the session key and the session field in the customer’s hash key automatically when the session expires.

1. **Active Session Tracking**: Store all active sessions in a hash key. Set each session's TTL to expire automatically after inactivity. Use `HLEN` to count active sessions.

Field expiration examples

Support for hash field expiration in the official client libraries is not yet available, but you can test hash field expiration now with beta versions of the [Python (redis-py)](https://github.com/redis/redis-py) and [Java (Jedis)](https://github.com/redis/jedis) client libraries.

Following are some Python examples that demonstrate how to use field expiration.

Consider a hash data set for storing sensor data that has the following structure:

[code example]

In the examples below, you will likely need to refresh the `sensor:sensor1` key after its fields expire.

Set and retrieve the TTL for multiple fields in a hash:

[code example]

Set and retrieve a hash field's TTL in milliseconds:

[code example]

Set and retrieve a hash field’s expiration timestamp:

[code example]

Performance

Most Redis hash commands are O(1).

A few commands, such as [`HKEYS`](), [`HVALS`](), [`HGETALL`](), and most of the expiration-related commands, are O(n), where _n_ is the number of field-value pairs.

Limits

Every hash can store up to 4,294,967,295 (2^32 - 1) field-value pairs.
In practice, your hashes are limited only by the overall memory on the VMs hosting your Redis deployment.

Learn more

* [Redis Hashes Explained](https://www.youtube.com/watch?v=-KdITaRkQ-U) is a short, comprehensive video explainer covering Redis hashes.
* [Redis University's RU101](https://university.redis.com/courses/ru101/) covers Redis hashes in detail.

> HSET bike:1 model Deimos brand Ergonom type 'Enduro bikes' price 4972
(integer) 4
> HGET bike:1 model
"Deimos"
> HGET bike:1 price
"4972"
> HGETALL bike:1
1) "model"
2) "Deimos"
3) "brand"
4) "Ergonom"
5) "type"
6) "Enduro bikes"
7) "price"
8) "4972"

db.HashSet("bike:1", [
            new("model", "Deimos"),
            new("brand", "Ergonom"),
            new("type", "Enduro bikes"),
            new("price", 4972)
        ]);

        Console.WriteLine("Hash Created");
        // Hash Created

        var model = db.HashGet("bike:1", "model");
        Console.WriteLine($"Model: {model}");
        // Model: Deimos

        var price = db.HashGet("bike:1", "price");
        Console.WriteLine($"Price: {price}");
        // Price: 4972

        var bike = db.HashGetAll("bike:1");
        Console.WriteLine("bike:1");
        Console.WriteLine(string.Join("\n", bike.Select(b => $"{b.Name}: {b.Value}")));
        // Bike:1:
        // model: Deimos
        // brand: Ergonom
        // type: Enduro bikes
        // price: 4972

hashFields := []string{
		"model", "Deimos",
		"brand", "Ergonom",
		"type", "Enduro bikes",
		"price", "4972",
	}

	res1, err := rdb.HSet(ctx, "bike:1", hashFields).Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(res1) // >>> 4

	res2, err := rdb.HGet(ctx, "bike:1", "model").Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(res2) // >>> Deimos

	res3, err := rdb.HGet(ctx, "bike:1", "price").Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(res3) // >>> 4972

	cmdReturn := rdb.HGetAll(ctx, "bike:1")
	res4, err := cmdReturn.Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(res4)
	// >>> map[brand:Ergonom model:Deimos price:4972 type:Enduro bikes]

	type BikeInfo struct {
		Model string `redis:"model"`
		Brand string `redis:"brand"`
		Type  string `redis:"type"`
		Price int    `redis:"price"`
	}

	var res4a BikeInfo

	if err := cmdReturn.Scan(&res4a); err != nil {
		panic(err)
	}

	fmt.Printf("Model: %v, Brand: %v, Type: %v, Price: $%v\n",
		res4a.Model, res4a.Brand, res4a.Type, res4a.Price)
	// >>> Model: Deimos, Brand: Ergonom, Type: Enduro bikes, Price: $4972

Map<String, String> bike1 = new HashMap<>();
            bike1.put("model", "Deimos");
            bike1.put("brand", "Ergonom");
            bike1.put("type", "Enduro bikes");
            bike1.put("price", "4972");

            CompletableFuture<Void> setGetAll = asyncCommands.hset("bike:1", bike1).thenCompose(res1 -> {
                System.out.println(res1); // >>> 4
                return asyncCommands.hget("bike:1", "model");
            }).thenCompose(res2 -> {
                System.out.println(res2); // >>> Deimos
                return asyncCommands.hget("bike:1", "price");
            }).thenCompose(res3 -> {
                System.out.println(res3); // >>> 4972
                return asyncCommands.hgetall("bike:1");
            })
                    .thenAccept(System.out::println)
                    // >>> {type=Enduro bikes, brand=Ergonom, price=4972, model=Deimos}
                    .toCompletableFuture();

Map<String, String> bike1 = new HashMap<>();
            bike1.put("model", "Deimos");
            bike1.put("brand", "Ergonom");
            bike1.put("type", "Enduro bikes");
            bike1.put("price", "4972");

            Mono<Long> setGetAll = reactiveCommands.hset("bike:1", bike1).doOnNext(result -> {
                System.out.println(result); // >>> 4
            });

            setGetAll.block();

            Mono<String> getModel = reactiveCommands.hget("bike:1", "model").doOnNext(result -> {
                System.out.println(result); // >>> Deimos
            });

            Mono<String> getPrice = reactiveCommands.hget("bike:1", "price").doOnNext(result -> {
                System.out.println(result); // >>> 4972
            });

            Mono<List<KeyValue<String, String>>> getAll = reactiveCommands.hgetall("bike:1").collectList().doOnNext(result -> {
                System.out.println(result);
                // >>> [KeyValue[type, Enduro bikes], KeyValue[brand, Ergonom],
                // KeyValue[price, 4972], KeyValue[model, Deimos]]
            });

Map<String, String> bike1 = new HashMap<>();
      bike1.put("model", "Deimos");
      bike1.put("brand", "Ergonom");
      bike1.put("type", "Enduro bikes");
      bike1.put("price", "4972");

      Long res1 = jedis.hset("bike:1", bike1);
      System.out.println(res1); // 4

      String res2 = jedis.hget("bike:1", "model");
      System.out.println(res2); // Deimos

      String res3 = jedis.hget("bike:1", "price");
      System.out.println(res3); // 4972

      Map<String, String> res4 = jedis.hgetAll("bike:1");
      System.out.println(res4); // {type=Enduro bikes, brand=Ergonom, price=4972, model=Deimos}

$res1 = $r->hmset('bike:1', [
            'model' => 'Deimos',
            'brand' => 'Ergonom',
            'type' => 'Enduro bikes',
            'price' => 4972,
        ]);

        echo $res1 . PHP_EOL;
        // >>> 4

        $res2 = $r->hget('bike:1', 'model');
        echo $res2 . PHP_EOL;
        // >>> Deimos

        $res3 = $r->hget('bike:1', 'price');
        echo $res3 . PHP_EOL;
        // >>> 4972

        $res4 = $r->hgetall('bike:1');
        echo json_encode($res3) . PHP_EOL;
        // >>> {"name":"Deimos","brand":"Ergonom","type":"Enduro bikes","price":"4972"}

res1 = r.hset(
    "bike:1",
    mapping={
        "model": "Deimos",
        "brand": "Ergonom",
        "type": "Enduro bikes",
        "price": 4972,
    },
)
print(res1)
# >>> 4

res2 = r.hget("bike:1", "model")
print(res2)
# >>> 'Deimos'

res3 = r.hget("bike:1", "price")
print(res3)
# >>> '4972'

res4 = r.hgetall("bike:1")
print(res4)
# >>> {'model': 'Deimos', 'brand': 'Ergonom', 'type': 'Enduro bikes', 'price': '4972'}

let hash_fields = [
            ("model", "Deimos"),
            ("brand", "Ergonom"),
            ("type", "Enduro bikes"),
            ("price", "4972"),
        ];

        if let Ok(res) = r.hset_multiple("bike:1", &hash_fields).await {
            let res: String = res;
            println!("{res}");    // >>> OK
        }

        match r.hget("bike:1", "model").await {
            Ok(res) => {
                let res: String = res;
                println!("{res}");   // >>> Deimos
            },
            Err(e) => {
                println!("Error getting bike:1 model: {e}");
                return;
            }
        };

        match r.hget("bike:1", "price").await {
            Ok(res) => {
                let res: String = res;
                println!("{res}");   // >>> 4972
            },
            Err(e) => {
                println!("Error getting bike:1 price: {e}");
                return;
            }
        };

        match r.hgetall("bike:1").await {
            Ok(res) => {
                let res: Vec<(String, String)> = res;
                println!("{res:?}");
                // >>> [("model", "Deimos"), ("brand", "Ergonom"), ("type", "Enduro bikes"), ("price", "4972")]
            },
            Err(e) => {
                println!("Error getting bike:1: {e}");
                return;
            }
        };

let hash_fields = [
            ("model", "Deimos"),
            ("brand", "Ergonom"),
            ("type", "Enduro bikes"),
            ("price", "4972"),
        ];

        if let Ok(res) = r.hset_multiple("bike:1", &hash_fields) {
            let res: String = res;
            println!("{res}");    // >>> OK
        }

        match r.hget("bike:1", "model") {
            Ok(res) => {
                let res: String = res;
                println!("{res}");   // >>> Deimos
            },
            Err(e) => {
                println!("Error getting bike:1 model: {e}");
                return;
            }
        };

        match r.hget("bike:1", "price") {
            Ok(res) => {
                let res: String = res;
                println!("{res}");   // >>> 4972
            },
            Err(e) => {
                println!("Error getting bike:1 price: {e}");
                return;
            }
        };

        match r.hgetall("bike:1") {
            Ok(res) => {
                let res: Vec<(String, String)> = res;
                println!("{res:?}");
                // >>> [("model", "Deimos"), ("brand", "Ergonom"), ("type", "Enduro bikes"), ("price", "4972")]
            },
            Err(e) => {
                println!("Error getting bike:1: {e}");
                return;
            }
        };

> HMGET bike:1 model price no-such-field
1) "Deimos"
2) "4972"
3) (nil)

var values = db.HashGet("bike:1", ["model", "price"]);
        Console.WriteLine(string.Join(" ", values));
        // Deimos 4972

cmdReturn := rdb.HMGet(ctx, "bike:1", "model", "price")
	res5, err := cmdReturn.Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(res5) // >>> [Deimos 4972]

	type BikeInfo struct {
		Model string `redis:"model"`
		Brand string `redis:"-"`
		Type  string `redis:"-"`
		Price int    `redis:"price"`
	}

	var res5a BikeInfo

	if err := cmdReturn.Scan(&res5a); err != nil {
		panic(err)
	}

	fmt.Printf("Model: %v, Price: $%v\n", res5a.Model, res5a.Price)
	// >>> Model: Deimos, Price: $4972

CompletableFuture<Void> hmGet = setGetAll.thenCompose(res4 -> {
                return asyncCommands.hmget("bike:1", "model", "price");
            })
                    .thenAccept(System.out::println)
                    // [KeyValue[model, Deimos], KeyValue[price, 4972]]
                    .toCompletableFuture();

Mono<List<KeyValue<String, String>>> hmGet = reactiveCommands.hmget("bike:1", "model", "price").collectList()
                    .doOnNext(result -> {
                        System.out.println(result);
                        // >>> [KeyValue[model, Deimos], KeyValue[price, 4972]]
                    });

List<String> res5 = jedis.hmget("bike:1", "model", "price");
      System.out.println(res5); // [Deimos, 4972]

$res5 = $r->hmget('bike:1', ['model', 'price']);
        echo json_encode($res5) . PHP_EOL;
        // >>> ["Deimos","4972"]

res5 = r.hmget("bike:1", ["model", "price"])
print(res5)
# >>> ['Deimos', '4972']

match r.hmget("bike:1", &["model", "price"]).await {
            Ok(res) => {
                let res: Vec<String> = res;
                println!("{res:?}");   // >>> ["Deimos", "4972"]
            },
            Err(e) => {
                println!("Error getting bike:1: {e}");
                return;
            }
        };

match r.hmget("bike:1", &["model", "price"]) {
            Ok(res) => {
                let res: Vec<String> = res;
                println!("{res:?}");   // >>> ["Deimos", "4972"]
            },
            Err(e) => {
                println!("Error getting bike:1: {e}");
                return;
            }
        };

> HINCRBY bike:1 price 100
(integer) 5072
> HINCRBY bike:1 price -100
(integer) 4972

var newPrice = db.HashIncrement("bike:1", "price", 100);
        Console.WriteLine($"New price: {newPrice}");
        // New price: 5072

        newPrice = db.HashIncrement("bike:1", "price", -100);
        Console.WriteLine($"New price: {newPrice}");
        // New price: 4972

res6, err := rdb.HIncrBy(ctx, "bike:1", "price", 100).Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(res6) // >>> 5072

	res7, err := rdb.HIncrBy(ctx, "bike:1", "price", -100).Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(res7) // >>> 4972

CompletableFuture<Void> hIncrBy = hmGet.thenCompose(r -> {
                return asyncCommands.hincrby("bike:1", "price", 100);
            }).thenCompose(res6 -> {
                System.out.println(res6); // >>> 5072
                return asyncCommands.hincrby("bike:1", "price", -100);
            })
                    .thenAccept(System.out::println)
                    // >>> 4972
                    .toCompletableFuture();

Mono<Void> hIncrBy = reactiveCommands.hincrby("bike:1", "price", 100).doOnNext(result -> {
                System.out.println(result); // >>> 5072
            }).flatMap(v -> reactiveCommands.hincrby("bike:1", "price", -100)).doOnNext(result -> {
                System.out.println(result); // >>> 4972
            }).then();

Long res6 = jedis.hincrBy("bike:1", "price", 100);
      System.out.println(res6); // 5072
      Long res7 = jedis.hincrBy("bike:1", "price", -100);
      System.out.println(res7); // 4972

res6 = r.hincrby("bike:1", "price", 100)
print(res6)
# >>> 5072
res7 = r.hincrby("bike:1", "price", -100)
print(res7)
# >>> 4972

if let Ok(res) = r.hincr("bike:1", "price", 100).await {
            let res: i32 = res;
            println!("{res}");    // >>> 5072
        }

        if let Ok(res) = r.hincr("bike:1", "price", -100).await {
            let res: i32 = res;
            println!("{res}");    // >>> 4972
        }

if let Ok(res) = r.hincr("bike:1", "price", 100) {
            let res: i32 = res;
            println!("{res}");    // >>> 5072
        }

        if let Ok(res) = r.hincr("bike:1", "price", -100) {
            let res: i32 = res;
            println!("{res}");    // >>> 4972
        }

> HINCRBY bike:1:stats rides 1
(integer) 1
> HINCRBY bike:1:stats rides 1
(integer) 2
> HINCRBY bike:1:stats rides 1
(integer) 3
> HINCRBY bike:1:stats crashes 1
(integer) 1
> HINCRBY bike:1:stats owners 1
(integer) 1
> HGET bike:1:stats rides
"3"
> HMGET bike:1:stats owners crashes
1) "1"
2) "1"

var rides = db.HashIncrement("bike:1", "rides");
        Console.WriteLine($"Rides: {rides}");
        // Rides: 1

        rides = db.HashIncrement("bike:1", "rides");
        Console.WriteLine($"Rides: {rides}");
        // Rides: 2

        rides = db.HashIncrement("bike:1", "rides");
        Console.WriteLine($"Rides: {rides}");
        // Rides: 3

        var crashes = db.HashIncrement("bike:1", "crashes");
        Console.WriteLine($"Crashes: {crashes}");
        // Crashes: 1

        var owners = db.HashIncrement("bike:1", "owners");
        Console.WriteLine($"Owners: {owners}");
        // Owners: 1

        var stats = db.HashGet("bike:1", ["crashes", "owners"]);
        Console.WriteLine($"Bike stats: crashes={stats[0]}, owners={stats[1]}");
        // Bike stats: crashes=1, owners=1

res8, err := rdb.HIncrBy(ctx, "bike:1:stats", "rides", 1).Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(res8) // >>> 1

	res9, err := rdb.HIncrBy(ctx, "bike:1:stats", "rides", 1).Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(res9) // >>> 2

	res10, err := rdb.HIncrBy(ctx, "bike:1:stats", "rides", 1).Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(res10) // >>> 3

	res11, err := rdb.HIncrBy(ctx, "bike:1:stats", "crashes", 1).Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(res11) // >>> 1

	res12, err := rdb.HIncrBy(ctx, "bike:1:stats", "owners", 1).Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(res12) // >>> 1

	res13, err := rdb.HGet(ctx, "bike:1:stats", "rides").Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(res13) // >>> 3

	res14, err := rdb.HMGet(ctx, "bike:1:stats", "crashes", "owners").Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(res14) // >>> [1 1]

CompletableFuture<Void> incrByGetMget = asyncCommands.hincrby("bike:1:stats", "rides", 1).thenCompose(res7 -> {
                System.out.println(res7); // >>> 1
                return asyncCommands.hincrby("bike:1:stats", "rides", 1);
            }).thenCompose(res8 -> {
                System.out.println(res8); // >>> 2
                return asyncCommands.hincrby("bike:1:stats", "rides", 1);
            }).thenCompose(res9 -> {
                System.out.println(res9); // >>> 3
                return asyncCommands.hincrby("bike:1:stats", "crashes", 1);
            }).thenCompose(res10 -> {
                System.out.println(res10); // >>> 1
                return asyncCommands.hincrby("bike:1:stats", "owners", 1);
            }).thenCompose(res11 -> {
                System.out.println(res11); // >>> 1
                return asyncCommands.hget("bike:1:stats", "rides");
            }).thenCompose(res12 -> {
                System.out.println(res12); // >>> 3
                return asyncCommands.hmget("bike:1:stats", "crashes", "owners");
            })
                    .thenAccept(System.out::println)
                    // >>> [KeyValue[crashes, 1], KeyValue[owners, 1]]
                    .toCompletableFuture();

Mono<Void> incrByGetMget = reactiveCommands.hincrby("bike:1:stats", "rides", 1).doOnNext(result -> {
                System.out.println(result); // >>> 1
            }).flatMap(v -> reactiveCommands.hincrby("bike:1:stats", "rides", 1)).doOnNext(result -> {
                System.out.println(result); // >>> 2
            }).flatMap(v -> reactiveCommands.hincrby("bike:1:stats", "rides", 1)).doOnNext(result -> {
                System.out.println(result); // >>> 3
            }).flatMap(v -> reactiveCommands.hincrby("bike:1:stats", "crashes", 1)).doOnNext(result -> {
                System.out.println(result); // >>> 1
            }).flatMap(v -> reactiveCommands.hincrby("bike:1:stats", "owners", 1)).doOnNext(result -> {
                System.out.println(result); // >>> 1
            }).then();

            incrByGetMget.block();

            Mono<String> getRides = reactiveCommands.hget("bike:1:stats", "rides").doOnNext(result -> {
                System.out.println(result); // >>> 3
            });

            Mono<List<KeyValue<String, String>>> getCrashesOwners = reactiveCommands.hmget("bike:1:stats", "crashes", "owners")
                    .collectList().doOnNext(result -> {
                        System.out.println(result);
                        // >>> [KeyValue[crashes, 1], KeyValue[owners, 1]]
                    });

Long res8 = jedis.hincrBy("bike:1:stats", "rides", 1);
      System.out.println(res8); // 1
      Long res9 = jedis.hincrBy("bike:1:stats", "rides", 1);
      System.out.println(res9); // 2
      Long res10 = jedis.hincrBy("bike:1:stats", "rides", 1);
      System.out.println(res10); // 3
      Long res11 = jedis.hincrBy("bike:1:stats", "crashes", 1);
      System.out.println(res11); // 1
      Long res12 = jedis.hincrBy("bike:1:stats", "owners", 1);
      System.out.println(res12); // 1
      String res13 = jedis.hget("bike:1:stats", "rides");
      System.out.println(res13); // 3
      List<String> res14 = jedis.hmget("bike:1:stats", "crashes", "owners");
      System.out.println(res14); // [1, 1]

import assert from 'assert';
import { createClient } from 'redis';

const client = createClient();
await client.connect();
const res1 = await client.hSet(
  'bike:1',
  {
    'model': 'Deimos',
    'brand': 'Ergonom',
    'type': 'Enduro bikes',
    'price': 4972,
  }
)
console.log(res1) // 4

const res2 = await client.hGet('bike:1', 'model')
console.log(res2)  // 'Deimos'

const res3 = await client.hGet('bike:1', 'price')
console.log(res3)  // '4972'

const res4 = await client.hGetAll('bike:1')
console.log(res4)
/*
{
  brand: 'Ergonom',
  model: 'Deimos',
  price: '4972',
  type: 'Enduro bikes'
}
*/

const res5 = await client.hmGet('bike:1', ['model', 'price'])
console.log(res5)  // ['Deimos', '4972']

const res6 = await client.hIncrBy('bike:1', 'price', 100)
console.log(res6)  // 5072
const res7 = await client.hIncrBy('bike:1', 'price', -100)
console.log(res7)  // 4972

const res11 = await client.hIncrBy('bike:1:stats', 'rides', 1)
console.log(res11)  // 1
const res12 = await client.hIncrBy('bike:1:stats', 'rides', 1)
console.log(res12)  // 2
const res13 = await client.hIncrBy('bike:1:stats', 'rides', 1)
console.log(res13)  // 3
const res14 = await client.hIncrBy('bike:1:stats', 'crashes', 1)
console.log(res14)  // 1
const res15 = await client.hIncrBy('bike:1:stats', 'owners', 1)
console.log(res15)  // 1
const res16 = await client.hGet('bike:1:stats', 'rides')
console.log(res16)  // 3
const res17 = await client.hmGet('bike:1:stats', ['crashes', 'owners'])
console.log(res17)  // ['1', '1']

$res8 = $r->hincrby('bike:1:stats', 'rides', 1);
        echo $res8 . PHP_EOL;
        // >>> 1

        $res9 = $r->hincrby('bike:1:stats', 'rides', 1);
        echo $res9 . PHP_EOL;
        // >>> 2

        $res10 = $r->hincrby('bike:1:stats', 'rides', 1);
        echo $res10 . PHP_EOL;
        // >>> 3

        $res11 = $r->hincrby('bike:1:stats', 'crashes', 1);
        echo $res11 . PHP_EOL;
        // >>> 1

        $res12 = $r->hincrby('bike:1:stats', 'owners', 1);
        echo $res12 . PHP_EOL;
        // >>> 1

        $res13 = $r->hget('bike:1:stats', 'rides');
        echo $res13 . PHP_EOL;
        // >>> 3

        $res14 = $r->hmget('bike:1:stats', ['crashes', 'owners']);
        echo json_encode($res14) . PHP_EOL;
        // >>> ["1","1"]

res11 = r.hincrby("bike:1:stats", "rides", 1)
print(res11)
# >>> 1
res12 = r.hincrby("bike:1:stats", "rides", 1)
print(res12)
# >>> 2
res13 = r.hincrby("bike:1:stats", "rides", 1)
print(res13)
# >>> 3
res14 = r.hincrby("bike:1:stats", "crashes", 1)
print(res14)
# >>> 1
res15 = r.hincrby("bike:1:stats", "owners", 1)
print(res15)
# >>> 1
res16 = r.hget("bike:1:stats", "rides")
print(res16)
# >>> 3
res17 = r.hmget("bike:1:stats", ["crashes", "owners"])
print(res17)
# >>> ['1', '1']

if let Ok(res) = r.hincr("bike:1:stats", "rides", 1).await {
            let res: i32 = res;
            println!("{res}");    // >>> 1
        }

        if let Ok(res) = r.hincr("bike:1:stats", "rides", 1).await {
            let res: i32 = res;
            println!("{res}");    // >>> 2
        }

        if let Ok(res) = r.hincr("bike:1:stats", "rides", 1).await {
            let res: i32 = res;
            println!("{res}");    // >>> 3
        }

        if let Ok(res) = r.hincr("bike:1:stats", "crashes", 1).await {
            let res: i32 = res;
            println!("{res}");    // >>> 1
        }

        if let Ok(res) = r.hincr("bike:1:stats", "owners", 1).await {
            let res: i32 = res;
            println!("{res}");    // >>> 1
        }

        match r.hget("bike:1:stats", "rides").await {
            Ok(res) => {
                let res: i32 = res;
                println!("{res}");   // >>> 3
            },
            Err(e) => {
                println!("Error getting bike:1:stats rides: {e}");
                return;
            }
        };

        match r.hmget("bike:1:stats", &["crashes", "owners"]).await {
            Ok(res) => {
                let res: Vec<i32> = res;
                println!("{res:?}");   // >>> [1, 1]
            },
            Err(e) => {
                println!("Error getting bike:1:stats crashes and owners: {e}");
                return;
            }
        };

if let Ok(res) = r.hincr("bike:1:stats", "rides", 1) {
            let res: i32 = res;
            println!("{res}");    // >>> 1
        }

        if let Ok(res) = r.hincr("bike:1:stats", "rides", 1) {
            let res: i32 = res;
            println!("{res}");    // >>> 2
        }

        if let Ok(res) = r.hincr("bike:1:stats", "rides", 1) {
            let res: i32 = res;
            println!("{res}");    // >>> 3
        }

        if let Ok(res) = r.hincr("bike:1:stats", "crashes", 1) {
            let res: i32 = res;
            println!("{res}");    // >>> 1
        }

        if let Ok(res) = r.hincr("bike:1:stats", "owners", 1) {
            let res: i32 = res;
            println!("{res}");    // >>> 1
        }

        match r.hget("bike:1:stats", "rides") {
            Ok(res) => {
                let res: i32 = res;
                println!("{res}");   // >>> 3
            },
            Err(e) => {
                println!("Error getting bike:1:stats rides: {e}");
                return;
            }
        };

        match r.hmget("bike:1:stats", &["crashes", "owners"]) {
            Ok(res) => {
                let res: Vec<i32> = res;
                println!("{res:?}");   // >>> [1, 1]
            },
            Err(e) => {
                println!("Error getting bike:1:stats crashes and owners: {e}");
                return;
            }
        };

event = {
    'air_quality': 256,
    'battery_level':89
}

r.hset('sensor:sensor1', mapping=event)

# set the TTL for two hash fields to 60 seconds
r.hexpire('sensor:sensor1', 60, 'air_quality', 'battery_level')
ttl = r.httl('sensor:sensor1', 'air_quality', 'battery_level')
print(ttl)
# prints [60, 60]

# set the TTL of the 'air_quality' field in milliseconds
r.hpexpire('sensor:sensor1', 60000, 'air_quality')
# and retrieve it
pttl = r.hpttl('sensor:sensor1', 'air_quality')
print(pttl)
# prints [59994] # your actual value may vary

# set the expiration of 'air_quality' to now + 24 hours
# (similar to setting the TTL to 24 hours)
r.hexpireat('sensor:sensor1',
    datetime.now() + timedelta(hours=24),
    'air_quality')
# and retrieve it
expire_time = r.hexpiretime('sensor:sensor1', 'air_quality')
print(expire_time)
# prints [1717668041] # your actual value may vary