metadataFieldNames) {
+ this.metadataFieldNames = metadataFieldNames;
+ return this;
+ }
+
+ public VearchConfig build() {
+ return new VearchConfig(this);
+ }
+ }
+}
diff --git a/embedding-stores/langchain4j-community-vearch/src/main/java/dev/langchain4j/community/store/embedding/vearch/VearchEmbeddingStore.java b/embedding-stores/langchain4j-community-vearch/src/main/java/dev/langchain4j/community/store/embedding/vearch/VearchEmbeddingStore.java
new file mode 100644
index 0000000..6611462
--- /dev/null
+++ b/embedding-stores/langchain4j-community-vearch/src/main/java/dev/langchain4j/community/store/embedding/vearch/VearchEmbeddingStore.java
@@ -0,0 +1,294 @@
+package dev.langchain4j.community.store.embedding.vearch;
+
+import dev.langchain4j.data.document.Metadata;
+import dev.langchain4j.data.embedding.Embedding;
+import dev.langchain4j.data.segment.TextSegment;
+import dev.langchain4j.store.embedding.EmbeddingMatch;
+import dev.langchain4j.store.embedding.EmbeddingSearchRequest;
+import dev.langchain4j.store.embedding.EmbeddingSearchResult;
+import dev.langchain4j.store.embedding.EmbeddingStore;
+import dev.langchain4j.store.embedding.RelevanceScore;
+
+import java.time.Duration;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static dev.langchain4j.community.store.embedding.vearch.VearchConfig.DEFAULT_ID_FIELD_NAME;
+import static dev.langchain4j.community.store.embedding.vearch.VearchConfig.DEFAULT_SCORE_FILED_NAME;
+import static dev.langchain4j.internal.Utils.getOrDefault;
+import static dev.langchain4j.internal.Utils.isNullOrBlank;
+import static dev.langchain4j.internal.Utils.isNullOrEmpty;
+import static dev.langchain4j.internal.Utils.randomUUID;
+import static dev.langchain4j.internal.ValidationUtils.ensureNotEmpty;
+import static dev.langchain4j.internal.ValidationUtils.ensureNotNull;
+import static dev.langchain4j.internal.ValidationUtils.ensureTrue;
+import static dev.langchain4j.store.embedding.CosineSimilarity.fromRelevanceScore;
+import static java.time.Duration.ofSeconds;
+import static java.util.Collections.singletonList;
+import static java.util.stream.Collectors.toList;
+
+/**
+ * Represent a Vearch index as an {@link EmbeddingStore}.
+ *
+ * Current implementation assumes the index uses the cosine distance metric.
+ *
+ * Supported Vearch version: 3.4.x and 3.5.x
+ */
+public class VearchEmbeddingStore implements EmbeddingStore {
+
+ private final VearchConfig vearchConfig;
+ private final VearchClient vearchClient;
+ /**
+ * whether to normalize embedding when add to embedding store
+ */
+ private final boolean normalizeEmbeddings;
+
+ public VearchEmbeddingStore(String baseUrl,
+ Duration timeout,
+ VearchConfig vearchConfig,
+ boolean normalizeEmbeddings,
+ boolean logRequests,
+ boolean logResponses) {
+ // Step 0: initialize some attribute
+ baseUrl = ensureNotNull(baseUrl, "baseUrl");
+ this.vearchConfig = ensureNotNull(vearchConfig, "vearchConfig");
+ this.normalizeEmbeddings = normalizeEmbeddings;
+
+ vearchClient = VearchClient.builder()
+ .baseUrl(baseUrl)
+ .timeout(getOrDefault(timeout, ofSeconds(60)))
+ .logRequests(logRequests)
+ .logResponses(logResponses)
+ .build();
+
+ // Step 1: check whether db exist, if not, create it
+ if (!isDatabaseExist(this.vearchConfig.getDatabaseName())) {
+ createDatabase(this.vearchConfig.getDatabaseName());
+ }
+
+ // Step 2: check whether space exist, if not, create it
+ if (!isSpaceExist(this.vearchConfig.getDatabaseName(), this.vearchConfig.getSpaceName())) {
+ createSpace(this.vearchConfig.getDatabaseName(), this.vearchConfig.getSpaceName());
+ }
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ @Override
+ public String add(Embedding embedding) {
+ String id = randomUUID();
+ add(id, embedding);
+ return id;
+ }
+
+ @Override
+ public void add(String id, Embedding embedding) {
+ addInternal(id, embedding, null);
+ }
+
+ @Override
+ public String add(Embedding embedding, TextSegment textSegment) {
+ String id = randomUUID();
+ addInternal(id, embedding, textSegment);
+ return id;
+ }
+
+ @Override
+ public List addAll(List embeddings) {
+ List ids = embeddings.stream()
+ .map(ignored -> randomUUID())
+ .collect(toList());
+ addAllInternal(ids, embeddings, null);
+ return ids;
+ }
+
+ @Override
+ public List addAll(List embeddings, List embedded) {
+ List ids = embeddings.stream()
+ .map(ignored -> randomUUID())
+ .collect(toList());
+ addAllInternal(ids, embeddings, embedded);
+ return ids;
+ }
+
+ @Override
+ public EmbeddingSearchResult search(EmbeddingSearchRequest request) {
+ double minSimilarity = fromRelevanceScore(request.minScore());
+ List fields = new ArrayList<>(Arrays.asList(vearchConfig.getTextFieldName(), vearchConfig.getEmbeddingFieldName()));
+ if (!isNullOrEmpty(vearchConfig.getMetadataFieldNames())) {
+ fields.addAll(vearchConfig.getMetadataFieldNames());
+ }
+ SearchRequest vearchRequest = SearchRequest.builder()
+ .dbName(vearchConfig.getDatabaseName())
+ .spaceName(vearchConfig.getSpaceName())
+ .vectors(singletonList(SearchRequest.Vector.builder()
+ .field(vearchConfig.getEmbeddingFieldName())
+ .feature(request.queryEmbedding().vectorAsList())
+ .minScore(minSimilarity)
+ .build()))
+ .fields(fields)
+ .vectorValue(true)
+ .limit(request.maxResults())
+ .indexParams(vearchConfig.getSearchIndexParam())
+ .build();
+
+ SearchResponse response = vearchClient.search(vearchRequest);
+ List> matches = toEmbeddingMatch(response.getDocuments().get(0));
+ return new EmbeddingSearchResult<>(matches);
+ }
+
+ public void deleteSpace() {
+ vearchClient.deleteSpace(vearchConfig.getDatabaseName(), vearchConfig.getSpaceName());
+ }
+
+ private void addInternal(String id, Embedding embedding, TextSegment embedded) {
+ addAllInternal(singletonList(id), singletonList(embedding), embedded == null ? null : singletonList(embedded));
+ }
+
+ private void addAllInternal(List ids, List embeddings, List embedded) {
+ ids = ensureNotEmpty(ids, "ids");
+ embeddings = ensureNotEmpty(embeddings, "embeddings");
+ ensureTrue(ids.size() == embeddings.size(), "ids size is not equal to embeddings size");
+ ensureTrue(embedded == null || embeddings.size() == embedded.size(), "embeddings size is not equal to embedded size");
+
+ List