diff --git a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/vectordbs.adoc b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/vectordbs.adoc index 851456dd75d..10bb7828331 100644 --- a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/vectordbs.adoc +++ b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/vectordbs.adoc @@ -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 similaritySearch(SearchRequest request); + + default List 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(); @@ -41,17 +61,15 @@ public interface VectorStore extends DocumentWriter { default void delete(String filterExpression) { ... }; - List similaritySearch(String query); - - List similaritySearch(SearchRequest request); - default Optional 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 { @@ -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 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 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 = - List similarDocuments = store.similaritySearch(this.question); +@Autowired +VectorStoreRetriever retriever; // Could also use VectorStore here + +String question = ""; +List 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 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 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 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 findSimilarDocuments(String query) { + return retriever.similaritySearch(query); + } + + public List 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 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. diff --git a/spring-ai-vector-store/src/main/java/org/springframework/ai/vectorstore/VectorStore.java b/spring-ai-vector-store/src/main/java/org/springframework/ai/vectorstore/VectorStore.java index ec9a6601c47..d32ba2a05db 100644 --- a/spring-ai-vector-store/src/main/java/org/springframework/ai/vectorstore/VectorStore.java +++ b/spring-ai-vector-store/src/main/java/org/springframework/ai/vectorstore/VectorStore.java @@ -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(); @@ -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 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 similaritySearch(String query) { - return this.similaritySearch(SearchRequest.builder().query(query).build()); - } - /** * Returns the native client if available in this vector store implementation. * diff --git a/spring-ai-vector-store/src/main/java/org/springframework/ai/vectorstore/VectorStoreRetriever.java b/spring-ai-vector-store/src/main/java/org/springframework/ai/vectorstore/VectorStoreRetriever.java new file mode 100644 index 00000000000..4877af21099 --- /dev/null +++ b/spring-ai-vector-store/src/main/java/org/springframework/ai/vectorstore/VectorStoreRetriever.java @@ -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. + * + *

+ * 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 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 similaritySearch(String query) { + return this.similaritySearch(SearchRequest.builder().query(query).build()); + } + +} diff --git a/spring-ai-vector-store/src/main/java/org/springframework/ai/vectorstore/package-info.java b/spring-ai-vector-store/src/main/java/org/springframework/ai/vectorstore/package-info.java index 3edee23fc81..685651703da 100644 --- a/spring-ai-vector-store/src/main/java/org/springframework/ai/vectorstore/package-info.java +++ b/spring-ai-vector-store/src/main/java/org/springframework/ai/vectorstore/package-info.java @@ -14,6 +14,45 @@ * limitations under the License. */ +/** + * Provides interfaces and implementations for working with vector databases in Spring AI. + *

+ * 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: + *

    + *
  • {@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.
  • + *
  • {@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.
  • + *
+ *

+ * The package also includes supporting classes such as: + *

    + *
  • {@link org.springframework.ai.vectorstore.SearchRequest} - Configures similarity + * search parameters including query text, result limits, similarity thresholds, and + * metadata filters.
  • + *
  • {@link org.springframework.ai.vectorstore.filter.Filter} - Provides filtering + * capabilities for metadata-based document selection (located in the filter + * subpackage).
  • + *
+ *

+ * 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;