Skip to content

Commit 2fdd089

Browse files
DOC-5743 docs for BITOP with Python TCEs
1 parent ab9b429 commit 2fdd089

File tree

2 files changed

+303
-2
lines changed

2 files changed

+303
-2
lines changed

content/develop/data-types/bitmaps.md

Lines changed: 148 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,15 +102,161 @@ 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+
Numbering the bits from left to right, starting at zero, the following `SETBIT` commands
114+
will create these bitmaps:
115+
116+
{{< clients-example set="bitmap_tutorial" step="bitop_setup" >}}
117+
> SETBIT A 0 1
118+
(integer) 0
119+
> SETBIT A 1 1
120+
(integer) 0
121+
> SETBIT A 3 1
122+
(integer) 0
123+
> SETBIT A 4 1
124+
(integer) 0
125+
> GET A
126+
"\xd8"
127+
# Hex value: 0xd8 = 0b11011000
128+
129+
> SETBIT B 3 1
130+
(integer) 0
131+
> SETBIT B 4 1
132+
(integer) 0
133+
> SETBIT B 7 1
134+
(integer) 0
135+
> GET B
136+
"\x19"
137+
# Hex value: 0x19 = 0b00011001
138+
139+
> SETBIT C 1 1
140+
(integer) 0
141+
> SETBIT C 2 1
142+
(integer) 0
143+
> SETBIT C 4 1
144+
(integer) 0
145+
> SETBIT C 5 1
146+
(integer) 0
147+
> GET C
148+
"l"
149+
# ASCII "l" = hex 0x6c = 0b01101100
150+
{{< /clients-example >}}
151+
152+
#### `AND`
153+
154+
Set a bit in the destination key to 1 only if it is set in all the source keys.
155+
156+
{{< clients-example set="bitmap_tutorial" step="bitop_and" >}}
157+
> BITOP AND R A B C
158+
(integer) 1
159+
> GET R
160+
"\b"
161+
# ASCII "\b" (backspace) = hex 0x08 = 0b00001000
162+
{{< /clients-example >}}
163+
164+
#### `OR`
165+
Set a bit in the destination key to 1 if it is set in at least one of the source keys.
166+
167+
{{< clients-example set="bitmap_tutorial" step="bitop_or" >}}
168+
> BITOP OR R A B C
169+
(integer) 1
170+
> GET R
171+
"\xfd"
172+
# Hex value: 0xfd = 0b11111101
173+
{{< /clients-example >}}
174+
175+
#### `XOR`
176+
177+
For two source keys, set a bit in the destination key to 1 if the value of the bit is
178+
different in the two keys. For three or more source keys, the result of XORing the first two
179+
keys is then XORed with the next key, and so forth.
180+
181+
{{< clients-example set="bitmap_tutorial" step="bitop_xor" >}}
182+
> BITOP XOR R A B
183+
(integer) 1
184+
> GET R
185+
"\xc1"
186+
# Hex value: 0xc1 = 0b11000001
187+
{{< /clients-example >}}
188+
189+
#### `NOT`
190+
191+
Set a bit in the destination key to 1 if it is not set in the source key (this
192+
is the only unary operator).
193+
194+
{{< clients-example set="bitmap_tutorial" step="bitop_not" >}}
195+
> BITOP NOT R A
196+
(integer) 1
197+
> GET R
198+
"'"
199+
# ASCII "'" (single quote) = hex 0x27 = 0b00100111
200+
{{< /clients-example >}}
201+
202+
#### `DIFF`
203+
204+
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.
205+
206+
{{< clients-example set="bitmap_tutorial" step="bitop_diff" >}}
207+
> BITOP DIFF R A B C
208+
(integer) 1
209+
> GET R
210+
"\x80"
211+
# Hex value: 0x80 = 0b10000000
212+
{{< /clients-example >}}
213+
214+
#### `DIFF1`
215+
216+
Set a bit in the destination key to 1 if it is not set in the first source key,
217+
but set in at least one of the other source keys.
218+
219+
{{< clients-example set="bitmap_tutorial" step="bitop_diff1" >}}
220+
> BITOP DIFF1 R A B C
221+
(integer) 1
222+
> GET R
223+
"%"
224+
# ASCII "%" (percent) = hex 0x25 = 0b00100101
225+
{{< /clients-example >}}
226+
227+
#### `ANDOR`
228+
229+
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.
230+
231+
{{< clients-example set="bitmap_tutorial" step="bitop_andor" >}}
232+
> BITOP ANDOR R A B C
233+
(integer) 1
234+
> GET R
235+
"X"
236+
# ASCII "X" = hex 0x58 = 0b01011000
237+
{{< /clients-example >}}
238+
239+
#### `ONE`
240+
241+
Set a bit in the destination key to 1 if it is set in exactly one of the source keys.
242+
243+
{{< clients-example set="bitmap_tutorial" step="bitop_one" >}}
244+
> BITOP ONE R A B C
245+
(integer) 1
246+
> GET R
247+
"\xa5"
248+
# Hex value: 0xa5 = 0b10100101
249+
{{< /clients-example >}}
250+
251+
## Split bitmaps into multiple keys
252+
105253
Bitmaps are trivial to split into multiple keys, for example for
106254
the sake of sharding the data set and because in general it is better to
107255
avoid working with huge keys. To split a bitmap across different keys
108256
instead of setting all the bits into a key, a trivial strategy is just
109257
to store M bits per key and obtain the key name with `bit-number/M` and
110258
the Nth bit to address inside the key with `bit-number MOD M`.
111259

112-
113-
114260
## Performance
115261

116262
[`SETBIT`]({{< relref "/commands/setbit" >}}) and [`GETBIT`]({{< relref "/commands/getbit" >}}) are O(1).

local_examples/tmp/dt_bitmap.py

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
# EXAMPLE: bitmap_tutorial
2+
# HIDE_START
3+
"""
4+
Code samples for Bitmap doc pages:
5+
https://redis.io/docs/latest/develop/data-types/bitmaps/
6+
"""
7+
import redis
8+
9+
# Connect without the usual `decode_responses=True` to
10+
# see the binary values in the responses more easily.
11+
r = redis.Redis()
12+
# HIDE_END
13+
14+
# REMOVE_START
15+
r.delete("pings:2024-01-01-00:00", "A", "B", "C", "R")
16+
# REMOVE_END
17+
18+
# STEP_START ping
19+
res1 = r.setbit("pings:2024-01-01-00:00", 123, 1)
20+
print(res1) # >>> 0
21+
22+
res2 = r.getbit("pings:2024-01-01-00:00", 123)
23+
print(res2) # >>> 1
24+
25+
res3 = r.getbit("pings:2024-01-01-00:00", 456)
26+
print(res3) # >>> 0
27+
# STEP_END
28+
29+
# REMOVE_START
30+
assert res1 == 0
31+
# REMOVE_END
32+
33+
# STEP_START bitcount
34+
# HIDE_START
35+
r.setbit("pings:2024-01-01-00:00", 123, 1)
36+
# HIDE_END
37+
res4 = r.bitcount("pings:2024-01-01-00:00")
38+
print(res4) # >>> 1
39+
# STEP_END
40+
# REMOVE_START
41+
assert res4 == 1
42+
# REMOVE_END
43+
44+
# STEP_START bitop_setup
45+
r.setbit("A", 0, 1)
46+
r.setbit("A", 1, 1)
47+
r.setbit("A", 3, 1)
48+
r.setbit("A", 4, 1)
49+
50+
res5 = r.get("A")
51+
print("{:08b}".format(int.from_bytes(res5, "big")))
52+
# >>> 11011000
53+
54+
r.setbit("B", 3, 1)
55+
r.setbit("B", 4, 1)
56+
r.setbit("B", 7, 1)
57+
58+
res6 = r.get("B")
59+
print("{:08b}".format(int.from_bytes(res6, "big")))
60+
# >>> 00011001
61+
62+
r.setbit("C", 1, 1)
63+
r.setbit("C", 2, 1)
64+
r.setbit("C", 4, 1)
65+
r.setbit("C", 5, 1)
66+
67+
res7 = r.get("C")
68+
print("{:08b}".format(int.from_bytes(res7, "big")))
69+
# >>> 01101100
70+
# STEP_END
71+
# REMOVE_START
72+
assert int.from_bytes(res5, "big") == 0b11011000
73+
assert int.from_bytes(res6, "big") == 0b00011001
74+
assert int.from_bytes(res7, "big") == 0b01101100
75+
# REMOVE_END
76+
77+
# STEP_START bitop_and
78+
r.bitop("AND", "R", "A", "B", "C")
79+
res8 = r.get("R")
80+
print("{:08b}".format(int.from_bytes(res8, "big")))
81+
# >>> 00001000
82+
# STEP_END
83+
# REMOVE_START
84+
assert int.from_bytes(res8, "big") == 0b00001000
85+
# REMOVE_END
86+
87+
# STEP_START bitop_or
88+
r.bitop("OR", "R", "A", "B", "C")
89+
res9 = r.get("R")
90+
print("{:08b}".format(int.from_bytes(res9, "big")))
91+
# >>> 11111101
92+
# STEP_END
93+
# REMOVE_START
94+
assert int.from_bytes(res9, "big") == 0b11111101
95+
# REMOVE_END
96+
97+
# STEP_START bitop_xor
98+
r.bitop("XOR", "R", "A", "B")
99+
res10 = r.get("R")
100+
print("{:08b}".format(int.from_bytes(res10, "big")))
101+
# >>> 11000001
102+
# STEP_END
103+
# REMOVE_START
104+
assert int.from_bytes(res10, "big") == 0b11000001
105+
# REMOVE_END
106+
107+
# STEP_START bitop_not
108+
r.bitop("NOT", "R", "A")
109+
res11 = r.get("R")
110+
print("{:08b}".format(int.from_bytes(res11, "big")))
111+
# >>> 00100111
112+
# STEP_END
113+
# REMOVE_START
114+
assert int.from_bytes(res11, "big") == 0b00100111
115+
# REMOVE_END
116+
117+
# STEP_START bitop_diff
118+
r.bitop("DIFF", "R", "A", "B", "C")
119+
res12 = r.get("R")
120+
print("{:08b}".format(int.from_bytes(res12, "big")))
121+
# >>> 10000000
122+
# STEP_END
123+
# REMOVE_START
124+
assert int.from_bytes(res12, "big") == 0b10000000
125+
# REMOVE_END
126+
127+
# STEP_START bitop_diff1
128+
r.bitop("DIFF1", "R", "A", "B", "C")
129+
res13 = r.get("R")
130+
print("{:08b}".format(int.from_bytes(res13, "big")))
131+
# >>> 00100101
132+
# STEP_END
133+
# REMOVE_START
134+
assert int.from_bytes(res13, "big") == 0b00100101
135+
# REMOVE_END
136+
137+
# STEP_START bitop_andor
138+
r.bitop("ANDOR", "R", "A", "B", "C")
139+
res14 = r.get("R")
140+
print("{:08b}".format(int.from_bytes(res14, "big")))
141+
# >>> 01011000
142+
# STEP_END
143+
# REMOVE_START
144+
assert int.from_bytes(res14, "big") == 0b01011000
145+
# REMOVE_END
146+
147+
# STEP_START bitop_one
148+
r.bitop("ONE", "R", "A", "B", "C")
149+
res15 = r.get("R")
150+
print("{:08b}".format(int.from_bytes(res15, "big")))
151+
# >>> 10100101
152+
# STEP_END
153+
# REMOVE_START
154+
assert int.from_bytes(res15, "big") == 0b10100101
155+
# REMOVE_END

0 commit comments

Comments
 (0)