Skip to content

Commit 248140a

Browse files
refactor: Upgrade to Driver 6, and test against last Neo4j 5.26 LTS release by default.
Signed-off-by: Michael Simons <[email protected]>
1 parent f95d078 commit 248140a

File tree

10 files changed

+97
-107
lines changed

10 files changed

+97
-107
lines changed

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@
9797
<maven-install-plugin.version>3.1.4</maven-install-plugin.version>
9898
<maven-site-plugin.version>3.7.1</maven-site-plugin.version>
9999
<maven.compiler.release>${java.version}</maven.compiler.release>
100-
<neo4j-java-driver.version>5.28.9</neo4j-java-driver.version>
100+
<neo4j-java-driver.version>6.0.0-beta01</neo4j-java-driver.version>
101101
<neo4j-migrations.version>2.17.3</neo4j-migrations.version>
102102
<neo4j.version>5.26.12</neo4j.version>
103103
<objenesis.version>3.0.1</objenesis.version>

src/main/java/org/springframework/data/neo4j/core/ResultSummaries.java

Lines changed: 39 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,9 @@
2323

2424
import org.apache.commons.logging.LogFactory;
2525
import org.jspecify.annotations.Nullable;
26-
import org.neo4j.driver.NotificationCategory;
26+
import org.neo4j.driver.NotificationClassification;
2727
import org.neo4j.driver.NotificationSeverity;
28-
import org.neo4j.driver.summary.InputPosition;
29-
import org.neo4j.driver.summary.Notification;
28+
import org.neo4j.driver.summary.GqlNotification;
3029
import org.neo4j.driver.summary.Plan;
3130
import org.neo4j.driver.summary.ResultSummary;
3231

@@ -66,9 +65,15 @@ final class ResultSummaries {
6665
private static final LogAccessor cypherTopologyNotificationLog = new LogAccessor(
6766
LogFactory.getLog("org.springframework.data.neo4j.cypher.topology"));
6867

68+
private static final LogAccessor cypherSchemaNotificationLog = new LogAccessor(
69+
LogFactory.getLog("org.springframework.data.neo4j.cypher.schema"));
70+
6971
private static final Pattern DEPRECATED_ID_PATTERN = Pattern
7072
.compile("(?im)The query used a deprecated function[.:] \\(?[`']id.+");
7173

74+
private static final Pattern DEPRECATED_ID_PATTERN_GQL = Pattern
75+
.compile("(?im).*id is deprecated and will be removed without a replacement\\.");
76+
7277
private ResultSummaries() {
7378
}
7479

@@ -86,29 +91,33 @@ static ResultSummary process(ResultSummary resultSummary) {
8691

8792
private static void logNotifications(ResultSummary resultSummary) {
8893

89-
if (resultSummary.notifications().isEmpty() || !Neo4jClient.cypherLog.isWarnEnabled()) {
94+
if (resultSummary.gqlStatusObjects().isEmpty() || !Neo4jClient.cypherLog.isWarnEnabled()) {
9095
return;
9196
}
9297

9398
boolean supressIdDeprecations = Neo4jClient.SUPPRESS_ID_DEPRECATIONS.getAcquire();
94-
Predicate<Notification> isDeprecationWarningForId;
99+
Predicate<GqlNotification> isDeprecationWarningForId;
95100
try {
96101
isDeprecationWarningForId = notification -> supressIdDeprecations
97-
&& notification.category()
98-
.orElse(NotificationCategory.UNRECOGNIZED)
99-
.equals(NotificationCategory.DEPRECATION)
100-
&& DEPRECATED_ID_PATTERN.matcher(notification.description()).matches();
102+
&& notification.classification()
103+
.filter(cat -> cat == NotificationClassification.UNRECOGNIZED
104+
|| cat == NotificationClassification.DEPRECATION)
105+
.isPresent()
106+
&& (DEPRECATED_ID_PATTERN.matcher(notification.statusDescription()).matches()
107+
|| DEPRECATED_ID_PATTERN_GQL.matcher(notification.statusDescription()).matches());
101108
}
102109
finally {
103110
Neo4jClient.SUPPRESS_ID_DEPRECATIONS.setRelease(supressIdDeprecations);
104111
}
105112

106113
String query = resultSummary.query().text();
107-
resultSummary.notifications()
114+
resultSummary.gqlStatusObjects()
108115
.stream()
116+
.filter(GqlNotification.class::isInstance)
117+
.map(GqlNotification.class::cast)
109118
.filter(Predicate.not(isDeprecationWarningForId))
110-
.forEach(notification -> notification.severityLevel().ifPresent(severityLevel -> {
111-
var category = notification.category().orElse(null);
119+
.forEach(notification -> notification.severity().ifPresent(severityLevel -> {
120+
var category = notification.classification().orElse(null);
112121

113122
var logger = getLogAccessor(category);
114123
Consumer<String> logFunction;
@@ -130,35 +139,22 @@ else if (severityLevel == NotificationSeverity.OFF) {
130139
}));
131140
}
132141

133-
private static LogAccessor getLogAccessor(@Nullable NotificationCategory category) {
134-
if (category == null) {
142+
private static LogAccessor getLogAccessor(@Nullable NotificationClassification classification) {
143+
if (classification == null) {
135144
return Neo4jClient.cypherLog;
136145
}
137-
if (category.equals(NotificationCategory.HINT)) {
138-
return cypherHintNotificationLog;
139-
}
140-
else if (category.equals(NotificationCategory.DEPRECATION)) {
141-
return cypherDeprecationNotificationLog;
142-
}
143-
else if (category.equals(NotificationCategory.PERFORMANCE)) {
144-
return cypherPerformanceNotificationLog;
145-
}
146-
else if (category.equals(NotificationCategory.GENERIC)) {
147-
return cypherGenericNotificationLog;
148-
}
149-
else if (category.equals(NotificationCategory.UNSUPPORTED)) {
150-
return cypherUnsupportedNotificationLog;
151-
}
152-
else if (category.equals(NotificationCategory.UNRECOGNIZED)) {
153-
return cypherUnrecognizedNotificationLog;
154-
}
155-
else if (category.equals(NotificationCategory.SECURITY)) {
156-
return cypherSecurityNotificationLog;
157-
}
158-
else if (category.equals(NotificationCategory.TOPOLOGY)) {
159-
return cypherTopologyNotificationLog;
160-
}
161-
return Neo4jClient.cypherLog;
146+
147+
return switch (classification) {
148+
case HINT -> cypherHintNotificationLog;
149+
case UNRECOGNIZED -> cypherUnrecognizedNotificationLog;
150+
case UNSUPPORTED -> cypherUnsupportedNotificationLog;
151+
case PERFORMANCE -> cypherPerformanceNotificationLog;
152+
case DEPRECATION -> cypherDeprecationNotificationLog;
153+
case SECURITY -> cypherSecurityNotificationLog;
154+
case TOPOLOGY -> cypherTopologyNotificationLog;
155+
case GENERIC -> cypherGenericNotificationLog;
156+
case SCHEMA -> cypherSchemaNotificationLog;
157+
};
162158
}
163159

164160
/**
@@ -167,25 +163,23 @@ else if (category.equals(NotificationCategory.TOPOLOGY)) {
167163
* @param forQuery the query that caused the notification
168164
* @return a formatted string
169165
*/
170-
static String format(Notification notification, String forQuery) {
166+
static String format(GqlNotification notification, String forQuery) {
171167

172-
InputPosition position = notification.position();
173-
boolean hasPosition = position != null;
168+
var position = notification.position().orElse(null);
174169

175170
StringBuilder queryHint = new StringBuilder();
176171
String[] lines = forQuery.split("(\r\n|\n)");
177172
for (int i = 0; i < lines.length; i++) {
178173
String line = lines[i];
179174
queryHint.append("\t").append(line).append(LINE_SEPARATOR);
180-
if (hasPosition && i + 1 == position.line()) {
175+
if (position != null && i + 1 == position.line()) {
181176
queryHint.append("\t")
182177
.append(Stream.generate(() -> " ").limit(position.column() - 1).collect(Collectors.joining()))
183178
.append("^")
184179
.append(System.lineSeparator());
185180
}
186181
}
187-
return String.format("%s: %s%n%s%s", notification.code(), notification.title(), queryHint,
188-
notification.description());
182+
return String.format("%s (%s):%n%s", notification.statusDescription(), notification.gqlStatus(), queryHint);
189183
}
190184

191185
/**

src/main/java/org/springframework/data/neo4j/repository/query/AbstractNeo4jQuery.java

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
import java.util.function.LongSupplier;
2424
import java.util.function.Supplier;
2525
import java.util.function.UnaryOperator;
26-
import java.util.stream.Collectors;
2726

2827
import org.jspecify.annotations.Nullable;
2928
import org.neo4j.driver.types.MapAccessor;
@@ -166,7 +165,7 @@ else if (geoNearQuery) {
166165
rawResult = newGeoResults(rawResult);
167166
}
168167
else if (this.queryMethod.isSearchQuery()) {
169-
rawResult = createSearchResult((List<?>) rawResult, returnedType.getReturnedType());
168+
rawResult = createSearchResult((List<?>) rawResult);
170169
}
171170

172171
return resultProcessor.processResult(rawResult, preparingConverter);
@@ -207,11 +206,9 @@ private Slice<?> createSlice(boolean incrementLimit, Neo4jParameterAccessor para
207206
}
208207
}
209208

210-
private <T> SearchResults<?> createSearchResult(List<?> rawResult, Class<T> returnedType) {
211-
List<SearchResult<T>> searchResults = rawResult.stream()
212-
.map(rawValue -> (SearchResult<T>) rawValue)
213-
.collect(Collectors.toUnmodifiableList());
214-
return new SearchResults<>(searchResults);
209+
@SuppressWarnings("unchecked")
210+
private <T> SearchResults<?> createSearchResult(List<?> rawResult) {
211+
return new SearchResults<>(rawResult.stream().map(rawValue -> (SearchResult<T>) rawValue).toList());
215212
}
216213

217214
protected abstract <T> PreparedQuery<T> prepareQuery(Class<T> returnedType,

src/test/java/org/springframework/data/neo4j/core/Neo4jClientTests.java

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ void databaseSelectionShouldWorkBeforeAsUser() {
141141
verify(this.session).run(eq(cypher), MockitoHamcrest.argThat(new MapAssertionMatcher(expectedParameters)));
142142
verify(this.result).stream();
143143
verify(this.result).consume();
144-
verify(this.resultSummary).notifications();
144+
verify(this.resultSummary).gqlStatusObjects();
145145
verify(this.resultSummary).hasPlan();
146146
verify(this.record1).asMap();
147147
verify(this.session).close();
@@ -181,7 +181,7 @@ void databaseSelectionShouldWorkAfterAsUser() {
181181
verify(this.session).run(eq(cypher), MockitoHamcrest.argThat(new MapAssertionMatcher(expectedParameters)));
182182
verify(this.result).stream();
183183
verify(this.result).consume();
184-
verify(this.resultSummary).notifications();
184+
verify(this.resultSummary).gqlStatusObjects();
185185
verify(this.resultSummary).hasPlan();
186186
verify(this.record1).asMap();
187187
verify(this.session).close();
@@ -220,7 +220,7 @@ void userSelectionShouldWork() {
220220
verify(this.session).run(eq(cypher), MockitoHamcrest.argThat(new MapAssertionMatcher(expectedParameters)));
221221
verify(this.result).stream();
222222
verify(this.result).consume();
223-
verify(this.resultSummary).notifications();
223+
verify(this.resultSummary).gqlStatusObjects();
224224
verify(this.resultSummary).hasPlan();
225225
verify(this.record1).asMap();
226226
verify(this.session).close();
@@ -268,7 +268,7 @@ void queryCreationShouldFeelGood() {
268268

269269
verify(this.result).stream();
270270
verify(this.result).consume();
271-
verify(this.resultSummary).notifications();
271+
verify(this.resultSummary).gqlStatusObjects();
272272
verify(this.resultSummary).hasPlan();
273273
verify(this.record1).asMap();
274274
verify(this.record2).asMap();
@@ -305,7 +305,7 @@ void databaseSelectionShouldBePossibleOnlyOnce() {
305305
verify(this.session).run(eq(cypher), MockitoHamcrest.argThat(new MapAssertionMatcher(expectedParameters)));
306306
verify(this.result).stream();
307307
verify(this.result).consume();
308-
verify(this.resultSummary).notifications();
308+
verify(this.resultSummary).gqlStatusObjects();
309309
verify(this.resultSummary).hasPlan();
310310
verify(this.record1).asMap();
311311
verify(this.session).close();
@@ -350,7 +350,7 @@ void databaseSelectionBeanShouldGetRespectedIfExisting() {
350350
verify(this.session).run(eq(query), anyMap());
351351
verify(this.result).stream();
352352
verify(this.result).consume();
353-
verify(this.resultSummary).notifications();
353+
verify(this.resultSummary).gqlStatusObjects();
354354
verify(this.resultSummary).hasPlan();
355355
verify(this.record1).asMap();
356356
verify(this.session).close();
@@ -379,7 +379,7 @@ void queriesWithoutResultShouldFitInAsWell() {
379379

380380
verify(this.session).run(eq(cypher), MockitoHamcrest.argThat(new MapAssertionMatcher(expectedParameters)));
381381
verify(this.result).consume();
382-
verify(this.resultSummary).notifications();
382+
verify(this.resultSummary).gqlStatusObjects();
383383
verify(this.resultSummary).hasPlan();
384384
verify(this.session).close();
385385
}
@@ -577,7 +577,7 @@ void reading() {
577577
MockitoHamcrest.argThat(new MapAssertionMatcher(expectedParameters)));
578578
verify(Neo4jClientTests.this.result).stream();
579579
verify(Neo4jClientTests.this.result).consume();
580-
verify(Neo4jClientTests.this.resultSummary).notifications();
580+
verify(Neo4jClientTests.this.resultSummary).gqlStatusObjects();
581581
verify(Neo4jClientTests.this.resultSummary).hasPlan();
582582
verify(Neo4jClientTests.this.record1).get("name");
583583
verify(Neo4jClientTests.this.session).close();
@@ -614,7 +614,7 @@ void shouldApplyNullChecksDuringReading() {
614614
MockitoHamcrest.argThat(new MapAssertionMatcher(Collections.emptyMap())));
615615
verify(Neo4jClientTests.this.result).stream();
616616
verify(Neo4jClientTests.this.result).consume();
617-
verify(Neo4jClientTests.this.resultSummary).notifications();
617+
verify(Neo4jClientTests.this.resultSummary).gqlStatusObjects();
618618
verify(Neo4jClientTests.this.resultSummary).hasPlan();
619619
verify(Neo4jClientTests.this.record1).get("name");
620620
verify(Neo4jClientTests.this.session).close();
@@ -646,7 +646,7 @@ void writing() {
646646
verify(Neo4jClientTests.this.session).run(eq(cypher),
647647
MockitoHamcrest.argThat(new MapAssertionMatcher(expectedParameters)));
648648
verify(Neo4jClientTests.this.result).consume();
649-
verify(Neo4jClientTests.this.resultSummary).notifications();
649+
verify(Neo4jClientTests.this.resultSummary).gqlStatusObjects();
650650
verify(Neo4jClientTests.this.resultSummary).hasPlan();
651651
verify(Neo4jClientTests.this.session).close();
652652
}
@@ -677,7 +677,7 @@ void automaticConversion() {
677677
verify(Neo4jClientTests.this.result).hasNext();
678678
verify(Neo4jClientTests.this.result).single();
679679
verify(Neo4jClientTests.this.result).consume();
680-
verify(Neo4jClientTests.this.resultSummary).notifications();
680+
verify(Neo4jClientTests.this.resultSummary).gqlStatusObjects();
681681
verify(Neo4jClientTests.this.resultSummary).hasPlan();
682682
verify(Neo4jClientTests.this.session).close();
683683
}

src/test/java/org/springframework/data/neo4j/core/ReactiveNeo4jClientTests.java

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ void databaseSelectionShouldWorkBeforeAsUser() {
135135
MockitoHamcrest.argThat(new Neo4jClientTests.MapAssertionMatcher(expectedParameters)));
136136
verify(this.result).records();
137137
verify(this.result).consume();
138-
verify(this.resultSummary).notifications();
138+
verify(this.resultSummary).gqlStatusObjects();
139139
verify(this.resultSummary).hasPlan();
140140
verify(this.record1).asMap();
141141
verify(this.session).close();
@@ -175,7 +175,7 @@ void databaseSelectionShouldWorkAfterAsUser() {
175175
MockitoHamcrest.argThat(new Neo4jClientTests.MapAssertionMatcher(expectedParameters)));
176176
verify(this.result).records();
177177
verify(this.result).consume();
178-
verify(this.resultSummary).notifications();
178+
verify(this.resultSummary).gqlStatusObjects();
179179
verify(this.resultSummary).hasPlan();
180180
verify(this.record1).asMap();
181181
verify(this.session).close();
@@ -214,7 +214,7 @@ void userSelectionShouldWork() {
214214
MockitoHamcrest.argThat(new Neo4jClientTests.MapAssertionMatcher(expectedParameters)));
215215
verify(this.result).records();
216216
verify(this.result).consume();
217-
verify(this.resultSummary).notifications();
217+
verify(this.resultSummary).gqlStatusObjects();
218218
verify(this.resultSummary).hasPlan();
219219
verify(this.record1).asMap();
220220
verify(this.session).close();
@@ -265,7 +265,7 @@ void queryCreationShouldFeelGood() {
265265

266266
verify(this.result).records();
267267
verify(this.result).consume();
268-
verify(this.resultSummary).notifications();
268+
verify(this.resultSummary).gqlStatusObjects();
269269
verify(this.resultSummary).hasPlan();
270270
verify(this.record1).asMap();
271271
verify(this.record2).asMap();
@@ -302,7 +302,7 @@ void databaseSelectionShouldBePossibleOnlyOnce() {
302302
MockitoHamcrest.argThat(new Neo4jClientTests.MapAssertionMatcher(expectedParameters)));
303303
verify(this.result).records();
304304
verify(this.result).consume();
305-
verify(this.resultSummary).notifications();
305+
verify(this.resultSummary).gqlStatusObjects();
306306
verify(this.resultSummary).hasPlan();
307307
verify(this.record1).asMap();
308308
verify(this.session).close();
@@ -350,7 +350,7 @@ void databaseSelectionBeanShouldGetRespectedIfExisting() {
350350
verify(this.session).run(eq(cypher), anyMap());
351351
verify(this.result).records();
352352
verify(this.result).consume();
353-
verify(this.resultSummary).notifications();
353+
verify(this.resultSummary).gqlStatusObjects();
354354
verify(this.resultSummary).hasPlan();
355355
verify(this.record1).asMap();
356356
verify(this.session).close();
@@ -399,7 +399,7 @@ void queriesWithoutResultShouldFitInAsWell() {
399399
verify(this.session).run(eq(cypher),
400400
MockitoHamcrest.argThat(new Neo4jClientTests.MapAssertionMatcher(expectedParameters)));
401401
verify(this.result).consume();
402-
verify(this.resultSummary).notifications();
402+
verify(this.resultSummary).gqlStatusObjects();
403403
verify(this.resultSummary).hasPlan();
404404
verify(this.session).close();
405405
}
@@ -508,7 +508,7 @@ void reading() {
508508
verify(ReactiveNeo4jClientTests.this.session).run(eq(cypher),
509509
MockitoHamcrest.argThat(new Neo4jClientTests.MapAssertionMatcher(expectedParameters)));
510510
verify(ReactiveNeo4jClientTests.this.result).records();
511-
verify(ReactiveNeo4jClientTests.this.resultSummary).notifications();
511+
verify(ReactiveNeo4jClientTests.this.resultSummary).gqlStatusObjects();
512512
verify(ReactiveNeo4jClientTests.this.resultSummary).hasPlan();
513513
verify(ReactiveNeo4jClientTests.this.record1).get("name");
514514
verify(ReactiveNeo4jClientTests.this.session).close();
@@ -547,7 +547,7 @@ void shouldApplyNullChecksDuringReading() {
547547
verify(ReactiveNeo4jClientTests.this.session).run(eq("MATCH (n) RETURN n"),
548548
MockitoHamcrest.argThat(new Neo4jClientTests.MapAssertionMatcher(Collections.emptyMap())));
549549
verify(ReactiveNeo4jClientTests.this.result).records();
550-
verify(ReactiveNeo4jClientTests.this.resultSummary).notifications();
550+
verify(ReactiveNeo4jClientTests.this.resultSummary).gqlStatusObjects();
551551
verify(ReactiveNeo4jClientTests.this.resultSummary).hasPlan();
552552
verify(ReactiveNeo4jClientTests.this.record1).get("name");
553553
verify(ReactiveNeo4jClientTests.this.session).close();
@@ -584,7 +584,7 @@ void writing() {
584584
verify(ReactiveNeo4jClientTests.this.session).run(eq(cypher),
585585
MockitoHamcrest.argThat(new Neo4jClientTests.MapAssertionMatcher(expectedParameters)));
586586
verify(ReactiveNeo4jClientTests.this.result).consume();
587-
verify(ReactiveNeo4jClientTests.this.resultSummary).notifications();
587+
verify(ReactiveNeo4jClientTests.this.resultSummary).gqlStatusObjects();
588588
verify(ReactiveNeo4jClientTests.this.resultSummary).hasPlan();
589589
verify(ReactiveNeo4jClientTests.this.session).close();
590590
}
@@ -614,7 +614,7 @@ void automaticConversion() {
614614
verifyDatabaseSelection(null);
615615

616616
verify(ReactiveNeo4jClientTests.this.result).consume();
617-
verify(ReactiveNeo4jClientTests.this.resultSummary).notifications();
617+
verify(ReactiveNeo4jClientTests.this.resultSummary).gqlStatusObjects();
618618
verify(ReactiveNeo4jClientTests.this.resultSummary).hasPlan();
619619
verify(ReactiveNeo4jClientTests.this.session).run(eq(cypher), anyMap());
620620
verify(ReactiveNeo4jClientTests.this.session).close();

0 commit comments

Comments
 (0)