Elasticsearch集成SpringBoot(三)

接上篇,本文主要介绍ES高亮查询。
Elasticsearch参考官方文档:https://www.elastic.co/guide/cn/elasticsearch/guide/current/highlighting-intro.html
SpringDataElasticsearch:https://docs.spring.io/spring-data/elasticsearch/docs/4.0.0.RC2/reference/html/#preface

高亮查询语法:

高亮查询语法.png

from+size高亮查询

  • 创建@EsHighlight注解,用于标记需要查询高亮字段
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface EsHighlight {

    /**
     * filed name
     */
    String name() default "";

    /**
     * The size of a fragment in characters
     * @return
     */
    int fragmentSize() default HighlightBuilder.DEFAULT_FRAGMENT_CHAR_SIZE;

    /**
     * The (maximum) number of fragments
     * @return
     */
    int numberOfFragments() default  HighlightBuilder.DEFAULT_NUMBER_OF_FRAGMENTS;

    /**
     * The offset from the start of the fragment to the start of the highlight
     * @return
     */
    int fragmentOffset() default -1;
}
  • 创建@EsHighlightStyled注解用于写入高亮样式
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface EsHighlightStyled {

    /**
     * the default opening tag
     * @return
     */
    String[] preTags() default "<em>";


    /**
     * the default closing tag
     * @return
     */
    String[] postTags() default "</em>";

}
  • 拓展EsQueryParse.convertNativeSearchQuery(obj)方法

/**
 * 条件构造器转换类
 * QueryBuilder.matchAllQuery(); 匹配所有
 * QueryBuilder.ermQuery精准匹配(); 大小写敏感且不支持
 * QueryBuilder.matchPhraseQuery(); 对中文精确匹配
 * QueryBuilder.matchQuery();单个匹配, field不支持通配符, 前缀具高级特性
 * QueryBuilder.multiMatchQuery("text", "field1", "field2"..); 匹配多个字段
 *
 *参考ES官方文档:https://www.elastic.co/guide/en/elasticsearch/client/java-rest/current/java-rest-high-query-builders.html
 */
public class EsQueryParse {
    public static <T> Query convertNativeSearchQuery(T t) {
        try {
            NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
            BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
            HighlightBuilder highlightBuilder = new HighlightBuilder();
            highlightBuilder.requireFieldMatch(false);
            Class<?> clazz = t.getClass();
            Field[] fields = clazz.getDeclaredFields();
            for (Field field : fields) {
                Object value = ClassUtils.getPublicMethod(clazz, "get" + captureName(field.getName())).invoke(t);
                //高亮不需要用到val,条件必须放到前面
                if (field.isAnnotationPresent(EsHighlight.class)){
                    EsHighlight esHighlight = field.getAnnotation(EsHighlight.class);
                    String filedName = StringUtils.isBlank(esHighlight.name()) ? field.getName() : esHighlight.name();
                    highlightBuilder.field(filedName,esHighlight.fragmentSize(),esHighlight.numberOfFragments(),esHighlight.fragmentOffset());
                }
                if (value == null) {
                    continue;
                }
                if (field.isAnnotationPresent(EsLike.class)) {
                    WildcardQueryBuilder query = getLikeQuery(field, (String) value);
                    boolQueryBuilder.must(query);
                }
                if (field.isAnnotationPresent(EsEquals.class)) {
                    MatchPhraseQueryBuilder query = getEqualsQuery(field, value);
                    boolQueryBuilder.must(query);
                }
                if (field.isAnnotationPresent(EsRange.class)) {
                    RangeQueryBuilder rangeQueryBuilder = getRangeQuery(field, value);
                    boolQueryBuilder.must(rangeQueryBuilder);
                }
                if (field.isAnnotationPresent(EsIn.class)) {
                    TermsQueryBuilder query = getInQuery(field, (List<?>) value);
                    boolQueryBuilder.must(query);
                }
            }
            if (clazz.isAnnotationPresent(EsHighlightStyled.class)){
                EsHighlightStyled esHighlightStyled = clazz.getAnnotation(EsHighlightStyled.class);
                highlightBuilder.postTags(esHighlightStyled.postTags());
                highlightBuilder.preTags(esHighlightStyled.preTags());
                queryBuilder.withHighlightBuilder(highlightBuilder);
            }
            return queryBuilder.withQuery(boolQueryBuilder).build();
        } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
            e.printStackTrace();
        }
        return Query.findAll();
    }


    public static <T> SearchRequest convertSearchSourceQuery(T t, SearchSourceBuilder searchSourceBuilder){
        SearchRequest request = new SearchRequest();
        try {
            BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
            Class<?> clazz = t.getClass();
            Field[] fields = clazz.getDeclaredFields();
            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, (String) value);
                    boolQueryBuilder.must(query);
                }
                if (field.isAnnotationPresent(EsEquals.class)) {
                    MatchPhraseQueryBuilder query = getEqualsQuery(field, value);
                    boolQueryBuilder.must(query);
                }
                if (field.isAnnotationPresent(EsRange.class)) {
                    RangeQueryBuilder rangeQueryBuilder = getRangeQuery(field, value);
                    boolQueryBuilder.must(rangeQueryBuilder);
                }
                if (field.isAnnotationPresent(EsIn.class)) {
                    TermsQueryBuilder query = getInQuery(field, (List<?>) value);
                    boolQueryBuilder.must(query);
                }
            }
            if (clazz.isAnnotationPresent(Document.class)){
                Document document = clazz.getAnnotation(Document.class);
                request.indices(document.indexName());
            }
            searchSourceBuilder.query(boolQueryBuilder);
        } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
            e.printStackTrace();
        }
        return request;
    }

    /**
     * 一次匹配多个值
     * @param field
     * @param value
     * @return
     */
    private static TermsQueryBuilder getInQuery(Field field, List<?> value) {
        EsIn esIn = field.getAnnotation(EsIn.class);
        String filedName = StringUtils.isBlank(esIn.name()) ? field.getName() : esIn.name();
        return QueryBuilders.termsQuery(filedName, value);
    }

    /**
     * 大小范围查询
     * @param field
     * @param value
     * @return
     */
    private static RangeQueryBuilder getRangeQuery(Field field, Object value) {
        EsRange esRange = field.getAnnotation(EsRange.class);
        String filedName = StringUtils.isBlank(esRange.name()) ? field.getName() : esRange.name();
        RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery(filedName)
                .includeLower(esRange.includeLower())
                .includeUpper(esRange.includeUpper());
        if (esRange.lt()) {
            rangeQueryBuilder.lt(value);
        }
        if (esRange.gt()) {
            rangeQueryBuilder.gt(value);
        }
        return rangeQueryBuilder;
    }

    /**
     * 分词短语匹配:QueryBuilders。matchQuery 会将搜索词分词,再与目标查询字段进行匹配,若分词中的任意一个词与目标字段匹配上,则可查询到。
     * 精准匹配短语: QueryBuilders.matchPhraseQuery()
     * 完全匹配: QueryBuilders.termQuery()
     * @param field
     * @param value
     * @return
     */
    private static MatchPhraseQueryBuilder getEqualsQuery(Field field, Object value) {
        EsEquals esEquals = field.getAnnotation(EsEquals.class);
        String filedName = StringUtils.isBlank(esEquals.name()) ? field.getName() : esEquals.name();
        return QueryBuilders.matchPhraseQuery(filedName, value);
    }

    /**
     * 模糊查询,?匹配单个字符,*匹配多个字符
     * @param field
     * @param likeValue
     * @return
     */
    private static WildcardQueryBuilder getLikeQuery(Field field, String likeValue) {
        EsLike esLike = field.getAnnotation(EsLike.class);
        String filedName = StringUtils.isBlank(esLike.name()) ? field.getName() : esLike.name();
        if (esLike.leftLike()) {
            likeValue = "%" + likeValue;
        }
        if (esLike.rightLike()) {
            likeValue = likeValue + "%";
        }
        return QueryBuilders.wildcardQuery(filedName, likeValue);
    }

    /**
     * 首字母大写
     * @param name
     * @return
     */
    public static String captureName(String name) {
        char[] cs = name.toCharArray();
        cs[0] -= 32;
        return String.valueOf(cs);
    }

}
  • SimpleSearchQueryEngine工具类
    public SearchHits findPage(Object requestPara, Class clazz, Pageable pageable) {
        Query query = EsQueryParse.convertNativeSearchQuery(requestPara);
        //组装分页
        query.setPageable(pageable);
        return elasticsearchRestTemplate.search(query, clazz);
    }
  • service
    @Override
    public Page findElasticsearchOrderVoList(ElasticsearchOrderQueryVo elasticsearchOrderQueryVo) {
        SearchHits<ElasticsearchOrderVo> searchHits =  simpleSearchQueryEngine.findPage(elasticsearchOrderQueryVo,
                ElasticsearchOrderVo.class,
                PageRequest.of(elasticsearchOrderQueryVo.getPage(),elasticsearchOrderQueryVo.getSize(),Sort.by(Sort.Order.desc("orderId"),Sort.Order.desc("createTime"))));
        Page page = new Page(elasticsearchOrderQueryVo.getPage(),elasticsearchOrderQueryVo.getSize(),searchHits.getTotalHits());
        page.setRecords(searchHits.get().collect(Collectors.toList()));
        return page;
    }
  • controller
    @PostMapping("/page")
    public CommonResp findElasticsearchOrderVoList(@RequestBody ElasticsearchOrderQueryVo elasticsearchOrderQueryVo){
        return iomsOrderItemService.findElasticsearchOrderVoList(elasticsearchOrderQueryVo);
    }
  • ElasticsearchOrderQueryVo请求VO写入自定义注解
@EsHighlightStyled(postTags = "</font>",preTags = "<font color='red'>")
@Document(indexName="oms",shards=5,replicas=1,indexStoreType="fs",refreshInterval="-1")
@Data
public class ElasticsearchOrderQueryVo implements Serializable {

    /**
     * 分页
     */
    private Integer page;

    /**
     * 数量
     */
    private Integer size;

    /**
     * orderId
     */
    private Long orderId;

    /**
     * 订单编号
     */
    @EsHighlight
    private String orderSn;

    /**
     * 收货人
     */
    @EsHighlight
    @EsEquals
    private String receiverName;

    /**
     * 省市区
     */
    private String receiverAreaName;

    /**
     * 详细地址
     */
    private String receiverAddress;

    /**
     * 手机
     */
    @EsHighlight
    private String receiverPhone;


    /**
     * 实际支付金额
     */
    @EsRange(lt = true)
    private Long payAmount;


    /**
     * 创建时间
     */
    private Date createTime;

    /**
     * 发票抬头
     */
    @EsEquals
    @EsHighlight
    private String billHeader;


    /**
     * 游标:用全局唯一字段就行,也可以多个,我用orderID+createTime组合定义一个游标
     */
    private List<String> sortValues;
}

  • postman请求:


    form+size高亮查询结构.png
  • 响应JSON
{
    "code": "200",
    "message": "请求成功",
    "body": {
        "records": [
            {
                "id": "ed1e53f5-1ca2-45df-8a50-fdda58abe21b",
                "score": "NaN",
                "sortValues": [
                    5,
                    1654390747874
                ],
                "content": {
                    "id": "ed1e53f5-1ca2-45df-8a50-fdda58abe21b",
                    "orderId": 5,
                    "orderSn": "Order_5",
                    "receiverName": "小明5",
                    "receiverAreaName": "广东省深圳市南山区",
                    "receiverAddress": "四方精创资讯大厦5楼",
                    "receiverPhone": "12012121225",
                    "totalAmount": null,
                    "payAmount": 200,
                    "freightAmount": 8,
                    "integrationAmount": 1,
                    "couponAmount": 100,
                    "createTime": "2022-06-05T00:59:07.874+00:00",
                    "expireTime": null,
                    "integration": null,
                    "memo": null,
                    "status": 4,
                    "memberId": null,
                    "billType": null,
                    "billHeader": "深圳市 南山区 软基5层",
                    "billContent": null,
                    "billReceiverPhone": null,
                    "billReceiverEmail": "12456789@qq.com",
                    "orderType": 1
                },
                "highlightFields": {
                    "receiverName": [
                        "<font color='red'>小</font><font color='red'>明</font>5"
                    ],
                    "billHeader": [
                        "深圳市 南山区 <font color='red'>软</font><font color='red'>基</font><font color='red'>5</font><font color='red'>层</font>"
                    ]
                }
            }
        ],
        "total": 1,
        "size": 50,
        "current": 1,
        "orders": [],
        "optimizeCountSql": true,
        "searchCount": true,
        "countId": null,
        "maxLimit": null,
        "pages": 1
    },
    "ok": true
}

search_after高亮查询

使用ES自带的返回数据是没有带高亮标签,需要代码处理

  • Service
    @Override
    public Page findOrderVoListBySearchAfter(ElasticsearchOrderQueryVo elasticsearchOrderQueryVo) {
        SearchSourceBuilder searchSourceBuilder =  new SearchSourceBuilder();
        List<String> sortValues = elasticsearchOrderQueryVo.getSortValues();
        searchSourceBuilder.from(0); //from不用传值,默认是-1
        searchSourceBuilder.size(elasticsearchOrderQueryVo.getSize());
        searchSourceBuilder.sort("orderId").sort("createTime");//SortOrder.DESC
        if (CollectionUtils.isNotEmpty(sortValues)){
            searchSourceBuilder.searchAfter(sortValues.toArray());
        }
        org.elasticsearch.search.SearchHits searchHits = simpleSearchQueryEngine.findPage(elasticsearchOrderQueryVo,searchSourceBuilder);
        //处理高亮标签
        List<SearchHit> searchHitList = Arrays.stream(searchHits.getHits()).collect(Collectors.toList());
        searchHitList.stream().peek(hit->{
            Map<String, Object> sourceAsMap = hit.getSourceAsMap();
            Map<String, HighlightField> highlightFields = hit.getHighlightFields();
            sourceAsMap.forEach((sourceKey,sourceVal)->{
                HighlightField highlightField = highlightFields.get(sourceKey);
                if (highlightField != null){
                    //拿到的高亮字段覆盖sourceAsMap中的val
                    sourceAsMap.put(sourceKey,Arrays.stream(highlightField.getFragments()).map(map->map.toString()).collect(Collectors.joining("")));
                }
            });
        }).collect(Collectors.toList());
        Page page = new Page(0,elasticsearchOrderQueryVo.getSize(),searchHits.getTotalHits().value);
        page.setRecords(searchHitList);
        return page;
    }
  • SimpleSearchQueryEngine类还是一样不用调整
    @Value("${spring.elasticsearch.rest.uris}")
    private List<String> nodes;

    @Override
    public org.elasticsearch.search.SearchHits findPage(Object requestPara, SearchSourceBuilder searchSourceBuilder) {
        String[] hosts = nodes.get(0).split(":");
        RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(new HttpHost(hosts[0],Integer.parseInt(hosts[1]),HttpHost.DEFAULT_SCHEME_NAME)));
        SearchRequest request = EsQueryParse.convertSearchSourceQuery(requestPara,searchSourceBuilder);
        request.source(searchSourceBuilder);
        try {
            SearchResponse response = client.search(request, RequestOptions.DEFAULT);
            return Optional.ofNullable(response).map(SearchResponse::getHits).orElseThrow(()->new IllegalStateException("ES服务器异常"));
        } catch (IOException e) {
            log.error("SimpleSearchQueryEngine.findPage:e{}",e);
            throw new IllegalArgumentException(e.getMessage());
        }
    }
  • 条件构造器工具类

/**
 * 条件构造器转换类
 * QueryBuilder.matchAllQuery(); 匹配所有
 * QueryBuilder.ermQuery精准匹配(); 大小写敏感且不支持
 * QueryBuilder.matchPhraseQuery(); 对中文精确匹配
 * QueryBuilder.matchQuery();单个匹配, field不支持通配符, 前缀具高级特性
 * QueryBuilder.multiMatchQuery("text", "field1", "field2"..); 匹配多个字段
 *
 *参考ES官方文档:https://www.elastic.co/guide/en/elasticsearch/client/java-rest/current/java-rest-high-query-builders.html
 */
public class EsQueryParse {

    /**
     * form+size条件构造器
     * @param t
     * @param <T>
     * @return
     */
    public static <T> Query convertNativeSearchQuery(T t) {
        try {
            NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
            BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
            HighlightBuilder highlightBuilder = new HighlightBuilder();
            highlightBuilder.requireFieldMatch(false);
            Class<?> clazz = t.getClass();
            Field[] fields = clazz.getDeclaredFields();
            for (Field field : fields) {
                Object value = ClassUtils.getPublicMethod(clazz, "get" + captureName(field.getName())).invoke(t);
                //高亮不需要用到val,条件必须放到前面
                if (field.isAnnotationPresent(EsHighlight.class)){
                    EsHighlight esHighlight = field.getAnnotation(EsHighlight.class);
                    String filedName = StringUtils.isBlank(esHighlight.name()) ? field.getName() : esHighlight.name();
                    highlightBuilder.field(filedName,esHighlight.fragmentSize(),esHighlight.numberOfFragments(),esHighlight.fragmentOffset());
                }
                if (value == null) {
                    continue;
                }
                if (field.isAnnotationPresent(EsLike.class)) {
                    WildcardQueryBuilder query = getLikeQuery(field, (String) value);
                    boolQueryBuilder.must(query);
                }
                if (field.isAnnotationPresent(EsEquals.class)) {
                    MatchPhraseQueryBuilder query = getEqualsQuery(field, value);
                    boolQueryBuilder.must(query);
                }
                if (field.isAnnotationPresent(EsRange.class)) {
                    RangeQueryBuilder rangeQueryBuilder = getRangeQuery(field, value);
                    boolQueryBuilder.must(rangeQueryBuilder);
                }
                if (field.isAnnotationPresent(EsIn.class)) {
                    TermsQueryBuilder query = getInQuery(field, (List<?>) value);
                    boolQueryBuilder.must(query);
                }
            }
            if (clazz.isAnnotationPresent(EsHighlightStyled.class)){
                EsHighlightStyled esHighlightStyled = clazz.getAnnotation(EsHighlightStyled.class);
                highlightBuilder.postTags(esHighlightStyled.postTags());
                highlightBuilder.preTags(esHighlightStyled.preTags());
                queryBuilder.withHighlightBuilder(highlightBuilder);
            }
            return queryBuilder.withQuery(boolQueryBuilder).build();
        } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
            e.printStackTrace();
        }
        return Query.findAll();
    }


    /**
     * search_after条件构造器
     * @param t
     * @param searchSourceBuilder
     * @param <T>
     * @return
     */
    public static <T> SearchRequest convertSearchSourceQuery(T t, SearchSourceBuilder searchSourceBuilder){
        SearchRequest request = new SearchRequest();
        try {
            BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
            HighlightBuilder highlightBuilder = new HighlightBuilder();
            Class<?> clazz = t.getClass();
            Field[] fields = clazz.getDeclaredFields();
            for (Field field : fields) {
                Object value = ClassUtils.getPublicMethod(clazz, "get" + captureName(field.getName())).invoke(t);
                //高亮不需要用到val,条件必须放到前面
                if (field.isAnnotationPresent(EsHighlight.class)){
                    EsHighlight esHighlight = field.getAnnotation(EsHighlight.class);
                    String filedName = StringUtils.isBlank(esHighlight.name()) ? field.getName() : esHighlight.name();
                    highlightBuilder.field(filedName,esHighlight.fragmentSize(),esHighlight.numberOfFragments(),esHighlight.fragmentOffset());
                }
                if (value == null) {
                    continue;
                }
                if (field.isAnnotationPresent(EsLike.class)) {
                    WildcardQueryBuilder query = getLikeQuery(field, (String) value);
                    boolQueryBuilder.must(query);
                }
                if (field.isAnnotationPresent(EsEquals.class)) {
                    MatchPhraseQueryBuilder query = getEqualsQuery(field, value);
                    boolQueryBuilder.must(query);
                }
                if (field.isAnnotationPresent(EsRange.class)) {
                    RangeQueryBuilder rangeQueryBuilder = getRangeQuery(field, value);
                    boolQueryBuilder.must(rangeQueryBuilder);
                }
                if (field.isAnnotationPresent(EsIn.class)) {
                    TermsQueryBuilder query = getInQuery(field, (List<?>) value);
                    boolQueryBuilder.must(query);
                }
            }
            if (clazz.isAnnotationPresent(Document.class)){
                Document document = clazz.getAnnotation(Document.class);
                request.indices(document.indexName());
            }
            if (clazz.isAnnotationPresent(EsHighlightStyled.class)){
                EsHighlightStyled esHighlightStyled = clazz.getAnnotation(EsHighlightStyled.class);
                highlightBuilder.postTags(esHighlightStyled.postTags());
                highlightBuilder.preTags(esHighlightStyled.preTags());
                searchSourceBuilder.highlighter(highlightBuilder);
            }
            searchSourceBuilder.query(boolQueryBuilder);
        } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
            e.printStackTrace();
        }
        return request;
    }

    /**
     * 一次匹配多个值
     * @param field
     * @param value
     * @return
     */
    private static TermsQueryBuilder getInQuery(Field field, List<?> value) {
        EsIn esIn = field.getAnnotation(EsIn.class);
        String filedName = StringUtils.isBlank(esIn.name()) ? field.getName() : esIn.name();
        return QueryBuilders.termsQuery(filedName, value);
    }

    /**
     * 大小范围查询
     * @param field
     * @param value
     * @return
     */
    private static RangeQueryBuilder getRangeQuery(Field field, Object value) {
        EsRange esRange = field.getAnnotation(EsRange.class);
        String filedName = StringUtils.isBlank(esRange.name()) ? field.getName() : esRange.name();
        RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery(filedName)
                .includeLower(esRange.includeLower())
                .includeUpper(esRange.includeUpper());
        if (esRange.lt()) {
            rangeQueryBuilder.lt(value);
        }
        if (esRange.gt()) {
            rangeQueryBuilder.gt(value);
        }
        return rangeQueryBuilder;
    }

    /**
     * 分词短语匹配:QueryBuilders。matchQuery 会将搜索词分词,再与目标查询字段进行匹配,若分词中的任意一个词与目标字段匹配上,则可查询到。
     * 精准匹配短语: QueryBuilders.matchPhraseQuery()
     * 完全匹配: QueryBuilders.termQuery()
     * @param field
     * @param value
     * @return
     */
    private static MatchPhraseQueryBuilder getEqualsQuery(Field field, Object value) {
        EsEquals esEquals = field.getAnnotation(EsEquals.class);
        String filedName = StringUtils.isBlank(esEquals.name()) ? field.getName() : esEquals.name();
        return QueryBuilders.matchPhraseQuery(filedName, value);
    }

    /**
     * 模糊查询,?匹配单个字符,*匹配多个字符
     * @param field
     * @param likeValue
     * @return
     */
    private static WildcardQueryBuilder getLikeQuery(Field field, String likeValue) {
        EsLike esLike = field.getAnnotation(EsLike.class);
        String filedName = StringUtils.isBlank(esLike.name()) ? field.getName() : esLike.name();
        if (esLike.leftLike()) {
            likeValue = "%" + likeValue;
        }
        if (esLike.rightLike()) {
            likeValue = likeValue + "%";
        }
        return QueryBuilders.wildcardQuery(filedName, likeValue);
    }

    /**
     * 首字母大写
     * @param name
     * @return
     */
    public static String captureName(String name) {
        char[] cs = name.toCharArray();
        cs[0] -= 32;
        return String.valueOf(cs);
    }

}
  • Controller
    @PostMapping("/search/after")
    public CommonResp findOrderVoListBySearchAfter(@RequestBody ElasticsearchOrderQueryVo elasticsearchOrderQueryVo){
        return iomsOrderItemService.findOrderVoListBySearchAfter(elasticsearchOrderQueryVo);
    }
  • ElasticsearchOrderQueryVo请求VO

@EsHighlightStyled(postTags = "</font>",preTags = "<font color='red'>")
@Document(indexName="oms",shards=5,replicas=1,indexStoreType="fs",refreshInterval="-1")
@Data
public class ElasticsearchOrderQueryVo implements Serializable {

    /**
     * 分页
     */
    private Integer page;

    /**
     * 数量
     */
    private Integer size;

    /**
     * orderId
     */
    private Long orderId;

    /**
     * 订单编号
     */
    @EsHighlight
    private String orderSn;

    /**
     * 收货人
     */
    @EsHighlight
    @EsEquals
    private String receiverName;

    /**
     * 省市区
     */
    private String receiverAreaName;

    /**
     * 详细地址
     */
    private String receiverAddress;

    /**
     * 手机
     */
    @EsHighlight
    private String receiverPhone;


    /**
     * 实际支付金额
     */
    @EsRange(lt = true)
    private Long payAmount;


    /**
     * 创建时间
     */
    private Date createTime;

    /**
     * 发票抬头
     */
    @EsEquals
    @EsHighlight
    private String billHeader;


    /**
     * 游标:用全局唯一字段就行,也可以多个,我用orderID+createTime组合定义一个游标
     */
    private List<String> sortValues;
}
  • 配置文件
spring:
  main:
    allow-bean-definition-overriding: true
  flyway:
    locations: classpath:db/oms
  elasticsearch:
    rest:
      uris: 127.0.0.1:9200
  data:
    elasticsearch: #ElasticsearchProperties
      cluster-name: elasticsearch #默认即为elasticsearch
      cluster-nodes: 127.0.0.1:9300 #配置es节点信息,逗号分隔,如果没有指定,则启动ClientNode
  • postman请求


    search_after高亮查询响应结果.png
  • 响应JSON
{
    "code": "200",
    "message": "请求成功",
    "body": {
        "records": [
            {
                "score": "NaN",
                "id": "754ee3cb-80b1-4c81-9f5e-077171878a16",
                "type": "_doc",
                "nestedIdentity": null,
                "version": -1,
                "seqNo": -2,
                "primaryTerm": 0,
                "fields": {},
                "highlightFields": {
                    "receiverName": {
                        "name": "receiverName",
                        "fragments": [
                            {
                                "fragment": true
                            }
                        ],
                        "fragment": true
                    },
                    "billHeader": {
                        "name": "billHeader",
                        "fragments": [
                            {
                                "fragment": true
                            }
                        ],
                        "fragment": true
                    }
                },
                "sortValues": [
                    1,
                    1654045147873
                ],
                "matchedQueries": [],
                "explanation": null,
                "shard": null,
                "index": "oms",
                "clusterAlias": null,
                "sourceAsMap": {
                    "orderType": 1,
                    "freightAmount": 8,
                    "orderId": 1,
                    "orderSn": "Order_1",
                    "integrationAmount": 1,
                    "receiverName": "<font color='red'>小</font><font color='red'>明</font>1",
                    "billReceiverEmail": "12456789@qq.com",
                    "receiverAddress": "四方精创资讯大厦1楼",
                    "couponAmount": 100,
                    "receiverPhone": "12012121221",
                    "payAmount": 200,
                    "createTime": 1654045147873,
                    "receiverAreaName": "广东省深圳市南山区",
                    "_class": "com.formssi.mall.order.domain.vo.ElasticsearchOrderVo",
                    "id": "754ee3cb-80b1-4c81-9f5e-077171878a16",
                    "billHeader": "<font color='red'>深</font><font color='red'>圳</font><font color='red'>市</font> 南山区 软基1层",
                    "status": 4
                },
                "innerHits": null,
                "rawSortValues": [],
                "sourceRef": {
                    "fragment": true
                },
                "sourceAsString": "{\"_class\":\"com.formssi.mall.order.domain.vo.ElasticsearchOrderVo\",\"id\":\"754ee3cb-80b1-4c81-9f5e-077171878a16\",\"orderId\":1,\"orderSn\":\"Order_1\",\"receiverName\":\"小明1\",\"receiverAreaName\":\"广东省深圳市南山区\",\"receiverAddress\":\"四方精创资讯大厦1楼\",\"receiverPhone\":\"12012121221\",\"payAmount\":200,\"freightAmount\":8,\"integrationAmount\":1,\"couponAmount\":100,\"createTime\":1654045147873,\"status\":4,\"billHeader\":\"深圳市 南山区 软基1层\",\"billReceiverEmail\":\"12456789@qq.com\",\"orderType\":1}",
                "fragment": false
            },
            {
                "score": "NaN",
                "id": "da923359-3e2b-44be-989a-66daf14a6c26",
                "type": "_doc",
                "nestedIdentity": null,
                "version": -1,
                "seqNo": -2,
                "primaryTerm": 0,
                "fields": {},
                "highlightFields": {
                    "receiverName": {
                        "name": "receiverName",
                        "fragments": [
                            {
                                "fragment": true
                            }
                        ],
                        "fragment": true
                    },
                    "billHeader": {
                        "name": "billHeader",
                        "fragments": [
                            {
                                "fragment": true
                            }
                        ],
                        "fragment": true
                    }
                },
                "sortValues": [
                    2,
                    1654131547874
                ],
                "matchedQueries": [],
                "explanation": null,
                "shard": null,
                "index": "oms",
                "clusterAlias": null,
                "sourceAsMap": {
                    "orderType": 1,
                    "freightAmount": 8,
                    "orderId": 2,
                    "orderSn": "Order_2",
                    "integrationAmount": 1,
                    "receiverName": "<font color='red'>小</font><font color='red'>明</font>2",
                    "billReceiverEmail": "12456789@qq.com",
                    "receiverAddress": "四方精创资讯大厦2楼",
                    "couponAmount": 100,
                    "receiverPhone": "12012121222",
                    "payAmount": 200,
                    "createTime": 1654131547874,
                    "receiverAreaName": "广东省深圳市南山区",
                    "_class": "com.formssi.mall.order.domain.vo.ElasticsearchOrderVo",
                    "id": "da923359-3e2b-44be-989a-66daf14a6c26",
                    "billHeader": "<font color='red'>深</font><font color='red'>圳</font><font color='red'>市</font> 南山区 软基2层",
                    "status": 4
                },
                "innerHits": null,
                "rawSortValues": [],
                "sourceRef": {
                    "fragment": true
                },
                "sourceAsString": "{\"_class\":\"com.formssi.mall.order.domain.vo.ElasticsearchOrderVo\",\"id\":\"da923359-3e2b-44be-989a-66daf14a6c26\",\"orderId\":2,\"orderSn\":\"Order_2\",\"receiverName\":\"小明2\",\"receiverAreaName\":\"广东省深圳市南山区\",\"receiverAddress\":\"四方精创资讯大厦2楼\",\"receiverPhone\":\"12012121222\",\"payAmount\":200,\"freightAmount\":8,\"integrationAmount\":1,\"couponAmount\":100,\"createTime\":1654131547874,\"status\":4,\"billHeader\":\"深圳市 南山区 软基2层\",\"billReceiverEmail\":\"12456789@qq.com\",\"orderType\":1}",
                "fragment": false
            },
            {
                "score": "NaN",
                "id": "6eccde33-4b3d-44a6-97aa-73438fbadb47",
                "type": "_doc",
                "nestedIdentity": null,
                "version": -1,
                "seqNo": -2,
                "primaryTerm": 0,
                "fields": {},
                "highlightFields": {
                    "receiverName": {
                        "name": "receiverName",
                        "fragments": [
                            {
                                "fragment": true
                            }
                        ],
                        "fragment": true
                    },
                    "billHeader": {
                        "name": "billHeader",
                        "fragments": [
                            {
                                "fragment": true
                            }
                        ],
                        "fragment": true
                    }
                },
                "sortValues": [
                    3,
                    1654217947874
                ],
                "matchedQueries": [],
                "explanation": null,
                "shard": null,
                "index": "oms",
                "clusterAlias": null,
                "sourceAsMap": {
                    "orderType": 1,
                    "freightAmount": 8,
                    "orderId": 3,
                    "orderSn": "Order_3",
                    "integrationAmount": 1,
                    "receiverName": "<font color='red'>小</font><font color='red'>明</font>3",
                    "billReceiverEmail": "12456789@qq.com",
                    "receiverAddress": "四方精创资讯大厦3楼",
                    "couponAmount": 100,
                    "receiverPhone": "12012121223",
                    "payAmount": 200,
                    "createTime": 1654217947874,
                    "receiverAreaName": "广东省深圳市南山区",
                    "_class": "com.formssi.mall.order.domain.vo.ElasticsearchOrderVo",
                    "id": "6eccde33-4b3d-44a6-97aa-73438fbadb47",
                    "billHeader": "<font color='red'>深</font><font color='red'>圳</font><font color='red'>市</font> 南山区 软基3层",
                    "status": 4
                },
                "innerHits": null,
                "rawSortValues": [],
                "sourceRef": {
                    "fragment": true
                },
                "sourceAsString": "{\"_class\":\"com.formssi.mall.order.domain.vo.ElasticsearchOrderVo\",\"id\":\"6eccde33-4b3d-44a6-97aa-73438fbadb47\",\"orderId\":3,\"orderSn\":\"Order_3\",\"receiverName\":\"小明3\",\"receiverAreaName\":\"广东省深圳市南山区\",\"receiverAddress\":\"四方精创资讯大厦3楼\",\"receiverPhone\":\"12012121223\",\"payAmount\":200,\"freightAmount\":8,\"integrationAmount\":1,\"couponAmount\":100,\"createTime\":1654217947874,\"status\":4,\"billHeader\":\"深圳市 南山区 软基3层\",\"billReceiverEmail\":\"12456789@qq.com\",\"orderType\":1}",
                "fragment": false
            }
        ],
        "total": 5,
        "size": 3,
        "current": 1,
        "orders": [],
        "optimizeCountSql": true,
        "searchCount": true,
        "countId": null,
        "maxLimit": null,
        "pages": 2
    },
    "ok": true
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容