Using Dynamic Query Parameters in FlowInquiry
This document explains how to use the dynamic query parameter system in the FlowInquiry backend, focusing on the io.flowinquiry.query package and how to leverage these features in the service layer.
Overview
The dynamic query system allows clients to build complex, flexible queries for filtering and searching entities. This is achieved using a set of DTOs and utility classes that translate query parameters into JPA Specifications.
Core Classes
QueryDTO
The entry point for dynamic queries is the QueryDTO class:
public class QueryDTO {
private List<GroupFilter> groups;
private List<Filter> filters;
}filters: A list of simple filters (field, operator, value).groups: A list of grouped filters, allowing for nested logical operations (AND/OR).
Filter
Represents a single filter condition:
public class Filter {
@NotEmpty private String field;
@NotNull private FilterOperator operator;
@NotNull private Object value;
// ...constructor and accessors...
}field: The entity field to filter on.operator: The comparison operator (e.g., EQUALS, LIKE, GREATER_THAN).value: The value to compare against.
GroupFilter
Allows grouping of filters and/or other groups with a logical operator:
public class GroupFilter {
private List<Filter> filters; // Simple filters in this group
private List<GroupFilter> groups; // Nested groups
private LogicalOperator logicalOperator; // AND or OR
}Query Execution: QueryUtils
The QueryUtils class provides the method to convert a QueryDTO into a JPA Specification:
public static <Entity> Specification<Entity> createSpecification(QueryDTO queryDTO)- If
groupsare present, it recursively builds predicates for each group. - If only
filtersare present, it builds predicates for each filter. - Returns a
Specificationthat can be used with Spring Data JPA repositories.
Example Usage in Service Layer
See the ProjectService for a real-world example:
@Transactional(readOnly = true)
public Page<ProjectDTO> findProjects(Optional<QueryDTO> queryDTO, Pageable pageable) {
Specification<Project> spec = createSpecification(queryDTO.orElse(null));
return projectRepository.findAll(spec, pageable).map(projectMapper::toDto);
}- The service receives a
QueryDTO(often from a controller or API request). - It calls
QueryUtils.createSpecification(queryDTO)to build the query. - The resulting
Specificationis passed to the repository’sfindAllmethod.
Example QueryDTO JSON
A sample JSON payload for a dynamic query might look like:
{
"filters": [
{ "field": "name", "operator": "LIKE", "value": "%demo%" },
{ "field": "status", "operator": "EQUALS", "value": "ACTIVE" }
],
"groups": [
{
"filters": [
{
"field": "createdDate",
"operator": "GREATER_THAN",
"value": "2024-01-01"
}
],
"logicalOperator": "AND"
}
]
}Summary
- Use
QueryDTOto define dynamic filters and groups. - Use
QueryUtils.createSpecificationto convert the DTO to a JPA Specification. - Pass the Specification to your repository for flexible, dynamic querying.
Refer to the io.flowinquiry.query package for more details and available operators.