Skip to content

Commit f90c51a

Browse files
wmz7yeartzolov
authored andcommitted
Improve ElasticsearchVectorStore,
- add index configuration and add support for ES response error handling. - rename dims to dimension propety. - add property javadocs - improve the elasticsearch javadoc.
1 parent 082c6f1 commit f90c51a

File tree

7 files changed

+307
-96
lines changed

7 files changed

+307
-96
lines changed

spring-ai-docs/src/main/antora/modules/ROOT/pages/api/vectordbs/elasticsearch.adoc

Lines changed: 94 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,23 @@ link:https://www.elastic.co/elasticsearch[Elasticsearch] is an open source searc
66

77
== Prerequisites
88

9-
* A running Elasticsearch instance. The following options are available:
10-
** link:https://hub.docker.com/_/elasticsearch/[Docker]
11-
** link:https://www.elastic.co/guide/en/elasticsearch/reference/current/install-elasticsearch.html#elasticsearch-install-packages[Self-Managed Elasticsearch]
12-
** link:https://www.elastic.co/cloud/elasticsearch-service/signup?page=docs&placement=docs-body[Elastic Cloud]
9+
A running Elasticsearch instance. The following options are available:
1310

14-
== Dependencies
11+
* link:https://hub.docker.com/_/elasticsearch/[Docker]
12+
* link:https://www.elastic.co/guide/en/elasticsearch/reference/current/install-elasticsearch.html#elasticsearch-install-packages[Self-Managed Elasticsearch]
13+
* link:https://www.elastic.co/cloud/elasticsearch-service/signup?page=docs&placement=docs-body[Elastic Cloud]
1514

16-
Add the Elasticsearch Vector Store dependency to your project:
15+
16+
== Auto-configuration
17+
18+
Spring AI provides Spring Boot auto-configuration for the Elasticsearch Vector Store.
19+
To enable it, add the following dependency to your project's Maven `pom.xml` file:
1720

1821
[source,xml]
1922
----
2023
<dependency>
2124
<groupId>org.springframework.ai</groupId>
22-
<artifactId>spring-ai-elasticsearch-store</artifactId>
25+
<artifactId>spring-ai-elasticsearch-store-spring-boot-starter</artifactId>
2326
</dependency>
2427
----
2528

@@ -28,13 +31,62 @@ or to your Gradle `build.gradle` build file.
2831
[source,groovy]
2932
----
3033
dependencies {
31-
implementation 'org.springframework.ai:spring-ai-elasticsearch-store'
34+
implementation 'org.springframework.ai:spring-ai-elasticsearch-store-spring-boot-starter'
3235
}
3336
----
3437

3538
TIP: Refer to the xref:getting-started.adoc#dependency-management[Dependency Management] section to add the Spring AI BOM to your build file.
3639

37-
== Configuration
40+
Please have a look at the list of <<elasticsearchvector-properties,configuration parameters>> for the vector store to learn about the default values and configuration options.
41+
42+
Here is an example of the needed bean:
43+
44+
[source,java]
45+
----
46+
@Bean
47+
public EmbeddingClient embeddingCLient() {
48+
// Can be any other EmbeddingClient implementation
49+
return new OpenAiEmbeddingClient(new OpenAiApi(System.getenv("SPRING_AI_OPENAI_API_KEY")));
50+
}
51+
----
52+
53+
In cases where the Spring Boot auto-configured Elasticsearch `RestClient` bean is not what you want or need, you can still define your own bean.
54+
Please read the link:https://www.elastic.co/guide/en/elasticsearch/client/java-api-client/current/java-rest-low-usage-initialization.html[Elasticsearch Documentation]
55+
for more in-depth information about the configuration of a custom RestClient.
56+
57+
[source,java]
58+
----
59+
@Bean
60+
public RestClient restClienbt() {
61+
RestClientBuilder builder = RestClient.builder(new HttpHost("<host>", 9200, "http"));
62+
Header[] defaultHeaders = new Header[] { new BasicHeader("Authorization", "Basic <encoded username and password>") };
63+
builder.setDefaultHeaders(defaultHeaders);
64+
return builder.build();
65+
}
66+
----
67+
68+
Now you can auto-wire the `ElasticsearchVectorStore` as a vector store in your application.
69+
70+
[source,java]
71+
----
72+
@Autowired VectorStore vectorStore;
73+
74+
// ...
75+
76+
List <Document> documents = List.of(
77+
new Document("Spring AI rocks!! Spring AI rocks!! Spring AI rocks!! Spring AI rocks!! Spring AI rocks!!", Map.of("meta1", "meta1")),
78+
new Document("The World is Big and Salvation Lurks Around the Corner"),
79+
new Document("You walk forward facing the past and you turn back toward the future.", Map.of("meta2", "meta2")));
80+
81+
// Add the documents to Qdrant
82+
vectorStore.add(documents);
83+
84+
// Retrieve documents similar to a query
85+
List<Document> results = vectorStore.similaritySearch(SearchRequest.query("Spring").withTopK(5));
86+
----
87+
88+
[[elasticsearchvector-properties]]
89+
=== Configuration Properties
3890

3991
To connect to Elasticsearch and use the `ElasticsearchVectorStore`, you need to provide access details for your instance.
4092
A simple configuration can either be provided via Spring Boot's `application.yml`,
@@ -71,16 +123,44 @@ NOTE: If you choose to create a shell script for ease in future work, be sure to
71123

72124
Spring Boot's auto-configuration feature for the Elasticsearch RestClient will create a bean instance that will be used by the `ElasticsearchVectorStore`.
73125

74-
== Auto-configuration
126+
The Spring Boot properties starting with `spring.elasticsearch.*` are used to configure the Elasticsearch client:
75127

76-
Spring AI provides Spring Boot auto-configuration for the Elasticsearch Vector Store.
77-
To enable it, add the following dependency to your project's Maven `pom.xml` file:
128+
|===
129+
|Property | Description | Default Value
130+
131+
| spring.elasticsearch.connection-timeout | Connection timeout used when communicating with Elasticsearch. | 1s
132+
| spring.elasticsearch.password | Password for authentication with Elasticsearch. | -
133+
| spring.elasticsearch.username | Username for authentication with Elasticsearch.| -
134+
| spring.elasticsearch.path-prefix | Prefix added to the path of every request sent to Elasticsearch. | -
135+
| spring.elasticsearch.restclient.sniffer.delay-after-failure | Delay of a sniff execution scheduled after a failure.| 1m
136+
| spring.elasticsearch.restclient.sniffer.interval | Interval between consecutive ordinary sniff executions. | 5m
137+
| spring.elasticsearch.restclient.ssl.bundle | SSL bundle name. | -
138+
| spring.elasticsearch.socket-keep-alive | Whether to enable socket keep alive between client and Elasticsearch. | false
139+
| spring.elasticsearch.socket-timeout | Socket timeout used when communicating with Elasticsearch. | 30s
140+
| spring.elasticsearch.uris | Comma-separated list of the Elasticsearch instances to use. | http://localhost:9200
141+
|===
142+
143+
144+
The properties with `spring.ai.vectorstore.elasticsearch.*` prefix help to configure Elasticsearch vector store.
145+
146+
|===
147+
|Property | Description | Default Value
148+
149+
|`spring.ai.vectorstore.elasticsearch.index-name` | The name of the index to store the vectors. | spring-ai-document-index
150+
|`spring.ai.vectorstore.elasticsearch.dimensions` | The number of dimensions in the vector. | 1536
151+
|`spring.ai.vectorstore.elasticsearch.dense-vector-indexing` | Whether to use dense vector indexing. | true
152+
|`spring.ai.vectorstore.elasticsearch.similarity` | The similarity function to use. | `cosine`
153+
|===
154+
155+
=== Manual Configuration
156+
157+
Instead of using the Spring Boot auto-configuration, you can manually configure the Elasticsearch vector store. \For this you need to add the `spring-ai-elasticsearch-store` to your project:
78158

79159
[source,xml]
80160
----
81161
<dependency>
82162
<groupId>org.springframework.ai</groupId>
83-
<artifactId>spring-ai-elasticsearch-store-spring-boot-starter</artifactId>
163+
<artifactId>spring-ai-elasticsearch-store</artifactId>
84164
</dependency>
85165
----
86166

@@ -89,42 +169,10 @@ or to your Gradle `build.gradle` build file.
89169
[source,groovy]
90170
----
91171
dependencies {
92-
implementation 'org.springframework.ai:spring-ai-elasticsearch-store-spring-boot-starter'
93-
}
94-
----
95-
96-
TIP: Refer to the xref:getting-started.adoc#dependency-management[Dependency Management] section to add the Spring AI BOM to your build file.
97-
98-
Please have a look at the list of <<elasticsearchvector-properties,configuration parameters>> for the vector store to learn about the default values and configuration options.
99-
100-
Here is an example of the needed bean:
101-
102-
[source,java]
103-
----
104-
@Bean
105-
public EmbeddingClient embeddingCLient() {
106-
// Can be any other EmbeddingClient implementation
107-
return new OpenAiEmbeddingClient(new OpenAiApi(System.getenv("SPRING_AI_OPENAI_API_KEY")));
108-
}
109-
----
110-
111-
In cases where the Spring Boot auto-configured Elasticsearch `RestClient` bean is not what you want or need, you can still define your own bean.
112-
Please read the link:https://www.elastic.co/guide/en/elasticsearch/client/java-api-client/current/java-rest-low-usage-initialization.html[Elasticsearch Documentation]
113-
for more in-depth information about the configuration of a custom RestClient.
114-
115-
[source,java]
116-
----
117-
@Bean
118-
public RestClient restClienbt() {
119-
RestClientBuilder builder = RestClient.builder(new HttpHost("<host>", 9200, "http"));
120-
Header[] defaultHeaders = new Header[] { new BasicHeader("Authorization", "Basic <encoded username and password>") };
121-
builder.setDefaultHeaders(defaultHeaders);
122-
return builder.build();
172+
implementation 'org.springframework.ai:spring-ai-elasticsearch-store'
123173
}
124174
----
125175

126-
Now you can auto-wire the `ElasticsearchVectorStore` as a vector store in your application.
127-
128176
== Metadata Filtering
129177

130178
You can leverage the generic, portable xref:api/vectordbs.adoc#metadata-filters[metadata filters] with Elasticsearcg as well.
@@ -171,15 +219,3 @@ is converted into the proprietary Elasticsearch filter format:
171219
(metadata.author:john OR jill) AND metadata.article_type:blog
172220
----
173221

174-
[[elasticsearchvector-properties]]
175-
== ElasticsearchVectorStore Properties
176-
177-
You can use the following properties in your Spring Boot configuration to customize the Elasticsearch vector store.
178-
179-
|===
180-
|Property |Default Value
181-
182-
|`spring.ai.vectorstore.elasticsearch.index-name`
183-
|spring-ai-document-index
184-
|===
185-

spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/vectorstore/elasticsearch/ElasticsearchVectorStoreAutoConfiguration.java

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,18 @@
1818
import org.elasticsearch.client.RestClient;
1919
import org.springframework.ai.embedding.EmbeddingClient;
2020
import org.springframework.ai.vectorstore.ElasticsearchVectorStore;
21+
import org.springframework.ai.vectorstore.ElasticsearchVectorStoreOptions;
2122
import org.springframework.boot.autoconfigure.AutoConfiguration;
2223
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
2324
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
24-
import org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchClientAutoConfiguration;
2525
import org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchRestClientAutoConfiguration;
2626
import org.springframework.boot.context.properties.EnableConfigurationProperties;
2727
import org.springframework.context.annotation.Bean;
2828
import org.springframework.util.StringUtils;
2929

3030
/**
3131
* @author Eddú Meléndez
32+
* @author Wei Jiang
3233
* @since 1.0.0
3334
*/
3435
@AutoConfiguration(after = ElasticsearchRestClientAutoConfiguration.class)
@@ -40,10 +41,22 @@ class ElasticsearchVectorStoreAutoConfiguration {
4041
@ConditionalOnMissingBean
4142
ElasticsearchVectorStore vectorStore(ElasticsearchVectorStoreProperties properties, RestClient restClient,
4243
EmbeddingClient embeddingClient) {
44+
ElasticsearchVectorStoreOptions elasticsearchVectorStoreOptions = new ElasticsearchVectorStoreOptions();
45+
4346
if (StringUtils.hasText(properties.getIndexName())) {
44-
return new ElasticsearchVectorStore(properties.getIndexName(), restClient, embeddingClient);
47+
elasticsearchVectorStoreOptions.setIndexName(properties.getIndexName());
48+
}
49+
if (properties.getDimensions() != null) {
50+
elasticsearchVectorStoreOptions.setDimensions(properties.getDimensions());
51+
}
52+
if (properties.isDenseVectorIndexing() != null) {
53+
elasticsearchVectorStoreOptions.setDenseVectorIndexing(properties.isDenseVectorIndexing());
4554
}
46-
return new ElasticsearchVectorStore(restClient, embeddingClient);
55+
if (StringUtils.hasText(properties.getSimilarity())) {
56+
elasticsearchVectorStoreOptions.setSimilarity(properties.getSimilarity());
57+
}
58+
59+
return new ElasticsearchVectorStore(elasticsearchVectorStoreOptions, restClient, embeddingClient);
4760
}
4861

4962
}

spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/vectorstore/elasticsearch/ElasticsearchVectorStoreProperties.java

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,32 @@
1919

2020
/**
2121
* @author Eddú Meléndez
22+
* @author Wei Jiang
2223
* @since 1.0.0
2324
*/
2425
@ConfigurationProperties(prefix = "spring.ai.vectorstore.elasticsearch")
2526
public class ElasticsearchVectorStoreProperties {
2627

28+
/**
29+
* The name of the index to store the vectors.
30+
*/
2731
private String indexName;
2832

33+
/**
34+
* The number of dimensions in the vector.
35+
*/
36+
private Integer dimensions;
37+
38+
/**
39+
* Whether to use dense vector indexing.
40+
*/
41+
private Boolean denseVectorIndexing;
42+
43+
/**
44+
* The similarity function to use.
45+
*/
46+
private String similarity;
47+
2948
public String getIndexName() {
3049
return this.indexName;
3150
}
@@ -34,4 +53,28 @@ public void setIndexName(String indexName) {
3453
this.indexName = indexName;
3554
}
3655

56+
public Integer getDimensions() {
57+
return dimensions;
58+
}
59+
60+
public void setDimensions(Integer dimensions) {
61+
this.dimensions = dimensions;
62+
}
63+
64+
public Boolean isDenseVectorIndexing() {
65+
return denseVectorIndexing;
66+
}
67+
68+
public void setDenseVectorIndexing(Boolean denseVectorIndexing) {
69+
this.denseVectorIndexing = denseVectorIndexing;
70+
}
71+
72+
public String getSimilarity() {
73+
return similarity;
74+
}
75+
76+
public void setSimilarity(String similarity) {
77+
this.similarity = similarity;
78+
}
79+
3780
}

spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/vectorstore/elasticsearch/ElasticsearchVectorStoreAutoConfigurationIT.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
package org.springframework.ai.autoconfigure.vectorstore.elasticsearch;
1717

1818
import org.awaitility.Awaitility;
19+
import org.junit.jupiter.api.Test;
1920
import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable;
2021
import org.junit.jupiter.params.ParameterizedTest;
2122
import org.junit.jupiter.params.provider.ValueSource;
@@ -107,6 +108,33 @@ public void addAndSearchTest(String similarityFunction) {
107108
});
108109
}
109110

111+
@Test
112+
public void propertiesTest() {
113+
114+
new ApplicationContextRunner()
115+
.withConfiguration(AutoConfigurations.of(ElasticsearchRestClientAutoConfiguration.class,
116+
ElasticsearchVectorStoreAutoConfiguration.class, RestClientAutoConfiguration.class,
117+
SpringAiRetryAutoConfiguration.class, OpenAiAutoConfiguration.class))
118+
.withPropertyValues("spring.elasticsearch.uris=" + elasticsearchContainer.getHttpHostAddress(),
119+
"spring.ai.openai.api-key=" + System.getenv("OPENAI_API_KEY"),
120+
"spring.ai.vectorstore.elasticsearch.index-name=example",
121+
"spring.ai.vectorstore.elasticsearch.dimensions=1024",
122+
"spring.ai.vectorstore.elasticsearch.dense-vector-indexing=true",
123+
"spring.ai.vectorstore.elasticsearch.similarity=dot_product")
124+
.run(context -> {
125+
var properties = context.getBean(ElasticsearchVectorStoreProperties.class);
126+
var elasticsearchVectorStore = context.getBean(ElasticsearchVectorStore.class);
127+
128+
assertThat(properties).isNotNull();
129+
assertThat(properties.getIndexName()).isEqualTo("example");
130+
assertThat(properties.getDimensions()).isEqualTo(1024);
131+
assertThat(properties.isDenseVectorIndexing()).isTrue();
132+
assertThat(properties.getSimilarity()).isEqualTo("dot_product");
133+
134+
assertThat(elasticsearchVectorStore).isNotNull();
135+
});
136+
}
137+
110138
private String getText(String uri) {
111139
var resource = new DefaultResourceLoader().getResource(uri);
112140
try {

vector-stores/spring-ai-elasticsearch-store/pom.xml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@
3535
<dependency>
3636
<groupId>co.elastic.clients</groupId>
3737
<artifactId>elasticsearch-java</artifactId>
38-
<version>8.12.2</version>
3938
</dependency>
4039

4140
<!-- TESTING -->

0 commit comments

Comments
 (0)