Generic Embedding Store

The Quarkus LangChain4j Hibernate ORM extension provides a generic embedding store, that works with Hibernate ORM, to make use of databases supporting vector search for Retrieval-Augmented Generation (RAG). This extension enables you to persist and query embedding vectors for document retrieval and is based on the LangChain4j Hibernate ORM integration.

Contrary to other embedding store extensions, this extension has various advantages thanks to its usage of Hibernate ORM:

  • Support multiple databases through a unified configuration and user API

  • Advanced queries based on HQL and JPA Criteria

If you need to customize the metadata, consider defining a custom Hibernate ORM entity. For details, take a look into the Hibernate ORM Embedding store documentation.

Prerequisites

To use the Generic Embedding store:

  • A supported database with vector search capability is required.

  • A Quarkus datasource must be configured.

  • The embedding vector dimension must match the dimension of vectors produced by your embedding model.

The currently supported databases are:

  • DB2

  • MariaDB

  • MySQL Heatwave

  • PostgreSQL with pgvector

  • Oracle

  • SQL Server

Dependency

To enable the LangChain4j Hibernate ORM extension in your Quarkus project, add the following Maven dependency:

<dependency>
  <groupId>io.quarkiverse.langchain4j</groupId>
  <artifactId>quarkus-langchain4j</artifactId>
  <version>1.12.0.CR2</version>
</dependency>

Even better, if you use the Quarkus platform BOM (default for projects generated), add the Quarkus Langchain4J BOM and all dependency versions will align:

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>${quarkus.platform.group-id}</groupId>
                <artifactId>${quarkus.platform.artifact-id}</artifactId>
                <version>${quarkus.platform.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>${quarkus.platform.group-id}</groupId>
                <artifactId>quarkus-langchain4j-bom</artifactId> (1)
                <version>${quarkus.platform.version}</version> (2)
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
      <dependency>
        <groupId>io.quarkiverse.langchain4j</groupId>
        <artifactId>quarkus-langchain4j</artifactId>
        (3)
      </dependency>
    </dependencies>
1 In your dependencyManagement section, add the quarkus-langchain4j-bom
2 Inherit the version from your platform version
3 Voilà, no need for version alignment anymore

This extension requires a configured Quarkus datasource and a Hibernate ORM persistence unit. For configuration details, refer to the Quarkus DataSource Guide and the Hibernate ORM Guide.

Please beware that if you’re using PostgreSQL, you have to make sure that the pgvector extension is available. This can be done through Quarkus DevServices by configuring quarkus.datasource.devservices.image-name=pgvector/pgvector:pg17.

How It Works

The extension maps each ingested document to an entity. Each entity contains:

  • The original text content

  • Optional metadata

  • The vector embedding (stored as a vector type column)

During retrieval, a similarity search (e.g., cosine distance) is performed using a SELECT query with ORDER BY embedding +<⇒+ :query_vector FETCH FIRST N ROWS ONLY.

Comparison with quarkus-langchain4j-pgvector extension

The Quarkus LangChain4j Hibernate ORM extension provides similar features to the Quarkus LangChain4j pgvector extension and can also be used with pgvector on PostgreSQL.

The major difference is that the Quarkus LangChain4j Hibernate ORM extension is implemented through Hibernate ORM APIs, which abstract away the data store details. This extension can thus also be used with other supported data stores.

This also means the same entities defined for use in Quarkus LangChain4j Hibernate ORM can be used in Quarkus Hibernate ORM, unlocking additional APIs and addressing additional use cases:

  • Managed entities with Session

  • Quarkus Panache

  • Jakarta Data repositories

  • …​

Embedding Dimension

You must explicitly configure the dimensionality of the embedding vector via:

quarkus.langchain4j.generic.dimension=384

This value depends on the embedding model in use. For example:

  • AllMiniLmL6V2QuantizedEmbeddingModel → 384

  • OpenAI text-embedding-ada-002 → 1536

If the embedding dimension is missing or mismatched, ingestion and retrieval will fail or produce inaccurate results.

If you switch to a different embedding model, ensure the dimension value is updated accordingly.

Embedding Index

The create-index configuration controls whether the engine should create and use an index over the embeddings table. To create an index, depending on your database, you might need the index-options parameter, which is a fine-tuning parameter. For example, on PostgreSQL with pgvector, if you set create-index=true you will have to also set the lists index-option (detailed below).

This is something you rarely would need in development.

quarkus.langchain4j.generic.create-index=true
quarkus.langchain4j.generic.index-options=lists=10

Higher number of lists values speed up queries by reducing the search space during query time. However, it also decreases the region size, which can lead to more recall errors by excluding some points. Additionally, more distance comparisons are required to find the closest cluster during step one of the query process.

Here are some recommendations for setting the lists parameter:

  • For datasets with less than one million rows, use lists = rows / 1000.

  • For datasets with more than one million rows, use lists = sqrt(rows).

  • It is generally advisable to have at least 10 clusters.

For details about index options configurations, please refer to the documentation of the respective database vendor.

Usage Example

Once the extension is installed and configured, you can ingest documents into your database using the following code:

package io.quarkiverse.langchain4j.samples;

import static dev.langchain4j.data.document.splitter.DocumentSplitters.recursive;

import java.util.List;

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;

import dev.langchain4j.data.document.Document;
import dev.langchain4j.model.embedding.EmbeddingModel;
import dev.langchain4j.store.embedding.EmbeddingStoreIngestor;
import dev.langchain4j.store.embedding.hibernate.EmbeddingEntity;
import dev.langchain4j.store.embedding.hibernate.HibernateEmbeddingStore;

@ApplicationScoped
public class IngestorExampleWithGenericHibernate {

    /**
     * The embedding store (the database).
     * The bean is provided by the quarkus-langchain4j-hibernate extension.
     */
    @Inject
    HibernateEmbeddingStore<EmbeddingEntity> store;

    /**
     * The embedding model (how is computed the vector of a document).
     * The bean is provided by the LLM (like openai) extension.
     */
    @Inject
    EmbeddingModel embeddingModel;

    public void ingest(List<Document> documents) {
        EmbeddingStoreIngestor ingestor = EmbeddingStoreIngestor.builder()
                .embeddingStore(store)
                .embeddingModel(embeddingModel)
                .documentSplitter(recursive(500, 0))
                .build();
        // Warning - this can take a long time...
        ingestor.ingest(documents);
    }
}

This example shows how to embed and persist documents using the Generic Embedding Store, enabling efficient similarity search during RAG queries.

Query Example

Assuming that data was ingested, one of the various search methods on the HibernateEmbeddingStore can be used to search for matching entities.

package io.quarkiverse.langchain4j.samples;

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;

import dev.langchain4j.data.embedding.Embedding;
import dev.langchain4j.data.segment.TextSegment;
import dev.langchain4j.model.embedding.EmbeddingModel;
import dev.langchain4j.store.embedding.EmbeddingMatch;
import dev.langchain4j.store.embedding.EmbeddingSearchRequest;
import dev.langchain4j.store.embedding.EmbeddingSearchResult;
import dev.langchain4j.store.embedding.hibernate.EmbeddingEntity;
import dev.langchain4j.store.embedding.hibernate.HibernateEmbeddingStore;

@ApplicationScoped
public class QueryExampleWithGenericHibernate {

    /**
     * The embedding store (the database).
     * The bean is provided by the quarkus-langchain4j-hibernate extension.
     */
    @Inject
    HibernateEmbeddingStore<EmbeddingEntity> store;

    /**
     * The embedding model (how is computed the vector of a document).
     * The bean is provided by the LLM (like openai) extension.
     */
    @Inject
    EmbeddingModel embeddingModel;

    public void ingest() {
        // User's question
        String question = "What is the refund policy?";

        // Generate embedding for the question
        Embedding questionEmbedding = embeddingModel.embed(question).content();

        // Search for the most similar text segments (top 3 results)
        EmbeddingSearchResult<TextSegment> searchResult = store.search(
                EmbeddingSearchRequest.builder()
                        .queryEmbedding(questionEmbedding)
                        .maxResults(3) // Retrieve top 3 most similar chunks
                        .build());

        for (EmbeddingMatch<TextSegment> match : searchResult.matches()) {
            System.out.println("Matching id: " + match.embeddingId());
        }
    }
}

For further examples, take a look into the LangChain4j Hibernate ORM documentation examples.

Configuration

Customize the behavior of the extension using one the following configuration quarkus.langchain4j.generic options:

Configuration property fixed at build time - All other configuration properties are overridable at runtime

Configuration property

Type

Default

The name of the configured datasource to use for this store. If not set, the default datasource from the Agroal extension will be used.

Environment variable: QUARKUS_LANGCHAIN4J_GENERIC_DATASOURCE

string

The table name for storing embeddings

Environment variable: QUARKUS_LANGCHAIN4J_GENERIC_TABLE

string

embeddings

The dimension of the embedding vectors. This has to be the same as the dimension of vectors produced by the embedding model that you use. For example, AllMiniLmL6V2QuantizedEmbeddingModel produces vectors of dimension 384. OpenAI’s text-embedding-ada-002 produces vectors of dimension 1536.

Environment variable: QUARKUS_LANGCHAIN4J_GENERIC_DIMENSION

int

Whether the vector index should be created if not already existing.

Environment variable: QUARKUS_LANGCHAIN4J_GENERIC_CREATE_INDEX

boolean

false

A fine-tuning parameter for configuring the vector index, like e.g. lists=1 for the pgvector IVFFlat index. Consult the database vendor documentation for details about the possible index options.

Environment variable: QUARKUS_LANGCHAIN4J_GENERIC_INDEX_OPTIONS

string

The database specific type of the vector index, like e.g. hnsw or ivfflat on pgvector. Consult the database vendor documentation for details about the possible vector index types.

Environment variable: QUARKUS_LANGCHAIN4J_GENERIC_INDEX_TYPE

string

The distance function to use.

Environment variable: QUARKUS_LANGCHAIN4J_GENERIC_DISTANCE_FUNCTION

cosine, euclidean, euclidean-squared, manhattan, inner-product, negative-inner-product, hamming, jaccard

cosine

Whether the vector configuration of the database should be setup e.g. the PG extension should be created on Start. By Default, if it’s dev or test environment the value is overridden to true

Environment variable: QUARKUS_LANGCHAIN4J_HIBERNATE_ORM_SETUP_VECTOR_CONFIG

boolean

false

Schema management configuration

Type

Default

Select whether the database schema is generated or not. <p> drop-and-create is awesome in development mode. <p> This defaults to 'none'. <p> However if Dev Services is in use and no other extensions that manage the schema are present the value will be automatically overridden to 'drop-and-create'. <p> Accepted values: none, create, drop-and-create, drop, update, validate.

Environment variable: QUARKUS_LANGCHAIN4J_GENERIC_SCHEMA_MANAGEMENT_STRATEGY

tooltip:none[No schema action.], tooltip:create[Create the schema.], tooltip:drop-and-create[Drop and then recreate the schema.], tooltip:drop[Drop the schema.], tooltip:update[Update (alter) the database schema.], tooltip:validate[Validate the database schema.]

tooltip:none[No schema action.]

Known bugs

When you don’t use Hibernate ORM in your application, but use a named datasource for the Generic Embedding Store, you might face the following startup error:

Persistence unit '<default>' defines entities [dev.langchain4j.store.embedding.hibernate.EmbeddingEntity], but its datasource '<default>' cannot be found: Datasource '<default>' is not configured. To solve this, configure datasource '<default>'. Refer to https://quarkus.io/guides/datasource for guidance. Alternatively, disable Hibernate ORM by setting 'quarkus.hibernate-orm.enabled=false', and the entities will be ignored.

This error happens, because the Quarkus Hibernate ORM extension thinks that the EmbeddingEntity shall be added to the default persistence unit and hence activates the default persistence unit. Since it doesn’t have a datasource available though, it will fail to boot.

You have two solutions:

  • Add the configuration option quarkus.hibernate-orm.datasource=my-ds to let the default persistence unit use that named datasource

  • Don’t use a named datasource, but rely on the default datasource instead

Summary

To use Hibernate ORM as an embedding store with Quarkus LangChain4j:

  • Ensure your database is configured correctly for vector search.

  • Add the extension dependency.

  • Configure a datasource and set the correct embedding dimension.

  • Use HibernateEmbeddingStore to ingest and retrieve embedded documents.