Skip to content

Commit 560315c

Browse files
michael-simonstzolov
authored andcommitted
refactor: Use Neo4j transactional functions.
This change uses `session.executeWrite` and `session.executeRead` to interact with the database. Those methods use build-in retries for several Neo4j specific error states. Doing will improve the overall user experience in this case as the no other external retry mechanism should be used. Doing so is fine, as the Neo4j vector store is not subject to Springs general transaction management.
1 parent cd15b65 commit 560315c

File tree

1 file changed

+23
-17
lines changed
  • vector-stores/spring-ai-neo4j-store/src/main/java/org/springframework/ai/vectorstore

1 file changed

+23
-17
lines changed

vector-stores/spring-ai-neo4j-store/src/main/java/org/springframework/ai/vectorstore/Neo4jVectorStore.java

Lines changed: 23 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,9 @@ public void doAdd(List<Document> documents) {
119119
WITH row, u
120120
CALL db.create.setNodeVectorProperty(u, $embeddingProperty, row.embedding)
121121
""".formatted(this.config.label, this.config.idProperty);
122-
session.run(statement, Map.of("rows", rows, "embeddingProperty", this.config.embeddingProperty)).consume();
122+
session.executeWrite(
123+
tx -> tx.run(statement, Map.of("rows", rows, "embeddingProperty", this.config.embeddingProperty))
124+
.consume());
123125
}
124126
}
125127

@@ -128,6 +130,8 @@ public Optional<Boolean> doDelete(List<String> idList) {
128130

129131
try (var session = this.driver.session(this.config.sessionConfig)) {
130132

133+
// Those queries with internal, cypher based transaction management cannot be
134+
// run with executeWrite
131135
var summary = session
132136
.run("""
133137
MATCH (n:%s) WHERE n.%s IN $ids
@@ -158,10 +162,10 @@ public List<Document> doSimilaritySearch(SearchRequest request) {
158162
WHERE %s
159163
RETURN node, score""".formatted(condition);
160164

161-
return session
165+
return session.executeRead(tx -> tx
162166
.run(query, Map.of("indexName", this.config.indexNameNotSanitized, "numberOfNearestNeighbours",
163167
request.getTopK(), "embeddingValue", embedding, "threshold", request.getSimilarityThreshold()))
164-
.list(this::recordToDocument);
168+
.list(this::recordToDocument));
165169
}
166170
}
167171

@@ -174,20 +178,22 @@ public void afterPropertiesSet() {
174178

175179
try (var session = this.driver.session(this.config.sessionConfig)) {
176180

177-
session
178-
.run("CREATE CONSTRAINT %s IF NOT EXISTS FOR (n:%s) REQUIRE n.%s IS UNIQUE"
179-
.formatted(this.config.constraintName, this.config.label, this.config.idProperty))
180-
.consume();
181-
182-
var statement = """
183-
CREATE VECTOR INDEX %s IF NOT EXISTS FOR (n:%s) ON (n.%s)
184-
OPTIONS {indexConfig: {
185-
`vector.dimensions`: %d,
186-
`vector.similarity_function`: '%s'
187-
}}
188-
""".formatted(this.config.indexName, this.config.label, this.config.embeddingProperty,
189-
this.config.embeddingDimension, this.config.distanceType.name);
190-
session.run(statement).consume();
181+
session.executeWriteWithoutResult(tx -> {
182+
tx.run("CREATE CONSTRAINT %s IF NOT EXISTS FOR (n:%s) REQUIRE n.%s IS UNIQUE"
183+
.formatted(this.config.constraintName, this.config.label, this.config.idProperty)).consume();
184+
185+
var statement = """
186+
CREATE VECTOR INDEX %s IF NOT EXISTS FOR (n:%s) ON (n.%s)
187+
OPTIONS {indexConfig: {
188+
`vector.dimensions`: %d,
189+
`vector.similarity_function`: '%s'
190+
}}
191+
""".formatted(this.config.indexName, this.config.label, this.config.embeddingProperty,
192+
this.config.embeddingDimension, this.config.distanceType.name);
193+
tx.run(statement).consume();
194+
});
195+
196+
// Bad idea to retry this...
191197
session.run("CALL db.awaitIndexes()").consume();
192198
}
193199
}

0 commit comments

Comments
 (0)