Skip to content

Commit 5e6042e

Browse files
vaadin-botcaalador
andauthored
fix: page calculation when not equally divisible (#17920) (#17924)
Fix the query page and pageSize for cases where the offset is not equally divisible with the limit/pageSize. Fixes #17750 Co-authored-by: caalador <[email protected]>
1 parent b5d5965 commit 5e6042e

File tree

2 files changed

+55
-4
lines changed

2 files changed

+55
-4
lines changed

flow-data/src/main/java/com/vaadin/flow/data/provider/Query.java

+26-4
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ public class Query<T, F> implements Serializable {
3838
private final List<QuerySortOrder> sortOrders;
3939
private final Comparator<T> inMemorySorting;
4040
private final F filter;
41+
private Integer pageSize;
4142

4243
/**
4344
* Constructs a Query for all rows from 0 to {@link Integer#MAX_VALUE}
@@ -119,24 +120,45 @@ public int getLimit() {
119120
* <p>
120121
* Vaadin asks data from the backend in paged manner. This shorthand
121122
* calculates the page index for backends using paged data access, such as
122-
* Spring Data repositores.
123+
* Spring Data repositories.
123124
*
124125
* @return the zero-based page index
125126
*/
126127
public int getPage() {
127-
return getOffset() / getLimit();
128+
int pageSize = getPageSize();
129+
int pageOffset = getOffset();
130+
// If page offset is not evenly divisible with pageSize raise
131+
// pageSize until it is.
132+
// Else we will on the end pick the wrong items due to rounding error.
133+
if (pageOffset > pageSize && pageOffset % pageSize != 0) {
134+
while (pageOffset % pageSize != 0) {
135+
pageSize++;
136+
}
137+
setPageSize(pageSize);
138+
}
139+
return pageOffset / pageSize;
140+
}
141+
142+
private void setPageSize(Integer pageSize) {
143+
this.pageSize = pageSize;
128144
}
129145

130146
/**
131147
* Returns the page size that should be returned. The amount of items can be
132148
* smaller if there is no more items available in the backend.
133149
* <p>
134-
* Vaadin asks data from the backend in paged manner. This is an alias for
135-
* {@link #getLimit()}.
150+
* Vaadin asks data from the backend in paged manner.
151+
* <p>
152+
* This is an alias for {@link #getLimit()} if the page offset can be evenly
153+
* divided by the limit. Else the page size will be increased to evenly
154+
* divide offset so the items skip for page will go to the correct item.
136155
*
137156
* @return the page size used for data access
138157
*/
139158
public int getPageSize() {
159+
if (pageSize != null) {
160+
return pageSize;
161+
}
140162
return getLimit();
141163
}
142164

flow-data/src/test/java/com/vaadin/flow/data/provider/DataCommunicatorTest.java

+29
Original file line numberDiff line numberDiff line change
@@ -1324,6 +1324,35 @@ public void fetchFromProvider_backendRunsOutOfItems_secondPageRequestSkipped() {
13241324
.fetch(Mockito.any(Query.class));
13251325
}
13261326

1327+
@Test
1328+
public void fetchFromProvider_itemCountLessThanTwoPages_correctItemsReturned() {
1329+
List<Item> items = new ArrayList<>();
1330+
for (int i = 1; i <= 113; i++) {
1331+
items.add(new Item(i));
1332+
}
1333+
1334+
DataProvider<Item, Void> dataProvider = DataProvider
1335+
.fromCallbacks(query -> {
1336+
int offset = query.getPage() * query.getPageSize();
1337+
int end = offset + query.getPageSize();
1338+
if (end > items.size()) {
1339+
end = items.size();
1340+
}
1341+
return items.subList(offset, end).stream();
1342+
}, query -> items.size());
1343+
dataCommunicator.setDataProvider(dataProvider, null);
1344+
dataCommunicator.setPageSize(50);
1345+
1346+
dataCommunicator.setDataProvider(dataProvider, null);
1347+
// request second page with correct db size.
1348+
Stream<Item> itemStream = dataCommunicator.fetchFromProvider(100, 13);
1349+
List<Item> itemList = itemStream.toList();
1350+
1351+
Assert.assertEquals(13, itemList.size());
1352+
Assert.assertEquals(new Item(101), itemList.get(0));
1353+
1354+
}
1355+
13271356
@Test
13281357
public void fetchEnabled_getItemCount_stillReturnsItemsCount() {
13291358
dataCommunicator.setFetchEnabled(false);

0 commit comments

Comments
 (0)