diff --git a/content/develop/data-types/bitmaps.md b/content/develop/data-types/bitmaps.md index 1bbfa40f3c..12534e0138 100644 --- a/content/develop/data-types/bitmaps.md +++ b/content/develop/data-types/bitmaps.md @@ -102,6 +102,173 @@ the number of days a given user visited the web site, while with a few [`BITPOS`]({{< relref "/commands/bitpos" >}}) calls, or simply fetching and analyzing the bitmap client-side, it is possible to easily compute the longest streak. +### Bitwise operations + +The [`BITOP`]({{< relref "/commands/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`). + +{{< image filename="/images/dev/bitmap/BitopSetup.svg" alt="Bitop setup" >}} + +Numbering the bits from left to right, starting at zero, the following `SETBIT` commands +will create these bitmaps: + +{{< clients-example set="bitmap_tutorial" step="bitop_setup" >}} +> 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 +{{< /clients-example >}} + +#### `AND` + +Set a bit in the destination key to 1 only if it is set in all the source keys. + +{{< image filename="/images/dev/bitmap/BitopAnd.svg" alt="Bitop AND" >}} + +{{< clients-example set="bitmap_tutorial" step="bitop_and" >}} +> BITOP AND R A B C +(integer) 1 +> GET R +"\b" +# ASCII "\b" (backspace) = hex 0x08 = 0b00001000 +{{< /clients-example >}} + +#### `OR` +Set a bit in the destination key to 1 if it is set in at least one of the source keys. + +{{< image filename="/images/dev/bitmap/BitopOr.svg" alt="Bitop OR" >}} + +{{< clients-example set="bitmap_tutorial" step="bitop_or" >}} +> BITOP OR R A B C +(integer) 1 +> GET R +"\xfd" +# Hex value: 0xfd = 0b11111101 +{{< /clients-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. + +{{< image filename="/images/dev/bitmap/BitopXor.svg" alt="Bitop XOR" >}} + +{{< clients-example set="bitmap_tutorial" step="bitop_xor" >}} +> BITOP XOR R A B +(integer) 1 +> GET R +"\xc1" +# Hex value: 0xc1 = 0b11000001 +{{< /clients-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). + +{{< image filename="/images/dev/bitmap/BitopNot.svg" alt="Bitop NOT" >}} + +{{< clients-example set="bitmap_tutorial" step="bitop_not" >}} +> BITOP NOT R A +(integer) 1 +> GET R +"'" +# ASCII "'" (single quote) = hex 0x27 = 0b00100111 +{{< /clients-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. + +{{< image filename="/images/dev/bitmap/BitopDiff.svg" alt="Bitop DIFF" >}} + +{{< clients-example set="bitmap_tutorial" step="bitop_diff" >}} +> BITOP DIFF R A B C +(integer) 1 +> GET R +"\x80" +# Hex value: 0x80 = 0b10000000 +{{< /clients-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. + +{{< image filename="/images/dev/bitmap/BitopDiff1.svg" alt="Bitop DIFF1" >}} + +{{< clients-example set="bitmap_tutorial" step="bitop_diff1" >}} +> BITOP DIFF1 R A B C +(integer) 1 +> GET R +"%" +# ASCII "%" (percent) = hex 0x25 = 0b00100101 +{{< /clients-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. + +{{< image filename="/images/dev/bitmap/BitopAndOr.svg" alt="Bitop ANDOR" >}} + +{{< clients-example set="bitmap_tutorial" step="bitop_andor" >}} +> BITOP ANDOR R A B C +(integer) 1 +> GET R +"X" +# ASCII "X" = hex 0x58 = 0b01011000 +{{< /clients-example >}} + +#### `ONE` + +Set a bit in the destination key to 1 if it is set in exactly one of the source keys. + +{{< image filename="/images/dev/bitmap/BitopOne.svg" alt="Bitop ONE" >}} + +{{< clients-example set="bitmap_tutorial" step="bitop_one" >}} +> BITOP ONE R A B C +(integer) 1 +> GET R +"\xa5" +# Hex value: 0xa5 = 0b10100101 +{{< /clients-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 @@ -109,8 +276,6 @@ 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`]({{< relref "/commands/setbit" >}}) and [`GETBIT`]({{< relref "/commands/getbit" >}}) are O(1). diff --git a/local_examples/tmp/BitMapsExample.java b/local_examples/tmp/BitMapsExample.java new file mode 100644 index 0000000000..cd95b0287c --- /dev/null +++ b/local_examples/tmp/BitMapsExample.java @@ -0,0 +1,170 @@ +// EXAMPLE: bitmap_tutorial +package io.redis.examples; + +import redis.clients.jedis.UnifiedJedis; +import redis.clients.jedis.args.BitOP; +// REMOVE_START +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +// REMOVE_END + +public class BitMapsExample { + + @Test + public void run() { + UnifiedJedis jedis = new UnifiedJedis("redis://localhost:6379"); + + // REMOVE_START + jedis.del("pings:2024-01-01-00:00"); + // REMOVE_END + + // STEP_START ping + 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 + // STEP_END + + // REMOVE_START + assertFalse(res1); + assertTrue(res2); + assertFalse(res3); + // REMOVE_END + + // STEP_START bitcount + long res4 = jedis.bitcount("pings:2024-01-01-00:00"); + System.out.println(res4); // >>> 1 + // STEP_END + + // REMOVE_START + assertEquals(1, res4); + // REMOVE_END + // REMOVE_START + jedis.del("A", "B", "C", "R"); + // REMOVE_END + + // STEP_START bitop_setup + 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 + // STEP_END + // REMOVE_START + assertEquals(0b11011000, res5[0] & 0xFF); + assertEquals(0b00011001, res6[0] & 0xFF); + assertEquals(0b01101100, res7[0] & 0xFF); + // REMOVE_END + + // STEP_START bitop_and + 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 + // STEP_END + // REMOVE_START + assertEquals(0b00001000, res8[0] & 0xFF); + // REMOVE_END + + // STEP_START bitop_or + 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 + // STEP_END + // REMOVE_START + assertEquals(0b11111101, res9[0] & 0xFF); + // REMOVE_END + + // STEP_START bitop_xor + 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 + // STEP_END + // REMOVE_START + assertEquals(0b11000001, res10[0] & 0xFF); + // REMOVE_END + + // STEP_START bitop_not + 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 + // STEP_END + // REMOVE_START + assertEquals(0b00100111, res11[0] & 0xFF); + // REMOVE_END + + // STEP_START bitop_diff + 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 + // STEP_END + // REMOVE_START + assertEquals(0b10000000, res12[0] & 0xFF); + // REMOVE_END + + // STEP_START bitop_diff1 + 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 + // STEP_END + // REMOVE_START + assertEquals(0b00100101, res13[0] & 0xFF); + // REMOVE_END + + // STEP_START bitop_andor + 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 + // STEP_END + // REMOVE_START + assertEquals(0b01011000, res14[0] & 0xFF); + // REMOVE_END + + // STEP_START bitop_one + 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 + // STEP_END + // REMOVE_START + assertEquals(0b10100101, res15[0] & 0xFF); + // REMOVE_END + + +// HIDE_START + jedis.close(); + } +} +// HIDE_END diff --git a/local_examples/tmp/Bitmap_tutorial.cs b/local_examples/tmp/Bitmap_tutorial.cs new file mode 100644 index 0000000000..686496e9c7 --- /dev/null +++ b/local_examples/tmp/Bitmap_tutorial.cs @@ -0,0 +1,129 @@ +// EXAMPLE: bitmap_tutorial +// REMOVE_START +using NRedisStack.Tests; +// REMOVE_END +using StackExchange.Redis; + + +// REMOVE_START +namespace Doc; +[Collection("DocsTests")] +// REMOVE_END + +// HIDE_START +public class BitmapTutorial +// REMOVE_START +: AbstractNRedisStackTest, IDisposable +// REMOVE_END +{ + // REMOVE_START + public BitmapTutorial(EndpointsFixture fixture) : base(fixture) { } + + [SkippableFact] + // REMOVE_END + public void Run() + { + //REMOVE_START + // This is needed because we're constructing ConfigurationOptions in the test before calling GetConnection + SkipIfTargetConnectionDoesNotExist(EndpointsFixture.Env.Standalone); + var _ = GetCleanDatabase(EndpointsFixture.Env.Standalone); + //REMOVE_END + var muxer = ConnectionMultiplexer.Connect("localhost:6379"); + var db = muxer.GetDatabase(); + //REMOVE_START + // Clear any keys here before using them in tests. + db.KeyDelete("pings:2024-01-01-00:00"); + //REMOVE_END + // HIDE_END + + + // STEP_START ping + 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 + // STEP_END + + // Tests for 'ping' step. + // REMOVE_START + Assert.False(res1); + Assert.True(res2); + Assert.False(res3); + // REMOVE_END + + + // STEP_START bitcount + 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 + // STEP_END + + // Tests for 'bitcount' step. + // REMOVE_START + Assert.True(res4); + Assert.Equal(1, res5); + // REMOVE_END + + // STEP_START bitop_setup + + // Bitwise operations are not supported in NRedisStack. + + // STEP_END + + // STEP_START bitop_and + + // Bitwise operations are not supported in NRedisStack. + + // STEP_END + + // STEP_START bitop_or + + // Bitwise operations are not supported in NRedisStack. + + // STEP_END + + // STEP_START bitop_xor + + // Bitwise operations are not supported in NRedisStack. + + // STEP_END + + // STEP_START bitop_not + + // Bitwise operations are not supported in NRedisStack. + + // STEP_END + + // STEP_START bitop_diff + + // Bitwise operations are not supported in NRedisStack. + + // STEP_END + + // STEP_START bitop_diff1 + + // Bitwise operations are not supported in NRedisStack. + + // STEP_END + + // STEP_START bitop_andor + + // Bitwise operations are not supported in NRedisStack. + + // STEP_END + + // STEP_START bitop_one + + // Bitwise operations are not supported in NRedisStack. + + // STEP_END + + // HIDE_START + } +} +// HIDE_END + diff --git a/local_examples/tmp/DtBitmapTest.php b/local_examples/tmp/DtBitmapTest.php new file mode 100644 index 0000000000..a244d59966 --- /dev/null +++ b/local_examples/tmp/DtBitmapTest.php @@ -0,0 +1,167 @@ +// EXAMPLE: bitmap_tutorial + 'tcp', + 'host' => '127.0.0.1', + 'port' => 6379, + 'password' => '', + 'database' => 0, + ]); + // REMOVE_START + $r->flushall(); + // REMOVE_END + + // STEP_START ping + $res1 = $r->setbit('pings:2024-01-01-00:00', 123, 1); + echo $res1 . PHP_EOL; + // >>> 0 + + $res2 = $r->getbit('pings:2024-01-01-00:00', 123); + echo $res2 . PHP_EOL; + // >>> 1 + + $res3 = $r->getbit('pings:2024-01-01-00:00', 456); + echo $res3 . PHP_EOL; + // >>> 0 + // STEP_END + // REMOVE_START + $this->assertEquals(0, $res1); + // REMOVE_END + + // STEP_START bitcount + // Ensure the bit is set + $r->setbit('pings:2024-01-01-00:00', 123, 1); + $res4 = $r->bitcount('pings:2024-01-01-00:00'); + echo $res4 . PHP_EOL; + // >>> 1 + // STEP_END + // REMOVE_START + $this->assertEquals(1, $res4); + // REMOVE_END + + // STEP_START bitop_setup + $r->setbit('A', 0, 1); + $r->setbit('A', 1, 1); + $r->setbit('A', 3, 1); + $r->setbit('A', 4, 1); + + $res5 = $r->get('A'); + echo str_pad(decbin(ord($res5)), 8, '0', STR_PAD_LEFT) . PHP_EOL; + // >>> 11011000 + + $r->setbit('B', 3, 1); + $r->setbit('B', 4, 1); + $r->setbit('B', 7, 1); + + $res6 = $r->get('B'); + echo str_pad(decbin(ord($res6)), 8, '0', STR_PAD_LEFT) . PHP_EOL; + // >>> 00011001 + + $r->setbit('C', 1, 1); + $r->setbit('C', 2, 1); + $r->setbit('C', 4, 1); + $r->setbit('C', 5, 1); + + $res7 = $r->get('C'); + echo str_pad(decbin(ord($res7)), 8, '0', STR_PAD_LEFT) . PHP_EOL; + // >>> 01101100 + // STEP_END + // REMOVE_START + $this->assertEquals(0b11011000, ord($res5)); + $this->assertEquals(0b00011001, ord($res6)); + $this->assertEquals(0b01101100, ord($res7)); + // REMOVE_END + + // STEP_START bitop_and + $r->bitop('AND', 'R', 'A', 'B', 'C'); + $res8 = $r->get('R'); + echo str_pad(decbin(ord($res8)), 8, '0', STR_PAD_LEFT) . PHP_EOL; + // >>> 00001000 + // STEP_END + // REMOVE_START + $this->assertEquals(0b00001000, ord($res8)); + // REMOVE_END + + // STEP_START bitop_or + $r->bitop('OR', 'R', 'A', 'B', 'C'); + $res9 = $r->get('R'); + echo str_pad(decbin(ord($res9)), 8, '0', STR_PAD_LEFT) . PHP_EOL; + // >>> 11111101 + // STEP_END + // REMOVE_START + $this->assertEquals(0b11111101, ord($res9)); + // REMOVE_END + + // STEP_START bitop_xor + $r->bitop('XOR', 'R', 'A', 'B'); + $res10 = $r->get('R'); + echo str_pad(decbin(ord($res10)), 8, '0', STR_PAD_LEFT) . PHP_EOL; + // >>> 11000001 + // STEP_END + // REMOVE_START + $this->assertEquals(0b11000001, ord($res10)); + // REMOVE_END + + // STEP_START bitop_not + $r->bitop('NOT', 'R', 'A'); + $res11 = $r->get('R'); + echo str_pad(decbin(ord($res11)), 8, '0', STR_PAD_LEFT) . PHP_EOL; + // >>> 00100111 + // STEP_END + // REMOVE_START + $this->assertEquals(0b00100111, ord($res11)); + // REMOVE_END + + // STEP_START bitop_diff + $r->bitop('DIFF', 'R', 'A', 'B', 'C'); + $res12 = $r->get('R'); + echo str_pad(decbin(ord($res12)), 8, '0', STR_PAD_LEFT) . PHP_EOL; + // >>> 10000000 + // STEP_END + // REMOVE_START + $this->assertEquals(0b10000000, ord($res12)); + // REMOVE_END + + // STEP_START bitop_diff1 + $r->bitop('DIFF1', 'R', 'A', 'B', 'C'); + $res13 = $r->get('R'); + echo str_pad(decbin(ord($res13)), 8, '0', STR_PAD_LEFT) . PHP_EOL; + // >>> 00100101 + // STEP_END + // REMOVE_START + $this->assertEquals(0b00100101, ord($res13)); + // REMOVE_END + + // STEP_START bitop_andor + $r->bitop('ANDOR', 'R', 'A', 'B', 'C'); + $res14 = $r->get('R'); + echo str_pad(decbin(ord($res14)), 8, '0', STR_PAD_LEFT) . PHP_EOL; + // >>> 01011000 + // STEP_END + // REMOVE_START + $this->assertEquals(0b01011000, ord($res14)); + // REMOVE_END + + // STEP_START bitop_one + $r->bitop('ONE', 'R', 'A', 'B', 'C'); + $res15 = $r->get('R'); + echo str_pad(decbin(ord($res15)), 8, '0', STR_PAD_LEFT) . PHP_EOL; + // >>> 10100101 + // STEP_END + // REMOVE_START + $this->assertEquals(0b10100101, ord($res15)); + // REMOVE_END + } +} + diff --git a/local_examples/tmp/bitmap_tutorial_test.go b/local_examples/tmp/bitmap_tutorial_test.go new file mode 100644 index 0000000000..9204698075 --- /dev/null +++ b/local_examples/tmp/bitmap_tutorial_test.go @@ -0,0 +1,238 @@ +// EXAMPLE: bitmap_tutorial +// HIDE_START +package example_commands_test + +import ( + "context" + "fmt" + + "github.com/redis/go-redis/v9" +) + +// HIDE_END + +func ExampleClient_ping() { + ctx := context.Background() + + rdb := redis.NewClient(&redis.Options{ + Addr: "localhost:6379", + Password: "", // no password docs + DB: 0, // use default DB + }) + + // REMOVE_START + // make sure we are working with fresh database + rdb.FlushDB(ctx) + rdb.Del(ctx, "pings:2024-01-01-00:00") + // REMOVE_END + + // STEP_START ping + 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 + // STEP_END + + // Output: + // 0 + // 1 + // 0 +} + +func ExampleClient_bitcount() { + ctx := context.Background() + + rdb := redis.NewClient(&redis.Options{ + Addr: "localhost:6379", + Password: "", // no password docs + DB: 0, // use default DB + }) + + // REMOVE_START + // start with fresh database + rdb.FlushDB(ctx) + _, err := rdb.SetBit(ctx, "pings:2024-01-01-00:00", 123, 1).Result() + + if err != nil { + panic(err) + } + // REMOVE_END + + // STEP_START bitcount + 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 + // STEP_END + + // Output: + // 1 +} + +func ExampleClient_bitop_setup() { + ctx := context.Background() + + rdb := redis.NewClient(&redis.Options{ + Addr: "localhost:6379", + Password: "", // no password docs + DB: 0, // use default DB + }) + + // REMOVE_START + // start with fresh database + rdb.FlushDB(ctx) + rdb.Del(ctx, "A", "B", "C", "R") + // REMOVE_END + + // STEP_START bitop_setup + 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 + // STEP_END + + // Output: + // 11011000 + // 00011001 + // 01101100 +} + +func ExampleClient_bitop_ops() { + ctx := context.Background() + + rdb := redis.NewClient(&redis.Options{ + Addr: "localhost:6379", + Password: "", // no password docs + DB: 0, // use default DB + }) + + // REMOVE_START + // start with fresh database + rdb.FlushDB(ctx) + rdb.Del(ctx, "A", "B", "C", "R") + // REMOVE_END + + // HIDE_START + rdb.SetBit(ctx, "A", 0, 1) + rdb.SetBit(ctx, "A", 1, 1) + rdb.SetBit(ctx, "A", 3, 1) + rdb.SetBit(ctx, "A", 4, 1) + rdb.SetBit(ctx, "B", 3, 1) + rdb.SetBit(ctx, "B", 4, 1) + rdb.SetBit(ctx, "B", 7, 1) + rdb.SetBit(ctx, "C", 1, 1) + rdb.SetBit(ctx, "C", 2, 1) + rdb.SetBit(ctx, "C", 4, 1) + rdb.SetBit(ctx, "C", 5, 1) + // HIDE_END + + // STEP_START bitop_and + rdb.BitOpAnd(ctx, "R", "A", "B", "C") + br, _ := rdb.Get(ctx, "R").Bytes() + fmt.Printf("%08b\n", br[0]) + // >>> 00001000 + // STEP_END + + // STEP_START bitop_or + rdb.BitOpOr(ctx, "R", "A", "B", "C") + br, _ = rdb.Get(ctx, "R").Bytes() + fmt.Printf("%08b\n", br[0]) + // >>> 11111101 + // STEP_END + + // STEP_START bitop_xor + rdb.BitOpXor(ctx, "R", "A", "B") + br, _ = rdb.Get(ctx, "R").Bytes() + fmt.Printf("%08b\n", br[0]) + // >>> 11000001 + // STEP_END + + // STEP_START bitop_not + rdb.BitOpNot(ctx, "R", "A") + br, _ = rdb.Get(ctx, "R").Bytes() + fmt.Printf("%08b\n", br[0]) + // >>> 00100111 + // STEP_END + + // STEP_START bitop_diff + rdb.BitOpDiff(ctx, "R", "A", "B", "C") + br, _ = rdb.Get(ctx, "R").Bytes() + fmt.Printf("%08b\n", br[0]) + // >>> 10000000 + // STEP_END + + // STEP_START bitop_diff1 + rdb.BitOpDiff1(ctx, "R", "A", "B", "C") + br, _ = rdb.Get(ctx, "R").Bytes() + fmt.Printf("%08b\n", br[0]) + // >>> 00100101 + // STEP_END + + // STEP_START bitop_andor + rdb.BitOpAndOr(ctx, "R", "A", "B", "C") + br, _ = rdb.Get(ctx, "R").Bytes() + fmt.Printf("%08b\n", br[0]) + // >>> 01011000 + // STEP_END + + // STEP_START bitop_one + rdb.BitOpOne(ctx, "R", "A", "B", "C") + br, _ = rdb.Get(ctx, "R").Bytes() + fmt.Printf("%08b\n", br[0]) + // >>> 10100101 + // STEP_END + + // Output: + // 00001000 + // 11111101 + // 11000001 + // 00100111 + // 10000000 + // 00100101 + // 01011000 + // 10100101 +} diff --git a/local_examples/tmp/dt-bitmap.js b/local_examples/tmp/dt-bitmap.js new file mode 100644 index 0000000000..0d6a292d0f --- /dev/null +++ b/local_examples/tmp/dt-bitmap.js @@ -0,0 +1,159 @@ +// EXAMPLE: bitmap_tutorial +// REMOVE_START +import assert from 'assert'; +// REMOVE_END +import { createClient, RESP_TYPES } from 'redis'; + +const client = createClient({ + commandOptions: { + typeMapping: { + [RESP_TYPES.BLOB_STRING]: Buffer + } + } +}); +await client.connect(); + +// REMOVE_START +await client.flushDb(); +// REMOVE_END + +// STEP_START ping +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 +// STEP_END + +// REMOVE_START +assert.equal(res1, 0) +// REMOVE_END + +// STEP_START bitcount +// HIDE_START +await client.setBit("pings:2024-01-01-00:00", 123, 1) +// HIDE_END +const res4 = await client.bitCount("pings:2024-01-01-00:00") +console.log(res4) // >>> 1 +// STEP_END +// REMOVE_START +assert.equal(res4, 1) +// REMOVE_END + +// STEP_START bitop_setup +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 +// STEP_END +// REMOVE_START +assert.equal(res5.readUInt8(0), 0b11011000) +assert.equal(res6.readUInt8(0), 0b00011001) +assert.equal(res7.readUInt8(0), 0b01101100) +// REMOVE_END + +// STEP_START bitop_and +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 +// STEP_END +// REMOVE_START +assert.equal(res8.readUInt8(0), 0b00001000) +// REMOVE_END + +// STEP_START bitop_or +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 +// STEP_END +// REMOVE_START +assert.equal(res9.readUInt8(0), 0b11111101) +// REMOVE_END + +// STEP_START bitop_xor +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 +// STEP_END +// REMOVE_START +assert.equal(res10.readUInt8(0), 0b11000001) +// REMOVE_END + +// STEP_START bitop_not +await client.bitOp("NOT", "R", "A") +const res11 = await client.get("R") +console.log(res11.readUInt8(0).toString(2).padStart(8, '0')) +// >>> 00100111 +// STEP_END +// REMOVE_START +assert.equal(res11.readUInt8(0), 0b00100111) +// REMOVE_END + +// STEP_START bitop_diff +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 +// STEP_END +// REMOVE_START +assert.equal(res12.readUInt8(0), 0b10000000) +// REMOVE_END + +// STEP_START bitop_diff1 +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 +// STEP_END +// REMOVE_START +assert.equal(res13.readUInt8(0), 0b00100101) +// REMOVE_END + +// STEP_START bitop_andor +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 +// STEP_END +// REMOVE_START +assert.equal(res14.readUInt8(0), 0b01011000) +// REMOVE_END + +// STEP_START bitop_one +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 +// STEP_END +// REMOVE_START +assert.equal(res15.readUInt8(0), 0b10100101) + +await client.close(); +// REMOVE_END \ No newline at end of file diff --git a/local_examples/tmp/dt_bitmap.py b/local_examples/tmp/dt_bitmap.py new file mode 100644 index 0000000000..056bf55515 --- /dev/null +++ b/local_examples/tmp/dt_bitmap.py @@ -0,0 +1,155 @@ +# EXAMPLE: bitmap_tutorial +# HIDE_START +""" +Code samples for Bitmap doc pages: + https://redis.io/docs/latest/develop/data-types/bitmaps/ +""" +import redis + +# Connect without the usual `decode_responses=True` to +# see the binary values in the responses more easily. +r = redis.Redis() +# HIDE_END + +# REMOVE_START +r.delete("pings:2024-01-01-00:00", "A", "B", "C", "R") +# REMOVE_END + +# STEP_START ping +res1 = r.setbit("pings:2024-01-01-00:00", 123, 1) +print(res1) # >>> 0 + +res2 = r.getbit("pings:2024-01-01-00:00", 123) +print(res2) # >>> 1 + +res3 = r.getbit("pings:2024-01-01-00:00", 456) +print(res3) # >>> 0 +# STEP_END + +# REMOVE_START +assert res1 == 0 +# REMOVE_END + +# STEP_START bitcount +# HIDE_START +r.setbit("pings:2024-01-01-00:00", 123, 1) +# HIDE_END +res4 = r.bitcount("pings:2024-01-01-00:00") +print(res4) # >>> 1 +# STEP_END +# REMOVE_START +assert res4 == 1 +# REMOVE_END + +# STEP_START bitop_setup +r.setbit("A", 0, 1) +r.setbit("A", 1, 1) +r.setbit("A", 3, 1) +r.setbit("A", 4, 1) + +res5 = r.get("A") +print("{:08b}".format(int.from_bytes(res5, "big"))) +# >>> 11011000 + +r.setbit("B", 3, 1) +r.setbit("B", 4, 1) +r.setbit("B", 7, 1) + +res6 = r.get("B") +print("{:08b}".format(int.from_bytes(res6, "big"))) +# >>> 00011001 + +r.setbit("C", 1, 1) +r.setbit("C", 2, 1) +r.setbit("C", 4, 1) +r.setbit("C", 5, 1) + +res7 = r.get("C") +print("{:08b}".format(int.from_bytes(res7, "big"))) +# >>> 01101100 +# STEP_END +# REMOVE_START +assert int.from_bytes(res5, "big") == 0b11011000 +assert int.from_bytes(res6, "big") == 0b00011001 +assert int.from_bytes(res7, "big") == 0b01101100 +# REMOVE_END + +# STEP_START bitop_and +r.bitop("AND", "R", "A", "B", "C") +res8 = r.get("R") +print("{:08b}".format(int.from_bytes(res8, "big"))) +# >>> 00001000 +# STEP_END +# REMOVE_START +assert int.from_bytes(res8, "big") == 0b00001000 +# REMOVE_END + +# STEP_START bitop_or +r.bitop("OR", "R", "A", "B", "C") +res9 = r.get("R") +print("{:08b}".format(int.from_bytes(res9, "big"))) +# >>> 11111101 +# STEP_END +# REMOVE_START +assert int.from_bytes(res9, "big") == 0b11111101 +# REMOVE_END + +# STEP_START bitop_xor +r.bitop("XOR", "R", "A", "B") +res10 = r.get("R") +print("{:08b}".format(int.from_bytes(res10, "big"))) +# >>> 11000001 +# STEP_END +# REMOVE_START +assert int.from_bytes(res10, "big") == 0b11000001 +# REMOVE_END + +# STEP_START bitop_not +r.bitop("NOT", "R", "A") +res11 = r.get("R") +print("{:08b}".format(int.from_bytes(res11, "big"))) +# >>> 00100111 +# STEP_END +# REMOVE_START +assert int.from_bytes(res11, "big") == 0b00100111 +# REMOVE_END + +# STEP_START bitop_diff +r.bitop("DIFF", "R", "A", "B", "C") +res12 = r.get("R") +print("{:08b}".format(int.from_bytes(res12, "big"))) +# >>> 10000000 +# STEP_END +# REMOVE_START +assert int.from_bytes(res12, "big") == 0b10000000 +# REMOVE_END + +# STEP_START bitop_diff1 +r.bitop("DIFF1", "R", "A", "B", "C") +res13 = r.get("R") +print("{:08b}".format(int.from_bytes(res13, "big"))) +# >>> 00100101 +# STEP_END +# REMOVE_START +assert int.from_bytes(res13, "big") == 0b00100101 +# REMOVE_END + +# STEP_START bitop_andor +r.bitop("ANDOR", "R", "A", "B", "C") +res14 = r.get("R") +print("{:08b}".format(int.from_bytes(res14, "big"))) +# >>> 01011000 +# STEP_END +# REMOVE_START +assert int.from_bytes(res14, "big") == 0b01011000 +# REMOVE_END + +# STEP_START bitop_one +r.bitop("ONE", "R", "A", "B", "C") +res15 = r.get("R") +print("{:08b}".format(int.from_bytes(res15, "big"))) +# >>> 10100101 +# STEP_END +# REMOVE_START +assert int.from_bytes(res15, "big") == 0b10100101 +# REMOVE_END diff --git a/local_examples/tmp/lettuce-async/BitmapExample.java b/local_examples/tmp/lettuce-async/BitmapExample.java new file mode 100644 index 0000000000..60392634b0 --- /dev/null +++ b/local_examples/tmp/lettuce-async/BitmapExample.java @@ -0,0 +1,213 @@ +// EXAMPLE: bitmap_tutorial +package io.redis.examples.async; + +import io.lettuce.core.*; +import io.lettuce.core.api.StatefulRedisConnection; +import io.lettuce.core.api.async.RedisAsyncCommands; +import io.lettuce.core.codec.ByteArrayCodec; + +import java.util.concurrent.CompletableFuture; + +// REMOVE_START +import org.junit.jupiter.api.Test; +import static org.assertj.core.api.Assertions.assertThat; +// REMOVE_END + +public class BitmapExample { + + // REMOVE_START + @Test + // REMOVE_END + public void run() { + RedisClient redisClient = RedisClient.create("redis://localhost:6379"); + + try (StatefulRedisConnection connection = redisClient.connect(); + StatefulRedisConnection bytesConnection = redisClient.connect(ByteArrayCodec.INSTANCE)) { + RedisAsyncCommands async = connection.async(); + RedisAsyncCommands asyncBytes = bytesConnection.async(); + + // REMOVE_START + async.del("pings:2024-01-01-00:00", "A", "B", "C", "R").toCompletableFuture().join(); + // REMOVE_END + + // STEP_START ping + CompletableFuture ping = async.setbit("pings:2024-01-01-00:00", 123, 1).thenCompose(res1 -> { + System.out.println(res1); // >>> 0 + // REMOVE_START + assertThat(res1).isEqualTo(0); + // REMOVE_END + return async.getbit("pings:2024-01-01-00:00", 123); + }).thenCompose(res2 -> { + System.out.println(res2); // >>> 1 + // REMOVE_START + assertThat(res2).isEqualTo(1); + // REMOVE_END + return async.getbit("pings:2024-01-01-00:00", 456); + }).thenAccept(res3 -> { + System.out.println(res3); // >>> 0 + // REMOVE_START + assertThat(res3).isEqualTo(0); + // REMOVE_END + }).toCompletableFuture(); + // STEP_END + ping.join(); + + // STEP_START bitcount + CompletableFuture bitcount = async.bitcount("pings:2024-01-01-00:00").thenAccept(res4 -> { + System.out.println(res4); // >>> 1 + // REMOVE_START + assertThat(res4).isEqualTo(1); + // REMOVE_END + }).toCompletableFuture(); + // STEP_END + bitcount.join(); + + // STEP_START bitop_setup + CompletableFuture 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 + // REMOVE_START + assertThat(bitsA).isEqualTo("11011000"); + // REMOVE_END + 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 + // REMOVE_START + assertThat(bitsB).isEqualTo("00011001"); + // REMOVE_END + 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 + // REMOVE_START + assertThat(bitsC).isEqualTo("01101100"); + // REMOVE_END + }).toCompletableFuture(); + // STEP_END + setup.join(); + + // STEP_START bitop_and + CompletableFuture 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 + // REMOVE_START + assertThat(bits).isEqualTo("00001000"); + // REMOVE_END + }).toCompletableFuture(); + // STEP_END + andOp.join(); + + // STEP_START bitop_or + CompletableFuture 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 + // REMOVE_START + assertThat(bits).isEqualTo("11111101"); + // REMOVE_END + }).toCompletableFuture(); + // STEP_END + orOp.join(); + + // STEP_START bitop_xor + CompletableFuture 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 + // REMOVE_START + assertThat(bits).isEqualTo("11000001"); + // REMOVE_END + }).toCompletableFuture(); + // STEP_END + xorOp.join(); + + // STEP_START bitop_not + CompletableFuture 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 + // REMOVE_START + assertThat(bits).isEqualTo("00100111"); + // REMOVE_END + }).toCompletableFuture(); + // STEP_END + notOp.join(); + + // STEP_START bitop_diff + CompletableFuture 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 + // REMOVE_START + assertThat(bits).isEqualTo("10000000"); + // REMOVE_END + }).toCompletableFuture(); + // STEP_END + diffOp.join(); + + // STEP_START bitop_diff1 + CompletableFuture 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 + // REMOVE_START + assertThat(bits).isEqualTo("00100101"); + // REMOVE_END + }).toCompletableFuture(); + // STEP_END + diff1Op.join(); + + // STEP_START bitop_andor + CompletableFuture 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 + // REMOVE_START + assertThat(bits).isEqualTo("01011000"); + // REMOVE_END + }).toCompletableFuture(); + // STEP_END + andorOp.join(); + + // STEP_START bitop_one + CompletableFuture 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 + // REMOVE_START + assertThat(bits).isEqualTo("10100101"); + // REMOVE_END + }).toCompletableFuture(); + // STEP_END + oneOp.join(); + + // REMOVE_START + async.del("pings:2024-01-01-00:00", "A", "B", "C", "R").toCompletableFuture().join(); + // REMOVE_END + } finally { + redisClient.shutdown(); + } + } + +} diff --git a/local_examples/tmp/lettuce-reactive/BitmapExample.java b/local_examples/tmp/lettuce-reactive/BitmapExample.java new file mode 100644 index 0000000000..82b9eca801 --- /dev/null +++ b/local_examples/tmp/lettuce-reactive/BitmapExample.java @@ -0,0 +1,200 @@ +// EXAMPLE: bitmap_tutorial +package io.redis.examples.reactive; + +import io.lettuce.core.*; +import io.lettuce.core.api.StatefulRedisConnection; +import io.lettuce.core.api.reactive.RedisReactiveCommands; +import io.lettuce.core.codec.ByteArrayCodec; +import reactor.core.publisher.Mono; + +// REMOVE_START +import org.junit.jupiter.api.Test; +import static org.assertj.core.api.Assertions.assertThat; +// REMOVE_END + +public class BitmapExample { + + // REMOVE_START + @Test + // REMOVE_END + public void run() { + RedisClient redisClient = RedisClient.create("redis://localhost:6379"); + + try (StatefulRedisConnection connection = redisClient.connect(); + StatefulRedisConnection bytesConnection = redisClient.connect(ByteArrayCodec.INSTANCE)) { + + RedisReactiveCommands reactive = connection.reactive(); + RedisReactiveCommands reactiveBytes = bytesConnection.reactive(); + + // REMOVE_START + reactive.del("pings:2024-01-01-00:00", "A", "B", "C", "R").block(); + // REMOVE_END + + // STEP_START ping + Mono ping = reactive.setbit("pings:2024-01-01-00:00", 123, 1).doOnNext(res1 -> { + System.out.println(res1); // >>> 0 + // REMOVE_START + assertThat(res1).isEqualTo(0L); + // REMOVE_END + }).flatMap(v -> reactive.getbit("pings:2024-01-01-00:00", 123)).doOnNext(res2 -> { + System.out.println(res2); // >>> 1 + // REMOVE_START + assertThat(res2).isEqualTo(1L); + // REMOVE_END + }).flatMap(v -> reactive.getbit("pings:2024-01-01-00:00", 456)).doOnNext(res3 -> { + System.out.println(res3); // >>> 0 + // REMOVE_START + assertThat(res3).isEqualTo(0L); + // REMOVE_END + }).then(); + // STEP_END + ping.block(); + + // STEP_START bitcount + Mono bitcount = reactive.bitcount("pings:2024-01-01-00:00").doOnNext(res4 -> { + System.out.println(res4); // >>> 1 + // REMOVE_START + assertThat(res4).isEqualTo(1L); + // REMOVE_END + }).then(); + // STEP_END + bitcount.block(); + + // STEP_START bitop_setup + Mono 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 + // REMOVE_START + assertThat(bitsA).isEqualTo("11011000"); + // REMOVE_END + }).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 + // REMOVE_START + assertThat(bitsB).isEqualTo("00011001"); + // REMOVE_END + }).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 + // REMOVE_START + assertThat(bitsC).isEqualTo("01101100"); + // REMOVE_END + }).then(); + // STEP_END + setup.block(); + + // STEP_START bitop_and + Mono 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 + // REMOVE_START + assertThat(bits).isEqualTo("00001000"); + // REMOVE_END + }).then(); + // STEP_END + andOp.block(); + + // STEP_START bitop_or + Mono 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 + // REMOVE_START + assertThat(bits).isEqualTo("11111101"); + // REMOVE_END + }).then(); + // STEP_END + orOp.block(); + + // STEP_START bitop_xor + Mono 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 + // REMOVE_START + assertThat(bits).isEqualTo("11000001"); + // REMOVE_END + }).then(); + // STEP_END + xorOp.block(); + + // STEP_START bitop_not + Mono 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 + // REMOVE_START + assertThat(bits).isEqualTo("00100111"); + // REMOVE_END + }).then(); + // STEP_END + notOp.block(); + + // STEP_START bitop_diff + Mono 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 + // REMOVE_START + assertThat(bits).isEqualTo("10000000"); + // REMOVE_END + }).then(); + // STEP_END + diffOp.block(); + + // STEP_START bitop_diff1 + Mono 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 + // REMOVE_START + assertThat(bits).isEqualTo("00100101"); + // REMOVE_END + }).then(); + // STEP_END + diff1Op.block(); + + // STEP_START bitop_andor + Mono 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 + // REMOVE_START + assertThat(bits).isEqualTo("01011000"); + // REMOVE_END + }).then(); + // STEP_END + andorOp.block(); + + // STEP_START bitop_one + Mono 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 + // REMOVE_START + assertThat(bits).isEqualTo("10100101"); + // REMOVE_END + }).then(); + // STEP_END + oneOp.block(); + + // REMOVE_START + reactive.del("pings:2024-01-01-00:00", "A", "B", "C", "R").block(); + // REMOVE_END + + } finally { + redisClient.shutdown(); + } + } + +} diff --git a/static/images/dev/bitmap/BitopAnd.svg b/static/images/dev/bitmap/BitopAnd.svg new file mode 100644 index 0000000000..d252dcb8ea --- /dev/null +++ b/static/images/dev/bitmap/BitopAnd.svg @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + + + + A + 1 + 1 + 0 + 1 + 1 + 0 + 0 + 0 + + + B + 0 + 0 + 0 + 1 + 1 + 0 + 0 + 1 + + + C + 0 + 1 + 1 + 0 + 1 + 1 + 0 + 0 + + + + + + R + 0 + 0 + 0 + 0 + 1 + 0 + 0 + 0 + + + + diff --git a/static/images/dev/bitmap/BitopAndOr.svg b/static/images/dev/bitmap/BitopAndOr.svg new file mode 100644 index 0000000000..e8d7a3d8b0 --- /dev/null +++ b/static/images/dev/bitmap/BitopAndOr.svg @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + + + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + + + + A + 1 + 1 + 0 + 1 + 1 + 0 + 0 + 0 + + + B + 0 + 0 + 0 + 1 + 1 + 0 + 0 + 1 + + + C + 0 + 1 + 1 + 0 + 1 + 1 + 0 + 0 + + + + + + R + 0 + 1 + 0 + 1 + 1 + 0 + 0 + 0 + + + + diff --git a/static/images/dev/bitmap/BitopDiff.svg b/static/images/dev/bitmap/BitopDiff.svg new file mode 100644 index 0000000000..8ca846f55c --- /dev/null +++ b/static/images/dev/bitmap/BitopDiff.svg @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + + + + A + 1 + 1 + 0 + 1 + 1 + 0 + 0 + 0 + + + B + 0 + 0 + 0 + 1 + 1 + 0 + 0 + 1 + + + C + 0 + 1 + 1 + 0 + 1 + 1 + 0 + 0 + + + + + + R + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + + + + diff --git a/static/images/dev/bitmap/BitopDiff1.svg b/static/images/dev/bitmap/BitopDiff1.svg new file mode 100644 index 0000000000..8b5cc6049a --- /dev/null +++ b/static/images/dev/bitmap/BitopDiff1.svg @@ -0,0 +1,84 @@ + + + + + + + + + + + + + + + + + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + + + + A + 1 + 1 + 0 + 1 + 1 + 0 + 0 + 0 + + + B + 0 + 0 + 0 + 1 + 1 + 0 + 0 + 1 + + + C + 0 + 1 + 1 + 0 + 1 + 1 + 0 + 0 + + + + + + R + 0 + 0 + 1 + 0 + 0 + 1 + 0 + 1 + + + + diff --git a/static/images/dev/bitmap/BitopNot.svg b/static/images/dev/bitmap/BitopNot.svg new file mode 100644 index 0000000000..2e36e7ba77 --- /dev/null +++ b/static/images/dev/bitmap/BitopNot.svg @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + + + + A + 1 + 1 + 0 + 1 + 1 + 0 + 0 + 0 + + + + + + R + 0 + 0 + 1 + 0 + 0 + 1 + 1 + 1 + + + + diff --git a/static/images/dev/bitmap/BitopOne.svg b/static/images/dev/bitmap/BitopOne.svg new file mode 100644 index 0000000000..0a094329d8 --- /dev/null +++ b/static/images/dev/bitmap/BitopOne.svg @@ -0,0 +1,85 @@ + + + + + + + + + + + + + + + + + + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + + + + A + 1 + 1 + 0 + 1 + 1 + 0 + 0 + 0 + + + B + 0 + 0 + 0 + 1 + 1 + 0 + 0 + 1 + + + C + 0 + 1 + 1 + 0 + 1 + 1 + 0 + 0 + + + + + + R + 1 + 0 + 1 + 0 + 0 + 1 + 0 + 1 + + + + diff --git a/static/images/dev/bitmap/BitopOr.svg b/static/images/dev/bitmap/BitopOr.svg new file mode 100644 index 0000000000..1f11fcdb15 --- /dev/null +++ b/static/images/dev/bitmap/BitopOr.svg @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + + + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + + + + A + 1 + 1 + 0 + 1 + 1 + 0 + 0 + 0 + + + B + 0 + 0 + 0 + 1 + 1 + 0 + 0 + 1 + + + C + 0 + 1 + 1 + 0 + 1 + 1 + 0 + 0 + + + + + + R + 1 + 1 + 1 + 1 + 1 + 1 + 0 + 1 + + + + diff --git a/static/images/dev/bitmap/BitopSetup.svg b/static/images/dev/bitmap/BitopSetup.svg new file mode 100644 index 0000000000..334d12f5a4 --- /dev/null +++ b/static/images/dev/bitmap/BitopSetup.svg @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + + + + A + 1 + 1 + 0 + 1 + 1 + 0 + 0 + 0 + + + B + 0 + 0 + 0 + 1 + 1 + 0 + 0 + 1 + + + C + 0 + 1 + 1 + 0 + 1 + 1 + 0 + 0 + + + + diff --git a/static/images/dev/bitmap/BitopSetupR.svg b/static/images/dev/bitmap/BitopSetupR.svg new file mode 100644 index 0000000000..1657cc8c40 --- /dev/null +++ b/static/images/dev/bitmap/BitopSetupR.svg @@ -0,0 +1,70 @@ + + + + + + + + + + + + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + + + + A + 1 + 1 + 0 + 1 + 1 + 0 + 0 + 0 + + + B + 0 + 0 + 0 + 1 + 1 + 0 + 0 + 1 + + + C + 0 + 1 + 1 + 0 + 1 + 1 + 0 + 0 + + + + + + R + + + + diff --git a/static/images/dev/bitmap/BitopXor.svg b/static/images/dev/bitmap/BitopXor.svg new file mode 100644 index 0000000000..c26b99005a --- /dev/null +++ b/static/images/dev/bitmap/BitopXor.svg @@ -0,0 +1,73 @@ + + + + + + + + + + + + + + + + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + + + + A + 1 + 1 + 0 + 1 + 1 + 0 + 0 + 0 + + + B + 0 + 0 + 0 + 1 + 1 + 0 + 0 + 1 + + + + + + + R + 1 + 1 + 0 + 0 + 0 + 0 + 0 + 1 + + + +