import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.json.JSONObject;
import lombok.Data;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @Description: ES 查询工具类
 * @Author: hank.zhang
 * @Date: 2024/10/21 11:24
 */
@Data
public class ESQueryUtil {

    /**
     * 查询的条件 must 查询
     */
    private ESQuery must = new ESQuery();

    /**
     * 排除的条件 must_not
     */
    private ESQuery mustNot = new ESQuery();

    /**
     * 分页 - 查询当前页
     */
    private Long page;

    /**
     * 分页 - 页面大小
     */
    private Long pageSize;

    /**
     * 排序 - 倒叙排序的字段
     */
    private List<String> desc = new ArrayList<>();

    /**
     * 排序 - 正序排序的字段
     */
    private List<String> asc = new ArrayList<>();

    /**
     * 子文档查询 - 查询条件
     */
    private QueryChild childQuery = new QueryChild();

    /**
     * or 的子查询条件
     */
    private ESQuery should = new ESQuery();

    /**
     * 设置分页参数
     *
     * @param page     页码
     * @param pageSize 页面大小
     */
    public void setPageInfo(Integer page, Integer pageSize) {
        this.page = page.longValue();
        this.pageSize = pageSize.longValue();
    }

    /**
     * 构建基础查询
     *
     * @return 查询条件封装
     */
    public JSONObject buildQuery() {

        JSONObject queryBody = new JSONObject();
        // 2、排序
        List<JSONObject> sortList = buildSort();
        if (CollectionUtil.isNotEmpty(sortList)) {
            queryBody.set("sort", sortList);
        }

        // 3、must 查询条件
        JSONObject queryObj = new JSONObject();
        List<JSONObject> must = new ArrayList<>(this.must.buildMust());

        // 3.1、子文档查询
        JSONObject childQuery = getChildQuery().buildChildQuery();
        if (childQuery != null && !childQuery.isEmpty()) {
            must.add(childQuery);
        }

        // 3.2、should查询 - 或查询
        JSONObject should = buildShould();
        if (should != null) {
            must.add(should);
        }

        JSONObject queryArray = new JSONObject();
        if(CollectionUtil.isNotEmpty(must)) {
            queryArray.set("must", must);
        }

        // 4、must not
        List<JSONObject> mustNot = this.mustNot.buildMust();
        if(CollectionUtil.isNotEmpty(mustNot)) {
            queryArray.set("must_not", mustNot);
        }

        queryObj.set("bool", queryArray);
        queryBody.set("query", queryObj);
        return queryBody;

    }

    /**
     * 统计条件查询封装
     *
     * @param entries 查询对象
     * @return 查询对象
     */
    public JSONObject buildQueryCount(JSONObject entries) {
        // 1、分页处理
        entries.remove("sort");
        return entries;
    }

    /**
     * 构建分页查询对象
     *
     * @return 带上分页排序查询对象
     */
    public JSONObject buildQueryPage() {
        JSONObject entries = buildQuery();
        // 1、分页处理
        setPage(entries);
        return entries;
    }

    /**
     * 分页查询构建
     *
     * @param buildQuery 查询对象
     * @return 带上分页排序查询对象
     */
    public JSONObject buildQueryPage(JSONObject buildQuery) {
        // 1、分页处理
        setPage(buildQuery);

        // 2、排序
        List<JSONObject> sortList = buildSort();
        if (CollectionUtil.isNotEmpty(sortList)) {
            buildQuery.set("sort", sortList);
        }
        return buildQuery;
    }

    private JSONObject buildShould() {
        List<JSONObject> buildMust = this.should.buildMust();
        if (CollectionUtil.isEmpty(buildMust)) {
            return null;
        }
        JSONObject boolObj = new JSONObject();

        JSONObject bool = new JSONObject();
        bool.set("minimum_should_match", 1);
        bool.set("should", buildMust);

        boolObj.set("bool", bool);
        return boolObj;
    }


    private List<JSONObject> buildSort() {
        List<JSONObject> sortList = new ArrayList<>();
        if (CollectionUtil.isNotEmpty(this.getDesc())) {
            sortList.addAll(this.getDesc().stream().map(v -> buildSortItem(v, "desc")).collect(Collectors.toList()));
        }
        if (CollectionUtil.isNotEmpty(this.getAsc())) {
            sortList.addAll(this.getAsc().stream().map(v -> buildSortItem(v, "asc")).collect(Collectors.toList()));
        }
        return sortList;
    }

    private JSONObject buildSortItem(String fieldName, String sortType) {
        JSONObject order = new JSONObject();
        order.set("order", sortType);
        JSONObject field = new JSONObject();
        field.set(fieldName, order);
        return field;
    }

    private void setPage(JSONObject queryBody) {
        if (page == null || pageSize == null) {
            return;
        }
        queryBody.set("size", pageSize);
        queryBody.set("from", pageSize * (page - 1));
    }

    @Data
    public static class QueryChild {

        private ESQuery esQuery = new ESQuery();

        private String childType;

        public JSONObject buildChildQuery() {
            if (childType == null || this.esQuery.buildMust().isEmpty()) {
                return null;
            }
            JSONObject object = new JSONObject();
            JSONObject hasChild = new JSONObject();

            if (childType != null) {

                JSONObject must = new JSONObject();
                must.set("must", this.esQuery.buildMust());

                JSONObject bool = new JSONObject();
                bool.set("bool", must);

                hasChild.set("type", this.getChildType());
                hasChild.set("query", bool);
            }
            object.set("has_child", hasChild);
            return object;
        }

    }

    @Data
    public static class ESQuery {

        /**
         * 等值查询 <查询的字段名字, 查询字段对应的值>
         */
        private Map<String, Object> eq = new HashMap<>();

        /**
         * 迷糊查询 <查询的字段名字, 查询字段对应的值> 底层使用 fuzziness 查询
         */
        private Map<String, String> fuzziness = new HashMap<>();

        /**
         * 范围查询
         */
        private List<RangQuery> rangeE = new ArrayList<>();

        /**
         * 某个字段必须存在
         */
        private List<String> exists = new ArrayList<>();

        /**
         * in 查询
         */
        private Map<String, List<?>> in = new HashMap<>();

        /**
         * 迷糊查询 可用通配符 底层使用 es 的 wildcard 查询
         * Wildcard查询是一种基于通配符的查询方式,通配符包括 *(匹配任意字符)和 ?(匹配单个字符)
         */
        private Map<String, String> wildcard = new HashMap<>();

        /**
         * 迷糊查询 完全匹配查询,查询更类似于 mysql的模糊查询,不过这个无需拼接通配符 底层使用 es 的 match_phrase 查询
         */
        private Map<String, String> like = new HashMap<>();

        /**
         * 通过正则表达式进行匹配查询
         */
        private Map<String, String> regexp = new HashMap<>();


        public List<JSONObject> buildMust() {

            List<JSONObject> must = new ArrayList<>();

            // 1、等值查询
            if (CollectionUtil.isNotEmpty(this.eq)) {
                must.addAll(this.eq.entrySet().stream().map(v -> buildTerm(v.getKey(), v.getValue())).collect(Collectors.toList()));
            }

            // 2、模糊查询 - 使用es: match_phrase
            if (CollectionUtil.isNotEmpty(this.like)) {
                must.addAll(buildMatchPhrase());
            }

            // 3、范围查询 - 使用es:range
            if (CollectionUtil.isNotEmpty(this.rangeE)) {
                must.add(buildRange());
            }

            // 4、in 查询 - 使用es:terms
            if (CollectionUtil.isNotEmpty(this.in)) {
                must.addAll(buildIn());
            }

            // 5、exists 某个字段存在 - 使用es: exists
            if (CollectionUtil.isNotEmpty(this.exists)) {
                must.addAll(buildExists());
            }

            // 6、模糊查询 查询 - 使用es: wildcard
            if (CollectionUtil.isNotEmpty(wildcard)) {
                must.addAll(buildWildcard());
            }

            // 7、模糊查询 查询 - 使用es: fuzziness
            if (CollectionUtil.isNotEmpty(fuzziness)) {
                must.addAll(buildFuzziness());
            }

            // 8、正则查询 - 使用es:regexp
            if (CollectionUtil.isNotEmpty(regexp)) {
                must.addAll(buildRegexp());
            }

            return must;
        }

        private List<JSONObject> buildRegexp(){
            return this.regexp.entrySet().stream().map(v -> {
                JSONObject regexp = new JSONObject();
                JSONObject field = new JSONObject();
                field.set(v.getKey(), v.getValue());
                regexp.set("regexp", field);
                return regexp;
            }).collect(Collectors.toList());
        }

        private List<JSONObject> buildMatchPhrase(){
            return this.like.entrySet().stream().map(v -> {
                JSONObject matchPhrase = new JSONObject();
                JSONObject field = new JSONObject();
                field.set(v.getKey(), v.getValue());
                matchPhrase.set("match_phrase", field);
                return matchPhrase;
            }).collect(Collectors.toList());
        }

        private List<JSONObject> buildExists() {
            return this.exists.stream().map(v -> {
                JSONObject exists = new JSONObject();
                JSONObject field = new JSONObject();
                field.set("field", v);
                exists.set("exists", field);
                return exists;
            }).collect(Collectors.toList());
        }

        private List<JSONObject> buildWildcard() {
            return this.wildcard.entrySet().stream().map(v -> {
                JSONObject wildcard = new JSONObject();
                JSONObject wildcardItem = new JSONObject();
                JSONObject item = new JSONObject();
                item.set("value", v.getValue());
                item.set("case_insensitive", true);
                wildcardItem.set(v.getKey() + ".keyword", item);
                wildcard.set("wildcard", wildcardItem);
                return wildcard;
            }).collect(Collectors.toList());
        }

        private List<JSONObject> buildIn() {
            return this.in.entrySet().stream().map(v -> {
                JSONObject terms = new JSONObject();
                JSONObject item = new JSONObject();
                item.set(v.getKey(), v.getValue());
                terms.set("terms", item);
                return terms;
            }).collect(Collectors.toList());
        }

        private JSONObject buildRange() {
            JSONObject rangeObj = new JSONObject();
            JSONObject rangeItems = new JSONObject();
            List<RangQuery> rangeE = this.getRangeE();
            rangeE.forEach(v -> rangeItems.set(v.getFieldName(), v.buildRange()));
            rangeObj.set("range", rangeItems);
            return rangeObj;
        }

        private List<JSONObject> buildFuzziness() {
            return this.like.entrySet().stream().map(v -> buildLikeItem(v.getKey(), v.getValue())).collect(Collectors.toList());
        }

        private JSONObject buildLikeItem(String key, Object val) {
            JSONObject match = new JSONObject();
            JSONObject filedQuery = new JSONObject();

            JSONObject valObj = new JSONObject();
            valObj.set("query", val);
            valObj.set("fuzziness", "AUTO");
            filedQuery.set(key, valObj);

            match.set("match", filedQuery);
            return match;
        }

        private JSONObject buildTerm(String key, Object val) {
            JSONObject must = new JSONObject();
            JSONObject valObj = new JSONObject();
            valObj.set(key, val);
            must.set("term", valObj);
            return must;
        }

    }

    @Data
    public static class RangQuery {

        /**
         * 查询字段名字
         */
        private String fieldName;

        /**
         * 字段查询开始 - 值
         */
        private Object rangStart;

        /**
         * 字段查询结束 - 值
         */
        private Object rangEnd;

        public RangQuery() {
        }

        public RangQuery(String fieldName, Object rangStart, Object rangEnd) {
            this.fieldName = fieldName;
            this.rangStart = rangStart;
            this.rangEnd = rangEnd;
        }

        private JSONObject buildRange() {
            JSONObject rangeItemVal = new JSONObject();
            rangeItemVal.set("gte", this.rangStart);
            rangeItemVal.set("lte", this.rangEnd);
            return rangeItemVal;
        }
    }
}

 

最后修改于 2024-10-29 07:25:50
如果觉得我的文章对你有用,请随意赞赏
扫一扫支付
上一篇