Skip to content

Commit 2f8764d

Browse files
Merge pull request #2158 from redis/DOC-5743-bitops-examples
DOC-5743 bitops examples
2 parents 9b5e46e + 1e9f6d9 commit 2f8764d

File tree

19 files changed

+2366
-2
lines changed

19 files changed

+2366
-2
lines changed

content/develop/data-types/bitmaps.md

Lines changed: 167 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,15 +102,180 @@ the number of days a given user visited the web site, while with
102102
a few [`BITPOS`]({{< relref "/commands/bitpos" >}}) calls, or simply fetching and analyzing the bitmap client-side,
103103
it is possible to easily compute the longest streak.
104104

105+
### Bitwise operations
106+
107+
The [`BITOP`]({{< relref "/commands/bitop" >}}) command performs bitwise
108+
operations over two or more source keys, storing the result in a destination key.
109+
110+
The examples below show the available operations using three keys: `A` (with bit pattern
111+
`11011000`), `B` (`00011001`), and `C` (`01101100`).
112+
113+
{{< image filename="/images/dev/bitmap/BitopSetup.svg" alt="Bitop setup" >}}
114+
115+
Numbering the bits from left to right, starting at zero, the following `SETBIT` commands
116+
will create these bitmaps:
117+
118+
{{< clients-example set="bitmap_tutorial" step="bitop_setup" >}}
119+
> SETBIT A 0 1
120+
(integer) 0
121+
> SETBIT A 1 1
122+
(integer) 0
123+
> SETBIT A 3 1
124+
(integer) 0
125+
> SETBIT A 4 1
126+
(integer) 0
127+
> GET A
128+
"\xd8"
129+
# Hex value: 0xd8 = 0b11011000
130+
131+
> SETBIT B 3 1
132+
(integer) 0
133+
> SETBIT B 4 1
134+
(integer) 0
135+
> SETBIT B 7 1
136+
(integer) 0
137+
> GET B
138+
"\x19"
139+
# Hex value: 0x19 = 0b00011001
140+
141+
> SETBIT C 1 1
142+
(integer) 0
143+
> SETBIT C 2 1
144+
(integer) 0
145+
> SETBIT C 4 1
146+
(integer) 0
147+
> SETBIT C 5 1
148+
(integer) 0
149+
> GET C
150+
"l"
151+
# ASCII "l" = hex 0x6c = 0b01101100
152+
{{< /clients-example >}}
153+
154+
#### `AND`
155+
156+
Set a bit in the destination key to 1 only if it is set in all the source keys.
157+
158+
{{< image filename="/images/dev/bitmap/BitopAnd.svg" alt="Bitop AND" >}}
159+
160+
{{< clients-example set="bitmap_tutorial" step="bitop_and" >}}
161+
> BITOP AND R A B C
162+
(integer) 1
163+
> GET R
164+
"\b"
165+
# ASCII "\b" (backspace) = hex 0x08 = 0b00001000
166+
{{< /clients-example >}}
167+
168+
#### `OR`
169+
Set a bit in the destination key to 1 if it is set in at least one of the source keys.
170+
171+
{{< image filename="/images/dev/bitmap/BitopOr.svg" alt="Bitop OR" >}}
172+
173+
{{< clients-example set="bitmap_tutorial" step="bitop_or" >}}
174+
> BITOP OR R A B C
175+
(integer) 1
176+
> GET R
177+
"\xfd"
178+
# Hex value: 0xfd = 0b11111101
179+
{{< /clients-example >}}
180+
181+
#### `XOR`
182+
183+
For two source keys, set a bit in the destination key to 1 if the value of the bit is
184+
different in the two keys. For three or more source keys, the result of XORing the first two
185+
keys is then XORed with the next key, and so forth.
186+
187+
{{< image filename="/images/dev/bitmap/BitopXor.svg" alt="Bitop XOR" >}}
188+
189+
{{< clients-example set="bitmap_tutorial" step="bitop_xor" >}}
190+
> BITOP XOR R A B
191+
(integer) 1
192+
> GET R
193+
"\xc1"
194+
# Hex value: 0xc1 = 0b11000001
195+
{{< /clients-example >}}
196+
197+
#### `NOT`
198+
199+
Set a bit in the destination key to 1 if it is not set in the source key (this
200+
is the only unary operator).
201+
202+
{{< image filename="/images/dev/bitmap/BitopNot.svg" alt="Bitop NOT" >}}
203+
204+
{{< clients-example set="bitmap_tutorial" step="bitop_not" >}}
205+
> BITOP NOT R A
206+
(integer) 1
207+
> GET R
208+
"'"
209+
# ASCII "'" (single quote) = hex 0x27 = 0b00100111
210+
{{< /clients-example >}}
211+
212+
#### `DIFF`
213+
214+
Set a bit in the destination key to 1 if it is set in the first source key, but not in any
215+
of the other source keys.
216+
217+
{{< image filename="/images/dev/bitmap/BitopDiff.svg" alt="Bitop DIFF" >}}
218+
219+
{{< clients-example set="bitmap_tutorial" step="bitop_diff" >}}
220+
> BITOP DIFF R A B C
221+
(integer) 1
222+
> GET R
223+
"\x80"
224+
# Hex value: 0x80 = 0b10000000
225+
{{< /clients-example >}}
226+
227+
#### `DIFF1`
228+
229+
Set a bit in the destination key to 1 if it is not set in the first source key,
230+
but set in at least one of the other source keys.
231+
232+
{{< image filename="/images/dev/bitmap/BitopDiff1.svg" alt="Bitop DIFF1" >}}
233+
234+
{{< clients-example set="bitmap_tutorial" step="bitop_diff1" >}}
235+
> BITOP DIFF1 R A B C
236+
(integer) 1
237+
> GET R
238+
"%"
239+
# ASCII "%" (percent) = hex 0x25 = 0b00100101
240+
{{< /clients-example >}}
241+
242+
#### `ANDOR`
243+
244+
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.
245+
246+
{{< image filename="/images/dev/bitmap/BitopAndOr.svg" alt="Bitop ANDOR" >}}
247+
248+
{{< clients-example set="bitmap_tutorial" step="bitop_andor" >}}
249+
> BITOP ANDOR R A B C
250+
(integer) 1
251+
> GET R
252+
"X"
253+
# ASCII "X" = hex 0x58 = 0b01011000
254+
{{< /clients-example >}}
255+
256+
#### `ONE`
257+
258+
Set a bit in the destination key to 1 if it is set in exactly one of the source keys.
259+
260+
{{< image filename="/images/dev/bitmap/BitopOne.svg" alt="Bitop ONE" >}}
261+
262+
{{< clients-example set="bitmap_tutorial" step="bitop_one" >}}
263+
> BITOP ONE R A B C
264+
(integer) 1
265+
> GET R
266+
"\xa5"
267+
# Hex value: 0xa5 = 0b10100101
268+
{{< /clients-example >}}
269+
270+
## Split bitmaps into multiple keys
271+
105272
Bitmaps are trivial to split into multiple keys, for example for
106273
the sake of sharding the data set and because in general it is better to
107274
avoid working with huge keys. To split a bitmap across different keys
108275
instead of setting all the bits into a key, a trivial strategy is just
109276
to store M bits per key and obtain the key name with `bit-number/M` and
110277
the Nth bit to address inside the key with `bit-number MOD M`.
111278

112-
113-
114279
## Performance
115280

116281
[`SETBIT`]({{< relref "/commands/setbit" >}}) and [`GETBIT`]({{< relref "/commands/getbit" >}}) are O(1).
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
// EXAMPLE: bitmap_tutorial
2+
package io.redis.examples;
3+
4+
import redis.clients.jedis.UnifiedJedis;
5+
import redis.clients.jedis.args.BitOP;
6+
// REMOVE_START
7+
import org.junit.jupiter.api.Test;
8+
import static org.junit.jupiter.api.Assertions.assertEquals;
9+
import static org.junit.jupiter.api.Assertions.assertFalse;
10+
import static org.junit.jupiter.api.Assertions.assertTrue;
11+
// REMOVE_END
12+
13+
public class BitMapsExample {
14+
15+
@Test
16+
public void run() {
17+
UnifiedJedis jedis = new UnifiedJedis("redis://localhost:6379");
18+
19+
// REMOVE_START
20+
jedis.del("pings:2024-01-01-00:00");
21+
// REMOVE_END
22+
23+
// STEP_START ping
24+
boolean res1 = jedis.setbit("pings:2024-01-01-00:00", 123, true);
25+
System.out.println(res1); // >>> false
26+
27+
boolean res2 = jedis.getbit("pings:2024-01-01-00:00", 123);
28+
System.out.println(res2); // >>> true
29+
30+
boolean res3 = jedis.getbit("pings:2024-01-01-00:00", 456);
31+
System.out.println(res3); // >>> false
32+
// STEP_END
33+
34+
// REMOVE_START
35+
assertFalse(res1);
36+
assertTrue(res2);
37+
assertFalse(res3);
38+
// REMOVE_END
39+
40+
// STEP_START bitcount
41+
long res4 = jedis.bitcount("pings:2024-01-01-00:00");
42+
System.out.println(res4); // >>> 1
43+
// STEP_END
44+
45+
// REMOVE_START
46+
assertEquals(1, res4);
47+
// REMOVE_END
48+
// REMOVE_START
49+
jedis.del("A", "B", "C", "R");
50+
// REMOVE_END
51+
52+
// STEP_START bitop_setup
53+
jedis.setbit("A", 0, true);
54+
jedis.setbit("A", 1, true);
55+
jedis.setbit("A", 3, true);
56+
jedis.setbit("A", 4, true);
57+
58+
byte[] res5 = jedis.get("A".getBytes());
59+
System.out.println(String.format("%8s", Integer.toBinaryString(res5[0] & 0xFF)).replace(' ', '0'));
60+
// >>> 11011000
61+
62+
jedis.setbit("B", 3, true);
63+
jedis.setbit("B", 4, true);
64+
jedis.setbit("B", 7, true);
65+
66+
byte[] res6 = jedis.get("B".getBytes());
67+
System.out.println(String.format("%8s", Integer.toBinaryString(res6[0] & 0xFF)).replace(' ', '0'));
68+
// >>> 00011001
69+
70+
jedis.setbit("C", 1, true);
71+
jedis.setbit("C", 2, true);
72+
jedis.setbit("C", 4, true);
73+
jedis.setbit("C", 5, true);
74+
75+
byte[] res7 = jedis.get("C".getBytes());
76+
System.out.println(String.format("%8s", Integer.toBinaryString(res7[0] & 0xFF)).replace(' ', '0'));
77+
// >>> 01101100
78+
// STEP_END
79+
// REMOVE_START
80+
assertEquals(0b11011000, res5[0] & 0xFF);
81+
assertEquals(0b00011001, res6[0] & 0xFF);
82+
assertEquals(0b01101100, res7[0] & 0xFF);
83+
// REMOVE_END
84+
85+
// STEP_START bitop_and
86+
jedis.bitop(BitOP.AND, "R", "A", "B", "C");
87+
byte[] res8 = jedis.get("R".getBytes());
88+
System.out.println(String.format("%8s", Integer.toBinaryString(res8[0] & 0xFF)).replace(' ', '0'));
89+
// >>> 00001000
90+
// STEP_END
91+
// REMOVE_START
92+
assertEquals(0b00001000, res8[0] & 0xFF);
93+
// REMOVE_END
94+
95+
// STEP_START bitop_or
96+
jedis.bitop(BitOP.OR, "R", "A", "B", "C");
97+
byte[] res9 = jedis.get("R".getBytes());
98+
System.out.println(String.format("%8s", Integer.toBinaryString(res9[0] & 0xFF)).replace(' ', '0'));
99+
// >>> 11111101
100+
// STEP_END
101+
// REMOVE_START
102+
assertEquals(0b11111101, res9[0] & 0xFF);
103+
// REMOVE_END
104+
105+
// STEP_START bitop_xor
106+
jedis.bitop(BitOP.XOR, "R", "A", "B");
107+
byte[] res10 = jedis.get("R".getBytes());
108+
System.out.println(String.format("%8s", Integer.toBinaryString(res10[0] & 0xFF)).replace(' ', '0'));
109+
// >>> 11000001
110+
// STEP_END
111+
// REMOVE_START
112+
assertEquals(0b11000001, res10[0] & 0xFF);
113+
// REMOVE_END
114+
115+
// STEP_START bitop_not
116+
jedis.bitop(BitOP.NOT, "R", "A");
117+
byte[] res11 = jedis.get("R".getBytes());
118+
System.out.println(String.format("%8s", Integer.toBinaryString(res11[0] & 0xFF)).replace(' ', '0'));
119+
// >>> 00100111
120+
// STEP_END
121+
// REMOVE_START
122+
assertEquals(0b00100111, res11[0] & 0xFF);
123+
// REMOVE_END
124+
125+
// STEP_START bitop_diff
126+
jedis.bitop(BitOP.DIFF, "R", "A", "B", "C");
127+
byte[] res12 = jedis.get("R".getBytes());
128+
System.out.println(String.format("%8s", Integer.toBinaryString(res12[0] & 0xFF)).replace(' ', '0'));
129+
// >>> 10000000
130+
// STEP_END
131+
// REMOVE_START
132+
assertEquals(0b10000000, res12[0] & 0xFF);
133+
// REMOVE_END
134+
135+
// STEP_START bitop_diff1
136+
jedis.bitop(BitOP.DIFF1, "R", "A", "B", "C");
137+
byte[] res13 = jedis.get("R".getBytes());
138+
System.out.println(String.format("%8s", Integer.toBinaryString(res13[0] & 0xFF)).replace(' ', '0'));
139+
// >>> 00100101
140+
// STEP_END
141+
// REMOVE_START
142+
assertEquals(0b00100101, res13[0] & 0xFF);
143+
// REMOVE_END
144+
145+
// STEP_START bitop_andor
146+
jedis.bitop(BitOP.ANDOR, "R", "A", "B", "C");
147+
byte[] res14 = jedis.get("R".getBytes());
148+
System.out.println(String.format("%8s", Integer.toBinaryString(res14[0] & 0xFF)).replace(' ', '0'));
149+
// >>> 01011000
150+
// STEP_END
151+
// REMOVE_START
152+
assertEquals(0b01011000, res14[0] & 0xFF);
153+
// REMOVE_END
154+
155+
// STEP_START bitop_one
156+
jedis.bitop(BitOP.ONE, "R", "A", "B", "C");
157+
byte[] res15 = jedis.get("R".getBytes());
158+
System.out.println(String.format("%8s", Integer.toBinaryString(res15[0] & 0xFF)).replace(' ', '0'));
159+
// >>> 10100101
160+
// STEP_END
161+
// REMOVE_START
162+
assertEquals(0b10100101, res15[0] & 0xFF);
163+
// REMOVE_END
164+
165+
166+
// HIDE_START
167+
jedis.close();
168+
}
169+
}
170+
// HIDE_END

0 commit comments

Comments
 (0)