Skip to content

Commit a6d6830

Browse files
committed
add readme doc for pagination
1 parent 156738b commit a6d6830

File tree

4 files changed

+126
-18
lines changed

4 files changed

+126
-18
lines changed

plume-db-querydsl/README.md

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,3 +64,114 @@ Code generation
6464
To generate Querydsl entities, a good choice is to use this
6565
[Querydsl code generator](https://github.com/Coreoz/Plume/tree/master/plume-db-querydsl-codegen).
6666

67+
Pagination
68+
----------
69+
**Overview**:
70+
71+
The `SqlPaginatedQuery` class provides a robust and flexible mechanism for paginating results in a QueryDSL query. It abstracts the pagination logic into two generic interfaces —`Slice` and `Page`— which represent paginated results in different ways.
72+
73+
- `Page<U>`: A `Page` contains a list of results, total count of items, total number of pages, and a flag to indicate if there are more pages available.
74+
- `Slice<U>`: A `Slice` contains a list of results and a flag to indicate if there are more items to be fetched, without calculating the total number of items or pages.
75+
76+
This allows you to manage and paginate large datasets efficiently when working with QueryDSL.
77+
78+
**Key Features**:
79+
80+
- **Pagination Logic**: Handles offset-based pagination by calculating the number of records to skip (`offset`) and the number of records to fetch (`limit`) using the page number and page size.
81+
- **Sorting Support**: Allows dynamic sorting of query results by providing an `Expression` and an `Order` (ascending/descending).
82+
- **Efficient Slicing**: Fetches a "slice" of data without loading the entire dataset, useful when you only need to know if there are more results to load (e.g., in infinite scroll scenarios).
83+
- **Full Page Information**: Provides detailed information about the paginated dataset, including the total count, total pages, and whether there are more results.
84+
85+
**Working with Pagination from a WebService**:
86+
87+
First, you need to create a translation between the API sort key and a table column.
88+
This can be done like this:
89+
90+
```java
91+
public enum SortPath {
92+
93+
// users
94+
USER_LIST_EMAIL("email", QUser.user.email),
95+
USER_LIST_FIRST_NAME("first_name", QUser.user.firstName),
96+
USER_LIST_LAST_NAME("last_name", QUser.user.lastName),
97+
USER_LIST_LAST_LOGIN_DATE("last_login_date", QUser.user.lastLogin),
98+
// more complex cases
99+
PRIORITY(
100+
"user_priority",
101+
new CaseBuilder()
102+
.when(QUser.user.priority.eq(StudyPriority.MEDIUM.name())).then(1)
103+
.when(QUser.user.priority.eq(StudyPriority.HIGH.name())).then(2)
104+
.when(QUser.user.priority.eq(StudyPriority.VERY_HIGH.name())).then(3)
105+
.otherwise(1)
106+
),
107+
;
108+
109+
private final String sortKey;
110+
private final Expression<?> path;
111+
112+
@Nullable
113+
public static SortPath fromSortKey(String sortKey) {
114+
return Arrays.stream(SortPath.values())
115+
.filter(entry -> entry.sortKey.equals(sortKey))
116+
.findFirst()
117+
.orElse(null);
118+
}
119+
}
120+
```
121+
122+
Then declare your WebService:
123+
124+
```java
125+
@POST
126+
@Path("/search")
127+
@Operation(description = "Retrieves admin users")
128+
@Consumes(MediaType.APPLICATION_JSON)
129+
public Page<AdminUser> searchUsers(
130+
@QueryParam("page") Long page,
131+
@QueryParam("size") Long size,
132+
@QueryParam("sort") String sort,
133+
@QueryParam("sortDirection") Order sortDirection,
134+
UserSearchRequest userSearchRequest
135+
) {
136+
// check the pagination that comes from the API call
137+
if (page < 1) {
138+
throw new WsException(WsError.REQUEST_INVALID, List.of("page"));
139+
}
140+
if (size < 1) {
141+
throw new WsException(WsError.REQUEST_INVALID, List.of("size"));
142+
}
143+
return usersDao.searchUsers(
144+
userSearchRequest,
145+
page,
146+
size,
147+
SortPath.fromSortKey(sort),
148+
sortDirection
149+
);
150+
}
151+
```
152+
153+
Then apply the pagination from the API call with `SqlPaginatedQuery` :
154+
155+
```java
156+
public Page<AdminUser> searchUsers(
157+
UserSearchRequest userSearchRequest,
158+
Long page,
159+
Long size,
160+
Expression<?> path,
161+
Order sortDirection
162+
) {
163+
return SqlPaginatedQuery
164+
.fromQuery(
165+
this.transactionManagerQuerydsl.selectQuery()
166+
.select(QUser.user)
167+
.from(QUser.user)
168+
.where(
169+
QUser.user.firstName.containsIgnoreCase(userSearchRequest.searchText())
170+
.or(QUser.user.lastName.containsIgnoreCase(userSearchRequest.searchText()))
171+
.or(QUser.user.email.containsIgnoreCase(userSearchRequest.searchText()))
172+
)
173+
)
174+
.withSort(path, sortDirection)
175+
.fetchPage(page, size);
176+
}
177+
```

plume-db-querydsl/src/test/java/com/coreoz/plume/db/querydsl/pagination/SqlPaginatedQueryTest.java

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,9 @@ public void fetch_page_with_correct_pagination_should_paginate_users() {
3838
.withSort(QUser.user.name, Order.DESC)
3939
.fetchPage(1, 10);
4040

41-
assertThat(page.pagesCount()).isNotZero();
42-
assertThat(page.totalCount()).isNotZero();
43-
assertThat(page.items()).isNotEmpty();
44-
assertThat(page.pagesCount()).isNotZero();
41+
assertThat(page.pagesCount()).isEqualTo(1);
42+
assertThat(page.totalCount()).isEqualTo(10);
43+
assertThat(page.items()).hasSize(10);
4544
assertThat(page.hasMore()).isFalse();
4645
}
4746

@@ -56,10 +55,9 @@ public void fetch_page_with_wrong_pagination_should_return_empty_items() {
5655
.withSort(QUser.user.name, Order.DESC)
5756
.fetchPage(2, 10);
5857

59-
assertThat(page.pagesCount()).isNotZero();
60-
assertThat(page.totalCount()).isNotZero();
58+
assertThat(page.pagesCount()).isEqualTo(1);
59+
assertThat(page.totalCount()).isEqualTo(10);
6160
assertThat(page.items()).isEmpty();
62-
assertThat(page.pagesCount()).isNotZero();
6361
assertThat(page.hasMore()).isFalse();
6462
}
6563

@@ -74,8 +72,8 @@ public void fetch_page_with_minimum_page_and_page_size_should_return_results() {
7472
.withSort(QUser.user.name, Order.ASC)
7573
.fetchPage(1, 1); // Minimum page number and page size
7674

77-
assertThat(page.pagesCount()).isNotZero();
78-
assertThat(page.totalCount()).isNotZero();
75+
assertThat(page.pagesCount()).isEqualTo(10);
76+
assertThat(page.totalCount()).isEqualTo(10);
7977
assertThat(page.items()).hasSize(1); // Only one item expected due to page size of 1
8078
assertThat(page.hasMore()).isTrue(); // Has more items since page size is small
8179
}
@@ -91,8 +89,8 @@ public void fetch_page_with_page_size_larger_than_total_results_should_return_al
9189
.withSort(QUser.user.name, Order.ASC)
9290
.fetchPage(1, 100); // Large page size compared to available results
9391

94-
assertThat(page.totalCount()).isGreaterThan(0);
95-
assertThat(page.items().size()).isLessThanOrEqualTo(100); // Should return all available users
92+
assertThat(page.totalCount()).isEqualTo(10);
93+
assertThat(page.items()).hasSize(10); // Should return all available users
9694
assertThat(page.hasMore()).isFalse(); // No more items because page size exceeds total results
9795
}
9896

@@ -130,9 +128,9 @@ public void fetch_page_without_sorting_should_paginate_users() {
130128
)
131129
.fetchPage(1, 10); // No sorting applied
132130

133-
assertThat(page.pagesCount()).isNotZero();
134-
assertThat(page.totalCount()).isNotZero();
135-
assertThat(page.items()).isNotEmpty();
131+
assertThat(page.pagesCount()).isEqualTo(1);
132+
assertThat(page.totalCount()).isEqualTo(10);
133+
assertThat(page.items()).hasSize(10);
136134
assertThat(page.hasMore()).isFalse(); // Assuming there aren't more than 10 users in the test
137135
}
138136

plume-db/src/main/java/com/coreoz/plume/db/pagination/Page.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,15 @@
33
import javax.annotation.Nonnull;
44
import java.util.List;
55
import java.util.function.Function;
6-
import java.util.stream.Collectors;
76

87
/**
98
* Represents a page of data in a paginated structure.
109
*
1110
* @param items the items in the page
1211
* @param totalCount the total number of items in the collection
1312
* @param pagesCount the total number of pages
14-
* @param currentPage the current page
15-
* @param hasMore boolean set to true if there is another page after
13+
* @param currentPage the current page, starts at 1
14+
* @param hasMore boolean set to true if there is another page after this one
1615
*
1716
* @param <T> The type of items contained in the page.
1817
*/

plume-db/src/main/java/com/coreoz/plume/db/pagination/Slice.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
* Represents a portion (or slice) of a larger dataset.
99
*
1010
* @param items the items in the slice
11-
* @param hasMore boolean set to true if there is another slice after
11+
* @param hasMore boolean set to true if there is another slice after this one
1212
*
1313
* @param <T> The type of elements contained in the slice.
1414
*/

0 commit comments

Comments
 (0)