Elasticsearch(三) DSL 搜索


Domain Specific Language

DSL(Domain Specific Language)是 Elasticsearch 提出的基于 json 的搜索方式,在搜索时传入特定的 json 格式的数据来完成不同的搜索需求。

DSL 比 URI 搜索方式功能强大,在项目中建议使用 DSL 方式来完成搜索。

数据准备

为了方便执行搜索,我们创建一些用于搜索的测试数据。使用 postman 来创建

首先创建 xedu_course 索引库。并创建如下映射:

post   http://192.168.116.129:9200/xedu_course/_mapping

映射内容如下:

{
    "properties": {
        "description": {
            "type": "text",
            "analyzer": "ik_max_word",
            "search_analyzer": "ik_smart"
        },
        "name": {
            "type": "text",
            "analyzer": "ik_max_word",
            "search_analyzer": "ik_smart"
        },
		"pic":{
			"type":"text",
			"index":false
		},
        "price": {
            "type": "float"
        },
        "studymodel": {
            "type": "keyword"
        },
        "timestamp": {
            "type": "date",
            "format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis"
        }
    }
}

在索引中添加如下的测试数据:

http://192.168.116.129:9200/xedu_course/_doc/1
{
"name": "Bootstrap开发",
"description": "Bootstrap是由Twitter推出的一个前台页面开发框架,是一个非常流行的开发框架,此框架集成了多种页面效果。此开发框架包含了大量的CSS、JS程序代码,可以帮助开发者(尤其是不擅长页面开发的程序人员)轻松的实现一个不受浏览器限制的精美界面效果。",
"studymodel": "201002",
"price":38.6,
"timestamp":"2018-04-25 19:11:35",
"pic":"group1/M00/00/00/wKhlQFs6RCeAY0pHAAJx5ZjNDEM428.jpg"
}
http://192.168.116.129:9200/xedu_course/_doc/2
{
"name": "java编程基础",
"description": "java语言是世界第一编程语言,在软件开发领域使用人数最多。",
"studymodel": "201001",
"price":68.6,
"timestamp":"2018-03-25 19:11:35",
"pic":"group1/M00/00/00/wKhlQFs6RCeAY0pHAAJx5ZjNDEM428.jpg"
}
http://192.168.116.129:9200/xedu_course/_doc/3
{
"name": "spring开发基础",
"description": "spring 在java领域非常流行,java程序员都在用。",
"studymodel": "201001",
"price":88.6,
"timestamp":"2018-02-24 19:11:35",
"pic":"group1/M00/00/00/wKhlQFs6RCeAY0pHAAJx5ZjNDEM428.jpg"
}

项目准备

我们使用 Elasticsearch(二) RestClient 中创建的项目来进行我们的搜索测试。

重新创建一个新的单元测试类:

@SpringBootTest
@RunWith(SpringRunner.class)
public class TestSearch {
    // 注入对应的es客户端,优先使用高等级客户端
    @Autowired
    RestHighLevelClient restHighLevelClient;
    @Autowired
    RestClient restClient;
}

DSL 搜索

搜索全部记录

首先可以将索引库中所有的文件都查询出来,查询返回的结果说明:

  • took:本次操作花费的时间,单位为毫秒。
  • timed_out:请求是否超时
  • _shards:说明本次操作共搜索了哪些分片
  • hits:搜索命中的记录
  • hits.total : 符合条件的文档总数
  • hits.hits :匹配度较高的前N个文档
  • hits.max_score:文档匹配得分,这里为最高分
  • _score:每个文档都有一个匹配度得分,按照降序排列。
  • _source:显示了文档的原始内容。
    @Test
    public void testSearchAll() throws IOException, ParseException {
        // 搜索请求对象
        SearchRequest searchRequest = new SearchRequest("xedu_course");
        // 搜索源构建对象
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        // 搜索方式
        // matchAllQuery搜索全部
        searchSourceBuilder.query(QueryBuilders.matchAllQuery());
        // 设置源字段过滤,第一个参数表示结果集包括哪些字段,第二个参数表示结果集不包括哪些字段
        searchSourceBuilder.fetchSource(new String[]{"name","studymodel","price","timestamp"},new String[]{});
        // 向搜索请求对象设置搜索源
        searchRequest.source(searchSourceBuilder);
        // 执行搜索
        SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        // 搜索结果
        SearchHits hits = searchResponse.getHits();
        // 匹配到的总记录数
        TotalHits totalHits = hits.getTotalHits();
        // 得到匹配高的文档
        SearchHit[] searchHits = hits.getHits();
        // 日期格式化对象
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        for(SearchHit i : searchHits){
            // 文档的主键
            String id = i.getId();
            // 源文档内容
            Map<String,Object> map = i.getSourceAsMap();
            String name = (String) map.get("name");
            String studymodel = (String) map.get("studymodel");
            Double price = (Double) map.get("price");
            Date timestamp = dateFormat.parse((String) map.get("timestamp"));
            System.out.println(name);
            System.out.println(studymodel);
            System.out.println(price);
            System.out.println(timestamp);
        }
    }

分页搜索全部记录

Elasticsearch 支持分页查询,传入两个参数:from和size。

  • form:表示起始文档的下标,从0开始。
  • size:查询的文档数量。
    @Test
    public void testSearchPage() throws IOException, ParseException {
        // 搜索请求对象
        SearchRequest searchRequest = new SearchRequest("xedu_course");
        // 搜索源构建对象
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        // 当前页码
        int page = 1;
        // 每页显示记录数
        int size = 1;
        // 当前页码数据起始角标
        int from = (page-1)*size;
        // 设置分页起始角标
        searchSourceBuilder.from(from);
        // 设置当前也的显示数据量
        searchSourceBuilder.size(size);
        // 搜索方式
        // matchAllQuery搜索全部
        searchSourceBuilder.query(QueryBuilders.matchAllQuery());
        // 设置源字段过滤,第一个参数表示结果集包括哪些字段,第二个参数表示结果集不包括哪些字段
        searchSourceBuilder.fetchSource(new String[]{"name","studymodel","price","timestamp"},new String[]{});
        // 向搜索请求对象设置搜索源
        searchRequest.source(searchSourceBuilder);
        // 执行搜索
        SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        // 搜索结果
        SearchHits hits = searchResponse.getHits();
        // 匹配到的总记录数
        TotalHits totalHits = hits.getTotalHits();
        // 得到匹配高的文档
        SearchHit[] searchHits = hits.getHits();
        // 日期格式化对象
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        for(SearchHit i : searchHits){
            // 文档的主键
            String id = i.getId();
            // 源文档内容
            Map<String,Object> map = i.getSourceAsMap();
            String name = (String) map.get("name");
            String studymodel = (String) map.get("studymodel");
            Double price = (Double) map.get("price");
            Date timestamp = dateFormat.parse((String) map.get("timestamp"));
            System.out.println(name);
            System.out.println(studymodel);
            System.out.println(price);
            System.out.println(timestamp);
        }
    }

Term Query

Term Query 为精确查询,在搜索时会整体匹配关键字,不再将关键字分词。

    @Test
    public void testTermSearch() throws IOException, ParseException {
        // 搜索请求对象
        SearchRequest searchRequest = new SearchRequest("xedu_course");
        // 搜索源构建对象
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        // 当前页码
        int page = 1;
        // 每页显示记录数
        int size = 1;
        // 当前页码数据起始角标
        int from = (page-1)*size;
        // 设置分页起始角标
        searchSourceBuilder.from(from);
        // 设置当前也的显示数据量
        searchSourceBuilder.size(size);
        // 搜索方式
        // termQuery按照关键字来查询
        searchSourceBuilder.query(QueryBuilders.termQuery("name","spring"));
        // 设置源字段过滤,第一个参数表示结果集包括哪些字段,第二个参数表示结果集不包括哪些字段
        searchSourceBuilder.fetchSource(new String[]{"name","studymodel","price","timestamp"},new String[]{});
        // 向搜索请求对象设置搜索源
        searchRequest.source(searchSourceBuilder);
        // 执行搜索
        SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        // 搜索结果
        SearchHits hits = searchResponse.getHits();
        // 匹配到的总记录数
        TotalHits totalHits = hits.getTotalHits();
        // 得到匹配高的文档
        SearchHit[] searchHits = hits.getHits();
        // 日期格式化对象
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        for(SearchHit i : searchHits){
            // 文档的主键
            String id = i.getId();
            // 源文档内容
            Map<String,Object> map = i.getSourceAsMap();
            String name = (String) map.get("name");
            String studymodel = (String) map.get("studymodel");
            Double price = (Double) map.get("price");
            Date timestamp = dateFormat.parse((String) map.get("timestamp"));
            System.out.println(name);
            System.out.println(studymodel);
            System.out.println(price);
            System.out.println(timestamp);
        }
    }

根据id搜索

Elasticsearch 提供根据多个id值匹配的方法

    @Test
    public void testTermSearchById() throws IOException, ParseException {
        // 搜索请求对象
        SearchRequest searchRequest = new SearchRequest("xedu_course");
        // 搜索源构建对象
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        String[] ids = new String[]{"1","2"};
        // 搜索方式
        // termsQuery按照id查询,注意是termsQuery,中间有个s
        searchSourceBuilder.query(QueryBuilders.termsQuery("_id",ids));
        // 设置源字段过滤,第一个参数表示结果集包括哪些字段,第二个参数表示结果集不包括哪些字段
        searchSourceBuilder.fetchSource(new String[]{"name","studymodel","price","timestamp"},new String[]{});
        // 向搜索请求对象设置搜索源
        searchRequest.source(searchSourceBuilder);
        // 执行搜索
        SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        // 搜索结果
        SearchHits hits = searchResponse.getHits();
        // 匹配到的总记录数
        TotalHits totalHits = hits.getTotalHits();
        // 得到匹配高的文档
        SearchHit[] searchHits = hits.getHits();
        // 日期格式化对象
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        for(SearchHit i : searchHits){
            // 文档的主键
            String id = i.getId();
            // 源文档内容
            Map<String,Object> map = i.getSourceAsMap();
            String name = (String) map.get("name");
            String studymodel = (String) map.get("studymodel");
            Double price = (Double) map.get("price");
            Date timestamp = dateFormat.parse((String) map.get("timestamp"));
            System.out.println(name);
            System.out.println(studymodel);
            System.out.println(price);
            System.out.println(timestamp);
        }
    }

Match Query

Match Query 即全文检索,它的搜索方式是先将搜索字符串分词,再使用各各词条从索引中搜索。

Match Query 与 Term Query 区别是 Match Query 在搜索前先将搜索关键字分词,再拿各各词语去索引中搜索。

    @Test
    public void testmatchQuery() throws IOException, ParseException {
        // 搜索请求对象
        SearchRequest searchRequest = new SearchRequest("xedu_course");
        // 搜索源构建对象
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        String[] ids = new String[]{"1","2"};
        // 搜索方式
        // matchQuery,对文本进行分词搜索
        searchSourceBuilder.query(QueryBuilders.matchQuery("description","Spring开发").minimumShouldMatch("80%"));
        // 设置源字段过滤,第一个参数表示结果集包括哪些字段,第二个参数表示结果集不包括哪些字段
        searchSourceBuilder.fetchSource(new String[]{"name","studymodel","price","timestamp"},new String[]{});
        // 向搜索请求对象设置搜索源
        searchRequest.source(searchSourceBuilder);
        // 执行搜索
        SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        // 搜索结果
        SearchHits hits = searchResponse.getHits();
        // 匹配到的总记录数
        TotalHits totalHits = hits.getTotalHits();
        // 得到匹配高的文档
        SearchHit[] searchHits = hits.getHits();
        // 日期格式化对象
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        for(SearchHit i : searchHits){
            // 文档的主键
            String id = i.getId();
            // 源文档内容
            Map<String,Object> map = i.getSourceAsMap();
            String name = (String) map.get("name");
            String studymodel = (String) map.get("studymodel");
            Double price = (Double) map.get("price");
            Date timestamp = dateFormat.parse((String) map.get("timestamp"));
            System.out.println(name);
            System.out.println(studymodel);
            System.out.println(price);
            System.out.println(timestamp);
        }
    }

Multi Query

MultiMatchQuery 可以结合多种查询搜索条件:

    @Test
    public void testMultiMatchQuery() throws IOException, ParseException {
        // 搜索请求对象
        SearchRequest searchRequest = new SearchRequest("xedu_course");
        // 搜索源构建对象
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        String[] ids = new String[]{"1","2"};
        // 搜索方式
        // MultiMatchQuery,多个条件匹配
        searchSourceBuilder.query(QueryBuilders.multiMatchQuery("spring框架","name","description").
                field("name",10).
                minimumShouldMatch("50%"));
        // 设置源字段过滤,第一个参数表示结果集包括哪些字段,第二个参数表示结果集不包括哪些字段
        searchSourceBuilder.fetchSource(new String[]{"name","studymodel","price","timestamp"},new String[]{});
        // 向搜索请求对象设置搜索源
        searchRequest.source(searchSourceBuilder);
        // 执行搜索
        SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        // 搜索结果
        SearchHits hits = searchResponse.getHits();
        // 匹配到的总记录数
        TotalHits totalHits = hits.getTotalHits();
        // 得到匹配高的文档
        SearchHit[] searchHits = hits.getHits();
        // 日期格式化对象
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        for(SearchHit i : searchHits){
            // 文档的主键
            String id = i.getId();
            // 源文档内容
            Map<String,Object> map = i.getSourceAsMap();
            String name = (String) map.get("name");
            String studymodel = (String) map.get("studymodel");
            Double price = (Double) map.get("price");
            Date timestamp = dateFormat.parse((String) map.get("timestamp"));
            System.out.println(name);
            System.out.println(studymodel);
            System.out.println(price);
            System.out.println(timestamp);
        }
    }

布尔查询

布尔查询对应于 Lucene 的 BooleanQuery查询,实现将多个查询组合起来。

三个参数:

  • must:文档必须匹配must所包括的查询条件,相当于 “AND”
  • should:文档应该匹配should所包括的查询条件其中的一个或多个,相当于 “OR”
  • must_not:文档不能匹配must_not所包括的该查询条件,相当于“NOT”
    @Test
    public void testBooleanQuery() throws IOException, ParseException {
        // 搜索请求对象
        SearchRequest searchRequest = new SearchRequest("xedu_course");
        // 搜索源构建对象
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        String[] ids = new String[]{"1","2"};
        // multiBuilder查询条件
        MultiMatchQueryBuilder multiBuilder = QueryBuilders.multiMatchQuery("spring框架", "name", "description").
                field("name", 10).
                minimumShouldMatch("50%");
        // term查询条件
        TermQueryBuilder termBuilder = QueryBuilders.termQuery("studymodel", "201001");
        // 布尔查询条件
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        boolQueryBuilder.must(multiBuilder);
        boolQueryBuilder.must(termBuilder);
        // 搜索方式
        // boolQuery,and 查询
        searchSourceBuilder.query(boolQueryBuilder);
        // 设置源字段过滤,第一个参数表示结果集包括哪些字段,第二个参数表示结果集不包括哪些字段
        searchSourceBuilder.fetchSource(new String[]{"name","studymodel","price","timestamp"},new String[]{});
        // 向搜索请求对象设置搜索源
        searchRequest.source(searchSourceBuilder);
        // 执行搜索
        SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        // 搜索结果
        SearchHits hits = searchResponse.getHits();
        // 匹配到的总记录数
        TotalHits totalHits = hits.getTotalHits();
        // 得到匹配高的文档
        SearchHit[] searchHits = hits.getHits();
        // 日期格式化对象
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        for(SearchHit i : searchHits){
            // 文档的主键
            String id = i.getId();
            // 源文档内容
            Map<String,Object> map = i.getSourceAsMap();
            String name = (String) map.get("name");
            String studymodel = (String) map.get("studymodel");
            Double price = (Double) map.get("price");
            Date timestamp = dateFormat.parse((String) map.get("timestamp"));
            System.out.println(name);
            System.out.println(studymodel);
            System.out.println(price);
            System.out.println(timestamp);
        }
    }

过滤器

过虑是针对搜索的结果进行过虑,过虑器主要判断的是文档是否匹配,不去计算和判断文档的匹配度得分,所以过虑器性能比查询要高,且方便缓存,推荐尽量使用过虑器去实现查询或者过虑器和查询共同使用。

过虑器在布尔查询中使用,下边是在搜索结果的基础上进行过虑:

    @Test
    public void testFilerQuery() throws IOException, ParseException {
        // 搜索请求对象
        SearchRequest searchRequest = new SearchRequest("xedu_course");
        // 搜索源构建对象
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        String[] ids = new String[]{"1","2"};
        // multiBuilder查询条件
        MultiMatchQueryBuilder multiBuilder = QueryBuilders.multiMatchQuery("spring框架", "name", "description").
                field("name", 10).
                minimumShouldMatch("50%");
        // 布尔查询条件
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        boolQueryBuilder.must(multiBuilder);
        // 设置查询过滤器
        boolQueryBuilder.filter(QueryBuilders.termQuery("studymodel","201001"));
        boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").lte(100).gte(90));
        // 搜索方式
        // boolQuery,and 查询
        searchSourceBuilder.query(boolQueryBuilder);
        // 设置源字段过滤,第一个参数表示结果集包括哪些字段,第二个参数表示结果集不包括哪些字段
        searchSourceBuilder.fetchSource(new String[]{"name","studymodel","price","timestamp"},new String[]{});
        // 向搜索请求对象设置搜索源
        searchRequest.source(searchSourceBuilder);
        // 执行搜索
        SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        // 搜索结果
        SearchHits hits = searchResponse.getHits();
        // 匹配到的总记录数
        TotalHits totalHits = hits.getTotalHits();
        // 得到匹配高的文档
        SearchHit[] searchHits = hits.getHits();
        // 日期格式化对象
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        for(SearchHit i : searchHits){
            // 文档的主键
            String id = i.getId();
            // 源文档内容
            Map<String,Object> map = i.getSourceAsMap();
            String name = (String) map.get("name");
            String studymodel = (String) map.get("studymodel");
            Double price = (Double) map.get("price");
            Date timestamp = dateFormat.parse((String) map.get("timestamp"));
            System.out.println(name);
            System.out.println(studymodel);
            System.out.println(price);
            System.out.println(timestamp);
        }
    }

排序

可以在字段上添加一个或多个排序,支持在 keyword、date、float 等类型上添加,text 类型的字段上不允许添加排序。

    @Test
    public void testQuerySort() throws IOException, ParseException {
        // 搜索请求对象
        SearchRequest searchRequest = new SearchRequest("xedu_course");
        // 搜索源构建对象
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        // 布尔查询条件
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        // 设置查询过滤器
        boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").lte(100).gte(0));
        // 搜索方式
        // boolQuery,and 查询
        searchSourceBuilder.query(boolQueryBuilder);
        // 设置源字段过滤,第一个参数表示结果集包括哪些字段,第二个参数表示结果集不包括哪些字段
        searchSourceBuilder.fetchSource(new String[]{"name","studymodel","price","timestamp"},new String[]{});
        // 设置查询结果排序
        searchSourceBuilder.sort("studymodel", SortOrder.DESC);//学习模式降序
        searchSourceBuilder.sort("price",SortOrder.ASC);//价格升序
        // 向搜索请求对象设置搜索源
        searchRequest.source(searchSourceBuilder);
        // 执行搜索
        SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        // 搜索结果
        SearchHits hits = searchResponse.getHits();
        // 匹配到的总记录数
        TotalHits totalHits = hits.getTotalHits();
        // 得到匹配高的文档
        SearchHit[] searchHits = hits.getHits();
        // 日期格式化对象
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        for(SearchHit i : searchHits){
            // 文档的主键
            String id = i.getId();
            // 源文档内容
            Map<String,Object> map = i.getSourceAsMap();
            String name = (String) map.get("name");
            String studymodel = (String) map.get("studymodel");
            Double price = (Double) map.get("price");
            Date timestamp = dateFormat.parse((String) map.get("timestamp"));
            System.out.println(name);
            System.out.println(studymodel);
            System.out.println(price);
            System.out.println(timestamp);
        }
    }

高亮显示

高亮显示可以将搜索结果一个或多个字突出显示,以便向用户展示匹配关键字的位置。

在搜索语句中添加 highlight 即可实现,如下:

    @Test
    public void testHighLight() throws IOException, ParseException {
        // 搜索请求对象
        SearchRequest searchRequest = new SearchRequest("xedu_course");
        // 搜索源构建对象
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        String[] ids = new String[]{"1","2"};
        // 搜索方式
        // matchQuery,对文本进行分词搜索
        searchSourceBuilder.query(QueryBuilders.matchQuery("name","开发").minimumShouldMatch("80%"));
        // 设置源字段过滤,第一个参数表示结果集包括哪些字段,第二个参数表示结果集不包括哪些字段
        searchSourceBuilder.fetchSource(new String[]{"name","studymodel","price","timestamp"},new String[]{});

        // 设置高亮对象
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.preTags("<tag>");//设置高亮前缀
        highlightBuilder.postTags("</tag>");//设置高亮后缀
        highlightBuilder.fields().add(new HighlightBuilder.Field("name"));//设置高亮对象
        searchSourceBuilder.highlighter(highlightBuilder);//将高亮设置到搜索对象上

        // 向搜索请求对象设置搜索源
        searchRequest.source(searchSourceBuilder);
        // 执行搜索
        SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        // 搜索结果
        SearchHits hits = searchResponse.getHits();
        // 匹配到的总记录数
        TotalHits totalHits = hits.getTotalHits();
        // 得到匹配高的文档
        SearchHit[] searchHits = hits.getHits();
        // 日期格式化对象
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        for(SearchHit i : searchHits){
            // 文档的主键
            String id = i.getId();
            // 源文档内容
            Map<String,Object> map = i.getSourceAsMap();
            String name = (String) map.get("name");

            // 取出高亮设置内容字段
            Map<String, HighlightField> highlightFields = i.getHighlightFields();
            if(highlightFields != null){
                // 对于属性中的高亮内容
                HighlightField highlightField = highlightFields.get("name");
                if(highlightField != null){
                    Text[] texts = highlightField.getFragments();//获取高亮内容
                    StringBuffer sb = new StringBuffer();
                    // 拼接高亮内容
                    for(Text text : texts){
                        sb.append(text);
                    }
                    // 替换原name
                    name = sb.toString();
                }
            }
            String studymodel = (String) map.get("studymodel");
            Double price = (Double) map.get("price");
            Date timestamp = dateFormat.parse((String) map.get("timestamp"));
            System.out.println(name);
            System.out.println(studymodel);
            System.out.println(price);
            System.out.println(timestamp);
        }
    }