Use RedisAutoConfiguration in RedisVectorStoreAutoConfiguration

Currently, `RedisVectorStoreAutoConfiguration` creates its own
configuration to connect with Redis. This commit reuse
`RedisAutoConfiguration` from spring boot project. It's limited
to Jedis.
This commit is contained in:
Eddú Meléndez
2024-07-16 15:44:59 -05:00
committed by Christian Tzolov
parent 40d8671f3e
commit 0a07f65d6a
19 changed files with 64 additions and 400 deletions

View File

@@ -43,9 +43,6 @@ The following service connection factories are provided in the `spring-ai-spring
| `QdrantConnectionDetails`
| Containers named `qdrant/qdrant`
| `RedisConnectionDetails`
| Containers named `redis/redis-stack-server`, `redis/redis-stack`
| `TypesenseConnectionDetails`
| Containers named `typesense/typesense`

View File

@@ -46,9 +46,6 @@ The following service connection factories are provided in the `spring-ai-spring
| `QdrantConnectionDetails`
| Containers of type `QdrantContainer`
| `RedisConnectionDetails`
| Containers of type `RedisStackContainer`
| `TypesenseConnectionDetails`
| Containers named "typesense/typesense"

View File

@@ -202,6 +202,12 @@
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<optional>true</optional>
</dependency>
<!-- Override Jedis version -->
<dependency>
<groupId>redis.clients</groupId>

View File

@@ -1,27 +0,0 @@
/*
* Copyright 2023 - 2024 the original author or authors.
*
* 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
*
* https://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 org.springframework.ai.autoconfigure.vectorstore.redis;
import org.springframework.boot.autoconfigure.service.connection.ConnectionDetails;
/**
* @author Eddú Meléndez
*/
public interface RedisConnectionDetails extends ConnectionDetails {
String getUri();
}

View File

@@ -19,53 +19,38 @@ import org.springframework.ai.embedding.EmbeddingModel;
import org.springframework.ai.vectorstore.RedisVectorStore;
import org.springframework.ai.vectorstore.RedisVectorStore.RedisVectorStoreConfig;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import redis.clients.jedis.JedisPooled;
/**
* @author Christian Tzolov
* @author Eddú Meléndez
*/
@AutoConfiguration
@ConditionalOnClass({ RedisVectorStore.class, EmbeddingModel.class })
@AutoConfiguration(after = RedisAutoConfiguration.class)
@ConditionalOnClass({ JedisPooled.class, JedisConnectionFactory.class, RedisVectorStore.class, EmbeddingModel.class })
@ConditionalOnBean(JedisConnectionFactory.class)
@EnableConfigurationProperties(RedisVectorStoreProperties.class)
public class RedisVectorStoreAutoConfiguration {
@Bean
@ConditionalOnMissingBean(RedisConnectionDetails.class)
public PropertiesRedisConnectionDetails redisConnectionDetails(RedisVectorStoreProperties properties) {
return new PropertiesRedisConnectionDetails(properties);
}
@Bean
@ConditionalOnMissingBean
public RedisVectorStore vectorStore(EmbeddingModel embeddingModel, RedisVectorStoreProperties properties,
RedisConnectionDetails redisConnectionDetails) {
JedisConnectionFactory jedisConnectionFactory) {
var config = RedisVectorStoreConfig.builder()
.withURI(redisConnectionDetails.getUri())
.withIndexName(properties.getIndex())
.withPrefix(properties.getPrefix())
.build();
return new RedisVectorStore(config, embeddingModel, properties.isInitializeSchema());
}
static class PropertiesRedisConnectionDetails implements RedisConnectionDetails {
private final RedisVectorStoreProperties properties;
public PropertiesRedisConnectionDetails(RedisVectorStoreProperties properties) {
this.properties = properties;
}
@Override
public String getUri() {
return this.properties.getUri();
}
return new RedisVectorStore(config, embeddingModel,
new JedisPooled(jedisConnectionFactory.getHostName(), jedisConnectionFactory.getPort()),
properties.isInitializeSchema());
}
}

View File

@@ -20,26 +20,17 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* @author Julien Ruaux
* @author Eddú Meléndez
*/
@ConfigurationProperties(RedisVectorStoreProperties.CONFIG_PREFIX)
public class RedisVectorStoreProperties extends CommonVectorStoreProperties {
public static final String CONFIG_PREFIX = "spring.ai.vectorstore.redis";
private String uri = "redis://localhost:6379";
private String index = "default-index";
private String prefix = "default:";
public String getUri() {
return this.uri;
}
public void setUri(String uri) {
this.uri = uri;
}
public String getIndex() {
return this.index;
}

View File

@@ -28,6 +28,7 @@ import org.springframework.ai.transformers.TransformersEmbeddingModel;
import org.springframework.ai.vectorstore.SearchRequest;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -38,6 +39,7 @@ import com.redis.testcontainers.RedisStackContainer;
/**
* @author Julien Ruaux
* @author Eddú Meléndez
*/
@Testcontainers
class RedisVectorStoreAutoConfigurationIT {
@@ -52,32 +54,31 @@ class RedisVectorStoreAutoConfigurationIT {
ResourceUtils.getText("classpath:/test/data/great.depression.txt"), Map.of("depression", "bad")));
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(RedisVectorStoreAutoConfiguration.class))
.withConfiguration(AutoConfigurations.of(RedisAutoConfiguration.class, RedisVectorStoreAutoConfiguration.class))
.withUserConfiguration(Config.class)
.withPropertyValues("spring.ai.vectorstore.redis.index=myIdx")
.withPropertyValues("spring.ai.vectorstore.redis.prefix=doc:");
.withPropertyValues("spring.data.redis.url=" + redisContainer.getRedisURI(),
"spring.ai.vectorstore.redis.index=myIdx", "spring.ai.vectorstore.redis.prefix=doc:");
@Test
void addAndSearch() {
contextRunner.withPropertyValues("spring.ai.vectorstore.redis.uri=" + redisContainer.getRedisURI())
.run(context -> {
VectorStore vectorStore = context.getBean(VectorStore.class);
vectorStore.add(documents);
contextRunner.run(context -> {
VectorStore vectorStore = context.getBean(VectorStore.class);
vectorStore.add(documents);
List<Document> results = vectorStore.similaritySearch(SearchRequest.query("Spring").withTopK(1));
List<Document> results = vectorStore.similaritySearch(SearchRequest.query("Spring").withTopK(1));
assertThat(results).hasSize(1);
Document resultDoc = results.get(0);
assertThat(resultDoc.getId()).isEqualTo(documents.get(0).getId());
assertThat(resultDoc.getContent()).contains(
"Spring AI provides abstractions that serve as the foundation for developing AI applications.");
assertThat(results).hasSize(1);
Document resultDoc = results.get(0);
assertThat(resultDoc.getId()).isEqualTo(documents.get(0).getId());
assertThat(resultDoc.getContent()).contains(
"Spring AI provides abstractions that serve as the foundation for developing AI applications.");
// Remove all documents from the store
vectorStore.delete(documents.stream().map(doc -> doc.getId()).toList());
// Remove all documents from the store
vectorStore.delete(documents.stream().map(doc -> doc.getId()).toList());
results = vectorStore.similaritySearch(SearchRequest.query("Spring").withTopK(1));
assertThat(results).isEmpty();
});
results = vectorStore.similaritySearch(SearchRequest.query("Spring").withTopK(1));
assertThat(results).isEmpty();
});
}
@Configuration(proxyBeanMethods = false)

View File

@@ -21,13 +21,13 @@ import org.junit.jupiter.api.Test;
/**
* @author Julien Ruaux
* @author Eddú Meléndez
*/
class RedisVectorStorePropertiesTests {
@Test
void defaultValues() {
var props = new RedisVectorStoreProperties();
assertThat(props.getUri()).isEqualTo("redis://localhost:6379");
assertThat(props.getIndex()).isEqualTo("default-index");
assertThat(props.getPrefix()).isEqualTo("default:");
}
@@ -35,11 +35,9 @@ class RedisVectorStorePropertiesTests {
@Test
void customValues() {
var props = new RedisVectorStoreProperties();
props.setUri("redis://redis.com:12345");
props.setIndex("myIdx");
props.setPrefix("doc:");
assertThat(props.getUri()).isEqualTo("redis://redis.com:12345");
assertThat(props.getIndex()).isEqualTo("myIdx");
assertThat(props.getPrefix()).isEqualTo("doc:");
}

View File

@@ -1,61 +0,0 @@
/*
* Copyright 2023 - 2024 the original author or authors.
*
* 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
*
* https://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 org.springframework.ai.docker.compose.service.connection.redis;
import org.springframework.ai.autoconfigure.vectorstore.redis.RedisConnectionDetails;
import org.springframework.boot.docker.compose.core.RunningService;
import org.springframework.boot.docker.compose.service.connection.DockerComposeConnectionDetailsFactory;
import org.springframework.boot.docker.compose.service.connection.DockerComposeConnectionSource;
/**
* @author Eddú Meléndez
*/
class RedisDockerComposeConnectionDetailsFactory extends DockerComposeConnectionDetailsFactory<RedisConnectionDetails> {
private static final String[] REDIS_IMAGE_NAMES = { "redis/redis-stack", "redis/redis-stack-server" };
private static final int REDIS_PORT = 6379;
protected RedisDockerComposeConnectionDetailsFactory() {
super(REDIS_IMAGE_NAMES);
}
@Override
protected RedisConnectionDetails getDockerComposeConnectionDetails(DockerComposeConnectionSource source) {
return new RedisDockerComposeConnectionDetails(source.getRunningService());
}
/**
* {@link RedisConnectionDetails} backed by a {@code Redis} {@link RunningService}.
*/
static class RedisDockerComposeConnectionDetails extends DockerComposeConnectionDetails
implements RedisConnectionDetails {
private final String uri;
RedisDockerComposeConnectionDetails(RunningService service) {
super(service);
this.uri = "redis://" + service.host() + ":" + service.ports().get(REDIS_PORT);
}
@Override
public String getUri() {
return this.uri;
}
}
}

View File

@@ -3,6 +3,5 @@ org.springframework.ai.docker.compose.service.connection.chroma.ChromaDockerComp
org.springframework.ai.docker.compose.service.connection.ollama.OllamaDockerComposeConnectionDetailsFactory,\
org.springframework.ai.docker.compose.service.connection.opensearch.OpenSearchDockerComposeConnectionDetailsFactory,\
org.springframework.ai.docker.compose.service.connection.qdrant.QdrantDockerComposeConnectionDetailsFactory,\
org.springframework.ai.docker.compose.service.connection.redis.RedisDockerComposeConnectionDetailsFactory,\
org.springframework.ai.docker.compose.service.connection.typesense.TypesenseDockerComposeConnectionDetailsFactory,\
org.springframework.ai.docker.compose.service.connection.weaviate.WeaviateDockerComposeConnectionDetailsFactory

View File

@@ -1,37 +0,0 @@
/*
* Copyright 2023 - 2024 the original author or authors.
*
* 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
*
* https://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 org.springframework.ai.docker.compose.service.connection.redis;
import org.junit.jupiter.api.Test;
import org.springframework.ai.autoconfigure.vectorstore.redis.RedisConnectionDetails;
import org.springframework.boot.docker.compose.service.connection.test.AbstractDockerComposeIntegrationTests;
import org.testcontainers.utility.DockerImageName;
import static org.assertj.core.api.Assertions.assertThat;
class RedisStackDockerComposeConnectionDetailsFactoryTests extends AbstractDockerComposeIntegrationTests {
RedisStackDockerComposeConnectionDetailsFactoryTests() {
super("redis-compose.yaml", DockerImageName.parse("redis/redis-stack"));
}
@Test
void runCreatesConnectionDetails() {
RedisConnectionDetails connectionDetails = run(RedisConnectionDetails.class);
assertThat(connectionDetails.getUri()).startsWith("redis://");
}
}

View File

@@ -1,37 +0,0 @@
/*
* Copyright 2023 - 2024 the original author or authors.
*
* 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
*
* https://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 org.springframework.ai.docker.compose.service.connection.redis;
import org.junit.jupiter.api.Test;
import org.springframework.ai.autoconfigure.vectorstore.redis.RedisConnectionDetails;
import org.springframework.boot.docker.compose.service.connection.test.AbstractDockerComposeIntegrationTests;
import org.testcontainers.utility.DockerImageName;
import static org.assertj.core.api.Assertions.assertThat;
class RedisStackServerDockerComposeConnectionDetailsFactoryTests extends AbstractDockerComposeIntegrationTests {
RedisStackServerDockerComposeConnectionDetailsFactoryTests() {
super("redis-compose.yaml", DockerImageName.parse("redis/redis-stack-server"));
}
@Test
void runCreatesConnectionDetails() {
RedisConnectionDetails connectionDetails = run(RedisConnectionDetails.class);
assertThat(connectionDetails.getUri()).startsWith("redis://");
}
}

View File

@@ -1,51 +0,0 @@
/*
* Copyright 2023 - 2024 the original author or authors.
*
* 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
*
* https://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 org.springframework.ai.testcontainers.service.connection.redis;
import com.redis.testcontainers.RedisStackContainer;
import org.springframework.ai.autoconfigure.vectorstore.redis.RedisConnectionDetails;
import org.springframework.boot.testcontainers.service.connection.ContainerConnectionDetailsFactory;
import org.springframework.boot.testcontainers.service.connection.ContainerConnectionSource;
/**
* @author Eddú Meléndez
*/
class RedisContainerConnectionDetailsFactory
extends ContainerConnectionDetailsFactory<RedisStackContainer, RedisConnectionDetails> {
@Override
public RedisConnectionDetails getContainerConnectionDetails(ContainerConnectionSource<RedisStackContainer> source) {
return new RedisContainerConnectionDetails(source);
}
/**
* {@link RedisConnectionDetails} backed by a {@link ContainerConnectionSource}.
*/
private static final class RedisContainerConnectionDetails extends ContainerConnectionDetails<RedisStackContainer>
implements RedisConnectionDetails {
private RedisContainerConnectionDetails(ContainerConnectionSource<RedisStackContainer> source) {
super(source);
}
@Override
public String getUri() {
return getContainer().getRedisURI();
}
}
}

View File

@@ -4,6 +4,5 @@ org.springframework.ai.testcontainers.service.connection.milvus.MilvusContainerC
org.springframework.ai.testcontainers.service.connection.ollama.OllamaContainerConnectionDetailsFactory,\
org.springframework.ai.testcontainers.service.connection.opensearch.OpenSearchContainerConnectionDetailsFactory,\
org.springframework.ai.testcontainers.service.connection.qdrant.QdrantContainerConnectionDetailsFactory,\
org.springframework.ai.testcontainers.service.connection.redis.RedisContainerConnectionDetailsFactory,\
org.springframework.ai.testcontainers.service.connection.typesense.TypesenseContainerConnectionDetailsFactory,\
org.springframework.ai.testcontainers.service.connection.weaviate.WeaviateContainerConnectionDetailsFactory

View File

@@ -1,91 +0,0 @@
/*
* Copyright 2023 - 2024 the original author or authors.
*
* 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
*
* https://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 org.springframework.ai.testcontainers.service.connection.redis;
import com.redis.testcontainers.RedisStackContainer;
import org.junit.jupiter.api.Test;
import org.springframework.ai.ResourceUtils;
import org.springframework.ai.autoconfigure.vectorstore.redis.RedisVectorStoreAutoConfiguration;
import org.springframework.ai.document.Document;
import org.springframework.ai.embedding.EmbeddingModel;
import org.springframework.ai.transformers.TransformersEmbeddingModel;
import org.springframework.ai.vectorstore.SearchRequest;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import java.util.List;
import java.util.Map;
import static org.assertj.core.api.Assertions.assertThat;
@SpringJUnitConfig
@Testcontainers
@TestPropertySource(
properties = { "spring.ai.vectorstore.redis.index=myIdx", "spring.ai.vectorstore.redis.prefix=doc:" })
class RedisContainerConnectionDetailsFactoryTest {
@Container
@ServiceConnection
static RedisStackContainer redisContainer = new RedisStackContainer(
RedisStackContainer.DEFAULT_IMAGE_NAME.withTag(RedisStackContainer.DEFAULT_TAG));
private List<Document> documents = List.of(
new Document(ResourceUtils.getText("classpath:/test/data/spring.ai.txt"), Map.of("spring", "great")),
new Document(ResourceUtils.getText("classpath:/test/data/time.shelter.txt")), new Document(
ResourceUtils.getText("classpath:/test/data/great.depression.txt"), Map.of("depression", "bad")));
@Autowired
private VectorStore vectorStore;
@Test
void addAndSearch() {
vectorStore.add(documents);
List<Document> results = vectorStore.similaritySearch(SearchRequest.query("Spring").withTopK(1));
assertThat(results).hasSize(1);
Document resultDoc = results.get(0);
assertThat(resultDoc.getId()).isEqualTo(documents.get(0).getId());
assertThat(resultDoc.getContent())
.contains("Spring AI provides abstractions that serve as the foundation for developing AI applications.");
// Remove all documents from the store
vectorStore.delete(documents.stream().map(doc -> doc.getId()).toList());
results = vectorStore.similaritySearch(SearchRequest.query("Spring").withTopK(1));
assertThat(results).isEmpty();
}
@Configuration(proxyBeanMethods = false)
@ImportAutoConfiguration(RedisVectorStoreAutoConfiguration.class)
static class Config {
@Bean
public EmbeddingModel embeddingModel() {
return new TransformersEmbeddingModel();
}
}
}

View File

@@ -34,6 +34,11 @@
<version>${parent.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>

View File

@@ -67,6 +67,7 @@ import redis.clients.jedis.search.schemafields.VectorField.VectorAlgorithm;
*
* @author Julien Ruaux
* @author Christian Tzolov
* @author Eddú Meléndez
* @see VectorStore
* @see RedisVectorStoreConfig
* @see EmbeddingModel
@@ -100,8 +101,6 @@ public class RedisVectorStore implements VectorStore, InitializingBean {
*/
public static final class RedisVectorStoreConfig {
private final String uri;
private final String indexName;
private final String prefix;
@@ -119,7 +118,6 @@ public class RedisVectorStore implements VectorStore, InitializingBean {
}
private RedisVectorStoreConfig(Builder builder) {
this.uri = builder.uri;
this.indexName = builder.indexName;
this.prefix = builder.prefix;
this.contentFieldName = builder.contentFieldName;
@@ -147,8 +145,6 @@ public class RedisVectorStore implements VectorStore, InitializingBean {
public static class Builder {
private String uri = DEFAULT_URI;
private String indexName = DEFAULT_INDEX_NAME;
private String prefix = DEFAULT_PREFIX;
@@ -164,16 +160,6 @@ public class RedisVectorStore implements VectorStore, InitializingBean {
private Builder() {
}
/**
* Configures the Redis URI to use.
* @param uri the Redis URI to use
* @return this builder
*/
public Builder withURI(String uri) {
this.uri = uri;
return this;
}
/**
* Configures the Redis index name to use.
* @param name the index name to use
@@ -247,8 +233,6 @@ public class RedisVectorStore implements VectorStore, InitializingBean {
private final boolean initializeSchema;
public static final String DEFAULT_URI = "redis://localhost:6379";
public static final String DEFAULT_INDEX_NAME = "spring-ai-index";
public static final String DEFAULT_CONTENT_FIELD_NAME = "content";
@@ -287,13 +271,14 @@ public class RedisVectorStore implements VectorStore, InitializingBean {
private FilterExpressionConverter filterExpressionConverter;
public RedisVectorStore(RedisVectorStoreConfig config, EmbeddingModel embeddingModel, boolean initializeSchema) {
public RedisVectorStore(RedisVectorStoreConfig config, EmbeddingModel embeddingModel, JedisPooled jedis,
boolean initializeSchema) {
Assert.notNull(config, "Config must not be null");
Assert.notNull(embeddingModel, "Embedding model must not be null");
this.initializeSchema = initializeSchema;
this.jedis = new JedisPooled(config.uri);
this.jedis = jedis;
this.embeddingModel = embeddingModel;
this.config = config;
this.filterExpressionConverter = new RedisFilterExpressionConverter(this.config.metadataFields);

View File

@@ -32,18 +32,23 @@ import org.springframework.ai.transformers.TransformersEmbeddingModel;
import org.springframework.ai.vectorstore.RedisVectorStore.MetadataField;
import org.springframework.ai.vectorstore.RedisVectorStore.RedisVectorStoreConfig;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import com.redis.testcontainers.RedisStackContainer;
import redis.clients.jedis.JedisPooled;
/**
* @author Julien Ruaux
* @author Eddú Meléndez
*/
@Testcontainers
class RedisVectorStoreIT {
@@ -53,7 +58,9 @@ class RedisVectorStoreIT {
RedisStackContainer.DEFAULT_IMAGE_NAME.withTag(RedisStackContainer.DEFAULT_TAG));
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withUserConfiguration(TestApplication.class);
.withConfiguration(AutoConfigurations.of(RedisAutoConfiguration.class))
.withUserConfiguration(TestApplication.class)
.withPropertyValues("spring.data.redis.url=" + redisContainer.getRedisURI());
List<Document> documents = List.of(
new Document("1", getText("classpath:/test/data/spring.ai.txt"), Map.of("meta1", "meta1")),
@@ -243,12 +250,15 @@ class RedisVectorStoreIT {
public static class TestApplication {
@Bean
public RedisVectorStore vectorStore(EmbeddingModel embeddingModel) {
return new RedisVectorStore(RedisVectorStoreConfig.builder()
.withURI(redisContainer.getRedisURI())
.withMetadataFields(MetadataField.tag("meta1"), MetadataField.tag("meta2"),
MetadataField.tag("country"), MetadataField.numeric("year"))
.build(), embeddingModel, true);
public RedisVectorStore vectorStore(EmbeddingModel embeddingModel,
JedisConnectionFactory jedisConnectionFactory) {
return new RedisVectorStore(
RedisVectorStoreConfig.builder()
.withMetadataFields(MetadataField.tag("meta1"), MetadataField.tag("meta2"),
MetadataField.tag("country"), MetadataField.numeric("year"))
.build(),
embeddingModel,
new JedisPooled(jedisConnectionFactory.getHostName(), jedisConnectionFactory.getPort()), true);
}
@Bean