Skip to content

Commit cde8f74

Browse files
sobychackomarkpollack
authored andcommitted
Add builder pattern and refactor Neo4jVectorStore
The Neo4j vector store implementation has been enhanced with a builder pattern to be more intuitive than using ctors and follows spring ai builder conventions. Current constructors have been deprecated to maintain backward compatibility for one releaes cycle. The change includes: * Move classes to dedicated neo4j package for better organization * Add comprehensive builder pattern implementation with validation * Improve documentation with detailed usage examples * Deprecate but maintain old configuration approach for compatibility * Update integration tests to demonstrate new builder pattern * Enhance code readability and maintainability
1 parent 2d3bdcd commit cde8f74

File tree

9 files changed

+506
-149
lines changed

9 files changed

+506
-149
lines changed

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

Lines changed: 125 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,16 @@ Those indexes are powered by Lucene using a Hierarchical Navigable Small World G
2020
** link:https://neo4j.com/deployment-center/[Neo4j Server] instance
2121
* If required, an API key for the xref:api/embeddings.adoc#available-implementations[EmbeddingModel] to generate the embeddings stored by the `Neo4jVectorStore`.
2222

23-
== Dependencies
23+
== Auto-configuration
2424

25-
Add the Neo4j Vector Store dependency to your project:
25+
Spring AI provides Spring Boot auto-configuration for the Neo4j Vector Store.
26+
To enable it, add the following dependency to your project's Maven `pom.xml` file:
2627

2728
[source,xml]
2829
----
2930
<dependency>
30-
<groupId>org.springframework.ai</groupId>
31-
<artifactId>spring-ai-neo4j-store</artifactId>
31+
<groupId>org.springframework.ai</groupId>
32+
<artifactId>spring-ai-neo4j-store-spring-boot-starter</artifactId>
3233
</dependency>
3334
----
3435

@@ -37,64 +38,108 @@ or to your Gradle `build.gradle` build file.
3738
[source,groovy]
3839
----
3940
dependencies {
40-
implementation 'org.springframework.ai:spring-ai-neo4j-store'
41+
implementation 'org.springframework.ai:spring-ai-neo4j-store-spring-boot-starter'
4142
}
4243
----
4344

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

47+
Please have a look at the list of xref:#_neo4jvectorstore_properties[configuration parameters] for the vector store to learn about the default values and configuration options.
48+
49+
TIP: Refer to the xref:getting-started.adoc#repositories[Repositories] section to add Milestone and/or Snapshot Repositories to your build file.
4850

4951
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.
5052

5153
NOTE: this is a breaking change! In earlier versions of Spring AI, this schema initialization happened by default.
5254

55+
Additionally, you will need a configured `EmbeddingModel` bean. Refer to the xref:api/embeddings.adoc#available-implementations[EmbeddingModel] section for more information.
5356

54-
== Configuration
55-
56-
To connect to Neo4j and use the `Neo4jVectorStore`, you need to provide access details for your instance.
57-
A simple configuration can either be provided via Spring Boot's _application.properties_,
57+
Now you can auto-wire the `Neo4jVectorStore` as a vector store in your application.
5858

59-
[source,properties]
60-
----
61-
spring.neo4j.uri=<uri_for_your_neo4j_instance>
62-
spring.neo4j.authentication.username=<your_username>
63-
spring.neo4j.authentication.password=<your_password>
64-
# API key if needed, e.g. OpenAI
65-
spring.ai.openai.api.key=<api-key>
59+
[source,java]
6660
----
61+
@Autowired VectorStore vectorStore;
6762
68-
environment variables,
63+
// ...
6964
70-
[source,bash]
71-
----
72-
export SPRING_NEO4J_URI=<uri_for_your_neo4j_instance>
73-
export SPRING_NEO4J_AUTHENTICATION_USERNAME=<your_username>
74-
export SPRING_NEO4J_AUTHENTICATION_PASSWORD=<your_password>
75-
# API key if needed, e.g. OpenAI
76-
export SPRING_AI_OPENAI_API_KEY=<api-key>
65+
List<Document> documents = List.of(
66+
new Document("Spring AI rocks!! Spring AI rocks!! Spring AI rocks!! Spring AI rocks!! Spring AI rocks!!", Map.of("meta1", "meta1")),
67+
new Document("The World is Big and Salvation Lurks Around the Corner"),
68+
new Document("You walk forward facing the past and you turn back toward the future.", Map.of("meta2", "meta2")));
69+
70+
// Add the documents to Neo4j
71+
vectorStore.add(documents);
72+
73+
// Retrieve documents similar to a query
74+
List<Document> results = vectorStore.similaritySearch(SearchRequest.query("Spring").withTopK(5));
7775
----
7876

79-
or can be a mix of those.
80-
For example, if you want to store your API key as an environment variable but keep the rest in the plain _application.properties_ file.
77+
[[neo4jvector-properties]]
78+
=== Configuration Properties
79+
80+
To connect to Neo4j and use the `Neo4jVectorStore`, you need to provide access details for your instance.
81+
A simple configuration can be provided via Spring Boot's `application.yml`:
82+
83+
[source,yaml]
84+
----
85+
spring:
86+
neo4j:
87+
uri: <neo4j instance URI>
88+
authentication:
89+
username: <neo4j username>
90+
password: <neo4j password>
91+
ai:
92+
vectorstore:
93+
neo4j:
94+
initialize-schema: true
95+
database-name: neo4j
96+
index-name: custom-index
97+
dimensions: 1536
98+
distance-type: cosine
99+
batching-strategy: TOKEN_COUNT # Optional: Controls how documents are batched for embedding
100+
----
101+
102+
The Spring Boot properties starting with `spring.neo4j.*` are used to configure the Neo4j client:
103+
104+
[cols="2,5,1",stripes=even]
105+
|===
106+
|Property | Description | Default Value
107+
108+
| `spring.neo4j.uri` | URI for connecting to the Neo4j instance | `neo4j://localhost:7687`
109+
| `spring.neo4j.authentication.username` | Username for authentication with Neo4j | `neo4j`
110+
| `spring.neo4j.authentication.password` | Password for authentication with Neo4j | -
111+
|===
112+
113+
Properties starting with `spring.ai.vectorstore.neo4j.*` are used to configure the `Neo4jVectorStore`:
81114

82-
NOTE: If you choose to create a shell script for ease in future work, be sure to run it prior to starting your application by "sourcing" the file, i.e. `source <your_script_name>.sh`.
115+
[cols="2,5,1",stripes=even]
116+
|===
117+
|Property | Description | Default Value
118+
119+
|`spring.ai.vectorstore.neo4j.initialize-schema`| Whether to initialize the required schema | `false`
120+
|`spring.ai.vectorstore.neo4j.database-name` | The name of the Neo4j database to use | `neo4j`
121+
|`spring.ai.vectorstore.neo4j.index-name` | The name of the index to store the vectors | `spring-ai-document-index`
122+
|`spring.ai.vectorstore.neo4j.dimensions` | The number of dimensions in the vector | `1536`
123+
|`spring.ai.vectorstore.neo4j.distance-type` | The distance function to use | `cosine`
124+
|`spring.ai.vectorstore.neo4j.label` | The label used for document nodes | `Document`
125+
|`spring.ai.vectorstore.neo4j.embedding-property` | The property name used to store embeddings | `embedding`
126+
|`spring.ai.vectorstore.neo4j.batching-strategy` | Strategy for batching documents when calculating embeddings. Options are `TOKEN_COUNT` or `FIXED_SIZE` | `TOKEN_COUNT`
127+
|===
83128

84-
NOTE: Besides _application.properties_ and environment variables, Spring Boot offers https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.external-config[additional configuration options].
129+
The following distance functions are available:
85130

86-
Spring Boot's auto-configuration feature for the Neo4j Driver will create a bean instance that will be used by the `Neo4jVectorStore`.
131+
* `cosine` - Default, suitable for most use cases. Measures cosine similarity between vectors.
132+
* `euclidean` - Euclidean distance between vectors. Lower values indicate higher similarity.
87133

88-
== Auto-configuration
134+
== Manual Configuration
89135

90-
Spring AI provides Spring Boot auto-configuration for the Neo4j Vector Store.
91-
To enable it, add the following dependency to your project's Maven `pom.xml` file:
136+
Instead of using the Spring Boot auto-configuration, you can manually configure the Neo4j vector store. For this you need to add the `spring-ai-neo4j-store` to your project:
92137

93-
[source, xml]
138+
[source,xml]
94139
----
95140
<dependency>
96141
<groupId>org.springframework.ai</groupId>
97-
<artifactId>spring-ai-neo4j-store-spring-boot-starter</artifactId>
142+
<artifactId>spring-ai-neo4j-store</artifactId>
98143
</dependency>
99144
----
100145

@@ -103,44 +148,52 @@ or to your Gradle `build.gradle` build file.
103148
[source,groovy]
104149
----
105150
dependencies {
106-
implementation 'org.springframework.ai:spring-ai-neo4j-store-spring-boot-starter'
151+
implementation 'org.springframework.ai:spring-ai-neo4j-store'
107152
}
108153
----
109154

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

112-
Please have a look at the list of xref:#_neo4jvectorstore_properties[configuration parameters] for the vector store to learn about the default values and configuration options.
113-
114-
TIP: Refer to the xref:getting-started.adoc#repositories[Repositories] section to add Milestone and/or Snapshot Repositories to your build file.
115-
116-
Additionally, you will need a configured `EmbeddingModel` bean. Refer to the xref:api/embeddings.adoc#available-implementations[EmbeddingModel] section for more information.
117-
118-
Here is an example of the needed bean:
157+
Create a Neo4j `Driver` bean.
158+
Read the link:https://neo4j.com/docs/java-manual/current/client-applications/[Neo4j Documentation] for more in-depth information about the configuration of a custom driver.
119159

120160
[source,java]
121161
----
122162
@Bean
123-
public EmbeddingModel embeddingModel() {
124-
// Can be any other Embeddingmodel implementation.
125-
return new OpenAiEmbeddingModel(new OpenAiApi(System.getenv("SPRING_AI_OPENAI_API_KEY")));
163+
public Driver driver() {
164+
return GraphDatabase.driver("neo4j://<host>:<bolt-port>",
165+
AuthTokens.basic("<username>", "<password>"));
126166
}
127167
----
128168

129-
In cases where the Spring Boot auto-configured Neo4j `Driver` bean is not what you want or need, you can still define your own bean.
130-
Please read the https://neo4j.com/docs/java-manual/current/client-applications/[Neo4j Java Driver reference] for more in-depth information about the configuration of a custom driver.
169+
Then create the `Neo4jVectorStore` bean using the builder pattern:
131170

132171
[source,java]
133172
----
134173
@Bean
135-
public Driver driver() {
136-
return GraphDatabase.driver("neo4j://<host>:<bolt-port>",
137-
AuthTokens.basic("<username>", "<password>"));
174+
public VectorStore vectorStore(Driver driver, EmbeddingModel embeddingModel) {
175+
return Neo4jVectorStore.builder()
176+
.driver(driver)
177+
.embeddingModel(embeddingModel)
178+
.databaseName("neo4j") // Optional: defaults to "neo4j"
179+
.distanceType(Neo4jDistanceType.COSINE) // Optional: defaults to COSINE
180+
.dimensions(1536) // Optional: defaults to 1536
181+
.label("Document") // Optional: defaults to "Document"
182+
.embeddingProperty("embedding") // Optional: defaults to "embedding"
183+
.indexName("custom-index") // Optional: defaults to "spring-ai-document-index"
184+
.initializeSchema(true) // Optional: defaults to false
185+
.batchingStrategy(new TokenCountBatchingStrategy()) // Optional: defaults to TokenCountBatchingStrategy
186+
.build();
138187
}
139-
----
140188
141-
Now you can auto-wire the `Neo4jVectorStore` as a vector store in your application.
189+
// This can be any EmbeddingModel implementation
190+
@Bean
191+
public EmbeddingModel embeddingModel() {
192+
return new OpenAiEmbeddingModel(new OpenAiApi(System.getenv("OPENAI_API_KEY")));
193+
}
194+
----
142195

143-
== Metadata filtering
196+
== Metadata Filtering
144197

145198
You can leverage the generic, portable xref:api/vectordbs.adoc#metadata-filters[metadata filters] with Neo4j store as well.
146199

@@ -149,11 +202,11 @@ For example, you can use either the text expression language:
149202
[source,java]
150203
----
151204
vectorStore.similaritySearch(
152-
SearchRequest.defaults()
153-
.withQuery("The World")
154-
.withTopK(TOP_K)
155-
.withSimilarityThreshold(SIMILARITY_THRESHOLD)
156-
.withFilterExpression("author in ['john', 'jill'] && 'article_type' == 'blog'"));
205+
SearchRequest.defaults()
206+
.withQuery("The World")
207+
.withTopK(TOP_K)
208+
.withSimilarityThreshold(SIMILARITY_THRESHOLD)
209+
.withFilterExpression("author in ['john', 'jill'] && 'article_type' == 'blog'"));
157210
----
158211

159212
or programmatically using the `Filter.Expression` DSL:
@@ -163,41 +216,26 @@ or programmatically using the `Filter.Expression` DSL:
163216
FilterExpressionBuilder b = new FilterExpressionBuilder();
164217
165218
vectorStore.similaritySearch(SearchRequest.defaults()
166-
.withQuery("The World")
167-
.withTopK(TOP_K)
168-
.withSimilarityThreshold(SIMILARITY_THRESHOLD)
169-
.withFilterExpression(b.and(
170-
b.in("author", "john", "jill"),
171-
b.eq("article_type", "blog")).build()));
219+
.withQuery("The World")
220+
.withTopK(TOP_K)
221+
.withSimilarityThreshold(SIMILARITY_THRESHOLD)
222+
.withFilterExpression(b.and(
223+
b.in("author", "john", "jill"),
224+
b.eq("article_type", "blog")).build()));
172225
----
173226

174227
NOTE: Those (portable) filter expressions get automatically converted into the proprietary Neo4j `WHERE` link:https://neo4j.com/developer/cypher/filtering-query-results/[filter expressions].
175228

176229
For example, this portable filter expression:
177230

178-
```sql
231+
[source,sql]
232+
----
179233
author in ['john', 'jill'] && 'article_type' == 'blog'
180-
```
234+
----
181235

182236
is converted into the proprietary Neo4j filter format:
183237

184-
```
238+
[source,text]
239+
----
185240
node.`metadata.author` IN ["john","jill"] AND node.`metadata.'article_type'` = "blog"
186-
```
187-
188-
== Neo4jVectorStore properties
189-
190-
You can use the following properties in your Spring Boot configuration to customize the Neo4j vector store.
191-
192-
[stripes=even]
193-
|===
194-
|Property|Default value
195-
196-
|`spring.ai.vectorstore.neo4j.database-name`|neo4j
197-
|`spring.ai.vectorstore.neo4j.initialize-schema`|false
198-
|`spring.ai.vectorstore.neo4j.embedding-dimension`|1536
199-
|`spring.ai.vectorstore.neo4j.distance-type`|cosine
200-
|`spring.ai.vectorstore.neo4j.label`|Document
201-
|`spring.ai.vectorstore.neo4j.embedding-property`|embedding
202-
|`spring.ai.vectorstore.neo4j.index-name`|spring-ai-document-index
203-
|===
241+
----

spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/vectorstore/neo4j/Neo4jVectorStoreAutoConfiguration.java

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
import org.springframework.ai.embedding.BatchingStrategy;
2323
import org.springframework.ai.embedding.EmbeddingModel;
2424
import org.springframework.ai.embedding.TokenCountBatchingStrategy;
25-
import org.springframework.ai.vectorstore.Neo4jVectorStore;
25+
import org.springframework.ai.vectorstore.neo4j.Neo4jVectorStore;
2626
import org.springframework.ai.vectorstore.observation.VectorStoreObservationConvention;
2727
import org.springframework.beans.factory.ObjectProvider;
2828
import org.springframework.boot.autoconfigure.AutoConfiguration;
@@ -57,20 +57,23 @@ public Neo4jVectorStore vectorStore(Driver driver, EmbeddingModel embeddingModel
5757
Neo4jVectorStoreProperties properties, ObjectProvider<ObservationRegistry> observationRegistry,
5858
ObjectProvider<VectorStoreObservationConvention> customObservationConvention,
5959
BatchingStrategy batchingStrategy) {
60-
Neo4jVectorStore.Neo4jVectorStoreConfig config = Neo4jVectorStore.Neo4jVectorStoreConfig.builder()
61-
.withDatabaseName(properties.getDatabaseName())
62-
.withEmbeddingDimension(properties.getEmbeddingDimension())
63-
.withDistanceType(properties.getDistanceType())
64-
.withLabel(properties.getLabel())
65-
.withEmbeddingProperty(properties.getEmbeddingProperty())
66-
.withIndexName(properties.getIndexName())
67-
.withIdProperty(properties.getIdProperty())
68-
.withConstraintName(properties.getConstraintName())
69-
.build();
7060

71-
return new Neo4jVectorStore(driver, embeddingModel, config, properties.isInitializeSchema(),
72-
observationRegistry.getIfUnique(() -> ObservationRegistry.NOOP),
73-
customObservationConvention.getIfAvailable(() -> null), batchingStrategy);
61+
return Neo4jVectorStore.builder()
62+
.driver(driver)
63+
.embeddingModel(embeddingModel)
64+
.initializeSchema(properties.isInitializeSchema())
65+
.observationRegistry(observationRegistry.getIfUnique(() -> ObservationRegistry.NOOP))
66+
.customObservationConvention(customObservationConvention.getIfAvailable(() -> null))
67+
.batchingStrategy(batchingStrategy)
68+
.databaseName(properties.getDatabaseName())
69+
.embeddingDimension(properties.getEmbeddingDimension())
70+
.distanceType(properties.getDistanceType())
71+
.label(properties.getLabel())
72+
.embeddingProperty(properties.getEmbeddingProperty())
73+
.indexName(properties.getIndexName())
74+
.idProperty(properties.getIdProperty())
75+
.constraintName(properties.getConstraintName())
76+
.build();
7477
}
7578

7679
}

spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/vectorstore/neo4j/Neo4jVectorStoreProperties.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
package org.springframework.ai.autoconfigure.vectorstore.neo4j;
1818

1919
import org.springframework.ai.autoconfigure.vectorstore.CommonVectorStoreProperties;
20-
import org.springframework.ai.vectorstore.Neo4jVectorStore;
20+
import org.springframework.ai.vectorstore.neo4j.Neo4jVectorStore;
2121
import org.springframework.boot.context.properties.ConfigurationProperties;
2222

2323
/**

0 commit comments

Comments
 (0)