customObservationConvention,
BatchingStrategy batchingStrategy) {
- Neo4jVectorStore.Neo4jVectorStoreConfig config = Neo4jVectorStore.Neo4jVectorStoreConfig.builder()
- .withDatabaseName(properties.getDatabaseName())
- .withEmbeddingDimension(properties.getEmbeddingDimension())
- .withDistanceType(properties.getDistanceType())
- .withLabel(properties.getLabel())
- .withEmbeddingProperty(properties.getEmbeddingProperty())
- .withIndexName(properties.getIndexName())
- .withIdProperty(properties.getIdProperty())
- .withConstraintName(properties.getConstraintName())
- .build();
- return new Neo4jVectorStore(driver, embeddingModel, config, properties.isInitializeSchema(),
- observationRegistry.getIfUnique(() -> ObservationRegistry.NOOP),
- customObservationConvention.getIfAvailable(() -> null), batchingStrategy);
+ return Neo4jVectorStore.builder()
+ .driver(driver)
+ .embeddingModel(embeddingModel)
+ .initializeSchema(properties.isInitializeSchema())
+ .observationRegistry(observationRegistry.getIfUnique(() -> ObservationRegistry.NOOP))
+ .customObservationConvention(customObservationConvention.getIfAvailable(() -> null))
+ .batchingStrategy(batchingStrategy)
+ .databaseName(properties.getDatabaseName())
+ .embeddingDimension(properties.getEmbeddingDimension())
+ .distanceType(properties.getDistanceType())
+ .label(properties.getLabel())
+ .embeddingProperty(properties.getEmbeddingProperty())
+ .indexName(properties.getIndexName())
+ .idProperty(properties.getIdProperty())
+ .constraintName(properties.getConstraintName())
+ .build();
}
}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/vectorstore/neo4j/Neo4jVectorStoreProperties.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/vectorstore/neo4j/Neo4jVectorStoreProperties.java
index b6a4f1547..52606c754 100644
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/vectorstore/neo4j/Neo4jVectorStoreProperties.java
+++ b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/vectorstore/neo4j/Neo4jVectorStoreProperties.java
@@ -17,7 +17,7 @@
package org.springframework.ai.autoconfigure.vectorstore.neo4j;
import org.springframework.ai.autoconfigure.vectorstore.CommonVectorStoreProperties;
-import org.springframework.ai.vectorstore.Neo4jVectorStore;
+import org.springframework.ai.vectorstore.neo4j.Neo4jVectorStore;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
diff --git a/vector-stores/spring-ai-neo4j-store/src/main/java/org/springframework/ai/vectorstore/Neo4jVectorStore.java b/vector-stores/spring-ai-neo4j-store/src/main/java/org/springframework/ai/vectorstore/neo4j/Neo4jVectorStore.java
similarity index 55%
rename from vector-stores/spring-ai-neo4j-store/src/main/java/org/springframework/ai/vectorstore/Neo4jVectorStore.java
rename to vector-stores/spring-ai-neo4j-store/src/main/java/org/springframework/ai/vectorstore/neo4j/Neo4jVectorStore.java
index 989084504..f8e9edbbe 100644
--- a/vector-stores/spring-ai-neo4j-store/src/main/java/org/springframework/ai/vectorstore/Neo4jVectorStore.java
+++ b/vector-stores/spring-ai-neo4j-store/src/main/java/org/springframework/ai/vectorstore/neo4j/Neo4jVectorStore.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package org.springframework.ai.vectorstore;
+package org.springframework.ai.vectorstore.neo4j;
import java.util.HashMap;
import java.util.List;
@@ -36,15 +36,99 @@ import org.springframework.ai.embedding.EmbeddingOptionsBuilder;
import org.springframework.ai.embedding.TokenCountBatchingStrategy;
import org.springframework.ai.observation.conventions.VectorStoreProvider;
import org.springframework.ai.observation.conventions.VectorStoreSimilarityMetric;
-import org.springframework.ai.vectorstore.filter.Neo4jVectorFilterExpressionConverter;
+import org.springframework.ai.vectorstore.AbstractVectorStoreBuilder;
+import org.springframework.ai.vectorstore.SearchRequest;
+import org.springframework.ai.vectorstore.neo4j.filter.Neo4jVectorFilterExpressionConverter;
import org.springframework.ai.vectorstore.observation.AbstractObservationVectorStore;
import org.springframework.ai.vectorstore.observation.VectorStoreObservationContext;
import org.springframework.ai.vectorstore.observation.VectorStoreObservationConvention;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;
+import org.springframework.util.StringUtils;
/**
- * A vector store implementation that stores and retrieves vectors in a Neo4j database.
+ * Neo4j-based vector store implementation using Neo4j's vector search capabilities.
+ *
+ *
+ * The store uses Neo4j's vector search functionality to persist and query vector
+ * embeddings along with their associated document content and metadata. The
+ * implementation leverages Neo4j's HNSW (Hierarchical Navigable Small World) algorithm
+ * for efficient k-NN search operations.
+ *
+ *
+ *
+ * Features:
+ *
+ *
+ * - Automatic schema initialization with configurable index creation
+ * - Support for multiple distance functions: Cosine and Euclidean
+ * - Metadata filtering using Neo4j's WHERE clause expressions
+ * - Configurable similarity thresholds for search results
+ * - Batch processing support with configurable strategies
+ * - Observation and metrics support through Micrometer
+ *
+ *
+ *
+ * Basic usage example:
+ *
+ * {@code
+ * Neo4jVectorStore vectorStore = Neo4jVectorStore.builder()
+ * .driver(driver)
+ * .embeddingModel(embeddingModel)
+ * .initializeSchema(true)
+ * .build();
+ *
+ * // Add documents
+ * vectorStore.add(List.of(
+ * new Document("content1", Map.of("key1", "value1")),
+ * new Document("content2", Map.of("key2", "value2"))
+ * ));
+ *
+ * // Search with filters
+ * List results = vectorStore.similaritySearch(
+ * SearchRequest.query("search text")
+ * .withTopK(5)
+ * .withSimilarityThreshold(0.7)
+ * .withFilterExpression("key1 == 'value1'")
+ * );
+ * }
+ *
+ *
+ * Advanced configuration example:
+ *
+ * {@code
+ * Neo4jVectorStore vectorStore = Neo4jVectorStore.builder()
+ * .driver(driver)
+ * .embeddingModel(embeddingModel)
+ * .databaseName("neo4j")
+ * .distanceType(Neo4jDistanceType.COSINE)
+ * .dimensions(1536)
+ * .label("CustomDocument")
+ * .embeddingProperty("vector")
+ * .indexName("custom-vectors")
+ * .initializeSchema(true)
+ * .batchingStrategy(new TokenCountBatchingStrategy())
+ * .build();
+ * }
+ *
+ *
+ * Requirements:
+ *
+ *
+ * - Neo4j 5.15 or later
+ * - Node schema with id (string), text (string), metadata (object), and embedding
+ * (vector) properties
+ *
+ *
+ *
+ * Distance Functions:
+ *
+ *
+ * - cosine: Default, suitable for most use cases. Measures cosine similarity between
+ * vectors.
+ * - euclidean: Euclidean distance between vectors. Lower values indicate higher
+ * similarity.
+ *
*
* @author Gerrit Meier
* @author Michael Simons
@@ -52,6 +136,7 @@ import org.springframework.util.Assert;
* @author Thomas Vitale
* @author Soby Chacko
* @author Jihoon Kim
+ * @since 1.0.0
*/
public class Neo4jVectorStore extends AbstractObservationVectorStore implements InitializingBean {
@@ -73,36 +158,77 @@ public class Neo4jVectorStore extends AbstractObservationVectorStore implements
Neo4jDistanceType.COSINE, VectorStoreSimilarityMetric.COSINE, Neo4jDistanceType.EUCLIDEAN,
VectorStoreSimilarityMetric.EUCLIDEAN);
- private final Neo4jVectorFilterExpressionConverter filterExpressionConverter = new Neo4jVectorFilterExpressionConverter();
-
private final Driver driver;
- private final EmbeddingModel embeddingModel;
+ private final SessionConfig sessionConfig;
- private final Neo4jVectorStoreConfig config;
+ private final int embeddingDimension;
+
+ private final Neo4jDistanceType distanceType;
+
+ private final String embeddingProperty;
+
+ private final String label;
+
+ private final String indexName;
+
+ private final String indexNameNotSanitized;
+
+ private final String idProperty;
+
+ private final String constraintName;
+
+ private final Neo4jVectorFilterExpressionConverter filterExpressionConverter = new Neo4jVectorFilterExpressionConverter();
private final boolean initializeSchema;
private final BatchingStrategy batchingStrategy;
+ @Deprecated(since = "1.0.0-M5", forRemoval = true)
public Neo4jVectorStore(Driver driver, EmbeddingModel embeddingModel, Neo4jVectorStoreConfig config,
boolean initializeSchema) {
this(driver, embeddingModel, config, initializeSchema, ObservationRegistry.NOOP, null,
new TokenCountBatchingStrategy());
}
+ @Deprecated(since = "1.0.0-M5", forRemoval = true)
public Neo4jVectorStore(Driver driver, EmbeddingModel embeddingModel, Neo4jVectorStoreConfig config,
boolean initializeSchema, ObservationRegistry observationRegistry,
VectorStoreObservationConvention customObservationConvention, BatchingStrategy batchingStrategy) {
- super(observationRegistry, customObservationConvention);
- this.initializeSchema = initializeSchema;
- Assert.notNull(driver, "Neo4j driver must not be null");
- Assert.notNull(embeddingModel, "Embedding model must not be null");
- this.driver = driver;
- this.embeddingModel = embeddingModel;
- this.config = config;
- this.batchingStrategy = batchingStrategy;
+ this(builder().driver(driver)
+ .embeddingModel(embeddingModel)
+ .sessionConfig(config.sessionConfig)
+ .embeddingDimension(config.embeddingDimension)
+ .distanceType(config.distanceType)
+ .embeddingProperty(config.embeddingProperty)
+ .label(config.label)
+ .indexName(config.indexName)
+ .idProperty(config.idProperty)
+ .constraintName(config.constraintName)
+ .initializeSchema(initializeSchema)
+ .observationRegistry(observationRegistry)
+ .customObservationConvention(customObservationConvention)
+ .batchingStrategy(batchingStrategy));
+ }
+
+ protected Neo4jVectorStore(Neo4jBuilder builder) {
+ super(builder);
+
+ Assert.notNull(builder.driver, "Neo4j driver must not be null");
+
+ this.driver = builder.driver;
+ this.sessionConfig = builder.sessionConfig;
+ this.embeddingDimension = builder.embeddingDimension;
+ this.distanceType = builder.distanceType;
+ this.embeddingProperty = SchemaNames.sanitize(builder.embeddingProperty).orElseThrow();
+ this.label = SchemaNames.sanitize(builder.label).orElseThrow();
+ this.indexNameNotSanitized = builder.indexName;
+ this.indexName = SchemaNames.sanitize(builder.indexName, true).orElseThrow();
+ this.idProperty = SchemaNames.sanitize(builder.idProperty).orElseThrow();
+ this.constraintName = SchemaNames.sanitize(builder.constraintName).orElseThrow();
+ this.initializeSchema = builder.initializeSchema;
+ this.batchingStrategy = builder.batchingStrategy;
}
@Override
@@ -127,17 +253,17 @@ public class Neo4jVectorStore extends AbstractObservationVectorStore implements
u += row.properties
WITH row, u
CALL db.create.setNodeVectorProperty(u, $embeddingProperty, row[$embeddingProperty])
- """.formatted(this.config.label, this.config.idProperty);
- session.executeWrite(
- tx -> tx.run(statement, Map.of("rows", rows, "embeddingProperty", this.config.embeddingProperty))
- .consume());
+ """.formatted(this.label, this.idProperty);
+ session
+ .executeWrite(tx -> tx.run(statement, Map.of("rows", rows, "embeddingProperty", this.embeddingProperty))
+ .consume());
}
}
@Override
public Optional doDelete(List idList) {
- try (var session = this.driver.session(this.config.sessionConfig)) {
+ try (var session = this.driver.session(this.sessionConfig)) {
// Those queries with internal, cypher based transaction management cannot be
// run with executeWrite
@@ -145,7 +271,7 @@ public class Neo4jVectorStore extends AbstractObservationVectorStore implements
.run("""
MATCH (n:%s) WHERE n.%s IN $ids
CALL { WITH n DETACH DELETE n } IN TRANSACTIONS OF $transactionSize ROWS
- """.formatted(this.config.label, this.config.idProperty),
+ """.formatted(this.label, this.idProperty),
Map.of("ids", idList, "transactionSize", DEFAULT_TRANSACTION_SIZE))
.consume();
return Optional.of(idList.size() == summary.counters().nodesDeleted());
@@ -159,7 +285,7 @@ public class Neo4jVectorStore extends AbstractObservationVectorStore implements
"The similarity score is bounded between 0 and 1; least to most similar respectively.");
var embedding = Values.value(this.embeddingModel.embed(request.getQuery()));
- try (var session = this.driver.session(this.config.sessionConfig)) {
+ try (var session = this.driver.session(this.sessionConfig)) {
StringBuilder condition = new StringBuilder("score >= $threshold");
if (request.hasFilterExpression()) {
condition.append(" AND ")
@@ -172,8 +298,9 @@ public class Neo4jVectorStore extends AbstractObservationVectorStore implements
RETURN node, score""".formatted(condition);
return session.executeRead(tx -> tx
- .run(query, Map.of("indexName", this.config.indexNameNotSanitized, "numberOfNearestNeighbours",
- request.getTopK(), "embeddingValue", embedding, "threshold", request.getSimilarityThreshold()))
+ .run(query,
+ Map.of("indexName", this.indexNameNotSanitized, "numberOfNearestNeighbours", request.getTopK(),
+ "embeddingValue", embedding, "threshold", request.getSimilarityThreshold()))
.list(this::recordToDocument));
}
}
@@ -185,11 +312,11 @@ public class Neo4jVectorStore extends AbstractObservationVectorStore implements
return;
}
- try (var session = this.driver.session(this.config.sessionConfig)) {
+ try (var session = this.driver.session(this.sessionConfig)) {
session.executeWriteWithoutResult(tx -> {
tx.run("CREATE CONSTRAINT %s IF NOT EXISTS FOR (n:%s) REQUIRE n.%s IS UNIQUE"
- .formatted(this.config.constraintName, this.config.label, this.config.idProperty)).consume();
+ .formatted(this.constraintName, this.label, this.idProperty)).consume();
var statement = """
CREATE VECTOR INDEX %s IF NOT EXISTS FOR (n:%s) ON (n.%s)
@@ -197,8 +324,8 @@ public class Neo4jVectorStore extends AbstractObservationVectorStore implements
`vector.dimensions`: %d,
`vector.similarity_function`: '%s'
}}
- """.formatted(this.config.indexName, this.config.label, this.config.embeddingProperty,
- this.config.embeddingDimension, this.config.distanceType.name);
+ """.formatted(this.indexName, this.label, this.embeddingProperty, this.embeddingDimension,
+ this.distanceType.name);
tx.run(statement).consume();
});
@@ -219,7 +346,7 @@ public class Neo4jVectorStore extends AbstractObservationVectorStore implements
document.getMetadata().forEach((k, v) -> properties.put("metadata." + k, Values.value(v)));
row.put("properties", properties);
- row.put(this.config.embeddingProperty, Values.value(embedding));
+ row.put(this.embeddingProperty, Values.value(embedding));
return row;
}
@@ -235,7 +362,7 @@ public class Neo4jVectorStore extends AbstractObservationVectorStore implements
});
return Document.builder()
- .id(node.get(this.config.idProperty).asString())
+ .id(node.get(this.idProperty).asString())
.text(node.get("text").asString())
.metadata(Map.copyOf(metaData))
.score((double) score)
@@ -246,16 +373,16 @@ public class Neo4jVectorStore extends AbstractObservationVectorStore implements
public VectorStoreObservationContext.Builder createObservationContextBuilder(String operationName) {
return VectorStoreObservationContext.builder(VectorStoreProvider.NEO4J.value(), operationName)
- .withCollectionName(this.config.indexName)
+ .withCollectionName(this.indexName)
.withDimensions(this.embeddingModel.dimensions())
.withSimilarityMetric(getSimilarityMetric());
}
private String getSimilarityMetric() {
- if (!SIMILARITY_TYPE_MAPPING.containsKey(this.config.distanceType)) {
- return this.config.distanceType.name();
+ if (!SIMILARITY_TYPE_MAPPING.containsKey(this.distanceType)) {
+ return this.distanceType.name();
}
- return SIMILARITY_TYPE_MAPPING.get(this.config.distanceType).value();
+ return SIMILARITY_TYPE_MAPPING.get(this.distanceType).value();
}
/**
@@ -273,9 +400,181 @@ public class Neo4jVectorStore extends AbstractObservationVectorStore implements
}
+ public static Neo4jBuilder builder() {
+ return new Neo4jBuilder();
+ }
+
+ public static class Neo4jBuilder extends AbstractVectorStoreBuilder {
+
+ private Driver driver;
+
+ private SessionConfig sessionConfig = SessionConfig.defaultConfig();
+
+ private int embeddingDimension = DEFAULT_EMBEDDING_DIMENSION;
+
+ private Neo4jDistanceType distanceType = Neo4jDistanceType.COSINE;
+
+ private String label = DEFAULT_LABEL;
+
+ private String embeddingProperty = DEFAULT_EMBEDDING_PROPERTY;
+
+ private String indexName = DEFAULT_INDEX_NAME;
+
+ private String idProperty = DEFAULT_ID_PROPERTY;
+
+ private String constraintName = DEFAULT_CONSTRAINT_NAME;
+
+ private boolean initializeSchema = false;
+
+ private BatchingStrategy batchingStrategy = new TokenCountBatchingStrategy();
+
+ public Neo4jBuilder driver(Driver driver) {
+ Assert.notNull(driver, "Neo4j driver must not be null");
+ this.driver = driver;
+ return this;
+ }
+
+ /**
+ * Sets the database name. When provided and not blank, creates a session config
+ * for that database.
+ * @param databaseName the database name to use
+ * @return the builder instance
+ */
+ public Neo4jBuilder databaseName(String databaseName) {
+ if (StringUtils.hasText(databaseName)) {
+ this.sessionConfig = SessionConfig.forDatabase(databaseName);
+ }
+ return this;
+ }
+
+ /**
+ * Sets the session configuration directly.
+ * @param sessionConfig the session configuration to use
+ * @return the builder instance
+ */
+ public Neo4jBuilder sessionConfig(SessionConfig sessionConfig) {
+ this.sessionConfig = sessionConfig;
+ return this;
+ }
+
+ /**
+ * Sets the embedding dimension. Must be positive.
+ * @param dimension the dimension of the embedding
+ * @return the builder instance
+ * @throws IllegalArgumentException if dimension is less than 1
+ */
+ public Neo4jBuilder embeddingDimension(int dimension) {
+ Assert.isTrue(dimension >= 1, "Dimension has to be positive");
+ this.embeddingDimension = dimension;
+ return this;
+ }
+
+ /**
+ * Sets the distance type for index storage and queries.
+ * @param distanceType the distance type to use
+ * @return the builder instance
+ * @throws IllegalArgumentException if distanceType is null
+ */
+ public Neo4jBuilder distanceType(Neo4jDistanceType distanceType) {
+ Assert.notNull(distanceType, "Distance type may not be null");
+ this.distanceType = distanceType;
+ return this;
+ }
+
+ /**
+ * Sets the label for document nodes.
+ * @param label the label to use
+ * @return the builder instance
+ */
+ public Neo4jBuilder label(String label) {
+ if (StringUtils.hasText(label)) {
+ this.label = label;
+ }
+ return this;
+ }
+
+ /**
+ * Sets the property name for storing embeddings.
+ * @param embeddingProperty the property name to use
+ * @return the builder instance
+ */
+ public Neo4jBuilder embeddingProperty(String embeddingProperty) {
+ if (StringUtils.hasText(embeddingProperty)) {
+ this.embeddingProperty = embeddingProperty;
+ }
+ return this;
+ }
+
+ /**
+ * Sets the name of the vector index.
+ * @param indexName the index name to use
+ * @return the builder instance
+ */
+ public Neo4jBuilder indexName(String indexName) {
+ if (StringUtils.hasText(indexName)) {
+ this.indexName = indexName;
+ }
+ return this;
+ }
+
+ /**
+ * Sets the property name for document IDs.
+ * @param idProperty the property name to use
+ * @return the builder instance
+ */
+ public Neo4jBuilder idProperty(String idProperty) {
+ if (StringUtils.hasText(idProperty)) {
+ this.idProperty = idProperty;
+ }
+ return this;
+ }
+
+ /**
+ * Sets the name of the unique constraint.
+ * @param constraintName the constraint name to use
+ * @return the builder instance
+ */
+ public Neo4jBuilder constraintName(String constraintName) {
+ if (StringUtils.hasText(constraintName)) {
+ this.constraintName = constraintName;
+ }
+ return this;
+ }
+
+ /**
+ * Sets whether to initialize the schema.
+ * @param initializeSchema true to initialize schema, false otherwise
+ * @return the builder instance
+ */
+ public Neo4jBuilder initializeSchema(boolean initializeSchema) {
+ this.initializeSchema = initializeSchema;
+ return this;
+ }
+
+ /**
+ * Sets the batching strategy.
+ * @param batchingStrategy the strategy to use
+ * @return the builder instance
+ * @throws IllegalArgumentException if batchingStrategy is null
+ */
+ public Neo4jBuilder batchingStrategy(BatchingStrategy batchingStrategy) {
+ Assert.notNull(batchingStrategy, "BatchingStrategy must not be null");
+ this.batchingStrategy = batchingStrategy;
+ return this;
+ }
+
+ @Override
+ public Neo4jVectorStore build() {
+ validate();
+ return new Neo4jVectorStore(this);
+ }
+
+ }
+
/**
* Configuration for the Neo4j vector store.
*/
+ @Deprecated(since = "1.0.0-M5", forRemoval = true)
public static final class Neo4jVectorStoreConfig {
private final SessionConfig sessionConfig;
@@ -317,19 +616,20 @@ public class Neo4jVectorStore extends AbstractObservationVectorStore implements
* Start building a new configuration.
* @return The entry point for creating a new configuration.
*/
+ @Deprecated(since = "1.0.0-M5", forRemoval = true)
public static Builder builder() {
-
return new Builder();
}
/**
* {@return the default config}
*/
+ @Deprecated(since = "1.0.0-M5", forRemoval = true)
public static Neo4jVectorStoreConfig defaultConfig() {
-
return builder().build();
}
+ @Deprecated(since = "1.0.0-M5", forRemoval = true)
public static final class Builder {
private String databaseName;
diff --git a/vector-stores/spring-ai-neo4j-store/src/main/java/org/springframework/ai/vectorstore/filter/Neo4jVectorFilterExpressionConverter.java b/vector-stores/spring-ai-neo4j-store/src/main/java/org/springframework/ai/vectorstore/neo4j/filter/Neo4jVectorFilterExpressionConverter.java
similarity index 96%
rename from vector-stores/spring-ai-neo4j-store/src/main/java/org/springframework/ai/vectorstore/filter/Neo4jVectorFilterExpressionConverter.java
rename to vector-stores/spring-ai-neo4j-store/src/main/java/org/springframework/ai/vectorstore/neo4j/filter/Neo4jVectorFilterExpressionConverter.java
index 747c0807a..6786e0e3b 100644
--- a/vector-stores/spring-ai-neo4j-store/src/main/java/org/springframework/ai/vectorstore/filter/Neo4jVectorFilterExpressionConverter.java
+++ b/vector-stores/spring-ai-neo4j-store/src/main/java/org/springframework/ai/vectorstore/neo4j/filter/Neo4jVectorFilterExpressionConverter.java
@@ -14,8 +14,9 @@
* limitations under the License.
*/
-package org.springframework.ai.vectorstore.filter;
+package org.springframework.ai.vectorstore.neo4j.filter;
+import org.springframework.ai.vectorstore.filter.Filter;
import org.springframework.ai.vectorstore.filter.Filter.Expression;
import org.springframework.ai.vectorstore.filter.Filter.Group;
import org.springframework.ai.vectorstore.filter.Filter.Key;
diff --git a/vector-stores/spring-ai-neo4j-store/src/test/java/org/springframework/ai/vectorstore/Neo4jImage.java b/vector-stores/spring-ai-neo4j-store/src/test/java/org/springframework/ai/vectorstore/neo4j/Neo4jImage.java
similarity index 94%
rename from vector-stores/spring-ai-neo4j-store/src/test/java/org/springframework/ai/vectorstore/Neo4jImage.java
rename to vector-stores/spring-ai-neo4j-store/src/test/java/org/springframework/ai/vectorstore/neo4j/Neo4jImage.java
index e0133dbf1..17ab2e168 100644
--- a/vector-stores/spring-ai-neo4j-store/src/test/java/org/springframework/ai/vectorstore/Neo4jImage.java
+++ b/vector-stores/spring-ai-neo4j-store/src/test/java/org/springframework/ai/vectorstore/neo4j/Neo4jImage.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package org.springframework.ai.vectorstore;
+package org.springframework.ai.vectorstore.neo4j;
import org.testcontainers.utility.DockerImageName;
diff --git a/vector-stores/spring-ai-neo4j-store/src/test/java/org/springframework/ai/vectorstore/Neo4jVectorStoreIT.java b/vector-stores/spring-ai-neo4j-store/src/test/java/org/springframework/ai/vectorstore/neo4j/Neo4jVectorStoreIT.java
similarity index 97%
rename from vector-stores/spring-ai-neo4j-store/src/test/java/org/springframework/ai/vectorstore/Neo4jVectorStoreIT.java
rename to vector-stores/spring-ai-neo4j-store/src/test/java/org/springframework/ai/vectorstore/neo4j/Neo4jVectorStoreIT.java
index b5bc62ef9..401b8a559 100644
--- a/vector-stores/spring-ai-neo4j-store/src/test/java/org/springframework/ai/vectorstore/Neo4jVectorStoreIT.java
+++ b/vector-stores/spring-ai-neo4j-store/src/test/java/org/springframework/ai/vectorstore/neo4j/Neo4jVectorStoreIT.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package org.springframework.ai.vectorstore;
+package org.springframework.ai.vectorstore.neo4j;
import java.util.Collections;
import java.util.List;
@@ -37,6 +37,8 @@ import org.springframework.ai.document.Document;
import org.springframework.ai.embedding.EmbeddingModel;
import org.springframework.ai.openai.OpenAiEmbeddingModel;
import org.springframework.ai.openai.api.OpenAiApi;
+import org.springframework.ai.vectorstore.SearchRequest;
+import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.ai.vectorstore.filter.FilterExpressionTextParser;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
@@ -289,8 +291,11 @@ class Neo4jVectorStoreIT {
@Bean
public VectorStore vectorStore(Driver driver, EmbeddingModel embeddingModel) {
- return new Neo4jVectorStore(driver, embeddingModel, Neo4jVectorStore.Neo4jVectorStoreConfig.defaultConfig(),
- true);
+ return Neo4jVectorStore.builder()
+ .driver(driver)
+ .embeddingModel(embeddingModel)
+ .initializeSchema(true)
+ .build();
}
@Bean
diff --git a/vector-stores/spring-ai-neo4j-store/src/test/java/org/springframework/ai/vectorstore/Neo4jVectorStoreObservationIT.java b/vector-stores/spring-ai-neo4j-store/src/test/java/org/springframework/ai/vectorstore/neo4j/Neo4jVectorStoreObservationIT.java
similarity index 94%
rename from vector-stores/spring-ai-neo4j-store/src/test/java/org/springframework/ai/vectorstore/Neo4jVectorStoreObservationIT.java
rename to vector-stores/spring-ai-neo4j-store/src/test/java/org/springframework/ai/vectorstore/neo4j/Neo4jVectorStoreObservationIT.java
index 1c841b1c1..0239f5454 100644
--- a/vector-stores/spring-ai-neo4j-store/src/test/java/org/springframework/ai/vectorstore/Neo4jVectorStoreObservationIT.java
+++ b/vector-stores/spring-ai-neo4j-store/src/test/java/org/springframework/ai/vectorstore/neo4j/Neo4jVectorStoreObservationIT.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package org.springframework.ai.vectorstore;
+package org.springframework.ai.vectorstore.neo4j;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
@@ -43,6 +43,8 @@ import org.springframework.ai.observation.conventions.VectorStoreProvider;
import org.springframework.ai.observation.conventions.VectorStoreSimilarityMetric;
import org.springframework.ai.openai.OpenAiEmbeddingModel;
import org.springframework.ai.openai.api.OpenAiApi;
+import org.springframework.ai.vectorstore.SearchRequest;
+import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.ai.vectorstore.observation.DefaultVectorStoreObservationConvention;
import org.springframework.ai.vectorstore.observation.VectorStoreObservationDocumentation.HighCardinalityKeyNames;
import org.springframework.ai.vectorstore.observation.VectorStoreObservationDocumentation.LowCardinalityKeyNames;
@@ -176,8 +178,14 @@ public class Neo4jVectorStoreObservationIT {
public VectorStore vectorStore(Driver driver, EmbeddingModel embeddingModel,
ObservationRegistry observationRegistry) {
- return new Neo4jVectorStore(driver, embeddingModel, Neo4jVectorStore.Neo4jVectorStoreConfig.defaultConfig(),
- true, observationRegistry, null, new TokenCountBatchingStrategy());
+ return Neo4jVectorStore.builder()
+ .driver(driver)
+ .embeddingModel(embeddingModel)
+ .initializeSchema(true)
+ .observationRegistry(observationRegistry)
+ .customObservationConvention(null)
+ .batchingStrategy(new TokenCountBatchingStrategy())
+ .build();
}
@Bean
diff --git a/vector-stores/spring-ai-neo4j-store/src/test/java/org/springframework/ai/vectorstore/filter/Neo4jVectorFilterExpressionConverterTests.java b/vector-stores/spring-ai-neo4j-store/src/test/java/org/springframework/ai/vectorstore/neo4j/filter/Neo4jVectorFilterExpressionConverterTests.java
similarity index 95%
rename from vector-stores/spring-ai-neo4j-store/src/test/java/org/springframework/ai/vectorstore/filter/Neo4jVectorFilterExpressionConverterTests.java
rename to vector-stores/spring-ai-neo4j-store/src/test/java/org/springframework/ai/vectorstore/neo4j/filter/Neo4jVectorFilterExpressionConverterTests.java
index 4eeaa22f1..aeb6c01f4 100644
--- a/vector-stores/spring-ai-neo4j-store/src/test/java/org/springframework/ai/vectorstore/filter/Neo4jVectorFilterExpressionConverterTests.java
+++ b/vector-stores/spring-ai-neo4j-store/src/test/java/org/springframework/ai/vectorstore/neo4j/filter/Neo4jVectorFilterExpressionConverterTests.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package org.springframework.ai.vectorstore.filter;
+package org.springframework.ai.vectorstore.neo4j.filter;
import java.util.List;
@@ -24,6 +24,8 @@ import org.springframework.ai.vectorstore.filter.Filter.Expression;
import org.springframework.ai.vectorstore.filter.Filter.Group;
import org.springframework.ai.vectorstore.filter.Filter.Key;
import org.springframework.ai.vectorstore.filter.Filter.Value;
+import org.springframework.ai.vectorstore.filter.FilterExpressionConverter;
+import org.springframework.ai.vectorstore.filter.FilterExpressionTextParser;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.ai.vectorstore.filter.Filter.ExpressionType.AND;
@@ -130,7 +132,7 @@ public class Neo4jVectorFilterExpressionConverterTests {
@Test
public void testComplexIdentifiers2() {
- Filter.Expression expr = new FilterExpressionTextParser()
+ Expression expr = new FilterExpressionTextParser()
.parse("author in ['john', 'jill'] && 'article_type' == 'blog'");
String vectorExpr = this.converter.convertExpression(expr);
assertThat(vectorExpr)