RediSearch

Redis is famous for being a key-value store. But what if you want to find an object based on its attributes? What if you want to perform a full-text search across thousands of documents? That’s where RediSearch comes in.

1. What is RediSearch?

RediSearch is a query engine that allows you to create secondary indexes on your data (Hashes or JSON).

The Inverted Index

Under the hood, RediSearch maintains an Inverted Index. Instead of scanning every document to find the word “Matrix”, it stores a map where the key is the word, and the value is a list of all documents containing that word.

graph LR
  subgraph Documents
  D1[Doc 1: "Redis is fast"]
  D2[Doc 2: "Redis is cool"]
  D3[Doc 3: "Fast cars"]
  end

  subgraph Inverted_Index [Inverted Index]
  style Inverted_Index fill:var(--bg-main),stroke:var(--border-muted),color:var(--fg-default)

  T1(redis) --> L1[Doc 1, Doc 2]
  T2(fast) --> L2[Doc 1, Doc 3]
  T3(cool) --> L3[Doc 2]
  T4(cars) --> L4[Doc 3]
  end

  D1 -.-> T1
  D2 -.-> T3
  D3 -.-> T4

2. Key Features

  • Full-Text Search: Stemming (running → run), stop-words (the, a), and exact phrase matching.
  • Vector Search: Using KNN (K-Nearest Neighbors) to find semantically similar items (e.g., “iPhone” is close to “Samsung Galaxy”).
  • Aggregation: Group by, sort, and perform math operations on search results.

3. Creating an Index

To use RediSearch, you first define a Schema that tells Redis which fields to index.

Java & Go Implementation

import redis.clients.jedis.JedisPooled;
import redis.clients.jedis.search.IndexDefinition;
import redis.clients.jedis.search.IndexOptions;
import redis.clients.jedis.search.Schema;
import redis.clients.jedis.search.SearchResult;

public class RediSearchExample {
    public static void main(String[] args) {
        JedisPooled client = new JedisPooled("localhost", 6379);

        // 1. Create Index
        // FT.CREATE movieIdx ON HASH PREFIX 1 movie: SCHEMA title TEXT year NUMERIC
        Schema schema = new Schema()
            .addTextField("title", 1.0)
            .addNumericField("year");

        IndexDefinition def = new IndexDefinition()
            .setPrefixes(new String[]{"movie:"});

        try {
            client.ftCreate("movieIdx", IndexOptions.defaultOptions().setDefinition(def), schema);
        } catch (Exception e) {
            // Index already exists
        }

        // 2. Add Data (Standard HSET)
        client.hset("movie:1", java.util.Map.of("title", "The Matrix", "year", "1999"));
        client.hset("movie:2", java.util.Map.of("title", "The Matrix Reloaded", "year", "2003"));

        // 3. Search
        // FT.SEARCH movieIdx "Matrix"
        SearchResult result = client.ftSearch("movieIdx", "Matrix");
        System.out.println("Found: " + result.getTotalResults()); // 2
    }
}
package main

import (
    "context"
    "fmt"
    "github.com/redis/go-redis/v9"
)

func main() {
    ctx := context.Background()
    rdb := redis.NewClient(&redis.Options{Addr: "localhost:6379"})

    // 1. Create Index (Raw command often needed for FT.CREATE in some drivers,
    // but go-redis has dedicated hooks or use Do)
    // FT.CREATE movieIdx ON HASH PREFIX 1 movie: SCHEMA title TEXT year NUMERIC
    err := rdb.Do(ctx, "FT.CREATE", "movieIdx",
        "ON", "HASH",
        "PREFIX", "1", "movie:",
        "SCHEMA", "title", "TEXT", "year", "NUMERIC").Err()

    if err != nil {
        fmt.Println("Index might already exist:", err)
    }

    // 2. Add Data
    rdb.HSet(ctx, "movie:1", "title", "The Matrix", "year", 1999)
    rdb.HSet(ctx, "movie:2", "title", "The Matrix Reloaded", "year", 2003)

    // 3. Search
    // FT.SEARCH movieIdx "Matrix"
    res, _ := rdb.Do(ctx, "FT.SEARCH", "movieIdx", "Matrix").Result()
    fmt.Println(res)
}

4. Interactive: Inverted Index Builder

Add documents and see how the Inverted Index is built in real-time. This explains why search is so fast: we don’t scan docs; we look up words.

Documents (Storage)

Inverted Index (Lookup)

Index Empty

5. Why Use RediSearch?

  1. Speed: It runs inside Redis (written in C). No network overhead between the database and the search engine.
  2. Simplicity: You don’t need to manage a separate Elasticsearch cluster.
  3. Real-Time: Documents are indexed immediately upon insertion. There is no “refresh interval” lag.

In the next chapter, we will explore RedisGraph for analyzing relationships.