SpringBoot Elasticsearch

SpringBoot整合ES

创建SpringBoot项目,导入 ES 6.2.1 的 RestClient 依赖和 ES 依赖。在项目中直接引用 es-starter 的话会报容器初始化异常错误,导致项目无法启动。

  1. <!-- ES 客户端 -->
  2. <dependency>
  3. <groupId>org.elasticsearch.client</groupId>
  4. <artifactId>elasticsearch-rest-high-level-client</artifactId>
  5. <version>${elasticsearch.version}</version>
  6. </dependency>
  7. <!-- ES 版本 -->
  8. <dependency>
  9. <groupId>org.elasticsearch</groupId>
  10. <artifactId>elasticsearch</artifactId>
  11. <version>${elasticsearch.version}</version>
  12. </dependency>

为容器定义 RestClient 对象

  1. /**
  2. * 在Spring容器中定义 RestClient 对象
  3. * */
  4. @Configuration
  5. public class ESConfig {
  6. @Value("${application.elasticsearch.hostlist}")
  7. private String hostlist; // 127.0.0.1:9200
  8. @Bean // 高版本客户端
  9. public RestHighLevelClient restHighLevelClient() {
  10. // 解析 hostlist 配置信息。假如以后有多个,则需要用 , 分开
  11. String[] split = hostlist.split(",");
  12. // 创建 HttpHost 数组,其中存放es主机和端口的配置信息
  13. HttpHost[] httpHostArray = new HttpHost[split.length];
  14. for (int i = 0; i < split.length; i++) {
  15. String item = split[i];
  16. httpHostArray[i] = new HttpHost(item.split(":")[0], Integer.parseInt(item.split(":")[1]), "http");
  17. }
  18. // 创建RestHighLevelClient客户端
  19. return new RestHighLevelClient(RestClient.builder(httpHostArray));
  20. }
  21. // 项目主要使用 RestHighLevelClient,对于低级的客户端暂时不用
  22. @Bean
  23. public RestClient restClient() {
  24. // 解析hostlist配置信息
  25. String[] split = hostlist.split(",");
  26. // 创建HttpHost数组,其中存放es主机和端口的配置信息
  27. HttpHost[] httpHostArray = new HttpHost[split.length];
  28. for (int i = 0; i < split.length; i++) {
  29. String item = split[i];
  30. httpHostArray[i] = new HttpHost(item.split(":")[0], Integer.parseInt(item.split(":")[1]), "http");
  31. }
  32. return RestClient.builder(httpHostArray).build();
  33. }
  34. }

在 yml 文件中配置 eshost

  1. application:
  2. elasticsearch:
  3. hostlist: ${eshostlist:127.0.0.1:9200}

调用相关 API 执行操作

  1. 创建操作索引的对象
  2. 构建操作索引的请求
  3. 调用对象的相关API发送请求
  4. 获取响应消息
    1. /**
    2. * 删除索引库
    3. */
    4. @Test
    5. public void testDelIndex() throws IOException {
    6. // 操作索引的对象
    7. IndicesClient indices = client.indices();
    8. // 删除索引的请求
    9. DeleteIndexRequest deleteIndexRequest = new DeleteIndexRequest("course");
    10. // 删除索引
    11. DeleteIndexResponse response = indices.delete(deleteIndexRequest);
    12. // 得到响应
    13. boolean b = response.isAcknowledged();
    14. System.out.println(b);
    15. }
    创建索引, 步骤和删除类似,需要注意的是删除的时候需要指定 ES 库分片的数量和副本的数量,并且在创建索引的时候可以将映射一起指定了。代码如下 ```java public void testAddIndex() throws IOException { // 操作索引的对象 IndicesClient indices = client.indices(); // 创建索引的请求 CreateIndexRequest request = new CreateIndexRequest(“course”); request.settings(Settings.builder().put(“number_of_shards”, “1”).put(“number_of_replicas”, “0”)); // 创建映射 request.mapping(“doc”, “{\n” +
    1. " \"properties\": {\n" +
    2. " \"description\": {\n" +
    3. " \"type\": \"text\",\n" +
    4. " \"analyzer\": \"ik_max_word\",\n" +
    5. " \"search_analyzer\": \"ik_smart\"\n" +
    6. " },\n" +
    7. " \"name\": {\n" +
    8. " \"type\": \"text\",\n" +
    9. " \"analyzer\": \"ik_max_word\",\n" +
    10. " \"search_analyzer\": \"ik_smart\"\n" +
    11. " },\n" +
    12. "\"pic\":{ \n" +
    13. "\"type\":\"text\", \n" +
    14. "\"index\":false \n" +
    15. "}, \n" +
    16. " \"price\": {\n" +
    17. " \"type\": \"float\"\n" +
    18. " },\n" +
    19. " \"studymodel\": {\n" +
    20. " \"type\": \"keyword\"\n" +
    21. " },\n" +
    22. " \"timestamp\": {\n" +
    23. " \"type\": \"date\",\n" +
    24. " \"format\": \"yyyy-MM‐dd HH:mm:ss||yyyy‐MM‐dd||epoch_millis\"\n" +
    25. " }\n" +
    26. " }\n" +
    27. " }", XContentType.JSON);
  1. // 执行创建操作
  2. CreateIndexResponse response = indices.create(request);
  3. // 得到响应
  4. boolean b = response.isAcknowledged();
  5. System.out.println(b);

}

  1. <a name="kyxl7"></a>
  2. ## Java API操作ES
  3. <a name="ojwvA"></a>
  4. ### 准备数据环境
  5. 创建索引:course<br />创建映射:
  6. ```http
  7. PUT http://localhost:9200/course/doc/_mapping
  8. {
  9. "properties": {
  10. "description": { // 课程描述
  11. "type": "text", // String text 类型
  12. "analyzer": "ik_max_word", // 存入的分词模式:细粒度
  13. "search_analyzer": "ik_smart" // 查询的分词模式:粗粒度
  14. },
  15. "name": { // 课程名称
  16. "type": "text",
  17. "analyzer": "ik_max_word",
  18. "search_analyzer": "ik_smart"
  19. },
  20. "pic":{ // 图片地址
  21. "type":"text",
  22. "index":false // 地址不用来搜索,因此不为它构建索引
  23. },
  24. "price": { // 价格
  25. "type": "scaled_float", // 有比例浮点
  26. "scaling_factor": 100 // 比例因子 100
  27. },
  28. "studymodel": {
  29. "type": "keyword" // 不分词,全关键字匹配
  30. },
  31. "timestamp": {
  32. "type": "date",
  33. "format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis"
  34. }
  35. }
  36. }

加入原始数据:

  1. POST http://localhost:9200/course/doc/1
  2. {
  3. "name": "Bootstrap开发",
  4. "description": "Bootstrap是由Twitter推出的一个前台页面开发框架,是一个非常流行的开发框架,此框架集成了多种页面效果。此开发框架包含了大量的CSS、JS程序代码,可以帮助开发者(尤其是不擅长页面开发的程序人员)轻松的实现一个不受浏览器限制的精美界面效果。",
  5. "studymodel": "201002",
  6. "price":38.6,
  7. "timestamp":"2018-04-25 19:11:35",
  8. "pic":"group1/M00/00/00/wKhlQFs6RCeAY0pHAAJx5ZjNDEM428.jpg"
  9. }

DSL搜索

DSL(Domain Specific Language)是ES提出的基于json的搜索方式,在搜索时传入特定的json格式的数据来完成不 同的搜索需求。DSL比URI搜索方式功能强大,在项目中建议使用DSL方式来完成搜索。

查询全部

原本想要查询全部的话,需要使用 GET 请求发送 _search 命令,如今使用 DSL 方式搜索,可以使用 POST 请求,并在请求体中设置 JSON 字符串来构建查询条件

  1. POST http://localhost:9200/course/doc/_search
  2. 请求体 JSON
  3. {
  4. "query": {
  5. "match_all": {} // 查询全部
  6. },
  7. "_source" : ["name","studymodel"] // 查询结果包括 课程名 + 学习模式两个映射
  8. }

具体的测试方法如下:过程比较繁琐,好在条理还比较清晰

  1. // 搜索全部记录
  2. @Test
  3. public void testSearchAll() throws IOException, ParseException {
  4. // 搜索请求对象
  5. SearchRequest searchRequest = new SearchRequest("course");
  6. // 指定类型
  7. searchRequest.types("doc");
  8. // 搜索源构建对象
  9. SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
  10. // 搜索方式
  11. // matchAllQuery搜索全部
  12. searchSourceBuilder.query(QueryBuilders.matchAllQuery());
  13. // 设置源字段过虑,第一个参数结果集包括哪些字段,第二个参数表示结果集不包括哪些字段
  14. searchSourceBuilder.fetchSource(new String[]{"name","studymodel","price","timestamp"},new String[]{});
  15. // 向搜索请求对象中设置搜索源
  16. searchRequest.source(searchSourceBuilder);
  17. // 执行搜索,向ES发起http请求
  18. SearchResponse searchResponse = client.search(searchRequest);
  19. // 搜索结果
  20. SearchHits hits = searchResponse.getHits();
  21. // 匹配到的总记录数
  22. long totalHits = hits.getTotalHits();
  23. // 得到匹配度高的文档
  24. SearchHit[] searchHits = hits.getHits();
  25. // 日期格式化对象
  26. SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
  27. for(SearchHit hit:searchHits){
  28. // 文档的主键
  29. String id = hit.getId();
  30. // 源文档内容
  31. Map<String, Object> sourceAsMap = hit.getSourceAsMap();
  32. String name = (String) sourceAsMap.get("name");
  33. // 由于前边设置了源文档字段过虑,这时description是取不到的
  34. String description = (String) sourceAsMap.get("description");
  35. // 学习模式
  36. String studymodel = (String) sourceAsMap.get("studymodel");
  37. // 价格
  38. Double price = (Double) sourceAsMap.get("price");
  39. // 日期
  40. Date timestamp = dateFormat.parse((String) sourceAsMap.get("timestamp"));
  41. System.out.println(name);
  42. System.out.println(studymodel);
  43. System.out.println("你看不见我,看不见我~" + description);
  44. System.out.println(price);
  45. }
  46. }

坑:red>

执行过程中遇到的问题:不能对这个值进行初始化,导致 Spring 容器无法初始化

  1. Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder 'application.elasticsearch.hostlist' in value "${application.elasticsearch.hostlist}"

通过检查 target 目录发现,生成的 target 文件包中没有将 yml 配置文件带过来… 仔细对比发现,项目竟然变成了一个不是 Maven 的项目。重新使用 IDEA 导入 Mavaen 工程之后便能正常运行了

分页查询

来看一下 ES 的分页查询参数:

  1. {
  2. // from 起始索引
  3. // size 每页显示的条数
  4. "from" : 0, "size" : 1,
  5. "query": {
  6. "match_all": {}
  7. },
  8. "_source" : ["name","studymodel"]
  9. }

2021-07-11-18-15-48-897133.png
通过查询结果可以发现,设置了分页参数之后, hits.total 仍然是 3,表示它找到了 3 条数据,而按照分页规则,它只会返回一条数据,因此 hits.hits 里面只有一条数据。这也符合我们的业务规则,在查询前端页面显示总共的条数和当前的数据。
由此,就可以通过 Java API 来构建查询条件了:对上面查询全部的代码进行如下改造:

  1. // 搜索源构建对象
  2. SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
  3. int page = 2; // 页码
  4. int size = 1; // 每页显示的条数
  5. int index = (page - 1) * size;
  6. searchSourceBuilder.from(index);
  7. searchSourceBuilder.size(1);
  8. // 搜索方式
  9. // matchAllQuery搜索全部
  10. searchSourceBuilder.query(QueryBuilders.matchAllQuery());

精确查询 TermQuery

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

  1. {
  2. "query": {
  3. "term": { // 查询的方式为 term 精确查询
  4. "name": "spring" // 查询的字段为 name 关键字是 spring
  5. }
  6. },
  7. "_source": [
  8. "name",
  9. "studymodel"
  10. ]
  11. }

此时查询的结果是:

  1. "hits": [
  2. {
  3. "_index": "course",
  4. "_type": "doc",
  5. "_id": "3",
  6. "_score": 0.9331132,
  7. "_source": {
  8. "studymodel": "201001",
  9. "name": "spring开发基础"
  10. }
  11. }
  12. ]

查询到了上面这条数据,因为 spring开发基础 分完词后是 spring 开发 基础 ,而查询关键字是 spring 不分词,这样当然可以匹配到这条记录,但是当我们修改关键字为 spring开发,按照往常的查询方法,也是可以查询到的。但是 term 不一样,它不会对关键字分词。结果可想而知是查询不到的
JavaAPI如下:

  1. // 搜索方式
  2. // termQuery 精确查询
  3. searchSourceBuilder.query(QueryBuilders.termQuery("studymodel", "201002"));

根据 ID 查询:

根据 ID 精确查询和根据其他条件精确查询是一样的,不同的是 id 字段前面有一个下划线注意写上

  1. searchSourceBuilder.query(QueryBuilders.termQuery("_id", "1"));

但是,当一次查询多个 ID 时,相应的 API 也应该改变,使用 termsQuery 而不是 termQuery。多了一个 s

全文检索 MatchQuery

MatchQuery 即全文检索,会对关键字进行分词后匹配词条。
query:搜索的关键字,对于英文关键字如果有多个单词则中间要用半角逗号分隔,而对于中文关键字中间可以用逗号分隔也可以不用
operator:设置查询的结果取交集还是并集,并集用 or, 交集用 and

  1. {
  2. "query": {
  3. "match": {
  4. "description": {
  5. "query": "spring开发",
  6. "operator": "or"
  7. }
  8. }
  9. }
  10. }

有时,需要设定一个量化的表达方式,例如查询 spring开发基础,这三个词条。需求是至少匹配两个词条,这时 operator 属性就不能满足要求了,ES 还提供了另外一个属性:minimum_should_match 用一个百分数来设定应该有多少个词条满足要求。例如查询:
“spring开发框架”会被分为三个词:spring、开发、框架 设置”minimum_should_match”: “80%”表示,三个词在文档的匹配占比为80%,即3*0.8=2.4,向下取整得2,表 示至少有两个词在文档中要匹配成功。

JavaAPI

通过 matchQuery.minimumShouldMatch 的方式来设置条件

  1. // matchQuery全文检索
  2. searchSourceBuilder.query(QueryBuilders.matchQuery("description", "Spring开发框架").minimumShouldMatch("70%"));

多字段联合搜索 MultiQuery

上面的 MatchQuery 有一个短板,假如用户输入了某关键字,我们在查找的时候并不知道他输入的是 name 还是 description,这时我们用什么都不合适,而 MultiQuery 的出现解决了这个问题,他可以通过 fields 属性来设置多个域联合查找:具体用法如下

  1. {
  2. "query": {
  3. "multi_match": {
  4. "query": "Spring开发",
  5. "minimum_should_match": "70%",
  6. "fields": ["name", "description"]
  7. }
  8. }
  9. }

JavaAPI

  1. searchSourceBuilder.query(QueryBuilders.multiMatchQuery("Spring开发框架", "name", "description").minimumShouldMatch("70%"));

提升 boost

在多域联合查询的时候,可以通过 boost 来设置某个域在计算得分时候的比重,比重越高的域当他符合条件时计算的得分越高,相应的该记录也更靠前。通过在 fields 中给相应的字段用 ^权重倍数来实现

  1. "fields": ["name^10", "description"]

上面的代码表示给 name 字段提升十倍权重,查询到的结果:

  1. {
  2. "_index": "course",
  3. "_type": "doc",
  4. "_id": "3",
  5. "_score": 13.802518, // 可以清楚的发现,得分竟然是 13
  6. "_source": {
  7. "name": "spring开发基础",
  8. "description": "spring 在java领域非常流行,java程序员都在用。",
  9. "studymodel": "201001",
  10. "price": 88.6,
  11. "timestamp": "2018-02-24 19:11:35",
  12. "pic": "group1/M00/00/00/wKhlQFs6RCeAY0pHAAJx5ZjNDEM428.jpg"
  13. }
  14. },

而在 Java 中,仍然可以通过链式编程来实现

  1. searchSourceBuilder.query(QueryBuilders.multiMatchQuery("Spring开发框架", "name", "description").field("name", 10)); // 设置 name 10倍权重

布尔查询 BoolQuery

如果既要对一些字段进行分词查询,同时要对另一些字段进行精确查询,就需要使用布尔查询来实现了。布尔查询对应于Lucene的BooleanQuery查询,实现将多个查询组合起来,有三个可选的参数:
must:文档必须匹配must所包括的查询条件,相当于 “AND”
should:文档应该匹配should所包括的查询条件其中的一个或多个,相当于 “OR”
must_not:文档不能匹配must_not所包括的该查询条件,相当于“NOT”

  1. {
  2. "query": {
  3. "bool": { // 布尔查询
  4. "must": [ // 查询条件 must 表示数组中的查询方式所规定的条件都必须满足
  5. {
  6. "multi_match": {
  7. "query": "spring框架",
  8. "minimum_should_match": "50%",
  9. "fields": [
  10. "name^10",
  11. "description"
  12. ]
  13. }
  14. },
  15. {
  16. "term": {
  17. "studymodel": "201001"
  18. }
  19. }
  20. ]
  21. }
  22. }
  23. }

JavaAPI

  1. // 搜索方式
  2. // 首先构造多关键字查询条件
  3. MultiMatchQueryBuilder matchQueryBuilder = QueryBuilders.multiMatchQuery("Spring开发框架", "name", "description").field("name", 10);
  4. // 然后构造精确匹配查询条件
  5. TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("studymodel", "201002");
  6. // 组合两个条件,组合方式为 must 全满足
  7. BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
  8. boolQueryBuilder.must(matchQueryBuilder);
  9. boolQueryBuilder.must(termQueryBuilder);
  10. // 将查询条件封装给查询对象
  11. searchSourceBuilder.query(boolQueryBuilder);

过滤器

定义过滤器查询,是在原本查询结果的基础上对数据进行筛选,因此省略了重新计算的分的步骤,效率更高。并且方便缓存。推荐尽量使用过虑器去实现查询或者过虑器和查询共同使用,过滤器在布尔查询中使用,下边是在搜索结果的基础上进行过滤:

  1. {
  2. "query": {
  3. "bool": {
  4. "must": [
  5. {
  6. "multi_match": {
  7. "query": "spring框架",
  8. "minimum_should_match": "50%",
  9. "fields": [
  10. "name^10",
  11. "description"
  12. ]
  13. }
  14. }
  15. ],
  16. "filter": [
  17. {
  18. // 过滤条件:studymodel 必须是 201001
  19. "term": {"studymodel": "201001"}
  20. },
  21. {
  22. // 过滤条件:价格 >=60 <=100
  23. "range": {"price": {"gte": 60,"lte": 100}}
  24. }
  25. ]
  26. }
  27. }
  28. }

注意:range和term一次只能对一个Field设置范围过虑。
JavaAPI

  1. // 首先构造多关键字查询条件
  2. MultiMatchQueryBuilder matchQueryBuilder = QueryBuilders.multiMatchQuery("Spring框架", "name", "description").field("name", 10);
  3. // 添加条件到布尔查询
  4. BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
  5. boolQueryBuilder.must(matchQueryBuilder);
  6. // 通过布尔查询来构造过滤查询
  7. boolQueryBuilder.filter(QueryBuilders.termQuery("studymodel", "201001"));
  8. boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").gte(60).lte(100));
  9. // 将查询条件封装给查询对象
  10. searchSourceBuilder.query(boolQueryBuilder);

排序

可以在查询的结果上进行二次排序,支持对 keyword、date、float 等类型添加排序,text类型的字段不允许排序。排序使用的 JSON 格式如下:

  1. {
  2. "query": {
  3. "bool": {
  4. "filter": [
  5. {
  6. "range": {
  7. "price": {
  8. "gte": 0,
  9. "lte": 100
  10. }
  11. }
  12. }
  13. ]
  14. }
  15. },
  16. "sort": [ // 注意这里排序是写在 query key 的外面的。这就表示它的API也不是布尔查询提供
  17. {
  18. "studymodel": "desc" // studymodel(keyword)降序
  19. },
  20. {
  21. "price": "asc" // price(double)升序
  22. }
  23. ]
  24. }

由上面的 JSON 数据可以发现,排序所属的 API 是和 query 评级的,因此在调用 API 时也应该选择对应的 SearchSourceBuilder 对象

  1. // 排序查询
  2. @Test
  3. public void testSort() throws IOException, ParseException {
  4. // 搜索请求对象
  5. SearchRequest searchRequest = new SearchRequest("course");
  6. // 指定类型
  7. searchRequest.types("doc");
  8. // 搜索源构建对象
  9. SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
  10. // 搜索方式
  11. // 添加条件到布尔查询
  12. BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
  13. // 通过布尔查询来构造过滤查询
  14. boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").gte(0).lte(100));
  15. // 将查询条件封装给查询对象
  16. searchSourceBuilder.query(boolQueryBuilder);
  17. // 向搜索请求对象中设置搜索源
  18. searchRequest.source(searchSourceBuilder);
  19. // 设置排序规则
  20. searchSourceBuilder.sort("studymodel", SortOrder.DESC); // 第一排序规则
  21. searchSourceBuilder.sort("price", SortOrder.ASC); // 第二排序规则
  22. // 执行搜索,向ES发起http请求
  23. SearchResponse searchResponse = client.search(searchRequest);
  24. // 搜索结果
  25. SearchHits hits = searchResponse.getHits();
  26. // 匹配到的总记录数
  27. long totalHits = hits.getTotalHits();
  28. // 得到匹配度高的文档
  29. SearchHit[] searchHits = hits.getHits();
  30. // 日期格式化对象
  31. soutData(searchHits);
  32. }

高亮显示

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

  1. {
  2. "query": {
  3. "bool": {
  4. "must": [
  5. {
  6. "multi_match": {
  7. "query": "开发框架",
  8. "minimum_should_match": "50%",
  9. "fields": [
  10. "name^10",
  11. "description"
  12. ],
  13. "type": "best_fields"
  14. }
  15. }
  16. ]
  17. }
  18. },
  19. "sort": [
  20. {
  21. "price": "asc"
  22. }
  23. ],
  24. "highlight": {
  25. "pre_tags": [
  26. "<em>"
  27. ],
  28. "post_tags": [
  29. "</em>"
  30. ],
  31. "fields": {
  32. "name": {},
  33. "description": {}
  34. }
  35. }
  36. }

查询结果的数据如下:
2021-07-11-18-15-49-369126.png
Java 代码如下,注意到上面的 JSON 数据, highlight 和 sort 和 query 依然是同级的,所以也需要用 SearchSourceBuilder 对象来设置到搜索条件中

  1. // 高亮查询
  2. @Test
  3. public void testHighLight() throws IOException, ParseException {
  4. // 搜索请求对象
  5. SearchRequest searchRequest = new SearchRequest("course");
  6. // 指定类型
  7. searchRequest.types("doc");
  8. // 搜索源构建对象
  9. SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
  10. // 搜索方式
  11. // 首先构造多关键字查询条件
  12. MultiMatchQueryBuilder matchQueryBuilder = QueryBuilders.multiMatchQuery("Spring框架", "name", "description").field("name", 10);
  13. // 添加条件到布尔查询
  14. BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
  15. boolQueryBuilder.must(matchQueryBuilder);
  16. // 通过布尔查询来构造过滤查询
  17. boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").gte(60).lte(100));
  18. // 将查询条件封装给查询对象
  19. searchSourceBuilder.query(boolQueryBuilder);
  20. // ***********************
  21. // 高亮查询
  22. HighlightBuilder highlightBuilder = new HighlightBuilder();
  23. highlightBuilder.preTags("<em>"); // 高亮前缀
  24. highlightBuilder.postTags("</em>"); // 高亮后缀
  25. highlightBuilder.fields().add(new HighlightBuilder.Field("name")); // 高亮字段
  26. // 添加高亮查询条件到搜索源
  27. searchSourceBuilder.highlighter(highlightBuilder);
  28. // ***********************
  29. // 设置源字段过虑,第一个参数结果集包括哪些字段,第二个参数表示结果集不包括哪些字段
  30. searchSourceBuilder.fetchSource(new String[]{"name","studymodel","price","timestamp"},new String[]{});
  31. // 向搜索请求对象中设置搜索源
  32. searchRequest.source(searchSourceBuilder);
  33. // 执行搜索,向ES发起http请求
  34. SearchResponse searchResponse = client.search(searchRequest);
  35. // 搜索结果
  36. SearchHits hits = searchResponse.getHits();
  37. // 匹配到的总记录数
  38. long totalHits = hits.getTotalHits();
  39. // 得到匹配度高的文档
  40. SearchHit[] searchHits = hits.getHits();
  41. // 日期格式化对象
  42. soutData(searchHits);
  43. }

根据查询结果的数据结构来获取高亮的数据,替换原有的数据:

  1. private void soutData(SearchHit[] searchHits) throws ParseException {
  2. SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
  3. for (SearchHit hit : searchHits) {
  4. // 文档的主键
  5. String id = hit.getId();
  6. // 源文档内容
  7. Map<String, Object> sourceAsMap = hit.getSourceAsMap();
  8. String name = (String) sourceAsMap.get("name");
  9. // 获取高亮查询的内容。如果存在,则替换原来的name
  10. Map<String, HighlightField> highlightFields = hit.getHighlightFields();
  11. if( highlightFields != null ){
  12. HighlightField nameField = highlightFields.get("name");
  13. if(nameField!=null){
  14. Text[] fragments = nameField.getFragments();
  15. StringBuffer stringBuffer = new StringBuffer();
  16. for (Text str : fragments) {
  17. stringBuffer.append(str.string());
  18. }
  19. name = stringBuffer.toString();
  20. }
  21. }
  22. // 由于前边设置了源文档字段过虑,这时description是取不到的
  23. String description = (String) sourceAsMap.get("description");
  24. // 学习模式
  25. String studymodel = (String) sourceAsMap.get("studymodel");
  26. // 价格
  27. Double price = (Double) sourceAsMap.get("price");
  28. // 日期
  29. Date timestamp = dateFormat.parse((String) sourceAsMap.get("timestamp"));
  30. System.out.println(name);
  31. System.out.println(id);
  32. System.out.println(studymodel);
  33. System.out.println("你看不见我,看不见我~" + description);
  34. System.out.println(price);
  35. }
  36. }