CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-dev-langchain4j--langchain4j-core

Core classes and interfaces of LangChain4j providing foundational abstractions for LLM interaction, RAG, embeddings, agents, and observability

Overview
Eval results
Files

embeddings.mddocs/data/

Embeddings and Vector Search

Package: dev.langchain4j.store.embedding, dev.langchain4j.data.embedding Thread-Safety: Implementation-dependent (check provider documentation) Use Case: Semantic search, RAG systems, similarity matching

Embedding stores provide persistent vector storage with semantic search capabilities, forming the foundation of RAG (Retrieval Augmented Generation) systems.

Core Types

EmbeddingStore Interface

package dev.langchain4j.store.embedding;

import dev.langchain4j.data.embedding.Embedding;
import dev.langchain4j.data.segment.TextSegment;

/**
 * Storage and retrieval of embeddings with associated content
 * Thread-Safety: Implementation-dependent
 * @param <Embedded> Type of content stored with embeddings (usually TextSegment)
 */
public interface EmbeddingStore<Embedded> {
    /**
     * Add embedding with content, returns generated ID
     * @param embedding Vector embedding (non-null)
     * @param embedded Associated content (non-null)
     * @return Generated ID
     */
    String add(Embedding embedding, Embedded embedded);

    /**
     * Add embedding with specific ID
     * @param id Custom ID (non-null)
     * @param embedding Vector embedding (non-null)
     * @return The provided ID
     */
    String add(String id, Embedding embedding);

    /**
     * Add embedding with content and specific ID
     * @param id Custom ID (non-null)
     * @param embedding Vector embedding (non-null)
     * @param embedded Associated content (non-null)
     * @return The provided ID
     */
    String add(String id, Embedding embedding, Embedded embedded);

    /**
     * Batch add embeddings
     * @param embeddings List of embeddings (non-null)
     * @return List of generated IDs
     */
    List<String> addAll(List<Embedding> embeddings);

    /**
     * Batch add embeddings with content
     * @param embeddings List of embeddings (non-null)
     * @param embedded List of associated content (non-null, same size)
     * @return List of generated IDs
     */
    List<String> addAll(List<Embedding> embeddings, List<Embedded> embedded);

    /**
     * Search for similar embeddings
     * @param request Search parameters (non-null)
     * @return Search results with matches
     */
    EmbeddingSearchResult<Embedded> search(EmbeddingSearchRequest request);

    /**
     * Remove embedding by ID
     * @param id ID to remove (non-null)
     */
    void remove(String id);

    /**
     * Remove all embeddings matching IDs
     * @param ids IDs to remove (non-null)
     */
    void removeAll(Collection<String> ids);

    /**
     * Remove all embeddings matching filter
     * @param filter Metadata filter (non-null)
     */
    void removeAll(Filter filter);
}

EmbeddingSearchRequest

package dev.langchain4j.store.embedding;

import dev.langchain4j.data.embedding.Embedding;
import dev.langchain4j.store.embedding.filter.Filter;

/**
 * Parameters for semantic search
 * Immutability: Immutable, thread-safe
 */
public class EmbeddingSearchRequest {
    private final Embedding queryEmbedding;
    private final int maxResults;
    private final double minScore;
    private final Filter filter;

    public static Builder builder() { /* ... */ }

    public static class Builder {
        public Builder queryEmbedding(Embedding queryEmbedding) { /* ... */ }
        public Builder maxResults(int maxResults) { /* ... */ }
        public Builder minScore(double minScore) { /* ... */ }  // 0.0-1.0
        public Builder filter(Filter filter) { /* ... */ }
        public EmbeddingSearchRequest build() { /* ... */ }
    }
}

EmbeddingSearchResult

package dev.langchain4j.store.embedding;

/**
 * Results of semantic search
 * Immutability: Immutable, thread-safe
 * @param <Embedded> Type of embedded content
 */
public class EmbeddingSearchResult<Embedded> {
    private final List<EmbeddingMatch<Embedded>> matches;

    public List<EmbeddingMatch<Embedded>> matches() { return matches; }
}

EmbeddingMatch

package dev.langchain4j.store.embedding;

import dev.langchain4j.data.embedding.Embedding;

/**
 * Single search result with score and content
 * Immutability: Immutable, thread-safe
 * @param <Embedded> Type of embedded content
 */
public class EmbeddingMatch<Embedded> {
    private final double score;              // Similarity score 0.0-1.0
    private final String embeddingId;        // ID of the embedding
    private final Embedding embedding;       // The embedding vector
    private final Embedded embedded;         // Associated content

    public double score() { return score; }
    public String embeddingId() { return embeddingId; }
    public Embedding embedding() { return embedding; }
    public Embedded embedded() { return embedded; }
}

Filtering

Filter Types

package dev.langchain4j.store.embedding.filter;

/**
 * Metadata filters for search
 * Immutability: Immutable, thread-safe
 */
public interface Filter {
    // Static factory methods
    static Filter metadataKey(String key) { /* ... */ }
    static Filter and(Filter... filters) { /* ... */ }
    static Filter or(Filter... filters) { /* ... */ }
    static Filter not(Filter filter) { /* ... */ }
}

// Comparison filters
public interface ComparisonFilter extends Filter {
    Filter isEqualTo(Object value);
    Filter isNotEqualTo(Object value);
    Filter isGreaterThan(Comparable<?> value);
    Filter isGreaterThanOrEqualTo(Comparable<?> value);
    Filter isLessThan(Comparable<?> value);
    Filter isLessThanOrEqualTo(Comparable<?> value);
    Filter isIn(Collection<?> values);
    Filter isNotIn(Collection<?> values);
}

Usage Examples

Basic Storage and Retrieval

import dev.langchain4j.store.embedding.EmbeddingStore;
import dev.langchain4j.store.embedding.EmbeddingSearchRequest;
import dev.langchain4j.store.embedding.EmbeddingSearchResult;
import dev.langchain4j.data.segment.TextSegment;
import dev.langchain4j.data.embedding.Embedding;

// Initialize store (from provider-specific module)
EmbeddingStore<TextSegment> store = /* InMemoryEmbeddingStore, etc. */;

// Add embeddings
List<TextSegment> segments = List.of(
    TextSegment.from("Machine learning is a subset of AI"),
    TextSegment.from("Deep learning uses neural networks"),
    TextSegment.from("I love pizza and pasta")
);

// Generate embeddings
Response<List<Embedding>> response = embeddingModel.embedAll(segments);
List<Embedding> embeddings = response.content();

// Store with auto-generated IDs
List<String> ids = store.addAll(embeddings, segments);
System.out.println("Stored " + ids.size() + " embeddings");

// Search
String query = "What is machine learning?";
Embedding queryEmbedding = embeddingModel.embed(query).content();

EmbeddingSearchRequest request = EmbeddingSearchRequest.builder()
    .queryEmbedding(queryEmbedding)
    .maxResults(5)
    .minScore(0.7)
    .build();

EmbeddingSearchResult<TextSegment> result = store.search(request);

// Process results
for (EmbeddingMatch<TextSegment> match : result.matches()) {
    System.out.println("Score: " + match.score());
    System.out.println("Text: " + match.embedded().text());
    System.out.println("ID: " + match.embeddingId());
    System.out.println("---");
}

Search with Metadata Filters

import dev.langchain4j.store.embedding.filter.Filter;
import dev.langchain4j.data.document.Metadata;

// Add segments with metadata
List<TextSegment> segments = List.of(
    TextSegment.from("Technical doc 1", Metadata.from(Map.of(
        "category", "technical",
        "language", "en",
        "version", "2.0"
    ))),
    TextSegment.from("User guide 1", Metadata.from(Map.of(
        "category", "user-guide",
        "language", "en",
        "version", "2.0"
    ))),
    TextSegment.from("Technical doc 2", Metadata.from(Map.of(
        "category", "technical",
        "language", "fr",
        "version", "1.0"
    )))
);

// Generate and store embeddings
Response<List<Embedding>> response = embeddingModel.embedAll(segments);
store.addAll(response.content(), segments);

// Search with filter
Filter filter = Filter.metadataKey("category").isEqualTo("technical");

EmbeddingSearchRequest request = EmbeddingSearchRequest.builder()
    .queryEmbedding(queryEmbedding)
    .maxResults(5)
    .minScore(0.7)
    .filter(filter)  // Only technical docs
    .build();

EmbeddingSearchResult<TextSegment> result = store.search(request);

Complex Filters

import dev.langchain4j.store.embedding.filter.Filter;

// AND filter
Filter andFilter = Filter.and(
    Filter.metadataKey("category").isEqualTo("technical"),
    Filter.metadataKey("language").isEqualTo("en")
);

// OR filter
Filter orFilter = Filter.or(
    Filter.metadataKey("category").isEqualTo("technical"),
    Filter.metadataKey("category").isEqualTo("tutorial")
);

// NOT filter
Filter notFilter = Filter.not(
    Filter.metadataKey("deprecated").isEqualTo(true)
);

// Complex combination
Filter complexFilter = Filter.and(
    Filter.or(
        Filter.metadataKey("category").isEqualTo("technical"),
        Filter.metadataKey("category").isEqualTo("api-reference")
    ),
    Filter.metadataKey("language").isEqualTo("en"),
    Filter.metadataKey("version").isGreaterThanOrEqualTo("2.0"),
    Filter.not(Filter.metadataKey("archived").isEqualTo(true))
);

EmbeddingSearchRequest request = EmbeddingSearchRequest.builder()
    .queryEmbedding(queryEmbedding)
    .maxResults(10)
    .filter(complexFilter)
    .build();

Using Custom IDs

// Add with specific IDs
String id1 = "doc-123-chunk-0";
String id2 = "doc-123-chunk-1";

store.add(id1, embedding1, segment1);
store.add(id2, embedding2, segment2);

// Later, remove by ID
store.remove(id1);

// Or remove multiple
store.removeAll(List.of(id1, id2));

Batch Operations

// ✅ GOOD: Batch operations (much faster)
List<Embedding> embeddings = embeddingModel.embedAll(segments).content();
List<String> ids = store.addAll(embeddings, segments);

// ❌ BAD: Individual operations in loop
for (int i = 0; i < embeddings.size(); i++) {
    store.add(embeddings.get(i), segments.get(i));  // SLOW!
}

Common Implementations

InMemoryEmbeddingStore

import dev.langchain4j.store.embedding.inmemory.InMemoryEmbeddingStore;

// Simple in-memory store (good for testing, development)
EmbeddingStore<TextSegment> store = new InMemoryEmbeddingStore<>();

// Thread-safety: Uses ConcurrentHashMap, thread-safe
// Persistence: None - data lost on restart
// Performance: Very fast, but limited by RAM
// Best for: Testing, prototypes, small datasets

Provider-Specific Stores

// Examples (from provider-specific modules):

// Pinecone
EmbeddingStore<TextSegment> pinecone = PineconeEmbeddingStore.builder()
    .apiKey(apiKey)
    .index("my-index")
    .namespace("my-namespace")
    .build();

// Chroma
EmbeddingStore<TextSegment> chroma = ChromaEmbeddingStore.builder()
    .baseUrl("http://localhost:8000")
    .collectionName("my-collection")
    .build();

// Weaviate
EmbeddingStore<TextSegment> weaviate = WeaviateEmbeddingStore.builder()
    .apiKey(apiKey)
    .scheme("https")
    .host("my-instance.weaviate.network")
    .build();

// Qdrant
EmbeddingStore<TextSegment> qdrant = QdrantEmbeddingStore.builder()
    .host("localhost")
    .port(6334)
    .collectionName("my-collection")
    .build();

Complete RAG Example

import dev.langchain4j.data.document.Document;
import dev.langchain4j.data.segment.TextSegment;
import dev.langchain4j.data.embedding.Embedding;
import dev.langchain4j.store.embedding.EmbeddingStore;
import dev.langchain4j.model.embedding.EmbeddingModel;

public class RAGSystem {
    private final DocumentSplitter splitter;
    private final EmbeddingModel embeddingModel;
    private final EmbeddingStore<TextSegment> embeddingStore;
    private final ChatModel chatModel;

    /**
     * Ingest documents into vector store
     */
    public void ingest(List<Document> documents) {
        // 1. Split documents
        List<TextSegment> segments = splitter.splitAll(documents);

        // 2. Generate embeddings
        Response<List<Embedding>> response = embeddingModel.embedAll(segments);
        List<Embedding> embeddings = response.content();

        // 3. Store in vector database
        embeddingStore.addAll(embeddings, segments);

        System.out.println("Ingested " + segments.size() + " segments");
    }

    /**
     * Query with RAG
     */
    public String query(String userQuery, String category) {
        // 1. Embed query
        Embedding queryEmbedding = embeddingModel.embed(userQuery).content();

        // 2. Retrieve relevant segments
        Filter filter = Filter.metadataKey("category").isEqualTo(category);

        EmbeddingSearchRequest searchRequest = EmbeddingSearchRequest.builder()
            .queryEmbedding(queryEmbedding)
            .maxResults(5)
            .minScore(0.7)
            .filter(filter)
            .build();

        EmbeddingSearchResult<TextSegment> searchResult = embeddingStore.search(searchRequest);

        // 3. Build context from retrieved segments
        String context = searchResult.matches().stream()
            .map(match -> match.embedded().text())
            .collect(Collectors.joining("\n\n"));

        // 4. Generate response with context
        String prompt = String.format("""
            Answer the question based on the following context:

            Context:
            %s

            Question: %s

            Answer:
            """, context, userQuery);

        return chatModel.chat(prompt);
    }
}

Best Practices

1. Set Appropriate minScore

// Score ranges (cosine similarity):
// 0.9-1.0: Nearly identical
// 0.7-0.9: Highly similar (RECOMMENDED threshold)
// 0.5-0.7: Moderately similar
// 0.0-0.5: Weakly similar or unrelated

EmbeddingSearchRequest request = EmbeddingSearchRequest.builder()
    .queryEmbedding(queryEmbedding)
    .maxResults(10)
    .minScore(0.7)  // ✅ GOOD: Filters out weak matches
    .build();

2. Use Metadata Filters

// ✅ GOOD: Filter reduces search space, improves performance
Filter filter = Filter.metadataKey("category").isEqualTo("technical");
request.filter(filter);

// ❌ BAD: No filter searches entire store
// Slower and may return irrelevant results

3. Balance maxResults

// Too few: May miss relevant content
.maxResults(2)  // ❌ Too restrictive

// Balanced: Good for most use cases
.maxResults(5-10)  // ✅ RECOMMENDED

// Too many: Slower, may include irrelevant content
.maxResults(100)  // ❌ Too many for chat context

4. Handle Empty Results

EmbeddingSearchResult<TextSegment> result = store.search(request);

if (result.matches().isEmpty()) {
    // No relevant content found
    return "I don't have enough information to answer that question.";
}

// Proceed with matches

5. Update Stale Content

// Remove old versions
Filter oldVersions = Filter.metadataKey("version").isLessThan("2.0");
store.removeAll(oldVersions);

// Add new versions
ingest(updatedDocuments);

Performance Considerations

Search Performance

  • Small stores (<10K vectors): Any implementation works well
  • Medium stores (10K-1M vectors): Use indexed stores (Pinecone, Weaviate, Qdrant)
  • Large stores (>1M vectors): Use distributed stores with sharding

Memory Usage

// InMemoryEmbeddingStore RAM usage estimate:
// Per embedding: ~6-8 KB (1536 dims * 4 bytes + overhead)
// 10K embeddings: ~60-80 MB
// 100K embeddings: ~600-800 MB
// 1M embeddings: ~6-8 GB

// For large datasets, use persistent stores

Batch vs Individual Operations

// ✅ GOOD: Batch add (10-100x faster)
List<String> ids = store.addAll(embeddings, segments);

// ❌ BAD: Individual adds in loop
for (int i = 0; i < embeddings.size(); i++) {
    store.add(embeddings.get(i), segments.get(i));
}

Common Pitfalls

PitfallSolution
Not setting minScoreSet threshold (0.7 recommended)
No metadata filtersAdd category, language, version filters
Individual operationsUse batch addAll()
Using InMemory for productionUse persistent store (Pinecone, Weaviate, etc.)
Not handling empty resultsCheck matches().isEmpty()
maxResults too highLimit to 5-10 for chat context
Not removing stale contentImplement update/delete strategy

Vector Store Comparison

StorePersistenceFilteringPerformanceScalingBest For
InMemoryNoneVery FastSingle machineTesting, development
PineconeCloudFastDistributedProduction, managed
WeaviateSelf-hostedFastDistributedProduction, self-hosted
QdrantSelf-hostedFastDistributedProduction, self-hosted
ChromaSelf-hostedMediumSingle machineDevelopment, prototypes
RedisSelf-hostedFastDistributedProduction, Redis users

See Also

  • Embedding Models - Generating embeddings
  • Documents and Segments - Preparing content
  • RAG System - Complete retrieval pipeline

Install with Tessl CLI

npx tessl i tessl/maven-dev-langchain4j--langchain4j-core@1.11.0

docs

data

documents.md

embeddings.md

messages.md

guardrails.md

index.md

memory.md

observability.md

tools.md

tile.json