Skip to content

Commit 6b913fa

Browse files
committed
spring-projectsGH-2417 - Improve generated existsById queries.
Closes spring-projects#2417
1 parent 54a98aa commit 6b913fa

10 files changed

+76
-4
lines changed

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

+10
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,16 @@ public interface Neo4jOperations {
172172
*/
173173
<T> List<T> findAllById(Iterable<?> ids, Class<T> domainType);
174174

175+
/**
176+
* Check if an entity for a given id exists in the database.
177+
*
178+
* @param id the id of the entity to check. Must not be {@code null}.
179+
* @param domainType the type of the entity. Must not be {@code null}.
180+
* @param <T> the type of the entity.
181+
* @return If entity exists in the database, true, otherwise false.
182+
*/
183+
<T> boolean existsById(Object id, Class<T> domainType);
184+
175185
/**
176186
* Saves an instance of an entity, including all the related entities of the entity.
177187
*

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

+14
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,20 @@ <T, R> List<R> doFind(@Nullable String cypherQuery, @Nullable Map<String, Object
292292
.collect(Collectors.toList());
293293
}
294294

295+
@Override
296+
public <T> boolean existsById(Object id, Class<T> domainType) {
297+
298+
Neo4jPersistentEntity<?> entityMetaData = neo4jMappingContext.getRequiredPersistentEntity(domainType);
299+
300+
QueryFragmentsAndParameters fragmentsAndParameters = QueryFragmentsAndParameters
301+
.forExistsById(entityMetaData, convertIdValues(entityMetaData.getRequiredIdProperty(), id));
302+
303+
Statement statement = fragmentsAndParameters.getQueryFragments().toStatement();
304+
Map<String, Object> parameters = fragmentsAndParameters.getParameters();
305+
306+
return count(statement, parameters) > 0;
307+
}
308+
295309
@Override
296310
public <T> Optional<T> findById(Object id, Class<T> domainType) {
297311
Neo4jPersistentEntity<?> entityMetaData = neo4jMappingContext.getRequiredPersistentEntity(domainType);

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

+10
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,16 @@ public interface ReactiveNeo4jOperations {
171171
*/
172172
<T> Flux<T> findAllById(Iterable<?> ids, Class<T> domainType);
173173

174+
/**
175+
* Check if an entity for a given id exists in the database.
176+
*
177+
* @param id the id of the entity to check. Must not be {@code null}.
178+
* @param domainType the type of the entity. Must not be {@code null}.
179+
* @param <T> the type of the entity.
180+
* @return If entity exists in the database, true, otherwise false.
181+
*/
182+
<T> Mono<Boolean> existsById(Object id, Class<T> domainType);
183+
174184
/**
175185
* Saves an instance of an entity, including all the related entities of the entity.
176186
*

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

+14
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,20 @@ <T, R> Flux<R> doFind(@Nullable String cypherQuery, @Nullable Map<String, Object
267267
.mapNotNull(converter::convert);
268268
}
269269

270+
@Override
271+
public <T> Mono<Boolean> existsById(Object id, Class<T> domainType) {
272+
273+
Neo4jPersistentEntity<?> entityMetaData = neo4jMappingContext.getRequiredPersistentEntity(domainType);
274+
275+
QueryFragmentsAndParameters fragmentsAndParameters = QueryFragmentsAndParameters
276+
.forExistsById(entityMetaData, convertIdValues(entityMetaData.getRequiredIdProperty(), id));
277+
278+
Statement statement = fragmentsAndParameters.getQueryFragments().toStatement();
279+
Map<String, Object> parameters = fragmentsAndParameters.getParameters();
280+
281+
return count(statement, parameters).map(r -> r > 0);
282+
}
283+
270284
@Override
271285
public <T> Mono<T> findById(Object id, Class<T> domainType) {
272286

src/main/java/org/springframework/data/neo4j/core/mapping/CypherGenerator.java

+5
Original file line numberDiff line numberDiff line change
@@ -463,6 +463,11 @@ public Statement prepareDeleteOf(
463463
.build();
464464
}
465465

466+
public Collection<Expression> createReturnStatementForExists(Neo4jPersistentEntity<?> nodeDescription) {
467+
468+
return Collections.singleton(Functions.count(Constants.NAME_OF_TYPED_ROOT_NODE.apply(nodeDescription)));
469+
}
470+
466471
public Collection<Expression> createReturnStatementForMatch(Neo4jPersistentEntity<?> nodeDescription) {
467472
return createReturnStatementForMatch(nodeDescription, (pp -> true));
468473
}

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

+3-1
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,8 @@ public long count(Condition condition) {
131131

132132
@Override
133133
public boolean exists(Condition condition) {
134-
return findAll(condition).iterator().hasNext();
134+
Statement statement = CypherGenerator.INSTANCE.prepareMatchOf(this.metaData, condition)
135+
.returning(Functions.count(asterisk())).build();
136+
return this.neo4jOperations.count(statement, statement.getParameters()) > 0;
135137
}
136138
}

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

+11
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,17 @@ public static QueryFragmentsAndParameters forFindAll(Neo4jPersistentEntity<?> en
121121
return new QueryFragmentsAndParameters(entityMetaData, queryFragments, Collections.emptyMap());
122122
}
123123

124+
public static QueryFragmentsAndParameters forExistsById(Neo4jPersistentEntity<?> entityMetaData, Object idValues) {
125+
Map<String, Object> parameters = Collections.singletonMap(Constants.NAME_OF_ID, idValues);
126+
127+
Condition condition = entityMetaData.getIdExpression().isEqualTo(parameter(Constants.NAME_OF_ID));
128+
QueryFragments queryFragments = new QueryFragments();
129+
queryFragments.addMatchOn(cypherGenerator.createRootNode(entityMetaData));
130+
queryFragments.setCondition(condition);
131+
queryFragments.setReturnExpressions(cypherGenerator.createReturnStatementForExists(entityMetaData));
132+
return new QueryFragmentsAndParameters(entityMetaData, queryFragments, parameters);
133+
}
134+
124135
/*
125136
* Following methods are used by the Simple(Reactive)QueryByExampleExecutor
126137
*/

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

+6-1
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,12 @@ public <S extends T> long count(Example<S> example) {
101101

102102
@Override
103103
public <S extends T> boolean exists(Example<S> example) {
104-
return findAll(example).iterator().hasNext();
104+
105+
Predicate predicate = Predicate.create(mappingContext, example);
106+
Statement statement = predicate.useWithReadingFragment(cypherGenerator::prepareMatchOf)
107+
.returning(Functions.count(asterisk())).build();
108+
109+
return this.neo4jOperations.count(statement, predicate.getParameters()) > 0;
105110
}
106111

107112
@Override

src/main/java/org/springframework/data/neo4j/repository/support/SimpleNeo4jRepository.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,8 @@ public long count() {
108108

109109
@Override
110110
public boolean existsById(ID id) {
111-
return findById(id).isPresent();
111+
112+
return this.neo4jOperations.existsById(id, this.entityInformation.getJavaType());
112113
}
113114

114115
@Override

src/main/java/org/springframework/data/neo4j/repository/support/SimpleReactiveNeo4jRepository.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ public Mono<Long> count() {
107107

108108
@Override
109109
public Mono<Boolean> existsById(ID id) {
110-
return findById(id).hasElement();
110+
return this.neo4jOperations.existsById(id, this.entityInformation.getJavaType());
111111
}
112112

113113
@Override

0 commit comments

Comments
 (0)