ElasticSearch查询语句工具类

376 阅读3分钟

对日常且反复使用的功能进行封装,会大大提高我们的开发效率

Es常规查询注解

等于查询

@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface EsEquals {
    /**
     * filed name
     */
    String name() default "";
}

不等于查询

@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface EsNotNull {
    /**
     * filed name
     */
    String name() default "";
}

包含查询

@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface EsIn {
    /**
     * filed name
     */
    String name() default "";
}

模糊匹配查询

@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface EsLike {
    /**
     * filed name
     */
    String name() default "";

    boolean leftLike() default false;

    boolean rightLike() default false;
}

范围查询

@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface EsRange {
    /**
     * filed name
     */
    String name() default "";

    /**
     * 大于
     */
    boolean lt() default false;

    /**
     * 小于
     */
    boolean gt() default false;

    /**
     * 包含上界
     */
    boolean includeUpper() default false;

    /**
     * 包含下界
     */
    boolean includeLower() default false;

}

Nested查询

@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface EsNested {
    /**
     * filed name
     */
    String name() default "";
}

查询注解使用样例

@Data
@Accessors(chain = true)
public class EsDocBeanReq extends EsPageable {
    @EsEquals(name = "_id")
    private String id;

    @EsEquals
    private String firstCode;

    @EsEquals
    private String secordCode;

    @EsLike(leftLike = true,rightLike = true)
    private String content;

    @EsIn(name = "type")
    private List<Integer> typeList;

    @EsRange(lt = true, name = "type")
    private Integer typeRange;
}

ES工具类调用

public class EsQueryParse {
    private static final Logger logger = LoggerFactory.getLogger(EsQueryParse.class);
    private EsQueryParse() {
    }

    public static <T> Query convert2Query(T t) {
        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
        BoolQueryBuilder boolQueryBuilder = getBoolQueryBuilder(t);
        queryBuilder.withQuery(boolQueryBuilder);
        return queryBuilder.build();
    }

    private static <T> BoolQueryBuilder getBoolQueryBuilder(T t) {
        return getBoolQueryBuilder(t, null);
    }

    private static <T> BoolQueryBuilder getBoolQueryBuilder(T t, String nestedPath) {
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        Class<?> clazz = t.getClass();
        Field[] fields = clazz.getDeclaredFields();
        nestedPath = nestedPath == null ? "" : nestedPath;
        try {
            for (Field field : fields) {
                Object value = ClassUtils.getPublicMethod(clazz, "get" + captureName(field.getName())).invoke(t);
                if (value == null) {
                    continue;
                }
                if (field.isAnnotationPresent(EsLike.class)) {
                    WildcardQueryBuilder query = getLikeQuery(field, value, nestedPath);
                    boolQueryBuilder.must(query);
                }
                if (field.isAnnotationPresent(EsEquals.class)) {
                    MatchQueryBuilder query = getEqualsQuery(field, value, nestedPath);
                    boolQueryBuilder.must(query);
                }
                if (field.isAnnotationPresent(EsRange.class)) {
                    RangeQueryBuilder query = getRangeQuery(field, value, nestedPath);
                    boolQueryBuilder.must(query);
                }
                if (field.isAnnotationPresent(EsIn.class)) {
                    TermsQueryBuilder query = getInQuery(field, (List<?>) value, nestedPath);
                    boolQueryBuilder.must(query);
                }
                if (field.isAnnotationPresent(EsNotNull.class)) {
                    ExistsQueryBuilder query = getNotNullQuery(field, nestedPath);
                    boolQueryBuilder.must(query);
                }
                if (field.isAnnotationPresent(EsNotNullFields.class)) {
                    List<ExistsQueryBuilder> query = getNotNullQuery((List<String>) value, nestedPath);
                    Optional.ofNullable(query).orElse(new ArrayList<>())
                            .forEach(boolQueryBuilder::must);
                }
                if (field.isAnnotationPresent(EsNested.class)) {
                    NestedQueryBuilder query = getNestedQuery(field, value);
                    boolQueryBuilder.must(query);
                }
            }
        } catch (Exception e) {
            logger.info("ES查询解析异常:{}", e.getMessage());
        }
        return boolQueryBuilder;
    }

    private static TermsQueryBuilder getInQuery(Field field, List<?> value, String nestedPath) {
        EsIn esIn = field.getAnnotation(EsIn.class);
        String filedName = getFiledName(field, esIn.name(), nestedPath);
        return QueryBuilders.termsQuery(filedName, value);
    }

    private static RangeQueryBuilder getRangeQuery(Field field, Object value, String nestedPath) {
        EsRange esRange = field.getAnnotation(EsRange.class);
        String filedName = getFiledName(field, esRange.name(), nestedPath);
        RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery(filedName)
                .includeLower(esRange.includeLower())
                .includeUpper(esRange.includeUpper());
        if (esRange.lt()) {
            rangeQueryBuilder.gt(value);
        }
        if (esRange.gt()) {
            rangeQueryBuilder.gt(value);
        }
        return rangeQueryBuilder;
    }

    private static MatchQueryBuilder getEqualsQuery(Field field, Object value, String nestedPath) {
        EsEquals esEquals = field.getAnnotation(EsEquals.class);
        String filedName = getFiledName(field, esEquals.name(), nestedPath);
        return QueryBuilders.matchQuery(filedName, value);
    }

    private static WildcardQueryBuilder getLikeQuery(Field field, Object value, String nestedPath) {
        String likeValue = (String) value;
        EsLike esLike = field.getAnnotation(EsLike.class);
        String filedName = getFiledName(field, esLike.name(), nestedPath);
        if (esLike.leftLike()) {
            likeValue = "*" + likeValue;
        }
        if (esLike.rightLike()) {
            likeValue = likeValue + "*";
        }
        return QueryBuilders.wildcardQuery(filedName, likeValue);
    }

    private static ExistsQueryBuilder getNotNullQuery(Field field, String nestedPath) {
        EsNotNull esNotNull = field.getAnnotation(EsNotNull.class);
        String filedName = getFiledName(field, esNotNull.name(), nestedPath);
        return QueryBuilders.existsQuery(filedName);
    }

    private static List<ExistsQueryBuilder> getNotNullQuery(List<String> value, String nestedPath) {
        if (CollectionUtils.isEmpty(value)) {
            return new ArrayList<>();
        }
        return value.stream()
                .map(item-> getFiledName(item, nestedPath))
                .map(QueryBuilders::existsQuery)
                .collect(Collectors.toList());
    }

    private static NestedQueryBuilder getNestedQuery(Field field, Object object) {
        EsNested esNested = field.getAnnotation(EsNested.class);
        String nestedPath = getFiledName(field, esNested.name(), "");
        QueryBuilder boolQueryBuilder = getBoolQueryBuilder(object, nestedPath);
        return QueryBuilders.nestedQuery(nestedPath, boolQueryBuilder, ScoreMode.None);
    }

    private static String getFiledName(Field field, String name, String nestedPath) {
        String fileName = name;
        if (field != null) {
            fileName = StringUtils.isBlank(name) ? field.getName() : name;
        }
        if (StringUtils.isBlank(nestedPath)) {
            return fileName;
        }
        return nestedPath + "." + fileName;
    }

    private static String getFiledName(String name, String nestedPath) {
        return getFiledName(null, name, nestedPath);
    }

    public static String captureName(String name) {
        char[] cs = name.toCharArray();
        cs[0] -= 32;
        return String.valueOf(cs);
    }
}

ES实现层调用工具类

public class EsDataServiceImpl implements EsDataService {
    @Resource
    private RestClient restClient;
    @Resource
    private ElasticsearchRestTemplate elasticsearchTemplate;

    private static Gson gson = new Gson();

    @Override
    public <T> T save(T t) {
        return elasticsearchTemplate.save(t);
    }

    @Override
    public <T> boolean batchSave(List<T> tList) {
        elasticsearchTemplate.save(tList);
        return true;
    }

    @Override
    public <T> boolean update(T t) {
        return true;
    }

    @Override
    public <T> boolean batchUpdate(List<T> tList) {
        return true;
    }

    @Override
    public <T> boolean delete(Class<T> clazz, String id) {
        Annotation annotation = AnnotationUtils.getAnnotation(clazz, Document.class);
        Document document = (Document) annotation;
        IndexCoordinates index = IndexCoordinates.of(document.indexName());
        elasticsearchTemplate.delete(id, index);
        return true;
    }

    @Override
    public <T> boolean batchDelete(Class<T> clazz, List<String> idList) {
        Annotation annotation = AnnotationUtils.getAnnotation(clazz, Document.class);
        Document document = (Document) annotation;
        IndexCoordinates index = IndexCoordinates.of(document.indexName());
        idList.forEach(id -> elasticsearchTemplate.delete(id, index));
        return true;
    }

    @Override
    public <T> T findById(Class<T> clazz, String id) {
        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
        queryBuilder.withQuery(QueryBuilders.matchQuery("_id", id));
        SearchHit<T> searchHit = elasticsearchTemplate.searchOne(queryBuilder.build(), clazz);
        return searchHit == null ? null : searchHit.getContent();
    }

    @Override
    public <T> T findOne(Class<T> clazz, Query query) {
        SearchHit<T> searchHit = elasticsearchTemplate.searchOne(query, clazz);
        return searchHit == null ? null : searchHit.getContent();
    }

    @Override
    public <T> T findOne(Class<T> clazz, EsPageable esBaseReq) {
        Query query = EsQueryParse.convert2Query(esBaseReq);
        SearchHit<T> searchHit = elasticsearchTemplate.searchOne(query, clazz);
        return searchHit != null ? searchHit.getContent() : null;
    }

    @Override
    public <T> EsResult<List<T>> findAll(Class<T> clazz) {
        SearchHits<T> searchHits = elasticsearchTemplate.search(Query.findAll(), clazz);
        List<T> data = searchHits.get().map(SearchHit::getContent).collect(Collectors.toList());
        return EsResult.of(data, searchHits.getTotalHits());
    }

    @Override
    public <T> EsResult<List<T>> findAll(Class<T> clazz, EsPageable pageable) {
        Query query = new StringQuery(QueryBuilders.boolQuery().toString());
        query.setPageable(EsPageable.getQueryPageable(pageable));
        query.addSort(EsPageable.getQuerySort(pageable));

        SearchHits<T> search = elasticsearchTemplate.search(query, clazz);
        List<T> data = search.get().map(SearchHit::getContent).collect(Collectors.toList());
        long recordCount = search.getTotalHits();
        return EsResult.of(data, recordCount);
    }

    @Override
    public <T> EsResult<List<T>> search(Class<T> clazz, Query query) {
        SearchHits<T> searchHit = elasticsearchTemplate.search(query, clazz);
        List<T> data = searchHit.get().map(SearchHit::getContent).collect(Collectors.toList());
        long recordCount = searchHit.getTotalHits();
        return EsResult.of(data, recordCount);
    }

    @Override
    public <T> EsResult<List<T>> search(Class<T> t, EsPageable request) {
        //组装查询
        Query query = EsQueryParse.convert2Query(request);
        //组装分页
        query.setPageable(EsPageable.getQueryPageable(request));
        //组装排序
        query.addSort(EsPageable.getQuerySort(request));

        SearchHits<T> searchHits = elasticsearchTemplate.search(query, t);
        List<T> data = searchHits.get().map(SearchHit::getContent).collect(Collectors.toList());
        long recordCount = searchHits.getTotalHits();
        return EsResult.of(data, recordCount);
    }

    @Override
    public <T> EsResult<List<T>> dslSearch(Class<T> t, String dsl) {
        StringQuery stringQuery = new StringQuery(dsl);

        SearchHits<T> searchHits = elasticsearchTemplate.search(stringQuery, t);
        List<T> data = searchHits.get().map(SearchHit::getContent).collect(Collectors.toList());
        long recordCount = searchHits.getTotalHits();
        return EsResult.of(data, recordCount);
    }

    @Override
    public <T> T sqlFindOne(Class<T> t, String sql) {
        String queryStr = String.format("{"query":"%s", "fetch_size":1}", sql);
        return restClientSearch(t, queryStr).stream().findFirst().orElse(null);
    }

    @Override
    public <T> List<T> sqlSearch(Class<T> t, String sql) {
        String queryStr = String.format("{"query":"%s"}", sql);
        return restClientSearch(t, queryStr);
    }

    /**
     * 使用restClient 查询数据
     *
     * @param t
     * @param queryStr sql_dsl查询条件
     * @return
     */
    private <T> List<T> restClientSearch(Class<T> t, String queryStr) {
        try {
            Request request = new Request("POST", "/_xpack/sql");
            request.setJsonEntity(queryStr);

            Response response = restClient.performRequest(request);
            String jsonData = EntityUtils.toString(response.getEntity());

            SQLRestResponse sqlRestResponse = gson.fromJson(jsonData, SQLRestResponse.class);
            SQLResult<T> sqlResult = ResultUtil.buildFetchSQLResult(sqlRestResponse, t, (SQLResult<T>) null);

            return sqlResult.getDatas();
        } catch (IOException e) {
            e.printStackTrace();
        }

        return new ArrayList<>();
    }
}

总结

这套工具类经过我多次修改和调试可以完全适配ES的基本查询功能 ,下一期我准备把es的聚合查询工具类补上,希望大家喜欢,有意见也可以提,欢迎指正