Spring Boot: Redis as a Vector Database
|
This content was generated with the assistance of AI and should be verified against the official Redis documentation before being relied on in production. This section’s bibliography lists the reference material consulted while preparing these pages. |
Examples on this page target Redis Open Source 8.10 (8.10.2) with Spring Boot 4.1, Spring AI 2.0, Redis OM
Spring 1.0, and Jedis 8. This page assumes Vector Search: Query Engine & Vector Sets for the underlying Query
Engine VECTOR field, HNSW, distance metrics, and quantization — none of that theory is repeated here. What
this page covers instead is the Spring-side wiring: the Spring AI RedisVectorStore starter and its
spring.ai.vectorstore.redis.* properties, the RedisVectorStore Java API for RAG, and the same Bookshop KNN
search written by hand three ways (Spring AI, raw Jedis, Redis OM Spring) so the trade-offs between them are
visible side by side.
The Bookshop domain model
Every example below indexes the same three Bookshop records used elsewhere in this section, now as a Java record rather than the raw JSON document Query Engine: Indexing & Data Modeling declares:
package com.example.bookshop.model;
import java.math.BigDecimal;
import java.util.List;
public record Book(
String isbn,
String title,
String author,
List<String> genres,
BigDecimal price,
int year) {
}
The three catalog rows carry ISBNs 9780000000001, 9780000000002, and 9780000000003, matching the values
used throughout Vector Search: Query Engine & Vector Sets and
Query Engine: Indexing & Data Modeling.
spring-ai-starter-vector-store-redis
Spring AI 2.0 ships a dedicated starter that wraps the Query Engine VECTOR field behind its portable
VectorStore abstraction, so application code writes and searches embeddings through the same interface it
would use against any other Spring AI-supported store.
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-vector-store-redis</artifactId>
</dependency>
This derives from Spring AI Redis vector store.
spring.ai.vectorstore.redis.* properties
The starter auto-configures a RedisVectorStore bean from spring.ai.vectorstore.redis.*, alongside the
ordinary Spring Data Redis connection properties (spring.data.redis.host, .port, and so on) it connects
through:
spring:
data:
redis:
host: localhost
port: 6379
ai:
vectorstore:
redis:
index-name: spring-ai-books
prefix: "bookshop:ai:"
initialize-schema: true
hnsw:
m: 16
ef-construction: 200
ef-runtime: 10
-
index-name— the Query Engine index Spring AI creates and searches,spring-ai-booksfor the Bookshop scenario — deliberately a separate index from the hand-builtidx:booksQuery Engine: Indexing & Data Modeling declares, since Spring AI owns this index’s schema end to end. -
prefix— the key prefix each stored document lives under,bookshop:ai:here, mirroring thebookshop:book:convention used by the rest of this section’s hand-written examples. -
initialize-schema— whentrue, Spring AI issues its ownFT.CREATEforindex-nameon startup if the index doesn’t already exist; set tofalseagainst an index provisioned by another process or migration step. -
hnsw.m,hnsw.ef-construction,hnsw.ef-runtime— theHNSWgraph parameters Spring AI passes to theVECTORfield it creates (M,EF_CONSTRUCTION,EF_RUNTIME). The values above are the defaults. See Index algorithms for what each one trades off.
That is the whole property surface: RedisVectorStoreProperties binds only index-name, prefix,
initialize-schema and hnsw.*. The vector field’s dimension is not a property. Spring AI takes it from the
injected EmbeddingModel, so a 384-dimension embedding model produces the 384-dimension field the idx:books
schema also uses. The distance metric and the index algorithm default to COSINE and HNSW, which is what the
Bookshop scenario uses. Choosing something else (L2/IP, or FLAT) means declaring your own
RedisVectorStore bean with RedisVectorStore.builder(redisClient, embeddingModel) and its .distanceMetric(…)
and .vectorAlgorithm(…) options, instead of relying on the auto-configured one. These names derive from
Spring AI Redis vector store.
The RedisVectorStore Java API
Metadata fields and adding documents
A RedisVectorStore bean is autowired like any other Spring bean once the starter and an EmbeddingModel are
on the classpath; metadata fields used in filter expressions (below) are registered explicitly so the store
knows which JSON-document attributes to index as filterable TAG/NUMERIC fields alongside the vector itself:
package com.example.bookshop.search;
import com.example.bookshop.model.Book;
import org.springframework.ai.document.Document;
import org.springframework.ai.vectorstore.redis.RedisVectorStore;
import org.springframework.ai.vectorstore.redis.RedisVectorStore.MetadataField;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Map;
@Service
public class BookIngestionService {
private final RedisVectorStore vectorStore;
public BookIngestionService(RedisVectorStore vectorStore) {
this.vectorStore = vectorStore;
}
public void indexBook(Book book, String description) {
Document document = Document.builder()
.id(book.isbn())
.text(description)
.metadata(Map.of(
"isbn", book.isbn(),
"genres", book.genres(),
"price", book.price().doubleValue(),
"year", book.year()))
.build();
vectorStore.add(List.of(document));
}
}
MetadataField.tag("genres"), MetadataField.numeric("price"), and MetadataField.numeric("year") are
declared on the RedisVectorStore.builder(…) at bean-construction time (not shown above, since the starter’s
auto-configuration builds the bean from properties) when initialize-schema provisions the index itself, so
each metadata key ends up as its own filterable field rather than an opaque JSON blob. This derives from
Spring AI Redis vector store.
Similarity search and portable filter expressions
similaritySearch takes a SearchRequest built with Spring AI’s portable filter-expression DSL, which
RedisVectorStore translates into a Query Engine pre-filter ahead of the KNN clause — the same
filter-before-vector pattern Range
queries and hybrid filtering shows written by hand:
package com.example.bookshop.search;
import org.springframework.ai.document.Document;
import org.springframework.ai.vectorstore.SearchRequest;
import org.springframework.ai.vectorstore.filter.FilterExpressionBuilder;
import org.springframework.ai.vectorstore.redis.RedisVectorStore;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class BookSearchService {
private final RedisVectorStore vectorStore;
public BookSearchService(RedisVectorStore vectorStore) {
this.vectorStore = vectorStore;
}
public List<Document> findSimilar(String queryText, int topK) {
var b = new FilterExpressionBuilder();
SearchRequest request = SearchRequest.builder()
.query(queryText)
.topK(topK)
.filterExpression(b.eq("genres", "fiction").build())
.build();
return vectorStore.similaritySearch(request);
}
}
b.eq("genres", "fiction") compiles to a Query Engine TAG filter equivalent to @genres:{fiction} — RedisVectorStore escapes the braces Redis’s tag syntax requires (see
Query Engine: Search & Aggregations), so application code never writes raw
FT.SEARCH filter syntax itself. A numeric comparison such as b.gt("year", 2000) compiles instead to a
Query Engine NUMERIC range filter ahead of the same KNN clause. This derives from
Spring AI Redis vector store and
Spring AI metadata filters.
RAG with ChatClient and QuestionAnswerAdvisor
QuestionAnswerAdvisor wraps a VectorStore into a ChatClient advisor that runs the retrieval step
automatically ahead of every chat call: it embeds the incoming question, retrieves the nearest documents from
RedisVectorStore, and inserts them into the prompt before it ever reaches the LLM — the same shape
RAG, semantic cache, and
agent memory patterns describes at the Redis-command level, expressed through Spring AI’s advisor chain
instead of a hand-written FT.SEARCH call. The class ships in the spring-ai-vector-store-advisor artifact, in
the org.springframework.ai.chat.client.advisor.vectorstore package.
package com.example.bookshop.chat;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.vectorstore.QuestionAnswerAdvisor;
import org.springframework.ai.vectorstore.SearchRequest;
import org.springframework.ai.vectorstore.redis.RedisVectorStore;
import org.springframework.stereotype.Service;
@Service
public class BookAssistantService {
private final ChatClient chatClient;
public BookAssistantService(ChatClient.Builder chatClientBuilder, RedisVectorStore vectorStore) {
QuestionAnswerAdvisor qaAdvisor = QuestionAnswerAdvisor.builder(vectorStore)
.searchRequest(SearchRequest.builder().topK(5).build())
.build();
this.chatClient = chatClientBuilder.defaultAdvisors(qaAdvisor).build();
}
public String ask(String question) {
return chatClient.prompt()
.user(question)
.call()
.content();
}
}
This derives from Spring AI RAG.
Chunking with TokenTextSplitter
A book description is usually short enough to embed whole, but longer source documents (a full chapter, a support article) need splitting before ingestion so each chunk stays within the embedding model’s input limit and retrieval returns focused passages rather than an entire document:
package com.example.bookshop.search;
import org.springframework.ai.document.Document;
import org.springframework.ai.transformer.splitter.TokenTextSplitter;
import org.springframework.ai.vectorstore.redis.RedisVectorStore;
import java.util.List;
public class BookChunkIngestionService {
private final TokenTextSplitter splitter = new TokenTextSplitter();
private final RedisVectorStore vectorStore;
public BookChunkIngestionService(RedisVectorStore vectorStore) {
this.vectorStore = vectorStore;
}
public void indexLongDescription(Document sourceDocument) {
List<Document> chunks = splitter.apply(List.of(sourceDocument));
vectorStore.add(chunks);
}
}
TokenTextSplitter splits on token count (not raw character count), defaulting to roughly 800 tokens per
chunk with a small overlap between consecutive chunks so a fact near a chunk boundary isn’t lost entirely from
either side. This derives from
Spring AI ETL pipeline — TokenTextSplitter.
Chat memory and semantic cache on Redis
Spring AI’s ChatMemory abstraction can be backed by Redis the same way VectorStore is: a
RedisChatMemoryRepository persists conversation turns as ordinary Redis data structures so a multi-turn
conversation survives across requests and application instances, mirroring the agent memory pattern
RAG, semantic cache, and
agent memory patterns describes.
package com.example.bookshop.chat;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.ai.chat.memory.MessageWindowChatMemory;
import org.springframework.ai.chat.memory.repository.redis.RedisChatMemoryRepository;
import org.springframework.stereotype.Service;
@Service
public class BookConversationService {
private final ChatClient chatClient;
public BookConversationService(ChatClient.Builder chatClientBuilder,
RedisChatMemoryRepository chatMemoryRepository) {
ChatMemory chatMemory = MessageWindowChatMemory.builder()
.chatMemoryRepository(chatMemoryRepository)
.build();
this.chatClient = chatClientBuilder
.defaultAdvisors(MessageChatMemoryAdvisor.builder(chatMemory).build())
.build();
}
}
ChatMemory has no builder of its own; MessageWindowChatMemory is the implementation that keeps the last N
messages of each conversation and delegates storage to the repository. The Redis repository comes from the
spring-ai-starter-model-chat-memory-repository-redis starter, in the
org.springframework.ai.chat.memory.repository.redis package. Every call through this client must also carry a
conversation ID, derived from the authenticated user rather than taken from the request. The repository options, the
other stores and the retention and erasure jobs are covered on
Spring AI: Chat Memory Repositories, and how
MessageChatMemoryAdvisor sits in the advisor chain next to RAG and tool calling on
Spring AI: Advisors.
A semantic cache built on the same RedisVectorStore bean stores a previously answered question’s embedding
and response as ordinary vector-store documents, then checks similaritySearch for a near-duplicate question
before calling the LLM again — Spring AI has no dedicated semantic-cache advisor as of Spring AI 2.0, so this
is typically wired as an explicit pre-check in application code (a similaritySearch call with a distance
threshold, short-circuiting the ChatClient call on a close-enough match) rather than a drop-in advisor. This
derives from
Spring AI chat memory and
Redis RAG quickstart.
The same KNN search, three ways
Raw Jedis
Bypassing Spring AI entirely and issuing FT.CREATE/FT.SEARCH directly, exactly as
KNN queries and
Query Engine: Indexing & Data Modeling describe in full — repeated here only to
show the call shape next to the other two approaches:
package com.example.bookshop.search;
import redis.clients.jedis.RedisClient;
import redis.clients.jedis.search.FTCreateParams;
import redis.clients.jedis.search.Query;
import redis.clients.jedis.search.SearchResult;
import redis.clients.jedis.search.schemafields.TagField;
import redis.clients.jedis.search.schemafields.VectorField;
import java.util.Map;
public class RawJedisBookSearch {
public void createIndex(RedisClient jedis) {
jedis.ftCreate("idx:books",
FTCreateParams.createParams().on(redis.clients.jedis.args.SearchDataType.JSON)
.prefix("bookshop:book:"),
TagField.of("$.genres[*]").as("genres"),
VectorField.of("$.embedding", VectorField.VectorAlgorithm.HNSW, Map.of(
"TYPE", "FLOAT32", "DIM", 384, "DISTANCE_METRIC", "COSINE")).as("embedding"));
}
public SearchResult findSimilar(RedisClient jedis, byte[] queryEmbedding, int k) {
Query query = new Query("*=>[KNN " + k + " @embedding $q AS score]")
.addParam("q", queryEmbedding)
.setSortBy("score", true)
.dialect(2);
return jedis.ftSearch("idx:books", query);
}
}
This is the same query KNN queries walks through in
detail; the same page also covers FT.HYBRID for fusing a full-text score with the vector score, which isn’t
repeated here.
Redis OM Spring
Redis OM Spring maps a Java record or class straight onto a Query Engine index through annotations, closer in spirit to Spring Data than to either of the two approaches above:
package com.example.bookshop.search;
import com.redis.om.spring.annotations.Document;
import com.redis.om.spring.annotations.Indexed;
import com.redis.om.spring.annotations.VectorIndexed;
import redis.clients.jedis.search.schemafields.VectorField.VectorAlgorithm;
import com.redis.om.spring.indexing.DistanceMetric;
import com.redis.om.spring.indexing.VectorType;
import java.math.BigDecimal;
import java.util.List;
@Document
public record IndexedBook(
String isbn,
String title,
String author,
@Indexed List<String> genres,
BigDecimal price,
int year,
@VectorIndexed(
algorithm = VectorAlgorithm.HNSW,
type = VectorType.FLOAT32,
dimension = 384,
distanceMetric = DistanceMetric.COSINE)
float[] embedding) {
}
Querying goes through EntityStream, Redis OM Spring’s fluent, type-safe query builder, rather than a raw
query string:
package com.example.bookshop.search;
import com.redis.om.spring.search.stream.EntityStream;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class RedisOmBookSearch {
private final EntityStream entityStream;
public RedisOmBookSearch(EntityStream entityStream) {
this.entityStream = entityStream;
}
public List<IndexedBook> findSimilar(float[] queryEmbedding, int k) {
return entityStream.of(IndexedBook.class)
.filter(IndexedBook$.GENRES.in("fiction"))
.sorted(IndexedBook$.EMBEDDING.knn(k, queryEmbedding))
.collect(java.util.stream.Collectors.toList());
}
}
IndexedBook$ is a Redis OM Spring-generated metamodel class (analogous to a JPA static metamodel), produced
by its annotation processor at build time from the @Document-annotated IndexedBook record above.
Comparison
Spring AI RedisVectorStore |
Raw Jedis | Redis OM Spring | |
|---|---|---|---|
Abstraction level |
Spring AI’s portable |
Direct |
Annotation-driven entity mapping with a generated metamodel and fluent |
Filter expressions |
Portable |
Hand-written |
Type-safe metamodel predicates ( |
RAG/chat integration |
Built-in ( |
None — application code wires retrieval into a prompt itself |
None — application code wires retrieval into a prompt itself |
Portability |
Swappable for another Spring AI-supported vector store with no application code change |
Redis-specific, tied to the Jedis client API |
Redis-specific, tied to Redis OM Spring’s entity model |
Best fit |
A Spring AI-based RAG/chat application that wants a store-agnostic API |
Fine-grained control over index schema, query options, or |
A Spring Data-style application already modeling other Redis data through Redis OM Spring |
For the same comparison against other vector stores entirely, see
RAG Integration Patterns (Qdrant),
Vector & semantic search (Elasticsearch dense_vector/semantic_text),
and Vector Search and GenAI (Neo4j VECTOR type and GraphRAG); the underlying HNSW
graph algorithm any of these approximate-search engines run is covered once, in depth, in
kNN / HNSW vector search.
Ingestion and RAG query flow
Related pages
-
Vector Search: Query Engine & Vector Sets — the underlying Query Engine
VECTORfield, KNN/range queries,FT.HYBRID, vector sets, and the RAG/semantic-cache/agent-memory patterns this page wires into Spring Boot. -
Query Engine: Indexing & Data Modeling — the Bookshop
idx:booksschema this page’s raw-Jedis comparison builds on. -
RAG Integration Patterns — the same RAG ingestion/retrieval shape built on Qdrant instead, for comparison.
-
Integrating with Spring AI — the vendor-neutral Spring AI
VectorStoresurface this page’sRedisVectorStorewiring implements. -
RAG Fundamentals — the general RAG pipeline this page’s Spring AI setup builds on.
-
PostgreSQL & pgvector — the pgvector alternative for teams already running PostgreSQL.
References
-
Spring AI Redis vector store — the
spring-ai-starter-vector-store-redisstarter, its properties, and theRedisVectorStoreAPI. -
Spring AI vector databases — the portable
VectorStore/Document/SearchRequestabstraction andFilterExpressionBuilderDSL. -
Spring AI RAG —
QuestionAnswerAdvisorand the retrieval-augmentedChatClientpattern. -
Spring AI ETL pipeline —
TokenTextSplitterand the document transformer pipeline. -
Spring AI chat memory —
ChatMemoryand its Redis-backed repository.