Bitmaps
Use bitmaps for dense boolean flags addressed by integer offsets.
Define A Bitmap
Section titled “Define A Bitmap”import { bitmap } from "benni/schema";
export const dailyActive = bitmap("daily-active");Bitmaps take no value codec. Each bit is addressed by a non-negative integer offset and exposed as a boolean.
Set And Get Bits
Section titled “Set And Get Bits”const previous = await redis.bitmap(dailyActive).setbit("2026-07-04", 42, true);// ^? boolean (the bit's previous value)
const active = await redis.bitmap(dailyActive).getbit("2026-07-04", 42);Count Set Bits
Section titled “Count Set Bits”const total = await redis.bitmap(dailyActive).bitcount("2026-07-04");
const inFirstKilobyte = await redis.bitmap(dailyActive).bitcount("2026-07-04", { start: 0, end: 1023, unit: "BYTE"});The optional range takes start, end, and a unit of "BYTE" (the Redis default) or "BIT".
Find The First Bit
Section titled “Find The First Bit”const first = await redis.bitmap(dailyActive).bitpos("2026-07-04", true);// ^? number | nullbitpos wraps BITPOS and returns null when no matching bit exists. Pass start, end, and unit to limit the search; end requires start, and unit requires both.
Combine Bitmaps
Section titled “Combine Bitmaps”const sizeInBytes = await redis.bitmap(dailyActive).bitop("2026-week-27", "OR", [ "2026-07-01", "2026-07-02", "2026-07-03"]);bitop(destination, operation, sources) runs BITOP with "AND", "OR", "XOR", or "NOT" and stores the result under the destination ID. "NOT" requires exactly one source ID.
Packed Integer Fields
Section titled “Packed Integer Fields”bitfield treats a key as a row of arbitrary-width integers packed at bit offsets, ideal for compact per-entity counters. Chain operations and call exec; the result is a tuple typed to match the chain:
const [views, previous, level] = await redis.bitmap(dailyActive) .bitfield("2026-07-04") .get("u32", 0) // number (read a 32-bit unsigned field) .set("u32", 0, 100) // number | null (returns the previous value) .overflow("sat") // mode for the ops that follow it .incrby("u8", "#8", 1) // number | null (the new value) .exec();- Encoding:
u1–u63(unsigned) ori1–i64(signed). - Offset: an absolute bit offset (
0), or#nto address the nth field of that width ("#8"= the 9thu8, i.e. bit offset 64). getyields anumber;setandincrbyyieldnumber | null.overflowsets the mode for the operations after it:"wrap"(the default, modular),"sat"(clamp to the type’s min/max), or"fail"(leave the field unchanged and returnnull). Thatnullis whyset/incrbyare nullable.
// Atomic per-user counters clamped to a byte, no read-modify-write race.const [clamped] = await redis.bitmap(quotas) .bitfield(userId) .overflow("sat") .incrby("u8", 0, 1) .exec();Delete
Section titled “Delete”await redis.bitmap(dailyActive).del("2026-07-04");Raw Redis Equivalent
Section titled “Raw Redis Equivalent”await nodeRedis.setBit("daily-active:2026-07-04", 42, 1);const total = await nodeRedis.bitCount("daily-active:2026-07-04");Use bitmaps for daily-active tracking, feature rollouts keyed by numeric user ID, and any dense set of boolean flags where offsets map to entities. Use sets when members are sparse strings rather than dense integers.