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
vectortype 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 |
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: |
string |
|
The table name for storing embeddings Environment variable: |
string |
|
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: |
int |
|
Whether the vector index should be created if not already existing. Environment variable: |
boolean |
|
A fine-tuning parameter for configuring the vector index, like e.g. Environment variable: |
string |
|
The database specific type of the vector index, like e.g. Environment variable: |
string |
|
The distance function to use. Environment variable: |
|
|
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: |
boolean |
|
Type |
Default |
|
Select whether the database schema is generated or not.
<p>
Environment variable: |
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-dsto let the default persistence unit use that named datasource -
Don’t use a named datasource, but rely on the default datasource instead
This problem will be fixed in a future version of the Quarkus Hibernate ORM extension.
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
HibernateEmbeddingStoreto ingest and retrieve embedded documents.