Enterprise Java

Getting Started with Repository Vector Search in Spring Data

With Spring Data, vector search is supported via new repository methods, allowing us to define similarity queries using familiar repository abstractions. These methods work with vector-enabled databases like PGVector and MongoDB, enabling type-safe, consistent, and context-aware data retrieval across platforms. It’s important to note that these are preview features available in the Spring Data 4.0.0-M6 release. In this article, we explore how to build and use these repository methods for vector search in Spring Data.

1. Vector Search Overview

With the rise of Generative AI, vector databases have gained traction for efficiently storing and searching embeddings, high-dimensional vectors representing semantic meaning. Unlike traditional exact-match queries, vector search retrieves semantically similar data points by comparing vector representations using distance or similarity metrics, such as cosine similarity, Euclidean distance, or the dot product.

This capability powers intelligent, context-aware applications such as semantic document retrieval, recommendation systems, and natural language understanding. In Spring Data, vector search extends these capabilities to the repository layer, enabling us to perform similarity queries using familiar, declarative repository methods.

Spring Data introduces several key abstractions:

Vector

A Vector represents an n-dimensional embedding, commonly generated by an AI model. It serves as a lightweight and immutable wrapper over an array of floating-point numbers.

SearchResult<T> and SearchResults<T>

A SearchResult<T> represents one match from a vector similarity search, containing both the matched entity and its relevance score. SearchResults<T> encapsulates a collection of such results. Each SearchResult contains the matched entity (for example, a Document) along with a Score that represents its similarity or distance from the query vector. Depending on the chosen metric, a higher or lower score indicates a closer semantic match.

Score, Similarity, and Scoring Functions

A Score quantifies the similarity or distance between vectors and is primarily used to rank search results based on their relevance to a query vector. Each score is computed by a ScoringFunction, which defines how similarity or distance is measured. Similarity measures how closely two vectors align in a multidimensional space. Similarity expresses the degree of relatedness between vectors, typically on a normalised scale from 0 to 1. A value closer to 1 indicates stronger semantic alignment, meaning the vectors represent more similar concepts or data points.

2. Setting Up Dependencies

For PGVector (PostgreSQL), include the following dependencies in your pom.xml:

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
            <version>4.0.0-M2</version>
        </dependency>
        <dependency>
            <groupId>org.postgresql</groupId>
            <artifactId>postgresql</artifactId>
            <version>42.7.7</version>
        </dependency>
        <dependency>
            <groupId>org.hibernate.orm</groupId>
            <artifactId>hibernate-vector</artifactId>
            <version>7.1.0.Final</version>
        </dependency>

These dependencies set up the project to use Spring Data JPA with PostgreSQL, while also enabling vector search functionality through the Hibernate Vector extension. The postgresql dependency adds the JDBC driver required for establishing a connection between your application and the PostgreSQL database, while the hibernate-vector module introduces native support for vector operations within Hibernate, enabling efficient vector storage, indexing, and querying through the PGVector extension.

For MongoDB Vector Search:

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-mongodb</artifactId>
            <version>4.0.0-M2</version>   
        </dependency>

3. Vector Search with PostgreSQL (PGVector)

PGVector is a PostgreSQL extension designed for storing and querying vector embeddings efficiently. With Spring Data JDBC, we can map and search vectors using repository methods.

Entity Definition

The entity will need a field to store the vector. This can be represented as a float[].

@Entity(name = "documents")
public class Document {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private String id;

    private String content;

    @JdbcTypeCode(SqlTypes.VECTOR)
    @Array(length = 5)
    private float[] embedding; // This will store our vector

    public Document(String content, float[] embedding) {
        this.content = content;
        this.embedding = embedding;
    }

    // getters and setters
}

The content field stores textual data, while the embedding field holds the vector representation of that content. The @JdbcTypeCode(SqlTypes.VECTOR) annotation tells Hibernate to map this property to a database column of type VECTOR, enabling vector operations such as similarity or distance queries. The @Array(length = 5) annotation specifies that the array will contain five floating-point values, ensuring the correct vector dimensionality when persisted.

You can create the table using:

CREATE EXTENSION IF NOT EXISTS vector;
CREATE TABLE documents (
    id SERIAL PRIMARY KEY,
    content TEXT,
    embedding vector(5)
);

Repository Interface

Now, define your Spring Data repository.

@Repository("pgvectorDocumentRepository")
public interface DocumentRepository extends JpaRepository<Document, String> {

    SearchResults<Document> findByContentAndEmbeddingNear(String content, Vector vector, Score scoreThreshold);

    SearchResults<Document> findByContentAndEmbeddingWithin(String content, Vector vector, Range<Similarity> range, Limit topK);
}

The findByContentAndEmbeddingNear() method retrieves documents whose content matches the specified value and whose embedding vectors are near the given query vector, limited by a score threshold that determines similarity closeness. The findByContentAndEmbeddingWithin() method performs a similar search but restricts results to a range of similarity scores and limits the number of results using top K.

Using the Repository

To demonstrate how vector search works, we can integrate the DocumentRepository into a service that performs similarity searches based on document content embeddings.

@Service
public class DocumentSearchService {

    private final DocumentRepository documentRepository;

    public DocumentSearchService(DocumentRepository documentRepository) {
        this.documentRepository = documentRepository;
    }

    public void findDocumentsNearEmbedding() {
        Vector embedding = getEmbedding("Find documents related to machine learning techniques.");

        SearchResults<Document> results = documentRepository.findByContentAndEmbeddingNear(
                "machine learning", embedding,
                Score.of(0.9, ScoringFunction.euclidean())
        );

        if (results != null && !results.getContent().isEmpty()) {
            results.getContent().forEach(result -> {
                System.out.println("Document ID: " + result.getContent().getId());
                System.out.println("Score: " + result.getScore().getValue());
            });
        } else {
            System.out.println("No matching documents found.");
        }
    }

    public void findDocumentsWithinSimilarityRange() {
        Vector embedding = getEmbedding("Find documents related to machine learning techniques.");

        Range<Similarity> range = Range.closed(
                Similarity.of(0.7, ScoringFunction.cosine()),
                Similarity.of(0.9, ScoringFunction.cosine())
        );

        SearchResults<Document> results = documentRepository.findByContentAndEmbeddingWithin(
                "machine learning", embedding, range, Limit.of(5)
        );

        if (results != null && !results.getContent().isEmpty()) {
            results.getContent().forEach(result -> {
                System.out.println("Document ID: " + result.getContent().getId());
                System.out.println("Similarity: " + result.getScore().getValue());
            });
        } else {
            System.out.println("No documents found within the similarity range.");
        }
    }

    // Mock embedding function
    private Vector getEmbedding(String text) {
        return Vector.of(0.12f, 0.33f, 0.45f, 0.25f, 0.65f);
    }
}

This service class demonstrates how to use Spring Data repository methods for vector similarity searches. The findDocumentsNearEmbedding() method retrieves documents whose embeddings are close to the query vector, based on a Euclidean distance threshold, while findDocumentsWithinSimilarityRange() fetches results that fall within a defined cosine similarity range, limiting the top results to a specified count (Limit.of(5)).

4. Vector Search with MongoDB

Similar to PGVector, we can perform vector-based similarity searches in MongoDB using Spring Data’s new repository abstractions. Since MongoDB is a non-relational, document-oriented database, the application reads data from a CSV file and stores it in a collection. Each record contains the document’s textual content along with its vector embedding generated by an embedding model.

Example documents.csv:

content,embedding
Getting Started with Spring Data,"[-0.4212685227394104, 0.17233482003211975, 0.7823108434677124, -0.1874466985464096, 0.5032747983932495]"
Building Scalable REST APIs,"[-0.2593657672405243, 0.11524554342031479, 0.7558906674385071, -0.16230830550193787, 0.46891066431999207]"
Introduction to Vector Search,"[-0.3928740620613098, -0.031246516853570938, 0.7265470623970032, -0.1946125328540802, 0.4218537211418152]"
AI-Powered Content Retrieval,"[-0.2764120101928711, 0.10483137518167496, 0.7846322059631348, -0.15389062464237213, 0.5024381279945374]"
Efficient Database Indexing,"[-0.31875449419021606, 0.025489756882190704, 0.7012452483177185, -0.16587309539318085, 0.43712007999420166]"

Each row represents a Document entity, where the embedding column stores a 5-dimensional vector describing the semantic meaning of the text. An AI embedding model typically generates these embeddings and allow MongoDB to perform vector similarity searches.

Entity Definition

To represent our MongoDB collection, we define an entity class named Article. This class maps directly to the articles collection in MongoDB and includes fields for the document’s title and its vector embedding.

@Document(collection = "articles")
public class Article {

    @Id
    private String id;
    private String title;
    private Vector embedding;

    public Article(String title, Vector embedding) {
        this.title = title;
        this.embedding = embedding;
    }

    // getters and setters
}

In this entity, the @Document annotation specifies that instances of this class are stored in the articles collection. The @Id field uniquely identifies each document, while the title field contains the text content. The embedding field stores a vector representation of the article’s semantic meaning, which can be used for similarity-based vector searches.

Repository Interface

Next, we define a repository interface for performing vector-based searches on the Article collection.

@Repository("mongoDbArticleRepository")
public interface ArticleRepository extends MongoRepository<Article, String> {

    @VectorSearch(indexName = "article-vector-index", limit = "5", numCandidates = "100")
    SearchResults<Article> findByEmbeddingNear(Vector vector, Score score);

    @VectorSearch(indexName = "article-vector-index", limit = "5", numCandidates = "100")
    SearchResults<Article> findByEmbeddingWithin(Vector vector, Range<Similarity> range);
}

This repository extends MongoRepository, allowing access to standard CRUD operations along with advanced vector search methods. The @VectorSearch annotation declares that the method performs a similarity search using MongoDB’s vector index. The indexName parameter (article-vector-index) specifies which vector index to use, while limit defines the maximum number of results to return, and numCandidates determines how many potential matches to consider before filtering the final set.

The findByEmbeddingNear() method retrieves documents whose embeddings are closest to the provided query vector based on a score threshold, whereas findByEmbeddingWithin() restricts results to a specified similarity range.

Using the Repository

@Service
public class DocumentSearchService {

    private final DocumentRepository documentRepository;

    public DocumentSearchService(DocumentRepository documentRepository) {
        this.documentRepository = documentRepository;
    }

    public void findDocumentsNearEmbedding() {
        Vector embedding = getEmbedding("Find documents related to machine learning techniques.");

        SearchResults<Document> results = documentRepository.findByContentAndEmbeddingNear(
                "machine learning", embedding,
                Score.of(0.9, ScoringFunction.euclidean())
        );

        if (results != null && !results.getContent().isEmpty()) {
            results.getContent().forEach(result -> {
                System.out.println("Document ID: " + result.getContent().getId());
                System.out.println("Score: " + result.getScore().getValue());
            });
        } else {
            System.out.println("No matching documents found.");
        }
    }

    public void findDocumentsWithinSimilarityRange() {
        Vector embedding = getEmbedding("Find documents related to machine learning techniques.");

        Range<Similarity> range = Range.closed(
                Similarity.of(0.7, ScoringFunction.cosine()),
                Similarity.of(0.9, ScoringFunction.cosine())
        );

        SearchResults<Document> results = documentRepository.findByContentAndEmbeddingWithin(
                "machine learning", embedding, range, Limit.of(5)
        );

        if (results != null && !results.getContent().isEmpty()) {
            results.getContent().forEach(result -> {
                System.out.println("Document ID: " + result.getContent().getId());
                System.out.println("Similarity: " + result.getScore().getValue());
            });
        } else {
            System.out.println("No documents found within the similarity range.");
        }
    }

    // Mock embedding function
    private Vector getEmbedding(String text) {
        return Vector.of(0.12f, 0.33f, 0.45f, 0.25f, 0.65f);
    }
}

The findArticlesNearEmbedding() method retrieves articles whose vector embeddings are closest to the query vector, measured using a Euclidean distance threshold. The findArticlesWithinSimilarityRange() method limits results to a specific range of cosine similarity values, giving you better control over how closely the results match the query.

5. Conclusion

In this article, we explored how Spring Data 4.0.0-M6 introduces preview support for repository-based vector search with PGVector and MongoDB. By combining familiar Spring repository abstractions with vector-based querying, developers can easily build intelligent, context-aware applications that perform semantic and similarity searches using AI-generated embeddings.

6. Download the Source Code

This article covered Spring Data repository vector search methods for PGVector and MongoDB.

Download
You can download the full source code of this example here: spring data repository vector search methods

Omozegie Aziegbe

Omos Aziegbe is a technical writer and web/application developer with a BSc in Computer Science and Software Engineering from the University of Bedfordshire. Specializing in Java enterprise applications with the Jakarta EE framework, Omos also works with HTML5, CSS, and JavaScript for web development. As a freelance web developer, Omos combines technical expertise with research and writing on topics such as software engineering, programming, web application development, computer science, and technology.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Oldest
Newest Most Voted
Back to top button