What is a Redis Search?

Whether Redis is used as a cache, message broker, rate limiter, leaderboard system's backend, or traditional database, one feature is critical: search. The default Redis server features basic search options, but Redis Stack and Redis Enterprise include Redis Search (sometimes called RediSearch) and other helpful query tools. According to the official Redis documentation, Redis Search offers:

  • A rich query language
  • Incremental indexing on JSON and hash documents
  • Vector search
  • Full-text search
  • Geospatial queries
  • Aggregations

These search and query features make Redis an excellent choice for a wide range of apps, including a document database, a search engine, or a secondary index.

Redis Search features

Full-text searching is among Redis Search's features. It allows you to index and intelligently search text fields within Redis data, with support for stemming, stop words, and fuzzy matching. Full-text search supports ranking search results based on relevance. There's also a phrase search feature to find exact matches.

More advanced features include numeric range queries, which search and filter numeric fields within a given range. This is ideal for price ranges, rating systems, and other numerical data. There are also geospatial queries for indexing and searching geographic data (longitude and latitude values). With this feature, you can perform radius searches—finding locations within a specified radius of a starting point—and other location-based queries.

Redis Search also supports aggregations, which can group search results and perform statistical functions like count, sum, average, and min and max (minimum and maximum values).

Getting started with Redis Search

To get started with Redis Search, you must fire index your data. Indexes use JSON schemas defined by JSONPath expressions. The Redis syntax looks like this:

FT.CREATE {index_name} ON JSON SCHEMA {json_path} AS {attribute} {type}

Again, from the official Redis documentation, this command uses the above syntax to create an index that indexes the name, description, price, and image vector embedding of each JSON document that represents an inventory item:

FT.CREATE itemIdx ON JSON PREFIX 1 item: SCHEMA $.name AS name TEXT $.description as description TEXT $.price AS price NUMERIC $.embedding AS embedding VECTOR FLAT 6 DIM 4 DISTANCE_METRIC L2 TYPE FLOAT32

Here's a simple query that sorts the results by price:

FT.SEARCH myIndex "search terms" LIMIT 0 10 SORTBY price ASC

Working with Redis Search on Java

Although Redis doesn't natively support Java, Redisson simplifies the integration of Redis Search into Java applications. Redisson provides Java developer-friendly APIs that abstract the complexities of the Redis Search query language, making it simple to create indexes, build queries, handle results, and even map Java objects directly to Redis Search results.

Query execution

To create a RediSearch service with query execution for Redisson's RMap object:

RMap m = redisson.getMap("doc:1", new CompositeCodec(StringCodec.INSTANCE, redisson.getConfig().getCodec()));
m.put("v1", new SimpleObject("name1"));
m.put("v2", new SimpleObject("name2"));
RMap m2 = redisson.getMap("doc:2", new CompositeCodec(StringCodec.INSTANCE, redisson.getConfig().getCodec()));
m2.put("v1", new SimpleObject("name3"));
m2.put("v2", new SimpleObject("name4"));

RSearch s = redisson.getSearch();

// creates an index for documents with the prefix "doc."
s.createIndex("idx", IndexOptions.defaults()
                                  .on(IndexType.HASH)
                                  .prefix(Arrays.asList("doc:")),
                                  FieldIndex.text("v1"),
                                  FieldIndex.text("v2"));

SearchResult r = s.search("idx", "*", QueryOptions.defaults()
                                                  .returnAttributes(new ReturnAttribute("v1"), new ReturnAttribute("v2")));

SearchResult r = s.search("idx", "*", QueryOptions.defaults()
                                                  .filters(QueryFilter.geo("field")
                                                  .from(1, 1)
                                                  .radius(10, GeoUnit.FEET)));

And here's a code example for JSON objects using Redisson's RJsonBucket object:

public class TestClass {

     private List arr;
     private String value;

     public TestClass() {
     }

     public TestClass(List arr, String value) {
         this.arr = arr;
         this.value = value;
     }

     public List getArr() {
         return arr;
     }

     public TestClass setArr(List arr) {
         this.arr = arr;
         return this;
     }

     public String getValue() {
         return value;
     }

     public TestClass setValue(String value) {
         this.value = value;
         return this;
     }
}


RJsonBucket b = redisson.getJsonBucket("doc:1", new JacksonCodec<>(TestClass.class));
b.set(new TestClass(Arrays.asList(1, 2, 3), "hello"));

RSearch s = redisson.getSearch(StringCodec.INSTANCE);
// creates an index for documents with the prefix "doc"
s.createIndex("idx", IndexOptions.defaults()
                                 .on(IndexType.JSON)
                                 .prefix(Arrays.asList("doc:")),
                                 FieldIndex.numeric("$..arr").as("arr"),
                                 FieldIndex.text("$..value").as("val"));

SearchResult r = s.search("idx", "*", QueryOptions.defaults()
                                                  .returnAttributes(new ReturnAttribute("arr"), new ReturnAttribute("val")));
// total amount of found documents
long total = r.getTotal();
// found documents
List docs = r.getDocuments();

for (Document doc: docs) {
    String id = doc.getId();
    Map attrs = doc.getAttributes();
}

Aggregation

The aggregation feature of Redis Search is also easily integrated into an RMap object:

RMap m = redisson.getMap("doc:1", new CompositeCodec(StringCodec.INSTANCE, redisson.getConfig().getCodec()));
m.put("v1", new SimpleObject("name1"));
m.put("v2", new SimpleObject("name2"));
RMap m2 = redisson.getMap("doc:2", new CompositeCodec(StringCodec.INSTANCE, redisson.getConfig().getCodec()));
m2.put("v1", new SimpleObject("name3"));
m2.put("v2", new SimpleObject("name4"));

RSearch s = redisson.getSearch();
// creates an index for documents with the prefix "doc"
s.createIndex("idx", IndexOptions.defaults()
                                 .on(IndexType.HASH)
                                 .prefix(Arrays.asList("doc:")),
FieldIndex.text("v1"),
FieldIndex.text("v2"));

AggregationResult r = s.aggregate("idx", "*", AggregationOptions.defaults().load("v1", "v2"));

// total amount of attributes
long total = r.getTotal();

// list of attributes mapped by attribute name
List> attrs = r.getAttributes();

Here's the corresponding code for the JSON object:

public class TestClass {

     private List arr;
     private String value;

     public TestClass() {
     }

     public TestClass(List arr, String value) {
         this.arr = arr;
         this.value = value;
     }

     public List getArr() {
         return arr;
     }

     public TestClass setArr(List arr) {
         this.arr = arr;
         return this;
     }

     public String getValue() {
         return value;
     }

     public TestClass setValue(String value) {
         this.value = value;
         return this;
     }
}

RJsonBucket b = redisson.getJsonBucket("doc:1", new JacksonCodec<>(TestClass.class));
// stores object in JSON format
b.set(new TestClass(Arrays.asList(1, 2, 3), "hello"));

RSearch s = redisson.getSearch(StringCodec.INSTANCE);
// creates an index for documents with the prefix "doc"
s.createIndex("idx", IndexOptions.defaults()
                                 .on(IndexType.JSON)
                                 .prefix(Arrays.asList("doc:")),
                                 FieldIndex.numeric("$..arr").as("arr"),
                                 FieldIndex.text("$..value").as("val"));

AggregationResult r = s.aggregate("idx", "*", AggregationOptions.defaults().load("arr", "val"));

// total amount of attributes
long total = r.getTotal();

// list of attributes mapped by attribute name
List> attrs = r.getAttributes();

Spellcheck

Redisson can also create a RediSearch service to implement spellcheck features. This RSearch object will check the spelling of three words (name, hockey, and stik), with the last word spelled wrong:

RSearch s = redisson.getSearch();

s.createIndex("idx", IndexOptions.defaults()
                                 .on(IndexType.HASH)
                                 .prefix(Arrays.asList("doc:")),
                                 FieldIndex.text("t1"),
                                 FieldIndex.text("t2"));

s.addDict("name", "hockey", "stik");

Map> res = s.spellcheck("idx", "Hocke sti", SpellcheckOptions.defaults().includedTerms("name"));

// returns misspelled terms and their score - "hockey", 0
Map m = res.get("hocke");

// returns misspelled terms and their score - "stik", 0
Map m = res.get("sti");

To learn more about Redisson and its integration with Redis Search, visit the Redisson website today.

Similar terms