diff --git a/agents-flex-store/agents-flex-store-elasticsearch/src/main/java/com/agentsflex/store/elasticsearch/ElasticSearchVectorStore.java b/agents-flex-store/agents-flex-store-elasticsearch/src/main/java/com/agentsflex/store/elasticsearch/ElasticSearchVectorStore.java new file mode 100644 index 0000000..024bc98 --- /dev/null +++ b/agents-flex-store/agents-flex-store-elasticsearch/src/main/java/com/agentsflex/store/elasticsearch/ElasticSearchVectorStore.java @@ -0,0 +1,242 @@ +/* + * Copyright (c) 2023-2025, Agents-Flex (fuhai999@gmail.com). + *
+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *
+ * http://www.apache.org/licenses/LICENSE-2.0 + *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.agentsflex.store.elasticsearch;
+
+import co.elastic.clients.elasticsearch.ElasticsearchClient;
+import co.elastic.clients.elasticsearch._types.ErrorCause;
+import co.elastic.clients.elasticsearch._types.mapping.DenseVectorProperty;
+import co.elastic.clients.elasticsearch._types.mapping.Property;
+import co.elastic.clients.elasticsearch._types.mapping.TextProperty;
+import co.elastic.clients.elasticsearch._types.mapping.TypeMapping;
+import co.elastic.clients.elasticsearch._types.query_dsl.Query;
+import co.elastic.clients.elasticsearch._types.query_dsl.ScriptScoreQuery;
+import co.elastic.clients.elasticsearch.core.BulkRequest;
+import co.elastic.clients.elasticsearch.core.BulkResponse;
+import co.elastic.clients.elasticsearch.core.SearchRequest;
+import co.elastic.clients.elasticsearch.core.SearchResponse;
+import co.elastic.clients.elasticsearch.core.bulk.BulkResponseItem;
+import co.elastic.clients.json.JsonData;
+import co.elastic.clients.json.jackson.JacksonJsonpMapper;
+import co.elastic.clients.transport.ElasticsearchTransport;
+import co.elastic.clients.transport.endpoints.BooleanResponse;
+import co.elastic.clients.transport.rest_client.RestClientTransport;
+import com.agentsflex.core.document.Document;
+import com.agentsflex.core.store.DocumentStore;
+import com.agentsflex.core.store.SearchWrapper;
+import com.agentsflex.core.store.StoreOptions;
+import com.agentsflex.core.store.StoreResult;
+import com.agentsflex.core.store.exception.StoreException;
+import com.agentsflex.core.util.StringUtil;
+import org.apache.http.Header;
+import org.apache.http.HttpHost;
+import org.apache.http.auth.AuthScope;
+import org.apache.http.auth.UsernamePasswordCredentials;
+import org.apache.http.client.CredentialsProvider;
+import org.apache.http.impl.client.BasicCredentialsProvider;
+import org.apache.http.message.BasicHeader;
+import org.apache.http.ssl.SSLContextBuilder;
+import org.elasticsearch.client.RestClient;
+import org.elasticsearch.client.RestClientBuilder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.net.ssl.SSLContext;
+import java.io.IOException;
+import java.security.KeyManagementException;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static java.util.stream.Collectors.toList;
+
+/**
+ * es 向量存储:elasticsearch-java
+ *
+ * @author songyinyin
+ * @since 2024/8/12 下午4:17
+ */
+public class ElasticSearchVectorStore extends DocumentStore {
+
+ private static final Logger log = LoggerFactory.getLogger(ElasticSearchVectorStore.class);
+
+ private final ElasticsearchClient client;
+
+ private final ElasticSearchVectorStoreConfig config;
+
+ public ElasticSearchVectorStore(ElasticSearchVectorStoreConfig config) {
+ this.config = config;
+ RestClientBuilder restClientBuilder = RestClient.builder(HttpHost.create(config.getServerUrl()));
+
+ try {
+ SSLContext sslContext = SSLContextBuilder.create().loadTrustMaterial(null, (chains, authType) -> true).build();
+
+ if (StringUtil.hasText(config.getUsername())) {
+ CredentialsProvider provider = new BasicCredentialsProvider();
+ provider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(config.getUsername(), config.getPassword()));
+ restClientBuilder.setHttpClientConfigCallback(httpClientBuilder -> {
+ httpClientBuilder.setSSLContext(sslContext);
+ httpClientBuilder.setDefaultCredentialsProvider(provider);
+ return httpClientBuilder;
+ });
+ }
+
+ if (StringUtil.hasText(config.getApiKey())) {
+ restClientBuilder.setDefaultHeaders(new Header[]{
+ new BasicHeader("Authorization", "Apikey " + config.getApiKey())
+ });
+ }
+
+ ElasticsearchTransport transport = new RestClientTransport(restClientBuilder.build(), new JacksonJsonpMapper());
+
+ this.client = new ElasticsearchClient(transport);
+ } catch (NoSuchAlgorithmException | KeyManagementException | KeyStoreException e) {
+ throw new StoreException("Elasticsearch init error", e);
+ }
+ try {
+ client.ping();
+ } catch (IOException e) {
+ log.error("[I/O Elasticsearch Exception]", e);
+ throw new StoreException(e.getMessage());
+ }
+ }
+
+ public ElasticSearchVectorStore(ElasticSearchVectorStoreConfig config, ElasticsearchClient client) {
+ this.config = config;
+ this.client = client;
+ }
+
+ private static void throwIfError(BulkResponse bulkResponse) {
+ if (bulkResponse.errors()) {
+ for (BulkResponseItem item : bulkResponse.items()) {
+ if (item.error() == null) {
+ continue;
+ }
+ ErrorCause errorCause = item.error();
+ throw new StoreException("type: " + errorCause.type() + "," + "reason: " + errorCause.reason());
+ }
+ }
+ }
+
+ @Override
+ public StoreResult storeInternal(List