使用条件实现搜索过滤器 [英] Implement search filter with conditions

查看:31
本文介绍了使用条件实现搜索过滤器的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想用几个子条件实现搜索功能.我试过这个:

I want to implement search functionality with several sub conditions. I tried this:

    @GetMapping("find")
    public Page<PaymentTransactionsDTO> getAllBySpecification(
            @And({
                    @Spec(path = "name", spec = LikeIgnoreCase.class),
                    @Spec(path = "unique_id", spec = LikeIgnoreCase.class),
                    @Spec(path = "createdAt", params = "from", spec = GreaterThanOrEqual.class),
                    @Spec(path = "createdAt", params = "to", spec = LessThanOrEqual.class)
            }) Specification<PaymentTransactions> specification,
            Pageable pageable
    ) {        
        return transactionService.getAllBySpecification(specification, pageable));       
    }

存储库:

      @Override
      public Page<PaymentTransactions> getAllBySpecification(final Specification<PaymentTransactions> specification, final Pageable pageable) {
          return dao.findAll(specification, pageable);
      }

目前此请求正在运行:

GET /api/transactions/find?unique_id=22&page=0&size=10 

但我还想实现这些额外的搜索条件,不仅发送对 unique_id 的基本搜索:

But also I want to implement these additional search conditions not only sending basic search for unique_id:

start with 
=
end with 
contains

使用https://github.com/tkaczmarzyk/specification-arg-resolver 有没有办法发送额外的子条件?我通常找不到针对此问题的解决方案,发送这些值的最佳做法是什么?

Using https://github.com/tkaczmarzyk/specification-arg-resolver is there some way to send additional sub conditions? I can't find a solution for this issue in general what are the best practices to send these values?

推荐答案

如果您想创建非常特殊的过滤器,我相信您应该从发明搜索界面开始.例如像这样:

If you want to create your very special filter I believe you should start with invention of your search interface. For example like that:

GET /models?name=eq(john smith)&createdAt=between(2019-01-01,2019-01-31)
GET /models?name=like(sm)&createdAt=from(2019-01-01)
GET /models?name=sw(john)&createdAt=to(2019-01-31)

之后,您将能够尝试实施它.

After that, you will be able to try to implement it.

IMO 解决此类任务的最佳方法是使用 Spring Data JPA 规范(和 JPA Criteria API).例如:

IMO the best way to solve such a task is to use Spring Data JPA Specifications (and JPA Criteria API). For example:

1) 让我们创建一个 Filter 类来为我们的实体 Model 实现Specification:

1) Let's create a Filter class that implements Specification for our entity Model:

@Value
public class ModelFilter implements Specification<Model> {

    private String name;
    private String createdAt;

    @Override
    public Predicate toPredicate(Root<Model> root, CriteriaQuery<?> query, CriteriaBuilder builder) {

        List<Predicate> predicates = new ArrayList<>();

        // Prepare predicates and fill the list with them...

        return builder.and(predicates.toArray(new Predicate[0]));
    }
}

2) 然后创建一个控制器方法:

2) Then create a controller method:

@GetMapping
public List<Model> getAllByFilter(ModelFilter filter) {
    return repo.findAll(filter); 
}

剩下要做的就是准备我们的谓词 ))

All that's left to do is prepare our predicates ))

为此,我们可以先创建一个方便的Predicate Builder"界面:

To make this, we can create a handy 'Predicate Builder' interface first:

@FunctionalInterface
interface PredicateBuilder<T> {

    Optional<Predicate> get(String fieldName, String value, Root<T> root, CriteriaBuilder builder);

    static Matcher getMatcher(String op, String value) {
        return getMatcher(op, value, "(.+)");
    }

    static Matcher getMatcher(String op, String value, String pattern) {
        return Pattern.compile(op + "\(" + pattern + "\)").matcher(value);
    }
}

然后尝试制作我们的谓词:

Then try to make our predicates:

相等

PredicateBuilder<Model> eq = (fieldName, value, root, cb) -> {
    Matcher m = getMatcher("eq", value);
    if (m.matches()) {
        return Optional.of(cb.equal(cb.upper(root.get(fieldName)), m.group(1).toUpperCase()));
    } else {
        return Optional.empty();
    }
};

喜欢

PredicateBuilder<Model> like = (fn, value, root, cb) -> {
    Matcher m = getMatcher("like", value);
    if (m.matches()) {
        return Optional.of(cb.like(cb.upper(root.get(fn)), "%" + m.group(1).toUpperCase() + "%"));
    } else {
        return Optional.empty();
    }
};

开始于

PredicateBuilder<Model> sw = (fn, value, root, cb) -> {
    Matcher m = getMatcher("sw", value);
    if (m.matches()) {
        return Optional.of(cb.like(cb.upper(root.get(fn)), m.group(1).toUpperCase() + "%"));
    } else {
        return Optional.empty();
    }
};

之间

PredicateBuilder<Model> between = (fn, value, root, cb) -> {
    Matcher m = getMatcher("between", value, "(.+)\s*,\s*(.+)");
    if (m.matches()) {
        LocalDate from = LocalDate.parse(m.group(1));
        LocalDate to = LocalDate.parse(m.group(2));
        return Optional.of(cb.between(root.get(fn), from, to));
    } else {
        return Optional.empty();
    }
};

来自

PredicateBuilder<Model> from = (fn, value, root, cb) -> {
    Matcher m = getMatcher("from", value);
    if (m.matches()) {
        LocalDate from = LocalDate.parse(m.group(1));
        return Optional.of(cb.greaterThanOrEqualTo(root.get(fn), from));
    } else {
        return Optional.empty();
    }
};

PredicateBuilder<Model> to = (fn, value, root, cb) -> {
    Matcher m = getMatcher("to", value);
    if (m.matches()) {
        LocalDate to = LocalDate.parse(m.group(1));
        return Optional.of(cb.lessThanOrEqualTo(root.get(fn), to));
    } else {
        return Optional.empty();
    }
};

剩下的只是完成Filter类:

@Value
public class ModelFilter implements Specification<Model> {

    private String name;
    private String createdAt;

    PredicateBuilder<Model> eq = ... ;
    PredicateBuilder<Model> like = ... ;
    PredicateBuilder<Model> sw = ... ;
    PredicateBuilder<Model> between = ... ;
    PredicateBuilder<Model> from = ... ;
    PredicateBuilder<Model> to = ... ;

    @Override
    public Predicate toPredicate(Root<Model> root, CriteriaQuery<?> query, CriteriaBuilder builder) {

        List<Predicate> predicates = new ArrayList<>();

        if (name != null) {
            eq.get("name", name, root, builder).ifPresent(predicates::add);
            like.get("name", name, root, builder).ifPresent(predicates::add);
            sw.get("name", name, root, builder).ifPresent(predicates::add);
        }

        if (createdAt != null) {
            between.get("createdAt", createdAt, root, builder).ifPresent(predicates::add);
            from.get("createdAt", createdAt, root, builder).ifPresent(predicates::add);
            to.get("createdAt", createdAt, root, builder).ifPresent(predicates::add);
        }

        return builder.and(predicates.toArray(new Predicate[0]));
    }
}

当然,这只是实现的一个例子.您可以创建自己需要的规范和谓词的实现.这里的主要内容是:

Of course it's just one example of implementation. You can create your very own implementation of specifications and predicates you need. The main things here are:

  • 想出你的搜索界面
  • 制定您的过滤器"规范
  • 准备您需要的所有谓词
  • 在您的控制器方法中使用过滤器规范

这篇关于使用条件实现搜索过滤器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆