|
| 1 | +# Vector Search with Redis OM Spring (SpringBoot) |
| 2 | + |
| 3 | +Vector similarity search (also known as semantic search) is a powerful technique that allows you to find items based on their semantic meaning rather than exact keyword matches. Redis Query Engine supports vector similarity search through its vector indexing capabilities, enabling you to implement semantic search applications with high performance and low latency. |
| 4 | + |
| 5 | +This demo showcases how to implement vector similarity search using Redis OM Spring, a library that simplifies working with Redis data models and the Redis Query Engine. |
| 6 | + |
| 7 | +## Learning resources: |
| 8 | + |
| 9 | +- Article: [Semantic Search with Spring Boot & Redis](https://raphaeldelio.com/2025/04/29/semantic-search-with-spring-boot-redis/) |
| 10 | +- Video: [Autocomplete in Spring with Redis](https://www.youtube.com/watch?v=rjaR1PR5gVk) |
| 11 | +- Video: [What is an embedding model?](https://youtu.be/0U1S0WSsPuE) |
| 12 | +- Video: [Exact vs Approximate Nearest Neighbors - What's the difference?](https://youtu.be/9NvO-VdjY80) |
| 13 | +- Video: [What is semantic search?](https://youtu.be/o3XN4dImESE) |
| 14 | +- Video: [What is a vector database?](https://youtu.be/Yhv19le0sBw) |
| 15 | + |
| 16 | + |
| 17 | +## Repository |
| 18 | + |
| 19 | +The repository for this demo can be found [here](https://github.com/redis-developer/redis-springboot-resources/tree/main/search/vector-search) |
| 20 | + |
| 21 | +## Requirements |
| 22 | + |
| 23 | +To run this demo, you’ll need the following installed on your system: |
| 24 | +- Docker – [Install Docker](https://docs.docker.com/get-docker/) |
| 25 | +- Docker Compose – Included with Docker Desktop or available via CLI installation guide |
| 26 | + |
| 27 | +## Running the demo |
| 28 | + |
| 29 | +The easiest way to run the demo is with Docker Compose, which sets up all required services in one command. |
| 30 | + |
| 31 | +### Step 1: Clone the repository |
| 32 | + |
| 33 | +If you haven’t already: |
| 34 | + |
| 35 | +```bash |
| 36 | +git clone https://github.com/redis-developer/redis-springboot-recipes.git |
| 37 | +cd redis-springboot-recipes/search/full-text-search-and-autocomplete |
| 38 | +``` |
| 39 | + |
| 40 | +### Step 2: Start the services |
| 41 | + |
| 42 | +```bash |
| 43 | +docker compose up --build |
| 44 | +``` |
| 45 | + |
| 46 | +This will start: |
| 47 | + |
| 48 | +- redis: for storing documents |
| 49 | +- redis-insight: a UI to explore the Redis data |
| 50 | +- vector-search-app: the Spring Boot app that implements vector search |
| 51 | + |
| 52 | +## Using the demo |
| 53 | + |
| 54 | +When all of your services are up and running. Go to `localhost:8080` to access the demo. |
| 55 | + |
| 56 | +If you search using the extract box, the system will perform semantic search and find items on the database that are semantically similar to your query: |
| 57 | + |
| 58 | + |
| 59 | + |
| 60 | +You can also apply filters for pre-filtering the results before applying semantic search: |
| 61 | + |
| 62 | + |
| 63 | + |
| 64 | +This demo also supports autocompletion of the title: |
| 65 | + |
| 66 | + |
| 67 | + |
| 68 | +### Redis Insight |
| 69 | + |
| 70 | +RedisInsight is a graphical tool developed by Redis to help developers and administrators interact with and manage Redis databases more efficiently. It provides a visual interface for exploring keys, running commands, analyzing memory usage, and monitoring performance metrics in real-time. RedisInsight supports features like full-text search, time series, streams, and vector data structures, making it especially useful for working with more advanced Redis use cases. With its intuitive UI, it simplifies debugging, optimizing queries, and understanding data patterns without requiring deep familiarity with the Redis CLI. |
| 71 | + |
| 72 | +The Docker Compose file will also spin up an instance of Redis Insight. We can access it by going to `localhost:5540`: |
| 73 | + |
| 74 | +If we go to Redis Insight, we will be able to see the data stored in Redis: |
| 75 | + |
| 76 | + |
| 77 | + |
| 78 | +And if run the command `FT.INFO 'com.redis.fulltextsearchandautocomplete.domain.MovieIdx'`, we'll be able to see the schema that was created for indexing our documents efficiently: |
| 79 | + |
| 80 | + |
| 81 | + |
| 82 | +## How It Is Implemented |
| 83 | + |
| 84 | +The application uses Redis OM Spring to vectorize documents and perform vector similarity search. Here's how it works: |
| 85 | + |
| 86 | +### Defining Vector Fields with Redis OM Spring Annotations |
| 87 | + |
| 88 | +Documents are defined as Java classes with Redis OM Spring annotations that specify how they should be vectorized and indexed: |
| 89 | + |
| 90 | +```java |
| 91 | +@Document |
| 92 | +public class Movie { |
| 93 | + // Other fields... |
| 94 | + |
| 95 | + @Vectorize( |
| 96 | + destination = "embeddedExtract", |
| 97 | + embeddingType = EmbeddingType.SENTENCE |
| 98 | + ) |
| 99 | + private String extract; |
| 100 | + |
| 101 | + @Indexed( |
| 102 | + schemaFieldType = SchemaFieldType.VECTOR, |
| 103 | + algorithm = VectorField.VectorAlgorithm.HNSW, |
| 104 | + type = VectorType.FLOAT32, |
| 105 | + dimension = 384, |
| 106 | + distanceMetric = DistanceMetric.COSINE, |
| 107 | + initialCapacity = 10 |
| 108 | + ) |
| 109 | + private float[] embeddedExtract; |
| 110 | + |
| 111 | + // Getters and setters... |
| 112 | +} |
| 113 | +``` |
| 114 | + |
| 115 | +Let's break down the annotations: |
| 116 | + |
| 117 | +- `@Vectorize`: Automatically generates vector embeddings for the text field |
| 118 | + - `destination`: Specifies the field where the embedding will be stored |
| 119 | + - `embeddingType`: Defines the granularity of the embedding (SENTENCE in this case) |
| 120 | + |
| 121 | +- `@Indexed` with vector parameters: |
| 122 | + - `schemaFieldType = SchemaFieldType.VECTOR`: Marks this as a vector field |
| 123 | + - `algorithm = VectorField.VectorAlgorithm.HNSW`: Uses the Hierarchical Navigable Small World algorithm for efficient approximate nearest neighbor search |
| 124 | + - `type = VectorType.FLOAT32`: Specifies the vector data type |
| 125 | + - `dimension = 384`: Sets the vector dimension (must match the number of dimensions output by the embedding model) |
| 126 | + - `distanceMetric = DistanceMetric.COSINE`: Uses cosine similarity for distance calculation |
| 127 | + |
| 128 | +### Storing and Vectorizing Documents |
| 129 | + |
| 130 | +When documents are saved to Redis using the repository, Redis OM Spring automatically generates vector embeddings: |
| 131 | + |
| 132 | +```java |
| 133 | +public void loadAndSaveMovies(String filePath) throws Exception { |
| 134 | + // Load movies from JSON file |
| 135 | + List<Movie> movies = objectMapper.readValue(is, new TypeReference<>() {}); |
| 136 | + |
| 137 | + // Save movies in batches |
| 138 | + int batchSize = 500; |
| 139 | + for (int i = 0; i < unprocessedMovies.size(); i += batchSize) { |
| 140 | + int end = Math.min(i + batchSize, unprocessedMovies.size()); |
| 141 | + List<Movie> batch = unprocessedMovies.subList(i, end); |
| 142 | + movieRepository.saveAll(batch); |
| 143 | + } |
| 144 | +} |
| 145 | +``` |
| 146 | + |
| 147 | +When `movieRepository.saveAll(batch)` is called: |
| 148 | +1. Redis OM Spring generates vector embeddings for the `extract` field |
| 149 | +2. The embeddings are stored in the `embeddedExtract` field |
| 150 | +3. The documents are saved to Redis with their vector embeddings |
| 151 | +4. Redis creates a vector index for efficient similarity search |
| 152 | + |
| 153 | +### Performing Vector Similarity Search |
| 154 | + |
| 155 | +Vector similarity search is implemented using Redis OM Spring's EntityStream API: |
| 156 | + |
| 157 | +```java |
| 158 | +public Map<String, Object> search( |
| 159 | + String title, |
| 160 | + String extract, |
| 161 | + List<String> actors, |
| 162 | + Integer year, |
| 163 | + List<String> genres, |
| 164 | + Integer numberOfNearestNeighbors |
| 165 | +) { |
| 166 | + SearchStream<Movie> stream = entityStream.of(Movie.class); |
| 167 | + |
| 168 | + if (extract != null) { |
| 169 | + // Convert search query to vector embedding |
| 170 | + float[] embeddedQuery = embedder.getTextEmbeddingsAsFloats(List.of(extract), Movie$.EXTRACT).getFirst(); |
| 171 | + |
| 172 | + // Perform KNN search with the embedded query |
| 173 | + stream = stream.filter(Movie$.EMBEDDED_EXTRACT.knn(numberOfNearestNeighbors, embeddedQuery)) |
| 174 | + .sorted(Movie$._EMBEDDED_EXTRACT_SCORE); |
| 175 | + } |
| 176 | + |
| 177 | + // Apply additional filters |
| 178 | + List<Pair<Movie, Double>> matchedMovies = stream |
| 179 | + .filter(Movie$.TITLE.containing(title)) |
| 180 | + .filter(Movie$.CAST.eq(actors)) |
| 181 | + .filter(Movie$.YEAR.eq(year)) |
| 182 | + .filter(Movie$.GENRES.eq(genres)) |
| 183 | + .map(Fields.of(Movie$._THIS, Movie$._EMBEDDED_EXTRACT_SCORE)) |
| 184 | + .collect(Collectors.toList()); |
| 185 | + |
| 186 | + return result; |
| 187 | +} |
| 188 | +``` |
| 189 | + |
| 190 | +This method: |
| 191 | +1. Converts the search query text into a vector embedding using the same embedding model |
| 192 | +2. Performs a K-Nearest Neighbors (KNN) search to find the most similar vectors |
| 193 | +3. Applies additional filters to narrow down the results (pre-filtering) |
| 194 | +4. Returns the matched movies along with their similarity scores |
| 195 | + |
| 196 | +### Combining Vector Search with Autocomplete |
| 197 | + |
| 198 | +The application also supports autocomplete functionality alongside vector search: |
| 199 | + |
| 200 | +```java |
| 201 | +public interface MovieRepository extends RedisDocumentRepository<Movie, String> { |
| 202 | + List<Suggestion> autoCompleteTitle(String title, AutoCompleteOptions options); |
| 203 | +} |
| 204 | +``` |
| 205 | + |
| 206 | +The `autoCompleteTitle` method is automatically implemented by Redis OM Spring based on the `@AutoComplete` annotation on the `title` field in the Movie class. |
| 207 | + |
| 208 | +### How Redis Indexes the Vectors |
| 209 | + |
| 210 | +When the application starts, Redis OM Spring creates a vector index in Redis based on the annotations: |
| 211 | + |
| 212 | +``` |
| 213 | +FT.CREATE idx:com.redis.vectorsearch.domain.Movie ON JSON PREFIX 1 com.redis.vectorsearch.domain.Movie: SCHEMA |
| 214 | + $.title AS title TEXT SORTABLE |
| 215 | + $.year AS year NUMERIC SORTABLE |
| 216 | + $.cast AS cast TAG |
| 217 | + $.genres AS genres TAG |
| 218 | + $.embeddedExtract AS embeddedExtract VECTOR HNSW 6 TYPE FLOAT32 DIM 384 DISTANCE_METRIC COSINE INITIAL_CAP 10 |
| 219 | +``` |
| 220 | + |
| 221 | +This index enables efficient vector similarity search with the following features: |
| 222 | +- HNSW algorithm for approximate nearest neighbor search |
| 223 | +- 384-dimensional FLOAT32 vectors |
| 224 | +- Cosine similarity as the distance metric |
| 225 | +- Additional text and tag fields for filtering |
| 226 | + |
| 227 | +This approach allows for high-performance semantic search operations, even with large datasets, by leveraging Redis's in-memory data structures and the Redis Query Engine's vector search capabilities. |
0 commit comments