Skip to content

Extract VectorStoreRetriever interface from VectorStore #3827

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 17, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
191 changes: 170 additions & 21 deletions spring-ai-docs/src/main/antora/modules/ROOT/pages/api/vectordbs.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,32 @@ The last section is intended to demystify the underlying approach of similarity
== API Overview
This section serves as a guide to the `VectorStore` interface and its associated classes within the Spring AI framework.

Spring AI offers an abstracted API for interacting with vector databases through the `VectorStore` interface.
Spring AI offers an abstracted API for interacting with vector databases through the `VectorStore` interface and its read-only counterpart, the `VectorStoreRetriever` interface.

Here is the `VectorStore` interface definition:
=== VectorStoreRetriever Interface

Spring AI provides a read-only interface called `VectorStoreRetriever` that exposes only the document retrieval functionality:

```java
@FunctionalInterface
public interface VectorStoreRetriever {

List<Document> similaritySearch(SearchRequest request);

default List<Document> similaritySearch(String query) {
return this.similaritySearch(SearchRequest.builder().query(query).build());
}
}
```

This functional interface is designed for use cases where you only need to retrieve documents from a vector store without performing any mutation operations. It follows the principle of least privilege by exposing only the necessary functionality for document retrieval.

=== VectorStore Interface

The `VectorStore` interface extends `VectorStoreRetriever` and adds mutation capabilities:

```java
public interface VectorStore extends DocumentWriter {
public interface VectorStore extends DocumentWriter, VectorStoreRetriever {

default String getName() {
return this.getClass().getSimpleName();
Expand All @@ -41,17 +61,15 @@ public interface VectorStore extends DocumentWriter {

default void delete(String filterExpression) { ... };

List<Document> similaritySearch(String query);

List<Document> similaritySearch(SearchRequest request);

default <T> Optional<T> getNativeClient() {
return Optional.empty();
}
}
```

and the related `SearchRequest` builder:
The `VectorStore` interface combines both read and write operations, allowing you to add, delete, and search for documents in a vector database.

=== SearchRequest Builder

```java
public class SearchRequest {
Expand Down Expand Up @@ -392,32 +410,163 @@ For example, with OpenAI's ChatGPT, we use the `OpenAiEmbeddingModel` and a mode

The Spring Boot starter's auto-configuration for OpenAI makes an implementation of `EmbeddingModel` available in the Spring application context for dependency injection.

The general usage of loading data into a vector store is something you would do in a batch-like job, by first loading data into Spring AI's `Document` class and then calling the `save` method.
=== Writing to a Vector Store

The general usage of loading data into a vector store is something you would do in a batch-like job, by first loading data into Spring AI's `Document` class and then calling the `add` method on the `VectorStore` interface.

Given a `String` reference to a source file that represents a JSON file with data we want to load into the vector database, we use Spring AI's `JsonReader` to load specific fields in the JSON, which splits them up into small pieces and then passes those small pieces to the vector store implementation.
The `VectorStore` implementation computes the embeddings and stores the JSON and the embedding in the vector database:

```java
@Autowired
VectorStore vectorStore;

void load(String sourceFile) {
JsonReader jsonReader = new JsonReader(new FileSystemResource(sourceFile),
"price", "name", "shortDescription", "description", "tags");
List<Document> documents = jsonReader.get();
this.vectorStore.add(documents);
}
@Autowired
VectorStore vectorStore;

void load(String sourceFile) {
JsonReader jsonReader = new JsonReader(new FileSystemResource(sourceFile),
"price", "name", "shortDescription", "description", "tags");
List<Document> documents = jsonReader.get();
this.vectorStore.add(documents);
}
```

Later, when a user question is passed into the AI model, a similarity search is done to retrieve similar documents, which are then "'stuffed'" into the prompt as context for the user's question.
=== Reading from a Vector Store

Later, when a user question is passed into the AI model, a similarity search is done to retrieve similar documents, which are then "stuffed" into the prompt as context for the user's question.

For read-only operations, you can use either the `VectorStore` interface or the more focused `VectorStoreRetriever` interface:

```java
String question = <question from user>
List<Document> similarDocuments = store.similaritySearch(this.question);
@Autowired
VectorStoreRetriever retriever; // Could also use VectorStore here

String question = "<question from user>";
List<Document> similarDocuments = retriever.similaritySearch(question);

// Or with more specific search parameters
SearchRequest request = SearchRequest.builder()
.query(question)
.topK(5) // Return top 5 results
.similarityThreshold(0.7) // Only return results with similarity score >= 0.7
.build();

List<Document> filteredDocuments = retriever.similaritySearch(request);
```

Additional options can be passed into the `similaritySearch` method to define how many documents to retrieve and a threshold of the similarity search.

=== Separation of Read and Write Operations

Using the separate interfaces allows you to clearly define which components need write access and which only need read access:

```java
// Write operations in a service that needs full access
@Service
class DocumentIndexer {
private final VectorStore vectorStore;

DocumentIndexer(VectorStore vectorStore) {
this.vectorStore = vectorStore;
}

public void indexDocuments(List<Document> documents) {
vectorStore.add(documents);
}
}

// Read-only operations in a service that only needs retrieval
@Service
class DocumentRetriever {
private final VectorStoreRetriever retriever;

DocumentRetriever(VectorStoreRetriever retriever) {
this.retriever = retriever;
}

public List<Document> findSimilar(String query) {
return retriever.similaritySearch(query);
}
}
```

This separation of concerns helps create more maintainable and secure applications by limiting access to mutation operations only to components that truly need them.

== Retrieval Operations with VectorStoreRetriever

The `VectorStoreRetriever` interface provides a read-only view of a vector store, exposing only the similarity search functionality. This follows the principle of least privilege and is particularly useful in RAG (Retrieval-Augmented Generation) applications where you only need to retrieve documents without modifying the underlying data.

=== Benefits of Using VectorStoreRetriever

1. **Separation of Concerns**: Clearly separates read operations from write operations.
2. **Interface Segregation**: Clients that only need retrieval functionality aren't exposed to mutation methods.
3. **Functional Interface**: Can be implemented with lambda expressions or method references for simple use cases.
4. **Reduced Dependencies**: Components that only need to perform searches don't need to depend on the full `VectorStore` interface.

=== Example Usage

You can use `VectorStoreRetriever` directly when you only need to perform similarity searches:

```java
@Service
public class DocumentRetrievalService {

private final VectorStoreRetriever retriever;

public DocumentRetrievalService(VectorStoreRetriever retriever) {
this.retriever = retriever;
}

public List<Document> findSimilarDocuments(String query) {
return retriever.similaritySearch(query);
}

public List<Document> findSimilarDocumentsWithFilters(String query, String country) {
SearchRequest request = SearchRequest.builder()
.query(query)
.topK(5)
.filterExpression("country == '" + country + "'")
.build();

return retriever.similaritySearch(request);
}
}
```

In this example, the service only depends on the `VectorStoreRetriever` interface, making it clear that it only performs retrieval operations and doesn't modify the vector store.

=== Integration with RAG Applications

The `VectorStoreRetriever` interface is particularly useful in RAG applications, where you need to retrieve relevant documents to provide context for an AI model:

```java
@Service
public class RagService {

private final VectorStoreRetriever retriever;
private final ChatModel chatModel;

public RagService(VectorStoreRetriever retriever, ChatModel chatModel) {
this.retriever = retriever;
this.chatModel = chatModel;
}

public String generateResponse(String userQuery) {
// Retrieve relevant documents
List<Document> relevantDocs = retriever.similaritySearch(userQuery);

// Extract content from documents to use as context
String context = relevantDocs.stream()
.map(Document::getContent)
.collect(Collectors.joining("\n\n"));

// Generate response using the retrieved context
String prompt = "Context information:\n" + context + "\n\nUser query: " + userQuery;
return chatModel.generate(prompt);
}
}
```

This pattern allows for a clean separation between the retrieval component and the generation component in RAG applications.

== Metadata Filters [[metadata-filters]]

This section describes various filters that you can use against the results of a query.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
* This interface allows for adding, deleting, and searching documents based on their
* similarity to a given query.
*/
public interface VectorStore extends DocumentWriter {
public interface VectorStore extends DocumentWriter, VectorStoreRetriever {

default String getName() {
return this.getClass().getSimpleName();
Expand Down Expand Up @@ -83,26 +83,6 @@ default void delete(String filterExpression) {
this.delete(textExpression);
}

/**
* Retrieves documents by query embedding similarity and metadata filters to retrieve
* exactly the number of nearest-neighbor results that match the request criteria.
* @param request Search request for set search parameters, such as the query text,
* topK, similarity threshold and metadata filter expressions.
* @return Returns documents th match the query request conditions.
*/
List<Document> similaritySearch(SearchRequest request);

/**
* Retrieves documents by query embedding similarity using the default
* {@link SearchRequest}'s' search criteria.
* @param query Text to use for embedding similarity comparison.
* @return Returns a list of documents that have embeddings similar to the query text
* embedding.
*/
default List<Document> similaritySearch(String query) {
return this.similaritySearch(SearchRequest.builder().query(query).build());
}

/**
* Returns the native client if available in this vector store implementation.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* Copyright 2023-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.springframework.ai.vectorstore;

import java.util.List;

import org.springframework.ai.document.Document;

/**
* A functional interface that provides read-only access to vector store retrieval
* operations. This interface extracts only the document retrieval functionality from
* {@link VectorStore}, ensuring that mutation operations (add, delete) are not exposed.
*
* <p>
* This is useful when you want to provide retrieval-only access to a vector store,
* following the principle of least privilege by not exposing write operations.
*
* @author Mark Pollack
* @since 1.0.0
*/
@FunctionalInterface
public interface VectorStoreRetriever {

/**
* Retrieves documents by query embedding similarity and metadata filters to retrieve
* exactly the number of nearest-neighbor results that match the request criteria.
* @param request Search request for set search parameters, such as the query text,
* topK, similarity threshold and metadata filter expressions.
* @return Returns documents that match the query request conditions.
*/
List<Document> similaritySearch(SearchRequest request);

/**
* Retrieves documents by query embedding similarity using the default
* {@link SearchRequest}'s search criteria.
* @param query Text to use for embedding similarity comparison.
* @return Returns a list of documents that have embeddings similar to the query text
* embedding.
*/
default List<Document> similaritySearch(String query) {
return this.similaritySearch(SearchRequest.builder().query(query).build());
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,45 @@
* limitations under the License.
*/

/**
* Provides interfaces and implementations for working with vector databases in Spring AI.
* <p>
* Vector databases store embeddings (numerical vector representations) of data along with
* the original content and metadata, enabling similarity search operations. This package
* contains two primary interfaces:
* <ul>
* <li>{@link org.springframework.ai.vectorstore.VectorStoreRetriever} - A read-only
* functional interface that provides similarity search capabilities for retrieving
* documents from a vector store. This interface follows the principle of least privilege
* by exposing only retrieval operations.</li>
* <li>{@link org.springframework.ai.vectorstore.VectorStore} - Extends
* VectorStoreRetriever and adds mutation operations (add, delete) for managing documents
* in a vector store. This interface provides complete access to vector database
* functionality.</li>
* </ul>
* <p>
* The package also includes supporting classes such as:
* <ul>
* <li>{@link org.springframework.ai.vectorstore.SearchRequest} - Configures similarity
* search parameters including query text, result limits, similarity thresholds, and
* metadata filters.</li>
* <li>{@link org.springframework.ai.vectorstore.filter.Filter} - Provides filtering
* capabilities for metadata-based document selection (located in the filter
* subpackage).</li>
* </ul>
* <p>
* This package is designed to support Retrieval Augmented Generation (RAG) applications
* by providing a clean separation between read and write operations, allowing components
* to access only the functionality they need.
*
* @see org.springframework.ai.vectorstore.VectorStoreRetriever
* @see org.springframework.ai.vectorstore.VectorStore
* @see org.springframework.ai.vectorstore.SearchRequest
* @see org.springframework.ai.vectorstore.filter.Filter
*
* @author Mark Pollack
* @since 1.0.0
*/
@NonNullApi
@NonNullFields
package org.springframework.ai.vectorstore;
Expand Down