Created
February 15, 2025 15:20
-
-
Save yurikilian/36e4ab8cd6c8d0940c153f9a8d744528 to your computer and use it in GitHub Desktop.
Queyr filter DSL
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package io.lawtrackr.customer.model; | |
import static io.lawtrackr.database.filter.model.SearchFilterOperation.BETWEEN; | |
import static io.lawtrackr.database.filter.model.SearchFilterOperation.EQ; | |
import static io.lawtrackr.pagination.sort.SortableFields.Field.name; | |
import java.util.List; | |
import java.util.Set; | |
import io.lawtrackr.customer.collection.Customer.CustomerStatus; | |
import io.lawtrackr.database.filter.model.FilterableDto; | |
import io.lawtrackr.database.filter.model.LocalDateTimeRangeFilter; | |
import io.lawtrackr.database.filter.model.SearchFilter; | |
import io.lawtrackr.pagination.RequestDto; | |
import io.lawtrackr.pagination.sort.SortableFields; | |
import lombok.AllArgsConstructor; | |
import lombok.Getter; | |
import lombok.NoArgsConstructor; | |
import lombok.Setter; | |
import lombok.experimental.SuperBuilder; | |
@AllArgsConstructor | |
@NoArgsConstructor | |
@Getter | |
@Setter | |
@SuperBuilder | |
public class CustomerSearchDto | |
extends RequestDto implements FilterableDto { | |
private List<CustomerStatus> status; | |
private List<String> country; | |
private LocalDateTimeRangeFilter createdAt; | |
private String identification; | |
private String sortBy; | |
@Override | |
public String getSortBy() { | |
if (sortBy == null) { | |
return "id.asc"; | |
} | |
return sortBy; | |
} | |
@Override | |
public SortableFields getSortableFields() { | |
return SortableFields | |
.of(name("createdAt").withPath("createdDate")) | |
.and(name("name").withPath("name")) | |
.and(name("id").withPath("id")) | |
.and(name("country").withPath("addresses.country")) | |
.and(name("identification").withPath("identifications.value")) | |
.withDefaultSortBy("id.desc") | |
.get(); | |
} | |
@Override | |
public Set<SearchFilter> getSearchFilters() { | |
return Set.of( | |
new SearchFilter("status", EQ, this.status), | |
new SearchFilter("addresses.country", EQ, this.country), | |
new SearchFilter("createdDate", BETWEEN, this.createdAt), | |
new SearchFilter("identifications.value", EQ, this.identification) | |
); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package io.project.database.filter; | |
import java.util.ArrayList; | |
import java.util.Collection; | |
import javax.annotation.Nullable; | |
import io.lawtrackr.database.filter.model.FilterableDto; | |
import io.lawtrackr.database.filter.model.LocalDateTimeRangeFilter; | |
import org.springframework.data.mongodb.core.query.Criteria; | |
import org.springframework.data.mongodb.core.query.Query; | |
import org.springframework.stereotype.Component; | |
@Component | |
public class FilterParser { | |
public @Nullable Query resolve(final FilterableDto filterable) { | |
final var searchFilters = filterable.getSearchFilters(); | |
if (searchFilters.isEmpty()) { | |
return null; | |
} | |
final var criteriaList = new ArrayList<Criteria>(); | |
for (var searchFilter : searchFilters.stream().filter(searchFilter -> searchFilter.value() != null).toList()) { | |
final var value = searchFilter.value(); | |
switch (searchFilter.operation()) { | |
case EQ -> { | |
switch (value) { | |
case Collection<?> collection -> criteriaList.add(Criteria.where(searchFilter.path()).in(collection)); | |
default -> criteriaList.add(Criteria.where(searchFilter.path()).is(value)); | |
} | |
} | |
case BETWEEN -> { | |
switch (value) { | |
case LocalDateTimeRangeFilter range -> { | |
criteriaList.add(Criteria.where(searchFilter.path()).lt(range.getEnd())); | |
criteriaList.add(Criteria.where(searchFilter.path()).gte(range.getStart())); | |
} | |
default -> throw new IllegalStateException("Illegal range filter type: " + value.getClass().getName()); | |
} | |
} | |
} | |
} | |
if (criteriaList.isEmpty()) { | |
return null; | |
} | |
return new Query().addCriteria(new Criteria().andOperator(criteriaList.toArray(new Criteria[0]))); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package io.project.database.filter; | |
import io.lawtrackr.database.IdDocument; | |
import io.lawtrackr.database.filter.model.FilterableDto; | |
import org.springframework.data.domain.Page; | |
import org.springframework.data.domain.Pageable; | |
public interface QueryableByFilter<T extends IdDocument<ID>, ID> { | |
Page<T> findAll(FilterableDto filterableDto, Pageable pageable); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package io.project.database.filter; | |
import java.util.List; | |
import io.project.database.IdDocument; | |
import io.project.database.filter.model.FilterableDto; | |
import lombok.Setter; | |
import org.springframework.data.domain.Page; | |
import org.springframework.data.domain.Pageable; | |
import org.springframework.data.mongodb.core.MongoOperations; | |
import org.springframework.data.mongodb.core.query.Query; | |
import org.springframework.data.mongodb.repository.query.MongoEntityInformation; | |
import org.springframework.data.mongodb.repository.support.SimpleMongoRepository; | |
import org.springframework.data.support.PageableExecutionUtils; | |
public class QueryableByFilterBaseClass<T extends IdDocument<ID>, ID> extends SimpleMongoRepository<T, ID> implements QueryableByFilter<T, ID> { | |
private final MongoOperations mongoOperations; | |
private final MongoEntityInformation<T, ID> metadata; | |
@Setter | |
private FilterParser filterParser; | |
public QueryableByFilterBaseClass(MongoEntityInformation<T, ID> metadata, MongoOperations mongoOperations) { | |
super(metadata, mongoOperations); | |
this.mongoOperations = mongoOperations; | |
this.metadata = metadata; | |
} | |
@Override | |
public Page<T> findAll(FilterableDto filterableDto, Pageable pageable) { | |
final Query resolvedQuery = filterParser.resolve(filterableDto); | |
if (resolvedQuery == null) { | |
return findAll(pageable); | |
} | |
final var query = resolvedQuery.collation(metadata.getCollation()).with(pageable); | |
final Class<T> javaType = metadata.getJavaType(); | |
final List<T> list = mongoOperations.find(query, javaType, metadata.getCollectionName()); | |
return PageableExecutionUtils.getPage(list, pageable, () -> mongoOperations | |
.count(Query.of(query).limit(-1).skip(-1), javaType, metadata.getCollectionName())); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package io.project.database.filter; | |
import lombok.NonNull; | |
import org.springframework.data.mongodb.core.MongoOperations; | |
import org.springframework.data.mongodb.repository.support.MongoRepositoryFactory; | |
import org.springframework.data.repository.core.RepositoryInformation; | |
public class SearchFilterRepositoryFactory extends MongoRepositoryFactory { | |
private final FilterParser searchQueryParser; | |
public SearchFilterRepositoryFactory(MongoOperations mongoOperations, FilterParser searchQueryParser) { | |
super(mongoOperations); | |
this.searchQueryParser = searchQueryParser; | |
} | |
@Override | |
protected @NonNull Object getTargetRepository(@NonNull RepositoryInformation information) { | |
final Object targetRepository = super.getTargetRepository(information); | |
if (targetRepository instanceof QueryableByFilter<?, ?>) { | |
((QueryableByFilterBaseClass<?, ?>) targetRepository).setFilterParser(searchQueryParser); | |
} | |
return targetRepository; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package io.project.database.filter; | |
import java.io.Serializable; | |
import lombok.NonNull; | |
import org.springframework.beans.factory.annotation.Autowired; | |
import org.springframework.data.mongodb.core.MongoOperations; | |
import org.springframework.data.mongodb.repository.support.MongoRepositoryFactoryBean; | |
import org.springframework.data.repository.Repository; | |
import org.springframework.data.repository.core.support.RepositoryFactorySupport; | |
import org.springframework.lang.Nullable; | |
public class SearchFilterRepositoryFactoryBean<T extends Repository<S, ID>, S, ID extends Serializable> extends MongoRepositoryFactoryBean<T, S, ID> { | |
private @Nullable FilterParser parser; | |
public SearchFilterRepositoryFactoryBean(Class<? extends T> repositoryInterface) { | |
super(repositoryInterface); | |
} | |
@Override | |
protected @NonNull RepositoryFactorySupport getFactoryInstance(@NonNull final MongoOperations operations) { | |
return new SearchFilterRepositoryFactory(operations, parser); | |
} | |
@Autowired | |
public void setParser(@Nullable FilterParser parser) { | |
this.parser = parser; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment