jonnify·redis /redis-patterns/data-modeling/bitmap-patterns/bitcount-aggregates

R7.04.2 · dive 2

The analytics are bit arithmetic.

Once flags are bits, the analytics are bit arithmetic. BITCOUNT answers "how many are set"; BITOP combines bitmaps with AND / OR / XOR / NOT; BITPOS finds the first set or unset bit.

None of them enumerate the entities. A segment question becomes one BITOP followed by one BITCOUNT, over the bits, in the engine — which is what makes bitmaps an analytics encoding rather than only a flag store. Select a range; the readout runs the population count over the bits below.

A 16-bit row of users:active. Select a range; the readout reports the population count over it.
offsets 0–15 · set: 1, 3, 4, 9, 12, 14
readout · Select a range. BITCOUNT users:active over offsets 0–15 counts the set bits — six of them.
§1

BITCOUNT — the population count

bitmap-patterns.md.txt · count the set bits

BITCOUNT key counts the set bits in the string — the number of flagged entities. The optional start/end restrict it to a byte range, or a bit range with the BIT modifier:

BITCOUNT users:active              # count every set bit
BITCOUNT users:active 0 0          # set bits in byte 0
BITCOUNT users:active 0 0 BIT      # set bits in bit 0 (the BIT range form)

The work is a popcount, which the engine does in hardware-sized steps over the string, so counting a million-flag bitmap is one fast scan, not a million reads. BITCOUNT is the size of any cohort, window, or flag set: daily active users, online users, holders of a feature — each is the count of one bitmap.

BITCOUNT is the size of a set of flags, computed as a popcount over the bits — the whole-bitmap count, or a byte/bit range.

§2

BITOP — combine bitmaps into a cohort

bitmap-patterns.md.txt · AND / OR / XOR / NOT

BITOP writes a new bitmap from a bitwise operation over one or more inputs — a cohort is one BITOP plus a BITCOUNT. Select an operation; the result row is computed from the two input rows, bit by bit:

Two attribute bitmaps, one result. premium and mobile over offsets 0–11; select an operation and the result is computed bitwise.
readout · Select an operation. premium is set at {1, 3, 4, 7, 9}; mobile at {3, 4, 6, 9, 10} — pick AND, OR, XOR, or NOT to see the result bitmap and its count.
SETBIT users:premium 12345 1
SETBIT users:mobile  12345 1
BITOP AND cohort:premium_mobile users:premium users:mobile
BITCOUNT cohort:premium_mobile                       # premium AND mobile
BITOP NOT users:not_mobile users:mobile              # complement, then
BITOP AND cohort:premium_desktop users:premium users:not_mobile  # premium AND NOT mobile

Every attribute is a bitmap; every segment is a boolean expression over them. The result bitmap is the same size as its inputs and can itself feed another BITOP — cohorts compose. BITOP is O(N) in the length of the longest input string, so the cost is the size of the largest bitmap, and very large ones are sharded by id range.

A cohort is a boolean expression over attribute bitmaps: BITOP AND/OR/XOR/NOT writes the result, BITCOUNT sizes it, and the result composes into the next expression.

§3

BITPOS — the first set or unset bit

bitmap-patterns.md.txt · locate an edge

BITPOS key bit returns the offset of the first bit equal to bit (0 or 1), optionally within a range:

BITPOS users:active 1          # offset of the first set bit  → first active id
BITPOS users:active 0          # offset of the first unset bit → first free slot
BITPOS users:active 1 100 200  # within a byte range

It answers "the first active entity" or "the first free slot" without scanning client-side — useful for allocating the next id in a dense space, or finding where a run of activity begins.

BITPOS locates the first set or unset bit — the first active id, or the first free slot — in the engine, not in a client loop.

§4

The Bloom-filter alternative — and the contrast

bitmap-patterns.md.txt · exact vs approximate

A bitmap can stand in for a Bloom filter: hash an item to k positions with k hashes and set them; membership is "all k bits set". If any is 0 the item is definitely absent; if all are 1 it is probably present, with false positives whose rate depends on the bitmap size and k. That is the same accuracy-for-memory trade R7.03 teaches as a first-class structure — and where a real Bloom filter belongs, the module commands BF.* size it and choose the hashes for you.

The contrast is the point. Bitmaps as taught here are exact: BITCOUNT over a flag set is the true count (no estimate), and GETBIT is a definite yes/no for a known offset. You reach for the probabilistic structures of R7.03 only when the cardinality is so large that even one bit per entity is too much, and a bounded error rate is acceptable.

the pattern

BITCOUNT is an exact popcount; BITOP AND/OR/XOR/NOT composes cohorts; BITPOS finds the first set/unset bit — all over the bits, in the engine.

where it grounds

A standalone core-Valkey example. These are exact answers; R7.03's HyperLogLog and Bloom filters are the approximate counterparts you reach for when exact storage is too expensive.

Bitmaps turn a flag store into an analytics one: BITCOUNT for the size of a set, BITOP to compose cohorts, BITPOS to find an edge — exactly, and without enumerating entities. The approximate versions are a deliberate trade (R7.03), not the default.

Notes on ValkeyBITOP destkey op key [key ...] is O(N) in the length of the longest input string and writes a result the size of that input; BITCOUNT is a population count with an optional BYTE/BIT range modifier — valkey.io/commands/bitop.

R7.03 · in this course

The approximate counterpart

When exact storage is too expensive, the probabilistic data structures trade a bounded error for fixed memory — HyperLogLog for cardinality, Bloom filters for membership. Bitmap analytics are the exact baseline they depart from.

R7.04.3 · in this course

daily-active patterns

BITOP over a series of daily bitmaps is where the analytics earn their keep: WAU, MAU, and retention cohorts. The next dive: daily-active patterns.

§5

References

sources · related

Sources

Related in this course