From c03a9ffc86d7dd6d926a9b7f78309bbda7050c6a Mon Sep 17 00:00:00 2001 From: Andy Stark Date: Tue, 10 Nov 2026 10:50:16 +0000 Subject: [PATCH] DOC-7294 Add Python and Go BITRANGEINTARRAY examples to the bitmaps page Adds a `bitrangeintarray` step to the `bitmap_tutorial` set for Python (new dt_bitmap.py) and Go (new ExampleClient_bitrangeintarray), and wraps the BITRANGEINTARRAY redis-cli block in bitmaps.md in a clients-example shortcode limited to Python and Go with lang_filter. The redis-cli prompt prefix changes from `redis>` to `>` inside the shortcode to match the other blocks on the page. Parked: BITRANGEINTARRAY is on the default branch of redis-py (509306f) and go-redis (9f33ff1) but in no release (redis-py v8.2.0b1 lacks it; go-redis v9.23.0 lacks it). Static checks only: `go vet` against the go-redis master clone and an import/signature check of redis.Redis.bitrangeintarray from a redis-py master install. NOT run: no Redis 8.12 server was available (Docker not running; local redis-server is 7.2.7), so the printed values are copied from the server-authored redis-cli block on the same page. Not covered yet: Jedis (#4830 open), predis (#1754 open), lettuce (#3992 merged 2026-11-09, no tag), redis-rb (merged, Ruby has no bitmap_tutorial source), node-redis (BITRANGEINTARRAY in neither 6.3.1 nor 6.4.0-beta.0). Add each to the lang_filter allowlist only when it ships. Learned: - bitmap_tutorial had no Python, Node.js or Ruby source at all; the Roaring section's existing `roaring` step is CLI-only. The new Python file contains only this step, so the Python tab appears only on this block. - The step sets bitmap-default-roaring and resets it in REMOVE code, which the build strips; the harness must reset it. Co-Authored-By: Claude Sonnet 5.5 --- content/develop/data-types/strings/bitmaps.md | 26 +++--- local_examples/tmp/bitmap_tutorial_test.go | 93 +++++++++++++++++++ .../tmp/datatypes/strings/dt_bitmap.py | 46 +++++++++ 3 files changed, 152 insertions(+), 13 deletions(-) create mode 100644 local_examples/tmp/datatypes/strings/dt_bitmap.py diff --git a/content/develop/data-types/strings/bitmaps.md b/content/develop/data-types/strings/bitmaps.md index bfcb91e905..3d3d0e1f13 100644 --- a/content/develop/data-types/strings/bitmaps.md +++ b/content/develop/data-types/strings/bitmaps.md @@ -379,38 +379,38 @@ The key, start, and end are required. Ranges follow `BITCOUNT`: byte offsets by default, bit offsets with `BIT`, and negative indexes count from the end. Returned offsets are always bit offsets. -```redis -redis> CONFIG SET bitmap-default-roaring yes +{{< clients-example set="bitmap_tutorial" step="bitrangeintarray" lang_filter="Python, Go" description="Offset retrieval: Use BITRANGEINTARRAY to list set-bit or clear-bit offsets in a range and to page through them with LIMIT when you need the positions of bits, not just a count" difficulty="intermediate" buildsUpon="ping" runnable="false" try_it="false" >}} +> CONFIG SET bitmap-default-roaring yes OK -redis> SETBIT members 1 1 +> SETBIT members 1 1 (integer) 0 -redis> SETBIT members 7 1 +> SETBIT members 7 1 (integer) 0 -redis> SETBIT members 9 1 +> SETBIT members 9 1 (integer) 0 -redis> SETBIT members 30 1 +> SETBIT members 30 1 (integer) 0 -redis> BITRANGEINTARRAY members 0 -1 +> BITRANGEINTARRAY members 0 -1 1) (integer) 1 2) (integer) 7 3) (integer) 9 4) (integer) 30 -redis> BITRANGEINTARRAY members 28 31 BIT ZEROS +> BITRANGEINTARRAY members 28 31 BIT ZEROS 1) (integer) 28 2) (integer) 29 3) (integer) 31 -redis> BITRANGEINTARRAY members 0 -1 BIT LIMIT 2 +> BITRANGEINTARRAY members 0 -1 BIT LIMIT 2 1) (integer) 1 2) (integer) 7 -redis> BITRANGEINTARRAY members 8 -1 BIT LIMIT 2 +> BITRANGEINTARRAY members 8 -1 BIT LIMIT 2 1) (integer) 9 2) (integer) 30 -redis> BITRANGEINTARRAY members 0 -1 BYTE LIMIT 1 +> BITRANGEINTARRAY members 0 -1 BYTE LIMIT 1 1) (integer) 1 2) (integer) 7 -redis> BITRANGEINTARRAY members 1 -1 BYTE LIMIT 1 +> BITRANGEINTARRAY members 1 -1 BYTE LIMIT 1 1) (integer) 9 -``` +{{< /clients-example >}} Both set-bit and clear-bit enumeration stay within the value's logical length. Missing keys, empty values, and empty ranges return empty arrays. diff --git a/local_examples/tmp/bitmap_tutorial_test.go b/local_examples/tmp/bitmap_tutorial_test.go index 9204698075..ce717c88e4 100644 --- a/local_examples/tmp/bitmap_tutorial_test.go +++ b/local_examples/tmp/bitmap_tutorial_test.go @@ -236,3 +236,96 @@ func ExampleClient_bitop_ops() { // 01011000 // 10100101 } + +func ExampleClient_bitrangeintarray() { + ctx := context.Background() + + rdb := redis.NewClient(&redis.Options{ + Addr: "localhost:6379", + Password: "", // no password docs + DB: 0, // use default DB + }) + + // REMOVE_START + rdb.Del(ctx, "members") + // REMOVE_END + + // STEP_START bitrangeintarray + rdb.ConfigSet(ctx, "bitmap-default-roaring", "yes") + + for _, offset := range []int64{1, 7, 9, 30} { + rdb.SetBit(ctx, "members", offset, 1) + } + + res1, err := rdb.BitRangeIntArray(ctx, "members", 0, -1).Result() + + if err != nil { + panic(err) + } + + fmt.Println(res1) // >>> [1 7 9 30] + + res2, err := rdb.BitRangeIntArrayWithArgs(ctx, "members", 28, 31, + &redis.BitRangeIntArrayArgs{Unit: redis.BitCountIndexBit, Zeros: true}, + ).Result() + + if err != nil { + panic(err) + } + + fmt.Println(res2) // >>> [28 29 31] + + res3, err := rdb.BitRangeIntArrayWithArgs(ctx, "members", 0, -1, + &redis.BitRangeIntArrayArgs{Unit: redis.BitCountIndexBit, Limit: 2}, + ).Result() + + if err != nil { + panic(err) + } + + fmt.Println(res3) // >>> [1 7] + + res4, err := rdb.BitRangeIntArrayWithArgs(ctx, "members", 8, -1, + &redis.BitRangeIntArrayArgs{Unit: redis.BitCountIndexBit, Limit: 2}, + ).Result() + + if err != nil { + panic(err) + } + + fmt.Println(res4) // >>> [9 30] + + res5, err := rdb.BitRangeIntArrayWithArgs(ctx, "members", 0, -1, + &redis.BitRangeIntArrayArgs{Unit: redis.BitCountIndexByte, Limit: 1}, + ).Result() + + if err != nil { + panic(err) + } + + fmt.Println(res5) // >>> [1 7] + + res6, err := rdb.BitRangeIntArrayWithArgs(ctx, "members", 1, -1, + &redis.BitRangeIntArrayArgs{Unit: redis.BitCountIndexByte, Limit: 1}, + ).Result() + + if err != nil { + panic(err) + } + + fmt.Println(res6) // >>> [9] + // STEP_END + + // REMOVE_START + rdb.Del(ctx, "members") + rdb.ConfigSet(ctx, "bitmap-default-roaring", "no") + // REMOVE_END + + // Output: + // [1 7 9 30] + // [28 29 31] + // [1 7] + // [9 30] + // [1 7] + // [9] +} diff --git a/local_examples/tmp/datatypes/strings/dt_bitmap.py b/local_examples/tmp/datatypes/strings/dt_bitmap.py new file mode 100644 index 0000000000..e4c5bdb23d --- /dev/null +++ b/local_examples/tmp/datatypes/strings/dt_bitmap.py @@ -0,0 +1,46 @@ +# EXAMPLE: bitmap_tutorial +# HIDE_START +import redis + +r = redis.Redis(decode_responses=True) +# HIDE_END + +# REMOVE_START +r.delete("members") +# REMOVE_END + +# STEP_START bitrangeintarray +r.config_set("bitmap-default-roaring", "yes") + +for offset in (1, 7, 9, 30): + r.setbit("members", offset, 1) + +res1 = r.bitrangeintarray("members", 0, -1) +print(res1) # >>> [1, 7, 9, 30] + +res2 = r.bitrangeintarray("members", 28, 31, mode="BIT", zeros=True) +print(res2) # >>> [28, 29, 31] + +res3 = r.bitrangeintarray("members", 0, -1, mode="BIT", limit=2) +print(res3) # >>> [1, 7] + +res4 = r.bitrangeintarray("members", 8, -1, mode="BIT", limit=2) +print(res4) # >>> [9, 30] + +res5 = r.bitrangeintarray("members", 0, -1, mode="BYTE", limit=1) +print(res5) # >>> [1, 7] + +res6 = r.bitrangeintarray("members", 1, -1, mode="BYTE", limit=1) +print(res6) # >>> [9] +# STEP_END + +# REMOVE_START +assert res1 == [1, 7, 9, 30] +assert res2 == [28, 29, 31] +assert res3 == [1, 7] +assert res4 == [9, 30] +assert res5 == [1, 7] +assert res6 == [9] +r.delete("members") +r.config_set("bitmap-default-roaring", "no") +# REMOVE_END