From 4f62ac1ceeb49e086251588028e22bdea3c6ec34 Mon Sep 17 00:00:00 2001 From: Laura Trotta <153528055+l-trotta@users.noreply.github.com> Date: Mon, 9 Sep 2024 17:58:29 +0200 Subject: [PATCH] GH-1316: Elasticsearch vector store - Wrong error reported fix Fixes: https://github.com/spring-projects/spring-ai/issues/1316 * Handling missing index case * Docs update * User header for observability * Same exception management for delete --- .../pages/api/vectordbs/elasticsearch.adoc | 2 ++ .../vectorstore/ElasticsearchVectorStore.java | 25 ++++++++++--------- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/vectordbs/elasticsearch.adoc b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/vectordbs/elasticsearch.adoc index 1a70b2bd19..a791e588b2 100644 --- a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/vectordbs/elasticsearch.adoc +++ b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/vectordbs/elasticsearch.adoc @@ -50,12 +50,14 @@ TIP: Refer to the xref:getting-started.adoc#repositories[Repositories] section t The vector store implementation can initialize the requisite schema for you, but you must opt-in by specifying the `initializeSchema` boolean in the appropriate constructor or by setting `...initialize-schema=true` in the `application.properties` file. +Alternatively you can opt-out the initialization and create the index manually using the Elasticsearch client, which can be useful if the index needs advanced mapping or additional configuration. NOTE: this is a breaking change! In earlier versions of Spring AI, this schema initialization happened by default. Please have a look at the list of <> for the vector store to learn about the default values and configuration options. +These properties can be also set by configuring the `ElasticsearchVectorStoreOptions` bean. Additionally, you will need a configured `EmbeddingModel` bean. Refer to the xref:api/embeddings.adoc#available-implementations[EmbeddingModel] section for more information. diff --git a/vector-stores/spring-ai-elasticsearch-store/src/main/java/org/springframework/ai/vectorstore/ElasticsearchVectorStore.java b/vector-stores/spring-ai-elasticsearch-store/src/main/java/org/springframework/ai/vectorstore/ElasticsearchVectorStore.java index 56db121a85..ce4630cd73 100644 --- a/vector-stores/spring-ai-elasticsearch-store/src/main/java/org/springframework/ai/vectorstore/ElasticsearchVectorStore.java +++ b/vector-stores/spring-ai-elasticsearch-store/src/main/java/org/springframework/ai/vectorstore/ElasticsearchVectorStore.java @@ -105,6 +105,7 @@ public ElasticsearchVectorStore(ElasticsearchVectorStoreOptions options, RestCli Objects.requireNonNull(embeddingModel, "EmbeddingModel must not be null"); this.elasticsearchClient = new ElasticsearchClient(new RestClientTransport(restClient, new JacksonJsonpMapper( new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)))); + elasticsearchClient.withTransportOptions(t -> t.addHeader("user-agent", "spring-ai")); this.embeddingModel = embeddingModel; this.options = options; this.filterExpressionConverter = new ElasticsearchAiSearchFilterExpressionConverter(); @@ -112,6 +113,11 @@ public ElasticsearchVectorStore(ElasticsearchVectorStoreOptions options, RestCli @Override public void doAdd(List documents) { + // For the index to be present, either it must be pre-created or set the + // initializeSchema to true. + if (!indexExists()) { + throw new IllegalArgumentException("Index not found"); + } BulkRequest.Builder bulkRequestBuilder = new BulkRequest.Builder(); for (Document document : documents) { @@ -119,13 +125,8 @@ public void doAdd(List documents) { logger.debug("Calling EmbeddingModel for document id = " + document.getId()); document.setEmbedding(this.embeddingModel.embed(document)); } - // We call operations on BulkRequest.Builder only if the index exists. - // For the index to be present, either it must be pre-created or set the - // initializeSchema to true. - if (indexExists()) { - bulkRequestBuilder.operations(op -> op - .index(idx -> idx.index(this.options.getIndexName()).id(document.getId()).document(document))); - } + bulkRequestBuilder.operations(op -> op + .index(idx -> idx.index(this.options.getIndexName()).id(document.getId()).document(document))); } BulkResponse bulkRequest = bulkRequest(bulkRequestBuilder.build()); if (bulkRequest.errors()) { @@ -141,13 +142,13 @@ public void doAdd(List documents) { @Override public Optional doDelete(List idList) { BulkRequest.Builder bulkRequestBuilder = new BulkRequest.Builder(); - // We call operations on BulkRequest.Builder only if the index exists. // For the index to be present, either it must be pre-created or set the // initializeSchema to true. - if (indexExists()) { - for (String id : idList) { - bulkRequestBuilder.operations(op -> op.delete(idx -> idx.index(this.options.getIndexName()).id(id))); - } + if (!indexExists()) { + throw new IllegalArgumentException("Index not found"); + } + for (String id : idList) { + bulkRequestBuilder.operations(op -> op.delete(idx -> idx.index(this.options.getIndexName()).id(id))); } return Optional.of(bulkRequest(bulkRequestBuilder.build()).errors()); }