Java client libraries for Redis modules, built on Redisson.
Every module ships a synchronous and an asynchronous (RFuture) API, works on a single server, sentinel or
cluster through Redisson's configuration, supports pipelining, and can share one connection pool with the
other modules or with an existing Redisson instance.
| Module | Artifact | Redis 8 built-in | Status | Docs |
|---|---|---|---|---|
| RedisBloom (Bloom, Cuckoo, Count-Min Sketch, Top-K, t-digest) | redisbloom |
✅ | Active | commands |
| RediSearch | redisearch |
✅ | Active | commands |
| RedisJSON | redisjson |
✅ | Active | commands |
| RedisTimeSeries | redistimeseries |
✅ | Active | commands |
| Vector sets (Redis 8 native type) | vectorset |
✅ | Active | commands |
| FalkorDB / RedisGraph | redisgraph |
❌ | Active, tested against FalkorDB | commands |
| RedisAI | redisai |
❌ | Deprecated, upstream end of life | commands |
| RedisGears | redisgears |
❌ | Deprecated, upstream end of life | commands |
| Spring Boot starter | spring-boot-starter |
Active | guide | |
| Everything above | all |
Deprecated modules still work against the last released versions of their module and are kept for existing users; they will be removed in a future major release.
- Java 8 or later
- Redis 8.x (modules built in), or Redis 7.x with the corresponding module loaded
- Graph commands: FalkorDB (any Redis it ships with), or RedisGraph 2.x for the shared commands
- Vector sets need Redis 8.0+ (
VRANGEneeds 8.4+) - Redisson 4.7.x (pulled in transitively)
All modules in one dependency:
<dependency>
<groupId>io.github.dengliming.redismodule</groupId>
<artifactId>all</artifactId>
<version>3.0.0</version>
</dependency>Or a single module, for example RedisTimeSeries:
<dependency>
<groupId>io.github.dengliming.redismodule</groupId>
<artifactId>redistimeseries</artifactId>
<version>3.0.0</version>
</dependency>Gradle:
implementation 'io.github.dengliming.redismodule:all:3.0.0'Snapshots
Every push to master publishes 3.0.1-SNAPSHOT to the Central snapshot repository:
<repositories>
<repository>
<id>central-snapshots</id>
<url>https://central.sonatype.com/repository/maven-snapshots/</url>
<snapshots><enabled>true</enabled></snapshots>
</repository>
</repositories>Config config = new Config();
config.useSingleServer().setAddress("redis://127.0.0.1:6379");
RedisJSONClient client = new RedisJSONClient(config);
RedisJSON json = client.getRedisJSON();
json.set("user:1", SetArgs.Builder.create(".", "{\"name\":\"lisi\",\"age\":30}"));
Map<String, Object> user = json.get("user:1", Map.class, new GetArgs().path("."));
long age = json.incrBy("user:1", ".age", 1);
client.shutdown();Every synchronous method has an *Async twin returning an RFuture:
RFuture<Long> future = json.arrAppendAsync("user:1", ".tags", "vip");
future.thenAccept(size -> log.info("tags: {}", size));RedisBloomClient client = new RedisBloomClient(config);
BloomFilter bloomFilter = client.getRBloomFilter("bf");
bloomFilter.create(0.01d, 1000);
bloomFilter.madd("a", "b", "c");
List<Boolean> exists = bloomFilter.existsMulti("a", "z"); // [true, false]
CuckooFilter cuckooFilter = client.getCuckooFilter("cf");
cuckooFilter.reserve(1000);
cuckooFilter.add("a");
CountMinSketch sketch = client.getCountMinSketch("cms");
sketch.create(2000, 5);
Map<String, Integer> increments = new HashMap<>();
increments.put("a", 3);
sketch.incrby(increments);
TopKFilter topK = client.getTopKFilter("topk");
topK.reserve(3, 2000, 7, 0.925d);
topK.add("a", "b", "a");
List<String> top = topK.list();
TDigest tDigest = client.getTDigest("td");
tDigest.create(100);
tDigest.add(Arrays.asList(new AbstractMap.SimpleEntry<>(1.0, 1.0), new AbstractMap.SimpleEntry<>(2.0, 1.0))); // (value, weight)
List<Double> quantiles = tDigest.getQuantile(0.5);RediSearchClient client = new RediSearchClient(config);
RediSearch rediSearch = client.getRediSearch("idx:products");
rediSearch.createIndex(new Schema()
.addField(new TextField("title"))
.addField(new Field("price", FieldType.NUMERIC))
.addField(new Field("location", FieldType.GEO)),
new IndexOptions().definition(new IndexDefinition().setPrefixes(Arrays.asList("product:"))));
// documents are plain hashes under the configured prefix
SearchResult result = rediSearch.search("phone", new SearchOptions()
.withScores()
.filter(new NumericFilter("price", 100, 500))
.filter(new GeoFilter("location", 15, 37, 200, GeoFilter.Unit.KILOMETERS))
.page(0, 10));
AggregateResult aggregate = rediSearch.aggregate("*", new AggregateOptions()
.groups(new Group().fields("@brand").reducers(Reducers.count().as("count"))));
List<Suggestion> suggestions = rediSearch.getSuggestion("pho", new SuggestionOptions().withScores());Vector similarity search: declare a VectorField, store vectors as little-endian blobs and query with a KNN clause
bound through PARAMS (dialect 2):
rediSearch.createIndex(new Schema()
.addField(new TextField("title"))
.addField(new VectorField("embedding", VectorAlgorithm.HNSW, VectorType.FLOAT32, 768, DistanceMetric.COSINE)),
new IndexOptions().definition(new IndexDefinition().setPrefixes(Arrays.asList("doc:"))));
SearchResult nearest = rediSearch.search("*=>[KNN 10 @embedding $vec AS score]", new SearchOptions()
.param("vec", Vectors.toFloat32Bytes(queryEmbedding))
.dialect(2)
.returnFields("title", "score")
.sort(new SortBy("score", SortOrder.ASC)));RedisJSONClient client = new RedisJSONClient(config);
RedisJSON json = client.getRedisJSON();
json.set("user:1", SetArgs.Builder.create(".", "{\"name\":\"lisi\",\"tags\":[]}"));
json.arrAppend("user:1", ".tags", "vip", "beta");
long tags = json.arrLen("user:1", ".tags"); // 2
Class type = json.getType("user:1", ".name"); // String.class
List<Map> users = json.mget(".", Map.class, "user:1", "user:2"); // missing keys yield nullRedisJSON serializes with Gson by default. Gson is an optional dependency of the redisjson artifact (the
all artifact includes it): either add com.google.code.gson:gson yourself, or plug in your own JsonCodec:
JsonCodec jackson = new JsonCodec() {
private final ObjectMapper mapper = new ObjectMapper();
public String toJson(Object value) { return mapper.writeValueAsString(value); }
public <T> T fromJson(String json, Class<T> type) { return mapper.readValue(json, type); }
};
RedisJSONClient client = new RedisJSONClient(config, jackson);RedisTimeSeriesClient client = new RedisTimeSeriesClient(config);
RedisTimeSeries ts = client.getRedisTimeSeries();
ts.create("temperature:2:32", new TimeSeriesOptions()
.retentionTime(60_000L)
.labels(new Label("sensor_id", "2"), new Label("area_id", "32")));
ts.add(new Sample("temperature:2:32", Sample.Value.of(System.currentTimeMillis(), 26.5)), null);
ts.incrBy("requests:total", 1);
List<Sample.Value> values = ts.range("temperature:2:32", 0, Long.MAX_VALUE,
new RangeOptions().aggregationType(Aggregation.AVG, 60_000));
List<TimeSeries> byArea = ts.mrange(0, Long.MAX_VALUE, new RangeOptions().withLabels(), "area_id=32");VectorSetClient client = new VectorSetClient(config);
VectorSet movies = client.getVectorSet("movies");
movies.add("matrix", new double[]{0.9, 0.1, 0.0}, new AddArgs().attributes("{\"year\":1999}"));
movies.add("amelie", new double[]{0.0, 0.2, 0.9}, new AddArgs().attributes("{\"year\":2001}"));
List<Similarity> hits = movies.similar(new double[]{1.0, 0.0, 0.0}, new SimilarArgs().withScores().withAttribs().count(5));
List<Similarity> recent = movies.similarTo("matrix", new SimilarArgs().filter(".year > 2000"));
List<Double> vector = movies.getVector("matrix");RedisGraphClient client = new RedisGraphClient(config); // FalkorDB, or RedisGraph 2.x
RedisGraph graph = client.getRedisGraph();
graph.query("social", "CREATE (:person{name:'roi',age:32})-[:knows{since:2000}]->(:person{name:'amit',age:30})", 0L);
ResultSet resultSet = graph.query("social", "MATCH (a:person)-[r:knows]->(b:person) RETURN a, r, b.name", 0L);
for (Record record : resultSet.getResults()) {
Node a = (Node) record.getValue("a");
Edge r = (Edge) record.getValue("r");
// property names and relationship types are resolved, not just their indices
System.out.println(a.getProperty("name") + " " + r.getRelationshipType() + " " + record.getString("b.name"));
}
// FalkorDB extensions
graph.createConstraint("social", ConstraintType.UNIQUE, EntityType.NODE, "person", "name");
graph.copy("social", "social_backup");
Map<String, Object> memory = graph.memoryUsage("social");RedisAI redisAI = new RedisAIClient(config).getRedisAI();
redisAI.setTensor("tensor1", DataType.FLOAT, new int[]{2, 2}, null, new String[]{"1", "2", "3", "4"});
RedisGears redisGears = new RedisGearsClient(config).getRedisGears();
redisGears.pyExecute("GB().run()", false);Every client has createXxxBatch(). Objects obtained from the batch queue their *Async calls and
execute() sends them in a single round trip, returning the responses in call order:
RedisBloomBatch batch = redisBloomClient.createRedisBloomBatch();
BloomFilter bloomFilter = batch.getRBloomFilter("bf");
bloomFilter.createAsync(0.01d, 1000);
bloomFilter.maddAsync("a", "b", "c");
bloomFilter.existsAsync("a");
BatchResult<?> result = batch.execute();
result.getResponses(); // [true, [true, true, true], true]Each client can wrap an existing RedissonClient, so several modules (or your own Redisson code) share a
single connection pool. A wrapping client never shuts the shared instance down:
RedissonClient redisson = Redisson.create(config);
RedisJSONClient jsonClient = new RedisJSONClient(redisson);
RediSearchClient searchClient = new RediSearchClient(redisson);
RedisBloomClient bloomClient = new RedisBloomClient(redisson);
// ...
redisson.shutdown();Add the starter and enable the modules you need; they share one connection pool or use dedicated ones,
and pick up your own RedissonClient and JsonCodec beans when present.
redis-module:
config: |
singleServerConfig:
address: "redis://127.0.0.1:6379"
redisjson:
enabled: true
redisearch:
enabled: true@Autowired
private RedisJSONClient redisJSONClient;See the starter guide for every option.
- CI runs the test suite against the official
redis:8.8image, where Search, JSON, TimeSeries and Bloom are built in.FT.CONFIGwas removed in Redis 8;RediSearch.setConfig()/getConfig()fall back toCONFIG SET/GET search-*automatically. - RediSearch 1.x document commands (
FT.ADD,FT.GET,FT.DEL,FT.DROP, ...) are still exposed but no longer exist on RediSearch 2.x / Redis 8. Index hashes or JSON documents under a prefix instead. - Commands are routed by their key, so cluster deployments work with Redisson's
useClusterServers(). - Redisson 4.x: JSON configuration strings are still accepted (parsed as YAML). Redisson dropped the
retryIntervalsetting in favour ofretryDelay, andsingleServerConfig.passwordis deprecated in favour of the top-levelpassword.
./mvnw clean install -DskipTests -Dgpg.skipIntegration tests need a running Redis with the modules loaded; pass its location with -DREDIS_HOST and
-DREDIS_PORT (see build.yml for the full matrix). More runnable snippets
live in examples.
Issues and pull requests are welcome. Please run ./mvnw verify -DskipTests before opening a PR so that
Checkstyle passes.