How to Configure Persistent FlexSearch Indexes with Redis for Distributed Search Applications
Use the Redis storage adapter (flexsearch/db/redis) to persist FlexSearch indexes to a Redis server, enabling multiple application instances to share the same index state through a common namespace-based key schema and atomic transactions.
FlexSearch is a high-performance full-text search library that typically operates in memory. When building distributed search applications, you need to configure persistent indexes with Redis to share state across multiple nodes. The nextapps-de/flexsearch repository provides a pluggable storage layer with a Redis adapter that implements the full storage interface, allowing you to run horizontally-scaled search services where every node reads from and writes to a common Redis instance or cluster.
Architecture of the Redis Persistence Layer
The Redis adapter in src/db/redis/index.js bridges FlexSearch's in-memory engine with external storage. The architecture consists of four main components:
- FlexSearch Core (
src/index.js,src/document.js): The in-memory search engine that builds inverted maps, document caches, and tag sets. - Redis Storage Adapter (
src/db/redis/index.js): Implements the FlexSearch storage interface (open,close,get,set,search,tag,remove,clear) and persists internal maps to Redis. - Redis Server/Cluster: Central key/value store where each index entry is stored under a namespaced key (e.g.,
<prefix>|map:field,<prefix>|doc). The adapter uses Redis commands (HMSET,SADD,GET,KEYS,UNLINK) to read and write these structures. - Application Instances: Multiple FlexSearch instances running with the same Redis configuration, sharing or isolating indexes through unique prefixes.
Key Implementation Details
The adapter uses several mechanisms to ensure data integrity and performance:
-
Namespace-based key schema: Every index uses a prefix (default
flexsearch) to prevent collisions. Keys follow patterns likeflexsearch|map:field,flexsearch|doc, andflexsearch|cfg【/cache/repos/github.com/nextapps-de/flexsearch/master/doc/persistent-redis.md#L81-L93】. -
Auto-commit: After each mutating operation (
add,update,remove), the adapter writes changes to Redis automatically. You can await explicit durability withawait index.commit()【/cache/repos/github.com/nextapps-de/flexsearch/master/doc/persistent-redis.md#L36-L40】. -
Custom Redis client support: Pass a pre-configured Redis client via the
dboption to use clustering, TLS, or custom retry strategies【/cache/repos/github.com/nextapps-de/flexsearch/master/doc/persistent-redis.md#L48-L58】. -
ID type handling: Redis stores all values as strings. The adapter enforces ID types (
integer,string) so lookups return correctly typed identifiers【/cache/repos/github.com/nextapps-de/flexsearch/master/doc/persistent-redis.md#L62-L71】. -
Transaction abstraction: Write operations are wrapped in Redis transactions (
MULTI/EXEC) insrc/db/redis/index.jsto guarantee atomicity across multiple keys forming one logical update.
Setting Up Redis Persistence in FlexSearch
Follow these steps to configure persistent indexes with Redis for your distributed application.
Install Dependencies
Install FlexSearch and the Redis client in your Node.js application:
npm install flexsearch@latest redis@4.7.0
Configure the Redis Client
Create a shared Redis client to handle clustering, TLS, or connection pooling:
import { createClient } from "redis";
const redis = await createClient({
socket: {
host: "redis.example.com",
port: 6379,
// tls: { /* TLS options */ }
}
}).connect();
Mount the Redis Database
Instantiate your FlexSearch index and mount the Redis storage adapter:
import { Index } from "flexsearch";
import Database from "flexsearch/db/redis";
const index = new Index({
// FlexSearch options: tokenizer, encode, etc.
});
const db = new Database("shared-search", {
db: redis, // Reuse the custom client
type: "integer" // Enforce numeric IDs
});
await index.mount(db);
Perform CRUD Operations
Any node in your distributed system can now modify the shared index:
// Add a document
await index.add(101, "Node.js performance tips");
// Update
await index.update(101, "Advanced Node.js performance tips");
// Search from any instance
const results = await index.search("performance");
console.log(results); // [101, ...]
// Remove
await index.remove(101);
Graceful Shutdown
Close connections properly when terminating your application:
await db.close(); // Or await index.unmount()
Distributed Search Example
Here is a complete worker implementation that multiple instances can run to create a horizontally-scaled search API:
// worker.js
import http from "http";
import { Index } from "flexsearch";
import Database from "flexsearch/db/redis";
import { createClient } from "redis";
const redis = await createClient({ url: "redis://localhost:6379" }).connect();
const index = new Index();
await index.mount(new Database("search-api", { db: redis }));
http.createServer(async (req, res) => {
const url = new URL(req.url, `http://${req.headers.host}`);
if (url.pathname === "/add") {
const id = Number(url.searchParams.get("id"));
const text = url.searchParams.get("text");
await index.add(id, text);
res.end("added");
} else if (url.pathname === "/search") {
const q = url.searchParams.get("q");
const hits = await index.search(q);
res.end(JSON.stringify(hits));
} else {
res.statusCode = 404;
res.end("not found");
}
}).listen(3000);
Run several instances of this worker on different ports or containers. All instances share the same Redis-backed index, enabling true distributed search where a document added by one instance can be searched by any other instance instantly.
Summary
- Use the Redis adapter (
flexsearch/db/redis) to persist FlexSearch indexes to a Redis server, enabling shared state across distributed application instances. - Namespace isolation prevents key collisions when running multiple indexes on the same Redis instance using configurable prefixes like
shared-search|map:field. - Atomic transactions via Redis
MULTI/EXECensure data integrity across the multiple keys that constitute a single index operation, as implemented insrc/db/redis/index.js. - Custom client support allows integration with Redis clusters, TLS connections, and custom retry strategies by passing a pre-configured client to the
dboption. - Auto-commit with explicit control ensures durability after each mutation, with
await index.commit()available for synchronous durability guarantees before proceeding.
Frequently Asked Questions
Can I use Redis Cluster with FlexSearch?
Yes. Pass a pre-configured Redis Cluster client via the db option when instantiating the Database. The adapter uses standard Redis commands (HMSET, SADD, GET, KEYS, UNLINK) that are compatible with Redis Cluster, allowing you to distribute your index across multiple Redis nodes for higher availability and throughput【/cache/repos/github.com/nextapps-de/flexsearch/master/doc/persistent-redis.md#L48-L58】.
How does FlexSearch handle ID types when storing data in Redis?
Redis stores all values as strings, but FlexSearch allows you to enforce specific ID types using the type option when creating the Database. Set type: "integer" to ensure that document IDs returned from searches are converted to numbers, or use type: "string" to keep them as strings. This type enforcement happens in the Redis adapter's serialization layer【/cache/repos/github.com/nextapps-de/flexsearch/master/doc/persistent-redis.md#L62-L71】.
What happens if the Redis connection fails during an indexing operation?
The Redis adapter wraps write operations in Redis transactions (MULTI/EXEC) to ensure atomicity. If the connection fails during a transaction, Redis will discard the partial changes, preventing index corruption. The adapter implemented in src/db/redis/index.js handles connection management, and you can pass a custom client with retry strategies via the db option to implement automatic reconnection logic for transient network failures.
Can multiple applications share the same Redis index while keeping some indexes isolated?
Yes. Use the namespace prefix (the first argument to new Database()) to isolate indexes. For example, create one Database with new Database("app-a-index", { db: redis }) and another with new Database("app-b-index", { db: redis }). Each prefix creates a separate keyspace in Redis (e.g., app-a-index|map:field vs app-b-index|map:field), allowing complete isolation while sharing the same Redis server【/cache/repos/github.com/nextapps-de/flexsearch/master/doc/persistent-redis.md#L81-L93】.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →