Databases 9 min read

How to Combine Pagination and Multi‑Condition Fuzzy Search in Redis

This article explains how to implement pagination using Redis Sorted Sets, achieve multi‑condition fuzzy queries with Hashes and HSCAN, and then combine both techniques into a single solution while discussing performance trade‑offs and optimization strategies such as key expiration and data‑sync methods.

Architect's Guide
Architect's Guide
Architect's Guide
How to Combine Pagination and Multi‑Condition Fuzzy Search in Redis

Pagination with Redis Sorted Set

Redis Sorted Set (ZSet) stores members with a numeric score that defines ordering. The commands used are:

ZADD ZADD key score member [[score member]…] – adds members and binds a score for sorting.

ZREVRANGE ZREVRANGE key start stop – returns members in the specified range, enabling page retrieval.

ZREM ZREM key member – removes a member, useful for deleting items such as comments.

Because ZSet maintains order automatically and can filter by score ranges, it is preferred over List for pagination scenarios where ordering is required.

Multi‑Condition Fuzzy Query with Hashes

Redis does not provide SQL‑like conditional queries. The implementation stores each record as a hash field whose key encodes searchable attributes, e.g. <id>:<name>:<gender>, and the value holds the full JSON payload.

Pattern‑based matching is performed with HSCAN , which iterates all hash fields and supports glob patterns:

All female users: pattern *:*:female All users whose name starts with "阿": pattern *:阿*:* HSCAN scans the entire hash each time, so its efficiency is limited.

Combined Pagination and Fuzzy Query

When both pagination and dynamic filtering are required, two approaches exist:

Execute the conditional query in a persistent database, then load the result set into Redis for pagination.

Implement the fuzzy query and pagination entirely within Redis.

In scenarios where data resides only in the cache, the second approach is necessary. The workflow is:

Construct a match string that represents the desired filter, e.g. *:*:male or 100*:*.

Check whether a ZSet with that match string already exists.

If the ZSet does not exist, run HSCAN on the hash, collect matching fields, and create a new ZSet whose key is the match string. Insert each matching member into the ZSet with an appropriate score (commonly a timestamp or another sortable attribute).

If the ZSet exists, retrieve the required page directly with ZREVRANGE.

This on‑demand index provides fast pagination after the initial scan.

Performance Optimizations

Creating a ZSet for every distinct filter can exhaust cache space. To mitigate this, each generated ZSet is assigned an expiration time (TTL). When a cached ZSet is accessed, its TTL is refreshed, allowing unused indexes to be evicted automatically.

Two strategies keep the index synchronized with newly inserted hash entries:

At write time, insert the new record into the hash and also into any relevant ZSets. This requires a naming convention or metadata to identify which ZSets should receive the new member.

Periodically rebuild or refresh the ZSets. This approach is simpler but may produce stale pagination results between refreshes.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

RedisPaginationFuzzy SearchHSCANHashSorted Set
Architect's Guide
Written by

Architect's Guide

Dedicated to sharing programmer-architect skills—Java backend, system, microservice, and distributed architectures—to help you become a senior architect.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.