Skip to content

Commit e22bcbf

Browse files
committed
Fix paging for query methods.
Query methods returning a List or Streamable now consider Pageable limits. See #1620
1 parent 02d66ee commit e22bcbf

File tree

4 files changed

+71
-15
lines changed

4 files changed

+71
-15
lines changed

spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/aot/CassandraCodeBlocks.java

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
package org.springframework.data.cassandra.repository.aot;
1717

1818
import java.nio.ByteBuffer;
19+
import java.util.Collection;
1920
import java.util.Map;
2021

2122
import org.jspecify.annotations.NullUnmarked;
@@ -133,14 +134,14 @@ private CodeBlock buildStringQuery(StringAotQuery query) {
133134
Builder builder = CodeBlock.builder();
134135
String pagingState = null;
135136

136-
if (queryMethod.isSliceQuery() && StringUtils.hasText(context.getPageableParameterName())) {
137+
if (StringUtils.hasText(context.getPageableParameterName())) {
137138

138139
pagingState = context.localVariable("pagingState");
139140
builder.addStatement("$1T $2L = $3L instanceof $4T $5L ? $5L.getPagingState() : null", ByteBuffer.class,
140141
pagingState, context.getPageableParameterName(), CassandraPageRequest.class,
141142
context.localVariable("pageRequest"));
142143

143-
} else if (queryMethod.isScrollQuery() && StringUtils.hasText(context.getScrollPositionParameterName())) {
144+
} else if (StringUtils.hasText(context.getScrollPositionParameterName())) {
144145

145146
pagingState = context.localVariable("pagingState");
146147
builder.addStatement("$1T $2L = $3L instanceof $4T $5L && !$5L.isInitial() ? $5L.getPagingState() : null",
@@ -662,7 +663,14 @@ CodeBlock build() {
662663

663664
String terminatingMethod;
664665

665-
if (queryMethod.isScrollQuery() || queryMethod.isSliceQuery() || queryMethod.isPageQuery()) {
666+
boolean paginated = StringUtils.hasText(context.getPageableParameterName())
667+
|| StringUtils.hasText(context.getScrollPositionParameterName())
668+
|| StringUtils.hasText(context.getLimitParameterName());
669+
670+
boolean streamableResult = queryMethod.isScrollQuery() || queryMethod.isSliceQuery() || queryMethod.isPageQuery()
671+
|| (queryMethod.isCollectionQuery() && paginated);
672+
673+
if (streamableResult) {
666674
terminatingMethod = "slice()";
667675
} else if (queryMethod.isCollectionQuery()) {
668676
terminatingMethod = "all()";
@@ -688,28 +696,30 @@ CodeBlock build() {
688696
} else {
689697

690698
if (rawProjection && isMapProjection) {
691-
execution.add("($T) $L.$L", methodReturn.getClassName(), context.localVariable("select"),
692-
terminatingMethod);
699+
execution.add("($T) $L.$L", methodReturn.getClassName(), context.localVariable("select"), terminatingMethod);
693700
} else {
694701
execution.add("$L.$L", context.localVariable("select"), terminatingMethod);
695702
}
696703
}
697704

698705
LordOfTheStrings.TypedReturnBuilder returnBuilder = LordOfTheStrings.returning(methodReturn.toClass());
706+
699707
if (requiresConversion) {
700708

701709
String conversionMethod;
702710

703-
if (queryMethod.isCollectionQuery() || queryMethod.isPageQuery() || queryMethod.isSliceQuery()
704-
|| queryMethod.isStreamQuery()) {
711+
if (queryMethod.isCollectionQuery() || streamableResult || queryMethod.isStreamQuery()) {
705712
conversionMethod = "convertMany";
706713
} else {
707714
conversionMethod = "convertOne";
708715
}
709716

710717
CodeBlock result = CodeBlock.of("$L($L, $T.class)", conversionMethod, execution.build(), actualReturnType);
711-
if (queryMethod.getReturnType().getType().equals(Streamable.class)) {
712-
result = CodeBlock.of("$T.of($L)", Streamable.class, result);
718+
719+
if (streamableResult && Collection.class.isAssignableFrom(methodReturn.toClass())) {
720+
result = CodeBlock.of("$L.getContent()", result);
721+
} else if (!streamableResult && methodReturn.toClass().equals(Streamable.class)) {
722+
result = CodeBlock.of("$T.of(($T) $L)", Streamable.class, Iterable.class, result);
713723
}
714724

715725
builder.addStatement(returnBuilder //
@@ -721,7 +731,9 @@ CodeBlock build() {
721731

722732
if (query.isCount()) {
723733
returnBuilder.whenPrimitiveOrBoxed(Integer.class, "(int) $L", executionBlock);
724-
} else if (queryMethod.getReturnType().getType().equals(Streamable.class)) {
734+
} else if (streamableResult && Collection.class.isAssignableFrom(methodReturn.toClass())) {
735+
executionBlock = CodeBlock.of("$L.getContent()", executionBlock);
736+
} else if (!streamableResult && methodReturn.toClass().equals(Streamable.class)) {
725737
executionBlock = CodeBlock.of("$T.of($L)", Streamable.class, executionBlock);
726738
}
727739

spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/AbstractCassandraQuery.java

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,16 @@
1919

2020
import org.springframework.core.convert.converter.Converter;
2121
import org.springframework.data.cassandra.core.CassandraOperations;
22-
import org.springframework.data.cassandra.repository.query.CassandraQueryExecution.*;
22+
import org.springframework.data.cassandra.repository.query.CassandraQueryExecution.CollectionExecution;
23+
import org.springframework.data.cassandra.repository.query.CassandraQueryExecution.ExistsExecution;
24+
import org.springframework.data.cassandra.repository.query.CassandraQueryExecution.ResultProcessingConverter;
25+
import org.springframework.data.cassandra.repository.query.CassandraQueryExecution.ResultProcessingExecution;
26+
import org.springframework.data.cassandra.repository.query.CassandraQueryExecution.ResultSetQuery;
27+
import org.springframework.data.cassandra.repository.query.CassandraQueryExecution.SearchExecution;
28+
import org.springframework.data.cassandra.repository.query.CassandraQueryExecution.SingleEntityExecution;
29+
import org.springframework.data.cassandra.repository.query.CassandraQueryExecution.SlicedExecution;
30+
import org.springframework.data.cassandra.repository.query.CassandraQueryExecution.StreamExecution;
31+
import org.springframework.data.cassandra.repository.query.CassandraQueryExecution.WindowExecution;
2332
import org.springframework.data.repository.query.ParameterAccessor;
2433
import org.springframework.data.repository.query.RepositoryQuery;
2534
import org.springframework.data.repository.query.ResultProcessor;
@@ -116,7 +125,7 @@ private CassandraQueryExecution getExecutionToWrap(CassandraParameterAccessor pa
116125
} else if (getQueryMethod().isSearchQuery()) {
117126
return new SearchExecution(getOperations(), parameterAccessor);
118127
} else if (getQueryMethod().isCollectionQuery()) {
119-
return new CollectionExecution(getOperations());
128+
return new CollectionExecution(getOperations(), parameterAccessor);
120129
} else if (getQueryMethod().isResultSetQuery()) {
121130
return new ResultSetQuery(getOperations());
122131
} else if (getQueryMethod().isStreamQuery()) {

spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/CassandraQueryExecution.java

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
import org.springframework.data.domain.Similarity;
3737
import org.springframework.data.domain.Slice;
3838
import org.springframework.data.domain.SliceImpl;
39+
import org.springframework.data.domain.Window;
3940
import org.springframework.data.mapping.context.MappingContext;
4041
import org.springframework.data.mapping.model.EntityInstantiators;
4142
import org.springframework.data.repository.query.ResultProcessor;
@@ -96,7 +97,7 @@ final class SlicedExecution implements CassandraQueryExecution {
9697
private final Pageable pageable;
9798

9899
@Override
99-
public Object execute(Statement<?> statement, Class<?> type) {
100+
public Slice<?> execute(Statement<?> statement, Class<?> type) {
100101

101102
CassandraPageRequest.validatePageable(pageable);
102103

@@ -137,7 +138,7 @@ public WindowExecution(CassandraOperations operations, CassandraScrollPosition s
137138
}
138139

139140
@Override
140-
public Object execute(Statement<?> statement, Class<?> type) {
141+
public Window<?> execute(Statement<?> statement, Class<?> type) {
141142

142143
Statement<?> statementToUse = limit.isLimited() ? statement.setPageSize(limit.max()) : statement;
143144

@@ -158,13 +159,27 @@ public Object execute(Statement<?> statement, Class<?> type) {
158159
final class CollectionExecution implements CassandraQueryExecution {
159160

160161
private final CassandraOperations operations;
162+
private final CassandraParameterAccessor parameterAccessor;
161163

162-
CollectionExecution(CassandraOperations operations) {
164+
CollectionExecution(CassandraOperations operations, CassandraParameterAccessor parameterAccessor) {
163165
this.operations = operations;
166+
this.parameterAccessor = parameterAccessor;
164167
}
165168

166169
@Override
167170
public Object execute(Statement<?> statement, Class<?> type) {
171+
172+
Pageable pageable = parameterAccessor.getPageable();
173+
if (pageable.isPaged()) {
174+
return new SlicedExecution(operations, pageable).execute(statement, type).getContent();
175+
}
176+
177+
Limit limit = parameterAccessor.getLimit();
178+
CassandraScrollPosition scrollPosition = parameterAccessor.getScrollPosition();
179+
if (limit.isLimited() || !scrollPosition.isInitial()) {
180+
return new WindowExecution(operations, scrollPosition, limit).execute(statement, type).getContent();
181+
}
182+
168183
return operations.select(statement, type);
169184
}
170185

spring-data-cassandra/src/test/java/org/springframework/data/cassandra/repository/QueryDerivationIntegrationTests.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,22 @@ public void shouldSelectWithLimit() {
398398
assertThat(result).hasSize(2);
399399
}
400400

401+
@Test // GH-1620
402+
public void shouldSelectListWithPageRequest() {
403+
404+
List<Person> result = personRepository.findPagedByLastname("White", Pageable.ofSize(2));
405+
406+
assertThat(result).hasSize(2);
407+
}
408+
409+
@Test // GH-1620
410+
public void shouldSelectStreamableWithPageRequest() {
411+
412+
Streamable<Person> result = personRepository.findStreamableByLastname("White", Pageable.ofSize(2));
413+
414+
assertThat(result).hasSize(2);
415+
}
416+
401417
@Test // DATACASS-512
402418
public void shouldCountRecords() {
403419

@@ -491,6 +507,10 @@ public static interface PersonRepository extends MapIdCassandraRepository<Person
491507

492508
List<Person> findAllLimitedByLastname(String lastname, Limit limit);
493509

510+
List<Person> findPagedByLastname(String lastname, Pageable pageable);
511+
512+
Streamable<Person> findStreamableByLastname(String lastname, Pageable pageable);
513+
494514
Collection<PersonProjection> findPersonProjectedBy();
495515

496516
Collection<PersonDto> findPersonDtoBy();

0 commit comments

Comments
 (0)