diff --git a/README.md b/README.md index 1f8cabf..eb64091 100644 --- a/README.md +++ b/README.md @@ -34,11 +34,13 @@ dependencies { ### Pagination Parameters +Use default pagination from Spring. + The following query parameters manage pagination options: - `page`: Integer starting at 0, representing the desired page. -- `pageSize`: Integer from 1 to 100, representing the number of elements per page. Defaults to 10. -- `order`: Field name to sort by. -- `sort`: Sort direction, either asc (ascending) or desc (descending). Defaults to descending. +- `size`: Integer from 1 to 100, representing the number of elements per page. Defaults to 10. +- `sort`: Field name to sort by. +- `direction`: Sort direction, either asc (ascending) or desc (descending). Defaults to descending. **Example:** @@ -46,6 +48,49 @@ The following query parameters manage pagination options: http://localhost:8080/myEndpoint?page=2&pageSize=7&order=name&sort=asc ``` +You can use `sort` parameter for multiples sort: + +**Example:** + +```text +http://localhost:8080/myEndpoint?sort=name,asc&sort=price,desc +``` + +Here’s an improved version of your documentation section in English: + +--- + +## Usage in HTTP Requests + +### Pagination Parameters + +The library uses Spring's default pagination mechanism to manage paginated responses. + +You can control pagination through the following query parameters: + +- **`page`**: Specifies the zero-based page index to retrieve. Defaults to `0` if not provided. +- **`size`**: Specifies the number of elements per page. Must be between `1` and `100`, with a default of `10`. +- **`sort`**: Specifies the field(s) by which to sort the results. +- **`direction`**: Specifies the sorting direction. Acceptable values are `asc` (ascending) or `desc` (descending). Defaults to `desc`. + +#### Examples + +**Single Sort** + +To fetch the second page with 7 elements per page, sorted by `name` in ascending order: + +```text +GET http://localhost:8080/myEndpoint?page=1&size=7&sort=name,asc +``` + +**Multiple Sort Criteria** + +To sort by multiple fields, chain `sort` parameters. For example, to sort by `name` in ascending order and then by `price` in descending order: + +```text +GET http://localhost:8080/myEndpoint?sort=name,asc&sort=price,desc +``` + ### Filtering Operators Here is the list of operator that can be used to filter data: @@ -162,7 +207,7 @@ public class ExampleController { @GetMapping("/myEndpoint") public Page find(@RequestParam Map> allParams, - @ModelAttribute SpringQueryFilter queryFilter) { + @PageableDefault(page = 0, size = 10, sort = "lastName", direction = Sort.Direction.ASC) Pageable pageable) { return myService.find(allParams, queryFilter); } } @@ -185,7 +230,7 @@ public interface YourEntityRepository extends JpaRepository { public interface YourEntityService { (...) - Page find(Map> filters, SpringQueryFilter queryFilter); + Page find(Map> filters, Pageable pageable); } ``` @@ -199,20 +244,15 @@ public class YourEntityServiceImpl implements YourEntityService { @Override public Page find(final Map> filters, - final SpringQueryFilter queryFilter) { + final Pageable pageable) { return this.yourEntityRepository.find( new QueryFilterSpecification<>(YourEntity.class, filters), - queryFilter.getPageable() // or queryFilter.getPageable("name") to apply a default order. + pageable ); } } ``` -You can define default sort in your service by using: -```java -queryFilter.getPageable("name"); -``` - ### Filter without queryParameters To apply filters without using query parameters directly from the controller, you can manually define filter conditions: @@ -237,53 +277,11 @@ public class YourEntityServiceImpl implements YourEntityService { return this.yourEntityRepository.find( new QueryFilterSpecification<>(YourEntity.class, filters), - // Current page, limit, field to order and sort - new SpringQueryFilter(0, 10, "name", "asc").getPageable() + PageRequest.of(0, 10, Sort.by(Sort.Order.asc("name"))) ); } } ``` -## Custom Pagination Options - -If you need specific pagination settings, implement `ISpringQueryFilter` to define your own minimum, maximum, sort values, or any other pagination-related configurations. - -```java -@NoArgsConstructor -@AllArgsConstructor -public class SpringQueryFilter implements ISpringQueryFilter { - - private static final int DEFAULT_PAGE_SIZE = 1; - private static final int MIN_PAGE_SIZE = 1; - private static final int MAX_PAGE_SIZE = 10; - private Integer page = 0; - private Integer pageSize = 1; - private String order; - private String sort; - - @Override - public int getComputedPage() { - return Math.max(Optional.ofNullable(page).orElse(0), 0); - } - - @Override - public int getComputedSize() { - return Math.clamp(Optional.ofNullable(pageSize).orElse(DEFAULT_PAGE_SIZE), MIN_PAGE_SIZE, MAX_PAGE_SIZE); - } - - @Override - public String getOrder() { - return this.order; - } - - - @Override - public boolean isAscendantSort() { - return "asc".equalsIgnoreCase(sort); - } -} -``` - -This configuration allows you to control the min, max, sort values, and create a customized pageable option name in query parameters. ## Custom Types diff --git a/changelog.md b/changelog.md index b3fe7e2..a7cfd1c 100644 --- a/changelog.md +++ b/changelog.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html) +## [Unreleased] + +### Removed + +- `SpringQueryFilter` to uses `Pageable` of spring. + ## [1.1.3] 2024/11/20 ### Fixed diff --git a/pom.xml b/pom.xml index 2bc8ff8..e7b3d5d 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.github.zorin95670 spring-query-filter - 1.1.3 + 1.1.4-SNAPSHOT jar spring-query-filter diff --git a/src/main/java/io/github/zorin95670/query/ISpringQueryFilter.java b/src/main/java/io/github/zorin95670/query/ISpringQueryFilter.java deleted file mode 100644 index f7fdf99..0000000 --- a/src/main/java/io/github/zorin95670/query/ISpringQueryFilter.java +++ /dev/null @@ -1,89 +0,0 @@ -package io.github.zorin95670.query; - -import org.springframework.data.domain.PageRequest; -import org.springframework.data.domain.Pageable; -import org.springframework.data.domain.Sort; - -import java.util.Optional; - -/** - * Interface representing a query filter for pagination and sorting in a Spring application. - * Provides methods to retrieve pagination parameters (page number and page size), - * sorting criteria (order and sort direction), and utility methods to construct - * {@link Sort} and {@link Pageable} objects based on the specified or default values. - */ -public interface ISpringQueryFilter { - - /** - * Retrieves the page number for pagination. - * - * @return the page number (0-based index). - */ - int getComputedPage(); - - /** - * Retrieves the page size for pagination. - * - * @return the number of records per page. - */ - int getComputedPageSize(); - - /** - * Retrieves the field by which results should be ordered. - * - * @return the field name to order by. - */ - String getOrder(); - - /** - * Indicates whether the sorting order is ascending. - * - * @return {@code true} if ascending order, {@code false} for descending. - */ - boolean isAscendantSort(); - - /** - * Generates a {@link Sort} object based on the current sorting settings or a default field. - * - * @param defaultOrder the default field to sort by if no order field is specified. - * @return a {@link Sort} object with the specified direction, or {@code null} if no order is specified. - */ - default Sort getOrderBy(String defaultOrder) { - String currentOrder = Optional.ofNullable(this.getOrder()).orElse(defaultOrder); - - if (currentOrder == null) { - return null; - } - - if (isAscendantSort()) { - return Sort.by(Sort.Direction.ASC, currentOrder); - } - - return Sort.by(Sort.Direction.DESC, currentOrder); - } - - /** - * Generates a {@link Pageable} object for pagination using default sorting. - * - * @return a {@link Pageable} object based on the page number, page size, and sorting criteria. - */ - default Pageable getPageable() { - return this.getPageable(null); - } - - /** - * Generates a {@link Pageable} object for pagination with a specified default order field. - * - * @param defaultOrder the default field to sort by if no order field is specified. - * @return a {@link Pageable} object with the specified pagination and sorting settings. - */ - default Pageable getPageable(String defaultOrder) { - Sort sort = this.getOrderBy(defaultOrder); - - if (sort != null) { - return PageRequest.of(this.getComputedPage(), this.getComputedPageSize(), sort); - } - - return PageRequest.of(this.getComputedPage(), this.getComputedPageSize()); - } -} diff --git a/src/main/java/io/github/zorin95670/query/SpringQueryFilter.java b/src/main/java/io/github/zorin95670/query/SpringQueryFilter.java deleted file mode 100644 index 303e212..0000000 --- a/src/main/java/io/github/zorin95670/query/SpringQueryFilter.java +++ /dev/null @@ -1,137 +0,0 @@ -package io.github.zorin95670.query; - -import java.util.Optional; - -/** - * Class implementing {@link ISpringQueryFilter} for managing pagination and sorting parameters in a Spring application. - * This class provides default values for pagination and handles sorting direction based on the `sort` field. - */ -public class SpringQueryFilter implements ISpringQueryFilter { - - /** - * Minimum of elements by page. - */ - private static final int MIN_PAGE_SIZE = 1; - - /** - * Maximum of elements by page. - */ - private static final int MAX_PAGE_SIZE = 100; - - /** - * Default number of elements by page. - */ - private static final int DEFAULT_PAGE_SIZE = 10; - - /** - * Page number for pagination (0-based index). Defaults to 0 if not specified. - */ - private Integer page = 0; - - /** - * Number of records per page. Defaults to 1 if not specified. - */ - private Integer pageSize = 1; - - /** - * Field name by which results should be ordered. Can be set to null. - */ - private String order = null; - - /** - * Sorting direction. Should be "asc" for ascending order; any other value is treated as descending. - */ - private String sort = null; - - /** - * Default no-argument constructor. - */ - public SpringQueryFilter() { - } - - /** - * Constructor to initialize all properties. - * - * @param page the page number (0-based index). - * @param pageSize the number of records per page. - * @param order the field name by which results should be ordered. - * @param sort the sorting direction (ascending or descending). - */ - public SpringQueryFilter(final Integer page, final Integer pageSize, final String order, final String sort) { - this.page = page; - this.pageSize = pageSize; - this.order = order; - this.sort = sort; - } - - /** - * Returns the page number for pagination (0-based index). - * Defaults to 0 if not specified. - * - * @return the page number (0-based index) - */ - public Integer getPage() { - return page; - } - - /** - * Returns the number of records per page. - * Defaults to 1 if not specified. - * - * @return the number of records per page - */ - public Integer getPageSize() { - return pageSize; - } - - /** - * Returns the field name by which results should be ordered. - * Can be set to null. - * - * @return the field name for ordering, or null if not specified - */ - public String getOrder() { - return order; - } - - /** - * Returns the sorting direction. - * Should be "asc" for ascending order; any other value is treated as descending. - * - * @return the sorting direction, either "asc" for ascending or any other value for descending - */ - public String getSort() { - return sort; - } - - /** - * Retrieves the page number for pagination, defaulting to 0 if the page is null or less than 0. - * - * @return the page number, constrained to be non-negative. - */ - @Override - public int getComputedPage() { - return Math.max(Optional.ofNullable(getPage()).orElse(0), 0); - } - - /** - * Retrieves the page size for pagination, constrained between 1 and 10. - * Defaults to 1 if page size is null or less than 1. - * - * @return the page size, bounded by a minimum of 1 and a maximum of 10. - */ - @Override - public int getComputedPageSize() { - return Math.clamp(Optional.ofNullable(getPageSize()).orElse(DEFAULT_PAGE_SIZE), MIN_PAGE_SIZE, MAX_PAGE_SIZE); - } - - /** - * Determines if the sort order is ascending. - * - * @return {@code true} if `sort` is set to "asc" (case-insensitive), otherwise {@code false}. - */ - @Override - public boolean isAscendantSort() { - return "asc".equalsIgnoreCase(getSort()); - } -} diff --git a/src/main/java/io/github/zorin95670/query/package-info.java b/src/main/java/io/github/zorin95670/query/package-info.java deleted file mode 100644 index 2bf8cb4..0000000 --- a/src/main/java/io/github/zorin95670/query/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -/** - * Package that manages all classes for default query parameters in http request. - */ -package io.github.zorin95670.query; diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index 3413beb..cf7b2e3 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -1,7 +1,6 @@ module io.github.zorin95670 { exports io.github.zorin95670.exception; exports io.github.zorin95670.predicate; - exports io.github.zorin95670.query; exports io.github.zorin95670.specification; requires transitive jakarta.persistence; diff --git a/src/test/java/io/github/zorin95670/specification/SpringQueryFilterSpecificationTest.java b/src/test/java/io/github/zorin95670/specification/SpringQueryFilterSpecificationTest.java index febdd8d..4fc2cd2 100644 --- a/src/test/java/io/github/zorin95670/specification/SpringQueryFilterSpecificationTest.java +++ b/src/test/java/io/github/zorin95670/specification/SpringQueryFilterSpecificationTest.java @@ -9,11 +9,12 @@ import io.github.zorin95670.predicate.LongPredicateFilter; import io.github.zorin95670.predicate.StringPredicateFilter; import io.github.zorin95670.predicate.UUIDPredicateFilter; -import io.github.zorin95670.query.SpringQueryFilter; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Sort; import java.sql.Timestamp; import java.util.Date; @@ -87,7 +88,7 @@ void testShouldReturnAllEntitiesWithoutFilters() { repository.deleteAll(); Map> filters = new HashMap<>(); - var pagination = new SpringQueryFilter(0, 10, "text", "asc"); + var pageable = PageRequest.of(0, 10, Sort.by(Sort.Order.asc("text"))); UUID uuid1 = UUID.randomUUID(); MyEntity entity1 = createEntity(1, uuid1); @@ -98,7 +99,7 @@ void testShouldReturnAllEntitiesWithoutFilters() { entity2 = repository.save(entity2); List entities = repository - .findAll(new SpringQueryFilterSpecification<>(MyEntity.class, filters), pagination.getPageable()); + .findAll(new SpringQueryFilterSpecification<>(MyEntity.class, filters), pageable); assertNotNull(entities); assertEquals(2, entities.size()); @@ -107,7 +108,7 @@ void testShouldReturnAllEntitiesWithoutFilters() { filters.put("id", List.of()); entities = repository - .findAll(new SpringQueryFilterSpecification<>(MyEntity.class, filters), pagination.getPageable()); + .findAll(new SpringQueryFilterSpecification<>(MyEntity.class, filters), pageable); assertNotNull(entities); assertEquals(2, entities.size()); @@ -136,28 +137,31 @@ void testShouldReturnValidEntitiesDependOfPagination() { Map> filters = new HashMap<>(); filters.put("uuid", List.of(uuid1.toString())); - var pagination = new SpringQueryFilter(0, 2, null, "asc"); + + var pageable = PageRequest.of(0, 2); + List entities = repository - .findAll(new SpringQueryFilterSpecification<>(MyEntity.class, filters), pagination.getPageable()); + .findAll(new SpringQueryFilterSpecification<>(MyEntity.class, filters), pageable); assertNotNull(entities); assertEquals(1, entities.size()); assertEquals(entity1, entities.getFirst()); - pagination = new SpringQueryFilter(0, 2, "id", "desc"); + + pageable = PageRequest.of(0, 2, Sort.by(Sort.Order.desc("id"))); filters = new HashMap<>(); filters.put("uuid", List.of(uuid1 + "|" + uuid2 + "|" + uuid3)); entities = repository - .findAll(new SpringQueryFilterSpecification<>(MyEntity.class, filters), pagination.getPageable()); + .findAll(new SpringQueryFilterSpecification<>(MyEntity.class, filters), pageable); assertNotNull(entities); assertEquals(2, entities.size()); assertEquals(entity3, entities.getFirst()); assertEquals(entity2, entities.getLast()); - pagination = new SpringQueryFilter(1, 2, "id", "desc"); + pageable = PageRequest.of(1, 2, Sort.by(Sort.Order.desc("id"))); filters = new HashMap<>(); filters.put("uuid", List.of(uuid1 + "|" + uuid2 + "|" + uuid3)); entities = repository - .findAll(new SpringQueryFilterSpecification<>(MyEntity.class, filters), pagination.getPageable()); + .findAll(new SpringQueryFilterSpecification<>(MyEntity.class, filters), pageable); assertNotNull(entities); assertEquals(1, entities.size()); assertEquals(entity1, entities.getFirst()); @@ -182,12 +186,12 @@ void testShouldSimpleFilter() { entity3.setText(null); entity3 = repository.save(entity3); - var pagination = new SpringQueryFilter(0, 10, "text", "asc"); + var pageable = PageRequest.of(0, 10, Sort.by(Sort.Order.asc("text"))); Map> filters = new HashMap<>(); filters.put("id", List.of("2")); List entities = repository - .findAll(new SpringQueryFilterSpecification<>(MyEntity.class, filters), pagination.getPageable()); + .findAll(new SpringQueryFilterSpecification<>(MyEntity.class, filters), pageable); assertNotNull(entities); assertEquals(1, entities.size()); assertEquals(entity2, entities.getFirst()); @@ -195,7 +199,7 @@ void testShouldSimpleFilter() { filters = new HashMap<>(); filters.put("text", List.of("text2")); entities = repository - .findAll(new SpringQueryFilterSpecification<>(MyEntity.class, filters), pagination.getPageable()); + .findAll(new SpringQueryFilterSpecification<>(MyEntity.class, filters), pageable); assertNotNull(entities); assertEquals(1, entities.size()); assertEquals(entity2, entities.getFirst()); @@ -203,7 +207,7 @@ void testShouldSimpleFilter() { filters = new HashMap<>(); filters.put("date", List.of("20")); entities = repository - .findAll(new SpringQueryFilterSpecification<>(MyEntity.class, filters), pagination.getPageable()); + .findAll(new SpringQueryFilterSpecification<>(MyEntity.class, filters), pageable); assertNotNull(entities); assertEquals(1, entities.size()); assertEquals(entity2, entities.getFirst()); @@ -211,7 +215,7 @@ void testShouldSimpleFilter() { filters = new HashMap<>(); filters.put("uuid", List.of(uuid2.toString())); entities = repository - .findAll(new SpringQueryFilterSpecification<>(MyEntity.class, filters), pagination.getPageable()); + .findAll(new SpringQueryFilterSpecification<>(MyEntity.class, filters), pageable); assertNotNull(entities); assertEquals(1, entities.size()); assertEquals(entity2, entities.getFirst()); @@ -219,7 +223,7 @@ void testShouldSimpleFilter() { filters = new HashMap<>(); filters.put("numberInteger", List.of("200")); entities = repository - .findAll(new SpringQueryFilterSpecification<>(MyEntity.class, filters), pagination.getPageable()); + .findAll(new SpringQueryFilterSpecification<>(MyEntity.class, filters), pageable); assertNotNull(entities); assertEquals(1, entities.size()); assertEquals(entity2, entities.getFirst()); @@ -227,7 +231,7 @@ void testShouldSimpleFilter() { filters = new HashMap<>(); filters.put("numberFloat", List.of("2000.2")); entities = repository - .findAll(new SpringQueryFilterSpecification<>(MyEntity.class, filters), pagination.getPageable()); + .findAll(new SpringQueryFilterSpecification<>(MyEntity.class, filters), pageable); assertNotNull(entities); assertEquals(1, entities.size()); assertEquals(entity2, entities.getFirst()); @@ -235,7 +239,7 @@ void testShouldSimpleFilter() { filters = new HashMap<>(); filters.put("numberDouble", List.of("eq_20000.2")); entities = repository - .findAll(new SpringQueryFilterSpecification<>(MyEntity.class, filters), pagination.getPageable()); + .findAll(new SpringQueryFilterSpecification<>(MyEntity.class, filters), pageable); assertNotNull(entities); assertEquals(1, entities.size()); assertEquals(entity2, entities.getFirst()); @@ -243,7 +247,7 @@ void testShouldSimpleFilter() { filters = new HashMap<>(); filters.put("id", List.of("not_2", "not_3")); entities = repository - .findAll(new SpringQueryFilterSpecification<>(MyEntity.class, filters), pagination.getPageable()); + .findAll(new SpringQueryFilterSpecification<>(MyEntity.class, filters), pageable); assertNotNull(entities); assertEquals(1, entities.size()); assertEquals(entity1, entities.getFirst()); @@ -252,7 +256,7 @@ void testShouldSimpleFilter() { filters.put("id", List.of("not_1")); filters.put("text", List.of("null")); entities = repository - .findAll(new SpringQueryFilterSpecification<>(MyEntity.class, filters), pagination.getPageable()); + .findAll(new SpringQueryFilterSpecification<>(MyEntity.class, filters), pageable); assertNotNull(entities); assertEquals(1, entities.size()); assertEquals(entity3, entities.getFirst()); @@ -260,7 +264,7 @@ void testShouldSimpleFilter() { filters = new HashMap<>(); filters.put("text", List.of("not_null")); entities = repository - .findAll(new SpringQueryFilterSpecification<>(MyEntity.class, filters), pagination.getPageable()); + .findAll(new SpringQueryFilterSpecification<>(MyEntity.class, filters), pageable); assertNotNull(entities); assertEquals(1, entities.size()); assertEquals(entity2, entities.getFirst()); @@ -268,7 +272,7 @@ void testShouldSimpleFilter() { filters = new HashMap<>(); filters.put("id", List.of("lt_2", "gt_0")); entities = repository - .findAll(new SpringQueryFilterSpecification<>(MyEntity.class, filters), pagination.getPageable()); + .findAll(new SpringQueryFilterSpecification<>(MyEntity.class, filters), pageable); assertNotNull(entities); assertEquals(1, entities.size()); assertEquals(entity1, entities.getFirst()); @@ -276,7 +280,7 @@ void testShouldSimpleFilter() { filters = new HashMap<>(); filters.put("id", List.of("0_bt_1")); entities = repository - .findAll(new SpringQueryFilterSpecification<>(MyEntity.class, filters), pagination.getPageable()); + .findAll(new SpringQueryFilterSpecification<>(MyEntity.class, filters), pageable); assertNotNull(entities); assertEquals(1, entities.size()); assertEquals(entity1, entities.getFirst()); @@ -284,7 +288,7 @@ void testShouldSimpleFilter() { filters = new HashMap<>(); filters.put("text", List.of("not_lk_*4", "lk_*2")); entities = repository - .findAll(new SpringQueryFilterSpecification<>(MyEntity.class, filters), pagination.getPageable()); + .findAll(new SpringQueryFilterSpecification<>(MyEntity.class, filters), pageable); assertNotNull(entities); assertEquals(1, entities.size()); assertEquals(entity2, entities.getFirst()); @@ -292,7 +296,7 @@ void testShouldSimpleFilter() { filters = new HashMap<>(); filters.put("id", List.of("1|2")); entities = repository - .findAll(new SpringQueryFilterSpecification<>(MyEntity.class, filters), pagination.getPageable()); + .findAll(new SpringQueryFilterSpecification<>(MyEntity.class, filters), pageable); assertNotNull(entities); assertEquals(2, entities.size()); assertEquals(entity1, entities.getFirst()); @@ -300,7 +304,7 @@ void testShouldSimpleFilter() { filters.put("text", List.of("text2", "not_text1")); entities = repository - .findAll(new SpringQueryFilterSpecification<>(MyEntity.class, filters), pagination.getPageable()); + .findAll(new SpringQueryFilterSpecification<>(MyEntity.class, filters), pageable); assertNotNull(entities); assertEquals(1, entities.size()); assertEquals(entity2, entities.getFirst());