CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-org-springframework-ai--spring-ai-vector-store

Common vector store functionality for Spring AI providing a portable abstraction layer for integrating vector databases with comprehensive filtering, similarity search, and observability support.

Overview
Eval results
Files

vector-store-api.mddocs/reference/

Vector Store Operations

Core vector database operations for managing documents with embeddings and performing similarity-based searches. The VectorStore interface provides a portable abstraction that works across multiple vector database providers in the Spring AI ecosystem.

Capabilities

VectorStore Interface

The main interface for vector store operations, combining document writing and retrieval capabilities.

package org.springframework.ai.vectorstore;

import org.springframework.ai.document.Document;
import org.springframework.ai.document.DocumentWriter;
import org.springframework.ai.vectorstore.filter.Filter;
import io.micrometer.observation.ObservationRegistry;
import java.util.List;
import java.util.Optional;

/**
 * Main interface for managing and querying documents in a vector database.
 * Extends DocumentWriter and VectorStoreRetriever for complete CRUD operations.
 * 
 * This interface provides a portable abstraction that works across multiple
 * vector database providers including Pinecone, Chroma, Weaviate, PgVector,
 * Milvus, Qdrant, Redis, and SimpleVectorStore.
 */
public interface VectorStore extends DocumentWriter, VectorStoreRetriever {
    
    /**
     * Returns the name of this vector store implementation.
     * Defaults to the simple class name.
     * 
     * Used for logging, metrics, and debugging to identify the vector store type.
     * 
     * @return The name of the vector store (e.g., "SimpleVectorStore", "PineconeVectorStore")
     */
    default String getName() {
        return this.getClass().getSimpleName();
    }
    
    /**
     * Adds documents to the vector store.
     * Documents will be embedded using the configured EmbeddingModel.
     * 
     * Behavior:
     * - If a document already has an embedding, it will be used directly
     * - If a document lacks an embedding, the EmbeddingModel will generate one
     * - Documents are batched according to the configured BatchingStrategy
     * - Duplicate IDs may cause errors (provider-dependent)
     * 
     * @param documents List of Document objects to store
     * @throws RuntimeException if the provider detects duplicate IDs
     * @throws IllegalArgumentException if documents list is null or empty
     */
    void add(List<Document> documents);
    
    /**
     * Default implementation of DocumentWriter.accept() that delegates to add().
     * Enables VectorStore to be used as a Consumer<List<Document>> in functional pipelines.
     * 
     * @param documents List of documents to accept and store
     */
    @Override
    default void accept(List<Document> documents) {
        this.add(documents);
    }
    
    /**
     * Deletes documents by their IDs.
     * 
     * Behavior:
     * - If an ID doesn't exist, it is silently ignored (no error thrown)
     * - Deletion is typically synchronous but may be eventually consistent
     * - Empty ID list is a no-op
     * 
     * @param idList List of document IDs to delete
     * @throws IllegalArgumentException if idList is null
     */
    void delete(List<String> idList);
    
    /**
     * Deletes documents matching the filter expression.
     * Uses portable Filter.Expression for store-agnostic filtering.
     * 
     * Behavior:
     * - Filters are converted to provider-specific format at runtime
     * - Deletion is typically synchronous but may be eventually consistent
     * - No error if no documents match the filter
     * 
     * @param filterExpression Filter.Expression defining which documents to delete
     * @throws IllegalStateException if the underlying delete operation fails
     * @throws UnsupportedOperationException if filter-based deletion is not supported by the provider
     */
    void delete(Filter.Expression filterExpression);
    
    /**
     * Deletes documents using a SQL-like filter string.
     * Converts the string to a Filter.Expression and delegates to delete(Expression).
     * 
     * Supported syntax:
     * - Comparison: ==, !=, <, <=, >, >=
     * - Logical: &&, ||, NOT
     * - Inclusion: IN [...], NOT IN [...]
     * - Null checks: IS NULL, IS NOT NULL
     * - Grouping: ( )
     * 
     * Examples:
     * - "year >= 2020 && category == 'old'"
     * - "status IN ['archived', 'deleted']"
     * - "(year < 2020 OR featured == false) && status != 'active'"
     * 
     * @param filterExpression String filter expression
     * @throws IllegalArgumentException if the filter expression is null or invalid syntax
     * @throws IllegalStateException if the underlying delete operation fails
     * @throws UnsupportedOperationException if filter-based deletion is not supported
     */
    default void delete(String filterExpression) {
        if (filterExpression == null) {
            throw new IllegalArgumentException("Filter expression cannot be null");
        }
        FilterExpressionTextParser parser = new FilterExpressionTextParser();
        Filter.Expression expression = parser.parse(filterExpression);
        this.delete(expression);
    }
    
    /**
     * Performs similarity search using SearchRequest configuration.
     * Returns documents ordered by similarity score (highest first).
     * 
     * Process:
     * 1. Query text is embedded using the configured EmbeddingModel
     * 2. Vector similarity search is performed (typically cosine similarity)
     * 3. Results are filtered by metadata filter expression (if provided)
     * 4. Results are filtered by similarity threshold (client-side post-processing)
     * 5. Top-K results are returned, ordered by similarity score descending
     * 
     * @param request SearchRequest with query, topK, threshold, and filter parameters
     * @return List of similar documents ordered by similarity (highest first)
     */
    List<Document> similaritySearch(SearchRequest request);
    
    /**
     * Convenience method for simple text-based similarity search.
     * Uses default topK (4) and no filtering.
     * 
     * Equivalent to:
     * SearchRequest.builder()
     *     .query(query)
     *     .topK(4)
     *     .similarityThreshold(0.0)
     *     .build()
     * 
     * @param query The text query to search for
     * @return List of similar documents (up to 4 results)
     */
    default List<Document> similaritySearch(String query) {
        return this.similaritySearch(SearchRequest.builder().query(query).build());
    }
    
    /**
     * Returns the native vector store client if available.
     * Useful for accessing provider-specific features not exposed by the portable API.
     * 
     * Due to Java type erasure, callers should specify the expected client type:
     * 
     * Example:
     * Optional<PineconeClient> client = vectorStore.getNativeClient();
     * client.ifPresent(c -> c.describeIndexStats());
     * 
     * @param <T> The type of the native client
     * @return Optional containing the native client, or empty if unavailable
     */
    default <T> Optional<T> getNativeClient() {
        return Optional.empty();
    }
    
    /**
     * Builder interface for VectorStore implementations.
     * Provides fluent API for configuring vector store instances.
     * 
     * @param <T> The concrete builder type for method chaining
     */
    interface Builder<T extends Builder<T>> {
        
        /**
         * Sets the registry for collecting observations and metrics.
         * Defaults to ObservationRegistry.NOOP if not specified.
         * 
         * @param observationRegistry The registry to use for observations
         * @return The builder instance for method chaining
         */
        T observationRegistry(ObservationRegistry observationRegistry);
        
        /**
         * Sets a custom convention for creating observations.
         * If not specified, DefaultVectorStoreObservationConvention will be used.
         * 
         * @param convention The custom observation convention to use
         * @return The builder instance for method chaining
         */
        T customObservationConvention(VectorStoreObservationConvention convention);
        
        /**
         * Sets the batching strategy for document operations.
         * Defaults to TokenCountBatchingStrategy if not specified.
         * 
         * @param batchingStrategy The strategy to use
         * @return The builder instance for method chaining
         */
        T batchingStrategy(BatchingStrategy batchingStrategy);
        
        /**
         * Builds and returns a new VectorStore instance with the configured settings.
         * 
         * @return A new VectorStore instance
         */
        VectorStore build();
    }
}

Usage Examples:

import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.ai.document.Document;
import java.util.List;
import java.util.Map;

// Add documents with rich metadata
List<Document> docs = List.of(
    new Document("Spring Boot tutorial", Map.of(
        "topic", "spring",
        "level", "beginner",
        "year", 2024,
        "author", "John Doe"
    )),
    new Document("Advanced Spring concepts", Map.of(
        "topic", "spring",
        "level", "advanced",
        "year", 2024,
        "author", "Jane Smith"
    ))
);
vectorStore.add(docs);

// Simple search
List<Document> results = vectorStore.similaritySearch("Spring Boot basics");

// Delete by IDs
vectorStore.delete(List.of("doc-1", "doc-2"));

// Delete by filter expression
vectorStore.delete("level == 'beginner' && year < 2023");

// Get native client for provider-specific operations
Optional<CustomVectorClient> nativeClient = vectorStore.getNativeClient();
nativeClient.ifPresent(client -> {
    // Use provider-specific features
    client.performCustomOperation();
});

VectorStoreRetriever Interface

Functional interface for read-only retrieval operations.

package org.springframework.ai.vectorstore;

import org.springframework.ai.document.Document;
import java.util.List;

/**
 * Functional interface for vector store retrieval operations.
 * Enables VectorStore to be used as a function in retrieval pipelines.
 */
@FunctionalInterface
public interface VectorStoreRetriever {
    
    /**
     * Retrieves documents by similarity to the query in SearchRequest.
     * 
     * The search process:
     * 1. Query text is converted to an embedding vector
     * 2. Vector similarity is computed against all stored documents
     * 3. Metadata filters are applied (if specified)
     * 4. Results are sorted by similarity score (descending)
     * 5. Similarity threshold is applied (client-side filtering)
     * 6. Top-K results are returned
     * 
     * @param request SearchRequest with query, topK, threshold, and optional filters
     * @return List of documents ordered by similarity (highest first)
     */
    List<Document> similaritySearch(SearchRequest request);
    
    /**
     * Convenience method for simple text-based similarity search.
     * Uses default topK (4) and no filtering.
     * 
     * @param query The text query to search for
     * @return List of similar documents
     */
    default List<Document> similaritySearch(String query) {
        return this.similaritySearch(
            SearchRequest.builder()
                .query(query)
                .topK(SearchRequest.DEFAULT_TOP_K)
                .similarityThresholdAll()
                .build()
        );
    }
}

Usage Examples:

// Simple text search
List<Document> results = vectorStore.similaritySearch("What is Spring Boot?");

// Advanced search with SearchRequest
SearchRequest request = SearchRequest.builder()
    .query("machine learning basics")
    .topK(10)
    .similarityThreshold(0.75)
    .filterExpression("category == 'AI' && year >= 2023")
    .build();

List<Document> filteredResults = vectorStore.similaritySearch(request);

// Use as a function in a pipeline
Function<String, List<Document>> retriever = vectorStore::similaritySearch;
List<Document> pipelineResults = retriever.apply("search query");

SearchRequest Class

Immutable configuration object for similarity search operations.

package org.springframework.ai.vectorstore;

import org.springframework.ai.vectorstore.filter.Filter;
import org.springframework.ai.vectorstore.filter.FilterExpressionTextParser;
import org.springframework.lang.Nullable;

/**
 * Configuration for similarity search requests.
 * Use SearchRequest.builder() to create instances.
 * 
 * This class is immutable and thread-safe.
 */
public class SearchRequest {
    
    /**
     * Similarity threshold that accepts all scores (0.0 means no threshold filtering).
     * Use this constant when you want to retrieve all results regardless of similarity.
     */
    public static final double SIMILARITY_THRESHOLD_ACCEPT_ALL = 0.0;
    
    /**
     * Default number of top results to return.
     * This is the default value used when topK is not explicitly specified.
     */
    public static final int DEFAULT_TOP_K = 4;
    
    private final String query;
    private final int topK;
    private final double similarityThreshold;
    private final Filter.Expression filterExpression;
    
    /**
     * Default public no-arg constructor creating a SearchRequest with default values.
     * Query: null, topK: 4, similarityThreshold: 0.0, filterExpression: null
     * Primarily used by frameworks and serialization.
     */
    public SearchRequest() {
        this(null, DEFAULT_TOP_K, SIMILARITY_THRESHOLD_ACCEPT_ALL, null);
    }
    
    /**
     * Protected copy constructor - use builder() or from() to create instances.
     * Copies all values from the original SearchRequest.
     * Not intended for direct use by application code.
     * 
     * @param original SearchRequest to copy from
     */
    protected SearchRequest(SearchRequest original) {
        this(original.query, original.topK, original.similarityThreshold, original.filterExpression);
    }
    
    /**
     * Private constructor used by builder.
     */
    private SearchRequest(String query, int topK, double similarityThreshold, 
                         Filter.Expression filterExpression) {
        this.query = query;
        this.topK = topK;
        this.similarityThreshold = similarityThreshold;
        this.filterExpression = filterExpression;
    }
    
    /**
     * Returns the text query for embedding similarity comparison.
     * 
     * @return The query text, or null if not set
     */
    public String getQuery() {
        return this.query;
    }
    
    /**
     * Returns the number of top similar results to return.
     * 
     * @return The top-K value (always >= 0)
     */
    public int getTopK() {
        return this.topK;
    }
    
    /**
     * Returns the minimum similarity threshold for filtering results.
     * Only documents with similarity >= threshold are returned.
     * This is a client-side post-processing filter.
     * 
     * @return The similarity threshold in range [0.0, 1.0]
     */
    public double getSimilarityThreshold() {
        return this.similarityThreshold;
    }
    
    /**
     * Returns the filter expression for metadata filtering.
     * 
     * @return The filter expression, or null if no filtering
     */
    @Nullable
    public Filter.Expression getFilterExpression() {
        return this.filterExpression;
    }
    
    /**
     * Checks if a filter expression is present.
     * 
     * @return true if a filter expression is set, false otherwise
     */
    public boolean hasFilterExpression() {
        return this.filterExpression != null;
    }
    
    /**
     * Creates a new SearchRequest builder.
     * 
     * @return A new Builder instance with default values
     */
    public static Builder builder() {
        return new Builder();
    }
    
    /**
     * Creates a builder initialized with values from an existing SearchRequest.
     * Useful for creating modified copies of existing requests.
     * 
     * @param originalSearchRequest The request to copy values from
     * @return A new Builder instance initialized with the original values
     */
    public static Builder from(SearchRequest originalSearchRequest) {
        return new Builder(originalSearchRequest);
    }
    
    @Override
    public String toString() {
        return "SearchRequest{" +
            "query='" + query + '\'' +
            ", topK=" + topK +
            ", similarityThreshold=" + similarityThreshold +
            ", hasFilter=" + hasFilterExpression() +
            '}';
    }
    
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        SearchRequest that = (SearchRequest) o;
        return topK == that.topK &&
               Double.compare(that.similarityThreshold, similarityThreshold) == 0 &&
               Objects.equals(query, that.query) &&
               Objects.equals(filterExpression, that.filterExpression);
    }
    
    @Override
    public int hashCode() {
        return Objects.hash(query, topK, similarityThreshold, filterExpression);
    }
    
    /**
     * Builder for SearchRequest with fluent API.
     */
    public static final class Builder {
        
        private String query;
        private int topK = DEFAULT_TOP_K;
        private double similarityThreshold = SIMILARITY_THRESHOLD_ACCEPT_ALL;
        private Filter.Expression filterExpression;
        
        /**
         * Default constructor creating a builder with default values.
         */
        private Builder() {
        }
        
        /**
         * Copy constructor creating a builder from an existing SearchRequest.
         */
        private Builder(SearchRequest original) {
            this.query = original.query;
            this.topK = original.topK;
            this.similarityThreshold = original.similarityThreshold;
            this.filterExpression = original.filterExpression;
        }
        
        /**
         * Sets the text query for embedding similarity comparison.
         * 
         * @param query Text to convert to embedding and compare against stored documents
         * @return This builder for chaining
         * @throws IllegalArgumentException if query is null
         */
        public Builder query(String query) {
            if (query == null) {
                throw new IllegalArgumentException("Query cannot be null");
            }
            this.query = query;
            return this;
        }
        
        /**
         * Sets the number of top similar results to return.
         * 
         * @param topK Number of results (must be >= 0)
         * @return This builder for chaining
         * @throws IllegalArgumentException if topK is negative
         */
        public Builder topK(int topK) {
            if (topK < 0) {
                throw new IllegalArgumentException("topK must be >= 0, got: " + topK);
            }
            this.topK = topK;
            return this;
        }
        
        /**
         * Sets the minimum similarity threshold for filtering results.
         * Only documents with similarity >= threshold are returned.
         * This is a client-side post-processing filter applied after retrieval.
         * 
         * Note: This filter is applied AFTER the vector store returns results,
         * so it does not improve query performance. Use metadata filters for
         * server-side filtering.
         * 
         * @param threshold Similarity threshold in range [0.0, 1.0]
         *                  0.0 = accept all, 1.0 = exact match required
         * @return This builder for chaining
         * @throws IllegalArgumentException if threshold not in [0.0, 1.0]
         */
        public Builder similarityThreshold(double threshold) {
            if (threshold < 0.0 || threshold > 1.0) {
                throw new IllegalArgumentException(
                    "Similarity threshold must be in range [0.0, 1.0], got: " + threshold
                );
            }
            this.similarityThreshold = threshold;
            return this;
        }
        
        /**
         * Disables similarity threshold filtering by setting it to 0.0.
         * All results will be returned regardless of similarity score.
         * 
         * @return This builder for chaining
         */
        public Builder similarityThresholdAll() {
            this.similarityThreshold = SIMILARITY_THRESHOLD_ACCEPT_ALL;
            return this;
        }
        
        /**
         * Sets a programmatic filter expression for metadata filtering.
         * The filter is portable across all vector store implementations.
         * 
         * This filter is applied SERVER-SIDE (within the vector store),
         * which can significantly improve query performance by reducing
         * the number of documents that need to be compared.
         * 
         * @param expression Filter.Expression or null for no filtering
         * @return This builder for chaining
         */
        public Builder filterExpression(@Nullable Filter.Expression expression) {
            this.filterExpression = expression;
            return this;
        }
        
        /**
         * Sets a SQL-like text filter expression for metadata filtering.
         * The expression is parsed and converted to a portable Filter.Expression.
         * 
         * Supported syntax:
         * - Comparison: ==, !=, <, <=, >, >=
         * - Logical: &&, ||, NOT
         * - Inclusion: IN [...], NOT IN [...]
         * - Null checks: IS NULL, IS NOT NULL
         * - Grouping: ( )
         * - Literals: strings ('text'), numbers, booleans (true, false)
         * 
         * Examples:
         *   - "country == 'UK' && year >= 2020"
         *   - "category IN ['tech', 'science'] && active == true"
         *   - "(year > 2020 OR featured == true) && status != 'archived'"
         * 
         * @param textExpression SQL-like filter string or null for no filtering
         * @return This builder for chaining
         * @throws FilterExpressionParseException if the expression has invalid syntax
         */
        public Builder filterExpression(@Nullable String textExpression) {
            if (textExpression == null) {
                this.filterExpression = null;
            } else {
                FilterExpressionTextParser parser = new FilterExpressionTextParser();
                this.filterExpression = parser.parse(textExpression);
            }
            return this;
        }
        
        /**
         * Builds the immutable SearchRequest.
         * 
         * @return A new SearchRequest instance with the configured values
         */
        public SearchRequest build() {
            return new SearchRequest(query, topK, similarityThreshold, filterExpression);
        }
    }
}

Usage Examples:

import org.springframework.ai.vectorstore.SearchRequest;

// Basic search with defaults (topK=4, no threshold, no filter)
SearchRequest simple = SearchRequest.builder()
    .query("Spring Framework")
    .build();

// Advanced search with all parameters
SearchRequest advanced = SearchRequest.builder()
    .query("machine learning algorithms")
    .topK(20)
    .similarityThreshold(0.8)
    .filterExpression("category == 'AI' && (year >= 2023 OR featured == true)")
    .build();

// Copy and modify existing request
SearchRequest modified = SearchRequest.from(advanced)
    .topK(10)
    .similarityThreshold(0.7)
    .build();

// Search accepting all similarity scores
SearchRequest acceptAll = SearchRequest.builder()
    .query("general search")
    .topK(50)
    .similarityThresholdAll()
    .build();

// Search with programmatic filter
FilterExpressionBuilder b = new FilterExpressionBuilder();
SearchRequest programmatic = SearchRequest.builder()
    .query("search text")
    .filterExpression(b.and(
        b.gte("year", 2023),
        b.in("category", "tech", "science")
    ).build())
    .build();

SimpleVectorStore Class

In-memory vector store implementation with JSON persistence support.

package org.springframework.ai.vectorstore;

import org.springframework.ai.embedding.EmbeddingModel;
import org.springframework.ai.vectorstore.observation.AbstractObservationVectorStore;
import org.springframework.core.io.Resource;
import java.io.File;
import java.io.IOException;

/**
 * Simple in-memory vector store implementation.
 * Suitable for testing, development, and small-scale deployments.
 * Supports saving/loading state to/from JSON files.
 * 
 * Features:
 * - In-memory storage using ConcurrentHashMap for thread-safety
 * - Cosine similarity for vector comparison
 * - SpEL-based filter expression evaluation
 * - JSON persistence for state management
 * - Full observability support via Micrometer
 * 
 * Limitations:
 * - Not suitable for large datasets (> 10,000 documents)
 * - No distributed support
 * - Limited to single-node deployments
 * - Performance degrades linearly with document count
 * 
 * For production use cases, consider:
 * - Pinecone (cloud-managed, scalable)
 * - Chroma (open-source, self-hosted)
 * - PgVector (PostgreSQL extension)
 * - Milvus (distributed vector database)
 */
public class SimpleVectorStore extends AbstractObservationVectorStore {
    
    /**
     * Creates a builder for SimpleVectorStore.
     * 
     * @param embeddingModel The embedding model to use for document vectorization
     * @return SimpleVectorStoreBuilder instance
     * @throws IllegalArgumentException if embeddingModel is null
     */
    public static SimpleVectorStoreBuilder builder(EmbeddingModel embeddingModel) {
        return new SimpleVectorStoreBuilder(embeddingModel);
    }
    
    /**
     * Saves the current vector store state to a JSON file.
     * The file contains all documents with their embeddings and metadata.
     * 
     * JSON format:
     * [
     *   {
     *     "id": "doc-1",
     *     "text": "document content",
     *     "metadata": {"key": "value"},
     *     "embedding": [0.1, 0.2, ...]
     *   },
     *   ...
     * ]
     * 
     * @param file Target file for saving
     * @throws RuntimeException if writing fails
     */
    public void save(File file);
    
    /**
     * Loads vector store state from a JSON file.
     * Replaces all existing documents in the store.
     * 
     * The JSON file must have the format produced by save().
     * 
     * @param file Source file to load from
     * @throws IOException if reading or parsing fails
     * @throws IllegalArgumentException if file is null or doesn't exist
     */
    public void load(File file) throws IOException;
    
    /**
     * Loads vector store state from a Spring Resource.
     * Supports classpath resources, file system resources, and URL resources.
     * 
     * Examples:
     * - new ClassPathResource("data/vectorstore.json")
     * - new FileSystemResource("/path/to/vectorstore.json")
     * - new UrlResource("https://example.com/vectorstore.json")
     * 
     * @param resource Spring Resource to load from
     * @throws IOException if reading or parsing fails
     * @throws IllegalArgumentException if resource is null or doesn't exist
     */
    public void load(Resource resource) throws IOException;
    
    /**
     * Mathematical operations for embedding vectors.
     * Provides utility methods for vector similarity calculations.
     */
    public static class EmbeddingMath {
        
        /**
         * Computes cosine similarity between two embedding vectors.
         * Returns a value in range [-1.0, 1.0] where:
         * - 1.0 = identical direction (perfect similarity)
         * - 0.0 = orthogonal (no similarity)
         * - -1.0 = opposite direction (perfect dissimilarity)
         * 
         * Formula: cosine_similarity(A, B) = (A · B) / (||A|| * ||B||)
         * 
         * @param vectorX First embedding vector
         * @param vectorY Second embedding vector
         * @return Cosine similarity score in range [-1.0, 1.0]
         * @throws IllegalArgumentException if vectors have different dimensions
         */
        public static double cosineSimilarity(float[] vectorX, float[] vectorY);
        
        /**
         * Computes dot product of two embedding vectors.
         * The dot product is the sum of element-wise products.
         * 
         * Formula: dot_product(A, B) = Σ(A[i] * B[i])
         * 
         * @param vectorX First embedding vector
         * @param vectorY Second embedding vector
         * @return Dot product result
         * @throws IllegalArgumentException if vectors have different dimensions
         */
        public static float dotProduct(float[] vectorX, float[] vectorY);
        
        /**
         * Computes the Euclidean norm (magnitude) of an embedding vector.
         * The norm is the square root of the sum of squared elements.
         * 
         * Formula: norm(A) = sqrt(Σ(A[i]²))
         * 
         * @param vector Embedding vector
         * @return Vector norm (magnitude), always >= 0
         */
        public static float norm(float[] vector);
    }
}

Usage Examples:

import org.springframework.ai.vectorstore.SimpleVectorStore;
import org.springframework.ai.embedding.EmbeddingModel;
import io.micrometer.observation.ObservationRegistry;
import java.io.File;
import java.io.IOException;

// Create basic SimpleVectorStore
SimpleVectorStore store = SimpleVectorStore.builder(embeddingModel).build();

// Create with observation support
SimpleVectorStore observableStore = SimpleVectorStore.builder(embeddingModel)
    .observationRegistry(observationRegistry)
    .customObservationConvention(new DefaultVectorStoreObservationConvention())
    .build();

// Add documents and save to file
store.add(documents);
store.save(new File("vectorstore.json"));

// Load from file
SimpleVectorStore loadedStore = SimpleVectorStore.builder(embeddingModel).build();
try {
    loadedStore.load(new File("vectorstore.json"));
    System.out.println("Loaded existing vector store");
} catch (IOException e) {
    System.err.println("Could not load vector store: " + e.getMessage());
    // Start with empty store
}

// Load from classpath resource
import org.springframework.core.io.ClassPathResource;
loadedStore.load(new ClassPathResource("data/vectorstore.json"));

// Use EmbeddingMath utilities
float[] embedding1 = {0.5f, 0.3f, 0.8f};
float[] embedding2 = {0.6f, 0.4f, 0.7f};

double similarity = SimpleVectorStore.EmbeddingMath.cosineSimilarity(embedding1, embedding2);
float dotProd = SimpleVectorStore.EmbeddingMath.dotProduct(embedding1, embedding2);
float magnitude = SimpleVectorStore.EmbeddingMath.norm(embedding1);

System.out.println("Cosine similarity: " + similarity);
System.out.println("Dot product: " + dotProd);
System.out.println("Vector magnitude: " + magnitude);

SimpleVectorStore.SimpleVectorStoreBuilder Class

Builder for configuring SimpleVectorStore instances.

package org.springframework.ai.vectorstore;

import org.springframework.ai.embedding.EmbeddingModel;
import org.springframework.ai.document.BatchingStrategy;
import org.springframework.ai.vectorstore.observation.VectorStoreObservationConvention;
import io.micrometer.observation.ObservationRegistry;

/**
 * Builder for SimpleVectorStore with fluent API.
 * Extends AbstractVectorStoreBuilder for common configuration.
 */
public static class SimpleVectorStoreBuilder
        extends AbstractVectorStoreBuilder<SimpleVectorStoreBuilder> {
    
    /**
     * Package-private constructor - use SimpleVectorStore.builder() to create.
     * 
     * @param embeddingModel The embedding model (required)
     */
    SimpleVectorStoreBuilder(EmbeddingModel embeddingModel) {
        super(embeddingModel);
    }
    
    /**
     * Sets the observation registry for metrics collection.
     * Defaults to ObservationRegistry.NOOP if not specified.
     * 
     * @param observationRegistry Micrometer observation registry
     * @return This builder for chaining
     * @throws IllegalArgumentException if observationRegistry is null
     */
    @Override
    public SimpleVectorStoreBuilder observationRegistry(ObservationRegistry observationRegistry);
    
    /**
     * Sets a custom observation convention for metrics naming.
     * Defaults to DefaultVectorStoreObservationConvention if not specified.
     * 
     * @param convention Custom observation convention
     * @return This builder for chaining
     */
    @Override
    public SimpleVectorStoreBuilder customObservationConvention(
        VectorStoreObservationConvention convention
    );
    
    /**
     * Sets the batching strategy for document operations.
     * Defaults to TokenCountBatchingStrategy if not specified.
     * 
     * @param batchingStrategy Strategy for batching add operations
     * @return This builder for chaining
     * @throws IllegalArgumentException if batchingStrategy is null
     */
    @Override
    public SimpleVectorStoreBuilder batchingStrategy(BatchingStrategy batchingStrategy);
    
    /**
     * Builds the SimpleVectorStore instance.
     * 
     * @return A new SimpleVectorStore with the configured settings
     */
    @Override
    public SimpleVectorStore build();
}

Usage Examples:

// Minimal configuration
SimpleVectorStore minimal = SimpleVectorStore.builder(embeddingModel)
    .build();

// Full configuration
SimpleVectorStore full = SimpleVectorStore.builder(embeddingModel)
    .observationRegistry(observationRegistry)
    .customObservationConvention(new DefaultVectorStoreObservationConvention())
    .batchingStrategy(new TokenCountBatchingStrategy())
    .build();

// With custom batching
BatchingStrategy customBatching = new TokenCountBatchingStrategy(
    embeddingModel,
    1000,  // max tokens per batch
    100    // max documents per batch
);

SimpleVectorStore customBatch = SimpleVectorStore.builder(embeddingModel)
    .batchingStrategy(customBatching)
    .build();

Complete Usage Examples

Example 1: Basic CRUD Operations

import org.springframework.ai.vectorstore.SimpleVectorStore;
import org.springframework.ai.vectorstore.SearchRequest;
import org.springframework.ai.document.Document;
import java.util.List;
import java.util.Map;

// Create vector store
SimpleVectorStore store = SimpleVectorStore.builder(embeddingModel).build();

// CREATE: Add documents
List<Document> documents = List.of(
    new Document("Spring Boot makes it easy to create stand-alone applications", 
        Map.of("category", "framework", "year", 2024)),
    new Document("Java is a popular programming language", 
        Map.of("category", "language", "year", 2023)),
    new Document("Vector databases enable semantic search", 
        Map.of("category", "database", "year", 2024))
);
store.add(documents);

// READ: Search for similar documents
SearchRequest searchRequest = SearchRequest.builder()
    .query("What is Spring Boot?")
    .topK(2)
    .similarityThreshold(0.7)
    .build();

List<Document> results = store.similaritySearch(searchRequest);
results.forEach(doc -> {
    System.out.println("Content: " + doc.getContent());
    System.out.println("Score: " + doc.getMetadata().get("distance"));
    System.out.println("Metadata: " + doc.getMetadata());
});

// UPDATE: Modify by deleting and re-adding
store.delete(List.of("doc-id-1"));
Document updated = new Document("doc-id-1", "Updated content", 
    Map.of("category", "framework", "year", 2024, "updated", true));
store.add(List.of(updated));

// DELETE: Remove documents
store.delete(List.of("doc-id-2", "doc-id-3"));

// DELETE: Remove by filter
store.delete("year < 2023");

Example 2: Advanced Filtering

import org.springframework.ai.vectorstore.filter.FilterExpressionBuilder;

FilterExpressionBuilder b = new FilterExpressionBuilder();

// Complex filter with multiple conditions
SearchRequest complexSearch = SearchRequest.builder()
    .query("machine learning tutorial")
    .topK(10)
    .similarityThreshold(0.75)
    .filterExpression(b.and(
        b.or(
            b.gte("year", 2023),
            b.eq("featured", true)
        ),
        b.ne("status", "archived"),
        b.in("category", "AI", "ML", "Data Science")
    ).build())
    .build();

List<Document> filteredResults = store.similaritySearch(complexSearch);

Example 3: Persistence and Recovery

import java.io.File;
import java.io.IOException;

File storeFile = new File("vectorstore.json");

// Save state periodically
try {
    store.save(storeFile);
    logger.info("Vector store saved successfully");
} catch (Exception e) {
    logger.error("Failed to save vector store", e);
}

// Load state on startup
SimpleVectorStore recoveredStore = SimpleVectorStore.builder(embeddingModel).build();
try {
    recoveredStore.load(storeFile);
    logger.info("Vector store loaded successfully");
} catch (IOException e) {
    logger.warn("Could not load vector store, starting fresh", e);
    // Initialize with default data if needed
    recoveredStore.add(getDefaultDocuments());
}

Example 4: Batch Operations with Observability

import io.micrometer.observation.ObservationRegistry;
import org.springframework.ai.document.TokenCountBatchingStrategy;

// Configure with observability
ObservationRegistry registry = ObservationRegistry.create();
BatchingStrategy batching = new TokenCountBatchingStrategy();

SimpleVectorStore observableStore = SimpleVectorStore.builder(embeddingModel)
    .observationRegistry(registry)
    .batchingStrategy(batching)
    .build();

// Add large batch of documents - will be automatically batched
List<Document> largeBatch = generateLargeDocumentSet(10000);
observableStore.add(largeBatch);  // Batched automatically

// Metrics are collected automatically
registry.getMeters().forEach(meter -> 
    System.out.println(meter.getId() + ": " + meter.measure())
);

Example 5: Error Handling

// Handle duplicate IDs
try {
    store.add(documentsWithDuplicateIds);
} catch (RuntimeException e) {
    logger.error("Duplicate document IDs detected", e);
    // Generate new IDs or update existing documents
}

// Handle empty results
List<Document> results = store.similaritySearch(request);
if (results.isEmpty()) {
    logger.warn("No results found for query: {}", request.getQuery());
    // Consider lowering threshold or broadening filter
    SearchRequest relaxedRequest = SearchRequest.from(request)
        .similarityThreshold(0.5)
        .build();
    results = store.similaritySearch(relaxedRequest);
}

// Handle unsupported operations
try {
    store.delete("category == 'old'");
} catch (UnsupportedOperationException e) {
    // Fall back to ID-based deletion
    logger.warn("Filter-based deletion not supported, using ID-based deletion");
    List<String> idsToDelete = findDocumentIds("category == 'old'");
    store.delete(idsToDelete);
}

Install with Tessl CLI

npx tessl i tessl/maven-org-springframework-ai--spring-ai-vector-store

docs

index.md

tile.json