Skip to content

Commit 8b8fb4f

Browse files
sobychackoilayaperumalg
authored andcommitted
GH-886: feat(opensearch): Add path prefix configuration support
Fixes: #886 Add ability to configure path prefix for OpenSearch API endpoints via properties. This allows connecting to OpenSearch instances running behind a reverse proxy with a non-root path, similar to Spring Elasticsearch's path-prefix property. - Add pathPrefix property to OpenSearchVectorStoreProperties - Apply pathPrefix to OpenSearchClient when configured - Add documentation and unit tests Signed-off-by: Soby Chacko <[email protected]>
1 parent 0bbccf8 commit 8b8fb4f

File tree

4 files changed

+56
-7
lines changed

4 files changed

+56
-7
lines changed

auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-opensearch/src/main/java/org/springframework/ai/vectorstore/opensearch/autoconfigure/OpenSearchVectorStoreAutoConfiguration.java

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2023-2024 the original author or authors.
2+
* Copyright 2023-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -122,6 +122,10 @@ OpenSearchClient openSearchClient(OpenSearchVectorStoreProperties properties,
122122
httpClientBuilder.setDefaultRequestConfig(createRequestConfig(properties));
123123
return httpClientBuilder;
124124
});
125+
String pathPrefix = properties.getPathPrefix();
126+
if (StringUtils.hasText(pathPrefix)) {
127+
transportBuilder.setPathPrefix(pathPrefix);
128+
}
125129

126130
return new OpenSearchClient(transportBuilder.build());
127131
}

auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-opensearch/src/main/java/org/springframework/ai/vectorstore/opensearch/autoconfigure/OpenSearchVectorStoreProperties.java

+15
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,13 @@ public class OpenSearchVectorStoreProperties extends CommonVectorStoreProperties
5555
*/
5656
private Duration readTimeout;
5757

58+
/**
59+
* Path prefix for OpenSearch API endpoints. Used when OpenSearch is behind a reverse
60+
* proxy with a non-root path. For example, if your OpenSearch instance is accessible
61+
* at https://example.com/opensearch/, set this to "/opensearch".
62+
*/
63+
private String pathPrefix;
64+
5865
private Aws aws = new Aws();
5966

6067
public List<String> getUris() {
@@ -121,6 +128,14 @@ public void setReadTimeout(Duration readTimeout) {
121128
this.readTimeout = readTimeout;
122129
}
123130

131+
public String getPathPrefix() {
132+
return pathPrefix;
133+
}
134+
135+
public void setPathPrefix(String pathPrefix) {
136+
this.pathPrefix = pathPrefix;
137+
}
138+
124139
public Aws getAws() {
125140
return this.aws;
126141
}

auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-opensearch/src/test/java/org/springframework/ai/vectorstore/opensearch/autoconfigure/OpenSearchVectorStoreAutoConfigurationIT.java

+29-6
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2023-2024 the original author or authors.
2+
* Copyright 2023-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -25,6 +25,7 @@
2525
import org.awaitility.Awaitility;
2626
import org.junit.jupiter.api.Test;
2727
import org.opensearch.client.opensearch.OpenSearchClient;
28+
import org.opensearch.client.transport.Transport;
2829
import org.opensearch.testcontainers.OpensearchContainer;
2930
import org.testcontainers.junit.jupiter.Container;
3031
import org.testcontainers.junit.jupiter.Testcontainers;
@@ -50,6 +51,7 @@
5051
import org.springframework.context.annotation.Bean;
5152
import org.springframework.context.annotation.Configuration;
5253
import org.springframework.core.io.DefaultResourceLoader;
54+
import org.springframework.test.util.ReflectionTestUtils;
5355

5456
import static org.assertj.core.api.Assertions.assertThat;
5557
import static org.hamcrest.Matchers.hasSize;
@@ -89,7 +91,7 @@ class OpenSearchVectorStoreAutoConfigurationIT {
8991
new Document("3", getText("classpath:/test/data/great.depression.txt"), Map.of("meta2", "meta2")));
9092

9193
@Test
92-
public void addAndSearchTest() {
94+
void addAndSearchTest() {
9395

9496
this.contextRunner.run(context -> {
9597
OpenSearchVectorStore vectorStore = context.getBean(OpenSearchVectorStore.class);
@@ -148,7 +150,7 @@ public void addAndSearchTest() {
148150
}
149151

150152
@Test
151-
public void autoConfigurationDisabledWhenTypeIsNone() {
153+
void autoConfigurationDisabledWhenTypeIsNone() {
152154
this.contextRunner.withPropertyValues("spring.ai.vectorstore.type=none").run(context -> {
153155
assertThat(context.getBeansOfType(OpenSearchVectorStoreProperties.class)).isEmpty();
154156
assertThat(context.getBeansOfType(OpenSearchVectorStore.class)).isEmpty();
@@ -157,7 +159,7 @@ public void autoConfigurationDisabledWhenTypeIsNone() {
157159
}
158160

159161
@Test
160-
public void autoConfigurationEnabledByDefault() {
162+
void autoConfigurationEnabledByDefault() {
161163
this.contextRunner.run(context -> {
162164
assertThat(context.getBeansOfType(OpenSearchVectorStoreProperties.class)).isNotEmpty();
163165
assertThat(context.getBeansOfType(VectorStore.class)).isNotEmpty();
@@ -166,7 +168,7 @@ public void autoConfigurationEnabledByDefault() {
166168
}
167169

168170
@Test
169-
public void autoConfigurationEnabledWhenTypeIsOpensearch() {
171+
void autoConfigurationEnabledWhenTypeIsOpensearch() {
170172
this.contextRunner.withPropertyValues("spring.ai.vectorstore.type=opensearch").run(context -> {
171173
assertThat(context.getBeansOfType(OpenSearchVectorStoreProperties.class)).isNotEmpty();
172174
assertThat(context.getBeansOfType(VectorStore.class)).isNotEmpty();
@@ -175,7 +177,7 @@ public void autoConfigurationEnabledWhenTypeIsOpensearch() {
175177
}
176178

177179
@Test
178-
public void autoConfigurationWithSslBundles() {
180+
void autoConfigurationWithSslBundles() {
179181
this.contextRunner.withConfiguration(AutoConfigurations.of(SslAutoConfiguration.class)).run(context -> {
180182
assertThat(context.getBeansOfType(SslBundles.class)).isNotEmpty();
181183
assertThat(context.getBeansOfType(OpenSearchClient.class)).isNotEmpty();
@@ -185,6 +187,27 @@ public void autoConfigurationWithSslBundles() {
185187
});
186188
}
187189

190+
@Test
191+
void testPathPrefixIsConfigured() {
192+
this.contextRunner
193+
.withPropertyValues(OpenSearchVectorStoreProperties.CONFIG_PREFIX + ".pathPrefix=/custom-path",
194+
"spring.ai.vectorstore.opensearch.initialize-schema=false" // Prevent
195+
// schema
196+
// initialization
197+
)
198+
.run(context -> {
199+
// Verify the property is correctly set in the properties bean
200+
OpenSearchVectorStoreProperties properties = context.getBean(OpenSearchVectorStoreProperties.class);
201+
assertThat(properties.getPathPrefix()).isEqualTo("/custom-path");
202+
203+
// Verify the OpenSearchClient was configured with the correct pathPrefix
204+
OpenSearchClient client = context.getBean(OpenSearchClient.class);
205+
Transport transport = (Transport) ReflectionTestUtils.getField(client, "transport");
206+
String configuredPathPrefix = (String) ReflectionTestUtils.getField(transport, "pathPrefix");
207+
assertThat(configuredPathPrefix).isEqualTo("/custom-path");
208+
});
209+
}
210+
188211
private String getText(String uri) {
189212
var resource = new DefaultResourceLoader().getResource(uri);
190213
try {

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

+7
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ spring:
105105
similarity-function: cosinesimil
106106
read-timeout: <time to wait for response>
107107
connect-timeout: <time to wait until connection established>
108+
path-prefix: <custom path prefix>
108109
ssl-bundle: <name of SSL bundle>
109110
aws: # Only for Amazon OpenSearch Service
110111
host: <aws opensearch host>
@@ -128,6 +129,7 @@ Properties starting with `spring.ai.vectorstore.opensearch.*` are used to config
128129
|`spring.ai.vectorstore.opensearch.similarity-function`| The similarity function to use | `cosinesimil`
129130
|`spring.ai.vectorstore.opensearch.read-timeout`| Time to wait for response from the opposite endpoint. 0 - infinity. | -
130131
|`spring.ai.vectorstore.opensearch.connect-timeout`| Time to wait until connection established. 0 - infinity. | -
132+
|`spring.ai.vectorstore.opensearch.path-prefix`| Path prefix for OpenSearch API endpoints. Useful when OpenSearch is behind a reverse proxy with a non-root path. | -
131133
|`spring.ai.vectorstore.opensearch.ssl-bundle`| Name of the SSL Bundle to use in case of SSL connection | -
132134
|`spring.ai.vectorstore.opensearch.aws.host`| Hostname of the OpenSearch instance | -
133135
|`spring.ai.vectorstore.opensearch.aws.service-name`| AWS service name | -
@@ -147,6 +149,11 @@ You can control whether the AWS-specific OpenSearch auto-configuration is enable
147149
This fallback logic ensures that users have explicit control over the type of OpenSearch integration, preventing accidental activation of AWS-specific logic when not desired.
148150
====
149151

152+
[NOTE]
153+
====
154+
The `path-prefix` property allows you to specify a custom path prefix when OpenSearch is running behind a reverse proxy that uses a non-root path.
155+
For example, if your OpenSearch instance is accessible at `https://example.com/opensearch/` instead of `https://example.com/`, you would set `path-prefix: /opensearch`.
156+
====
150157

151158
The following similarity functions are available:
152159

0 commit comments

Comments
 (0)