LangChain4j Qdrant integration providing a vector store embedding implementation for Qdrant database with metadata filtering support
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Complete guide to exception handling and error recovery in QdrantEmbeddingStore.
Thrown for GRPC operation failures during communication with Qdrant.
Causes:
Operations that throw:
add() and addAll()search()remove() and removeAll()clearStore()Example:
try {
store.add(embedding, textSegment);
} catch (RuntimeException e) {
System.err.println("Failed to add embedding: " + e.getMessage());
// Log error, retry, or handle gracefully
}Thrown for invalid method parameters.
Causes:
remove(String id)Example:
try {
store.remove(""); // Empty string
} catch (IllegalArgumentException e) {
System.err.println("Invalid ID: " + e.getMessage());
}
try {
store.remove(null); // Null value
} catch (IllegalArgumentException e) {
System.err.println("ID cannot be null: " + e.getMessage());
}Thrown by the builder when required parameters are missing.
Causes:
collectionName in builderExample:
try {
QdrantEmbeddingStore store = QdrantEmbeddingStore.builder()
.host("localhost")
.build(); // Missing collectionName
} catch (NullPointerException e) {
System.err.println("Missing required configuration: " + e.getMessage());
}Thrown for unsupported filter types or value types.
Causes:
Example:
import dev.langchain4j.data.document.Metadata;
try {
Metadata metadata = new Metadata();
metadata.put("flag", true); // Boolean not supported
TextSegment segment = TextSegment.from("Text", metadata);
store.add(embedding, segment);
} catch (UnsupportedOperationException e) {
System.err.println("Unsupported value type: " + e.getMessage());
}import dev.langchain4j.data.embedding.Embedding;
import dev.langchain4j.data.segment.TextSegment;
try {
Embedding embedding = Embedding.from(new float[]{0.1f, 0.2f, 0.3f});
TextSegment segment = TextSegment.from("Sample text");
String id = store.add(embedding, segment);
System.out.println("Successfully added with ID: " + id);
} catch (RuntimeException e) {
System.err.println("Error adding embedding: " + e.getMessage());
e.printStackTrace();
}public String addWithRetry(Embedding embedding, TextSegment segment, int maxAttempts) {
int attempt = 0;
long waitTime = 1000; // Start with 1 second
while (attempt < maxAttempts) {
try {
return store.add(embedding, segment);
} catch (RuntimeException e) {
attempt++;
if (attempt >= maxAttempts) {
throw new RuntimeException("Failed after " + maxAttempts + " attempts", e);
}
System.err.println("Attempt " + attempt + " failed, retrying in " + waitTime + "ms");
try {
Thread.sleep(waitTime);
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
throw new RuntimeException("Interrupted during retry", ie);
}
waitTime *= 2; // Exponential backoff
}
}
throw new RuntimeException("Should not reach here");
}public List<EmbeddingMatch<TextSegment>> searchWithFallback(
Embedding query,
Filter primaryFilter,
Filter fallbackFilter
) {
try {
// Try with primary filter
EmbeddingSearchRequest request = EmbeddingSearchRequest.builder()
.queryEmbedding(query)
.maxResults(10)
.filter(primaryFilter)
.build();
EmbeddingSearchResult<TextSegment> results = store.search(request);
return results.matches();
} catch (RuntimeException e) {
System.err.println("Primary search failed, trying fallback: " + e.getMessage());
try {
// Fallback to simpler filter
EmbeddingSearchRequest fallbackRequest = EmbeddingSearchRequest.builder()
.queryEmbedding(query)
.maxResults(10)
.filter(fallbackFilter)
.build();
EmbeddingSearchResult<TextSegment> results = store.search(fallbackRequest);
return results.matches();
} catch (RuntimeException e2) {
System.err.println("Fallback search also failed: " + e2.getMessage());
return Collections.emptyList();
}
}
}import java.util.ArrayList;
import java.util.List;
public List<String> addAllWithErrorHandling(
List<Embedding> embeddings,
List<TextSegment> segments
) {
List<String> successfulIds = new ArrayList<>();
// Try bulk operation first
try {
return store.addAll(embeddings);
} catch (RuntimeException e) {
System.err.println("Bulk operation failed, falling back to individual inserts");
}
// Fallback to individual operations
for (int i = 0; i < embeddings.size(); i++) {
try {
String id = store.add(
embeddings.get(i),
segments != null && i < segments.size() ? segments.get(i) : null
);
successfulIds.add(id);
} catch (RuntimeException e) {
System.err.println("Failed to add embedding at index " + i + ": " + e.getMessage());
// Continue with next embedding
}
}
return successfulIds;
}public void addWithValidation(Embedding embedding, TextSegment segment) {
// Validate embedding
if (embedding == null) {
throw new IllegalArgumentException("Embedding cannot be null");
}
if (embedding.vector() == null || embedding.vector().length == 0) {
throw new IllegalArgumentException("Embedding vector cannot be empty");
}
// Validate text segment
if (segment != null && segment.metadata() != null) {
validateMetadata(segment.metadata());
}
// Perform operation
try {
store.add(embedding, segment);
} catch (RuntimeException e) {
System.err.println("Failed to add after validation: " + e.getMessage());
throw e;
}
}
private void validateMetadata(Metadata metadata) {
for (String key : metadata.asMap().keySet()) {
Object value = metadata.get(key);
if (value instanceof Boolean) {
throw new IllegalArgumentException(
"Boolean values not supported. Use Integer (0/1) for key: " + key
);
}
if (value instanceof List || value instanceof Map) {
throw new IllegalArgumentException(
"Collection types not supported for key: " + key
);
}
}
}import java.util.UUID;
public void removeWithValidation(String id) {
// Check null or empty
if (id == null || id.trim().isEmpty()) {
throw new IllegalArgumentException("ID cannot be null or empty");
}
// Validate UUID format
try {
UUID.fromString(id);
} catch (IllegalArgumentException e) {
throw new IllegalArgumentException("ID must be valid UUID format: " + id, e);
}
// Perform removal
try {
store.remove(id);
} catch (RuntimeException e) {
System.err.println("Failed to remove ID " + id + ": " + e.getMessage());
throw e;
}
}public QdrantEmbeddingStore createStoreWithRetry(int maxAttempts) {
int attempt = 0;
long waitTime = 1000;
while (attempt < maxAttempts) {
try {
QdrantEmbeddingStore store = QdrantEmbeddingStore.builder()
.collectionName("my-collection")
.build();
// Test connection
Embedding testEmbedding = Embedding.from(new float[]{1.0f});
String id = store.add(testEmbedding);
store.remove(id);
System.out.println("Connection successful");
return store;
} catch (RuntimeException e) {
attempt++;
if (attempt >= maxAttempts) {
throw new RuntimeException(
"Failed to connect after " + maxAttempts + " attempts", e
);
}
System.err.println("Connection attempt " + attempt + " failed, retrying...");
try {
Thread.sleep(waitTime);
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
throw new RuntimeException("Interrupted during connection retry", ie);
}
waitTime *= 2;
}
}
throw new RuntimeException("Should not reach here");
}public boolean isStoreHealthy(QdrantEmbeddingStore store) {
try {
// Perform simple operation to test connection
Embedding testEmbedding = Embedding.from(new float[]{1.0f, 0.0f});
String id = store.add(testEmbedding);
store.remove(id);
return true;
} catch (RuntimeException e) {
System.err.println("Health check failed: " + e.getMessage());
return false;
}
}import dev.langchain4j.store.embedding.EmbeddingSearchRequest;
import dev.langchain4j.store.embedding.EmbeddingSearchResult;
import java.util.Collections;
import java.util.List;
public List<EmbeddingMatch<TextSegment>> safeSearch(
Embedding query,
int maxResults,
double minScore
) {
try {
EmbeddingSearchRequest request = EmbeddingSearchRequest.builder()
.queryEmbedding(query)
.maxResults(maxResults)
.minScore(minScore)
.build();
EmbeddingSearchResult<TextSegment> results = store.search(request);
return results.matches();
} catch (RuntimeException e) {
System.err.println("Search failed: " + e.getMessage());
return Collections.emptyList();
}
}public List<EmbeddingMatch<TextSegment>> searchWithFilterRecovery(
Embedding query,
Filter filter
) {
try {
// Try with filter
EmbeddingSearchRequest request = EmbeddingSearchRequest.builder()
.queryEmbedding(query)
.maxResults(10)
.filter(filter)
.build();
return store.search(request).matches();
} catch (UnsupportedOperationException e) {
System.err.println("Filter not supported: " + e.getMessage());
// Retry without filter
EmbeddingSearchRequest simpleRequest = EmbeddingSearchRequest.builder()
.queryEmbedding(query)
.maxResults(10)
.build();
return store.search(simpleRequest).matches();
} catch (RuntimeException e) {
System.err.println("Search failed: " + e.getMessage());
return Collections.emptyList();
}
}public void processWithCleanup() {
QdrantEmbeddingStore store = null;
try {
store = QdrantEmbeddingStore.builder()
.collectionName("my-collection")
.build();
// Use store...
store.add(embedding, segment);
} catch (RuntimeException e) {
System.err.println("Error during processing: " + e.getMessage());
throw e;
} finally {
if (store != null) {
try {
store.close();
} catch (Exception e) {
System.err.println("Error closing store: " + e.getMessage());
}
}
}
}public class ManagedQdrantStore implements AutoCloseable {
private final QdrantEmbeddingStore store;
public ManagedQdrantStore(String collectionName) {
this.store = QdrantEmbeddingStore.builder()
.collectionName(collectionName)
.build();
}
public String add(Embedding embedding, TextSegment segment) {
return store.add(embedding, segment);
}
public EmbeddingSearchResult<TextSegment> search(EmbeddingSearchRequest request) {
return store.search(request);
}
@Override
public void close() {
try {
store.close();
} catch (Exception e) {
System.err.println("Error closing store: " + e.getMessage());
}
}
}
// Usage
try (ManagedQdrantStore store = new ManagedQdrantStore("my-collection")) {
store.add(embedding, segment);
// Store automatically closed
}import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class QdrantStoreService {
private static final Logger logger = LoggerFactory.getLogger(QdrantStoreService.class);
private final QdrantEmbeddingStore store;
public String addWithLogging(Embedding embedding, TextSegment segment) {
try {
logger.debug("Adding embedding to store");
String id = store.add(embedding, segment);
logger.info("Successfully added embedding with ID: {}", id);
return id;
} catch (RuntimeException e) {
logger.error("Failed to add embedding: {}", e.getMessage(), e);
throw e;
}
}
public List<EmbeddingMatch<TextSegment>> searchWithLogging(
Embedding query,
Filter filter
) {
try {
logger.debug("Searching with filter: {}", filter);
EmbeddingSearchRequest request = EmbeddingSearchRequest.builder()
.queryEmbedding(query)
.maxResults(10)
.filter(filter)
.build();
EmbeddingSearchResult<TextSegment> results = store.search(request);
logger.info("Found {} matches", results.matches().size());
return results.matches();
} catch (RuntimeException e) {
logger.error("Search failed: {}", e.getMessage(), e);
return Collections.emptyList();
}
}
}public class CircuitBreakerStore {
private final QdrantEmbeddingStore store;
private int failureCount = 0;
private long lastFailureTime = 0;
private static final int FAILURE_THRESHOLD = 5;
private static final long RESET_TIMEOUT = 60000; // 1 minute
public CircuitBreakerStore(QdrantEmbeddingStore store) {
this.store = store;
}
public String add(Embedding embedding, TextSegment segment) {
if (isCircuitOpen()) {
throw new RuntimeException("Circuit breaker is open");
}
try {
String id = store.add(embedding, segment);
onSuccess();
return id;
} catch (RuntimeException e) {
onFailure();
throw e;
}
}
private boolean isCircuitOpen() {
if (failureCount >= FAILURE_THRESHOLD) {
long timeSinceLastFailure = System.currentTimeMillis() - lastFailureTime;
if (timeSinceLastFailure > RESET_TIMEOUT) {
reset();
return false;
}
return true;
}
return false;
}
private void onSuccess() {
failureCount = 0;
}
private void onFailure() {
failureCount++;
lastFailureTime = System.currentTimeMillis();
}
private void reset() {
failureCount = 0;
lastFailureTime = 0;
}
}Error: Collection 'my-collection' not found
Solution:
import io.qdrant.client.QdrantClient;
import io.qdrant.client.QdrantGrpcClient;
import io.qdrant.client.grpc.Collections.VectorParams;
import io.qdrant.client.grpc.Collections.Distance;
public QdrantEmbeddingStore createOrGetStore(String collectionName, int vectorSize) {
QdrantClient client = new QdrantClient(
QdrantGrpcClient.newBuilder("localhost", 6334, false).build()
);
try {
// Try to get collection info
client.getCollectionInfoAsync(collectionName).get();
System.out.println("Collection exists");
} catch (Exception e) {
// Collection doesn't exist, create it
System.out.println("Creating collection: " + collectionName);
try {
client.createCollectionAsync(
collectionName,
VectorParams.newBuilder()
.setDistance(Distance.Cosine)
.setSize(vectorSize)
.build()
).get();
} catch (Exception createError) {
throw new RuntimeException("Failed to create collection", createError);
}
} finally {
client.close();
}
return QdrantEmbeddingStore.builder()
.collectionName(collectionName)
.build();
}Error: Vector dimension mismatch
Solution:
public void addWithDimensionCheck(
Embedding embedding,
TextSegment segment,
int expectedDimension
) {
if (embedding.vector().length != expectedDimension) {
throw new IllegalArgumentException(
String.format(
"Dimension mismatch: expected %d, got %d",
expectedDimension,
embedding.vector().length
)
);
}
try {
store.add(embedding, segment);
} catch (RuntimeException e) {
System.err.println("Failed to add embedding: " + e.getMessage());
throw e;
}
}Solution:
import io.qdrant.client.QdrantClient;
import io.qdrant.client.QdrantGrpcClient;
import java.time.Duration;
public QdrantEmbeddingStore createStoreWithTimeout(String collectionName) {
QdrantGrpcClient grpcClient = QdrantGrpcClient.newBuilder(
"localhost",
6334,
false
)
.withDeadline(Duration.ofSeconds(30)) // Set timeout
.build();
QdrantClient client = new QdrantClient(grpcClient);
return QdrantEmbeddingStore.builder()
.client(client)
.collectionName(collectionName)
.build();
}import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
@Test
public void testAddWithConnectionFailure() {
// Test with invalid host
QdrantEmbeddingStore store = QdrantEmbeddingStore.builder()
.host("invalid-host")
.collectionName("test-collection")
.build();
Embedding embedding = Embedding.from(new float[]{1.0f, 0.0f});
assertThrows(RuntimeException.class, () -> {
store.add(embedding);
});
}
@Test
public void testRemoveWithInvalidId() {
QdrantEmbeddingStore store = QdrantEmbeddingStore.builder()
.collectionName("test-collection")
.build();
assertThrows(IllegalArgumentException.class, () -> {
store.remove("");
});
assertThrows(IllegalArgumentException.class, () -> {
store.remove(null);
});
}
@Test
public void testBuilderWithMissingCollection() {
assertThrows(NullPointerException.class, () -> {
QdrantEmbeddingStore.builder().build();
});
}Install with Tessl CLI
npx tessl i tessl/maven-dev-langchain4j--langchain4j-qdrant@1.11.0