<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Spring Boot操作ES進(jìn)行各種高級(jí)查詢(值得收藏)

          共 45153字,需瀏覽 91分鐘

           ·

          2021-03-26 13:25


          作者 | 后青春期的Keats

          來(lái)源 | http://cnblogs.com/keatsCoder/p/11341835.html
          SpringBoot整合ES

          創(chuàng)建SpringBoot項(xiàng)目,導(dǎo)入 ES 6.2.1 的 RestClient 依賴和 ES 依賴。在項(xiàng)目中直接引用 es-starter 的話會(huì)報(bào)容器初始化異常錯(cuò)誤,導(dǎo)致項(xiàng)目無(wú)法啟動(dòng)。如果有讀者解決了這個(gè)問(wèn)題,歡迎留言交流

          <!-- ES 客戶端 -->
          <dependency>
              <groupId>org.elasticsearch.client</groupId>
              <artifactId>elasticsearch-rest-high-level-client</artifactId>
              <version>${elasticsearch.version}</version>
          </dependency>
          <!-- ES 版本 -->
          <dependency>
              <groupId>org.elasticsearch</groupId>
              <artifactId>elasticsearch</artifactId>
              <version>${elasticsearch.version}</version>
          </dependency>

          為容器定義 RestClient 對(duì)象

          /**
           * 在Spring容器中定義 RestClient 對(duì)象
           * @Author: keats_coder
           * @Date: 2019/8/9
           * @Version 1.0
           * */

          @Configuration
          public class ESConfig {
              @Value("${yunshangxue.elasticsearch.hostlist}")
              private String hostlist; // 127.0.0.1:9200

              @Bean // 高版本客戶端
              public RestHighLevelClient restHighLevelClient() {
                  // 解析 hostlist 配置信息。假如以后有多個(gè),則需要用 , 分開(kāi)
                  String[] split = hostlist.split(",");
                  // 創(chuàng)建 HttpHost 數(shù)組,其中存放es主機(jī)和端口的配置信息
                  HttpHost[] httpHostArray = new HttpHost[split.length];
                  for (int i = 0; i < split.length; i++) {
                      String item = split[i];
                      httpHostArray[i] = new HttpHost(item.split(":")[0], Integer.parseInt(item.split(":")[1]), "http");
                  }
                  // 創(chuàng)建RestHighLevelClient客戶端
                  return new RestHighLevelClient(RestClient.builder(httpHostArray));
              }

              // 項(xiàng)目主要使用 RestHighLevelClient,對(duì)于低級(jí)的客戶端暫時(shí)不用
              @Bean
              public RestClient restClient() {
                  // 解析hostlist配置信息
                  String[] split = hostlist.split(",");
                  // 創(chuàng)建HttpHost數(shù)組,其中存放es主機(jī)和端口的配置信息
                  HttpHost[] httpHostArray = new HttpHost[split.length];
                  for (int i = 0; i < split.length; i++) {
                      String item = split[i];
                      httpHostArray[i] = new HttpHost(item.split(":")[0], Integer.parseInt(item.split(":")[1]), "http");
                  }
                  return RestClient.builder(httpHostArray).build();
              }
          }

          在 yml 文件中配置 eshost

          yunshangxue:
            elasticsearch:
              hostlist: ${eshostlist:127.0.0.1:9200}

          調(diào)用相關(guān) API 執(zhí)行操作

          1. 創(chuàng)建操作索引的對(duì)象
          2. 構(gòu)建操作索引的請(qǐng)求
          3. 調(diào)用對(duì)象的相關(guān)API發(fā)送請(qǐng)求
          4. 獲取響應(yīng)消息
          /**
           * 刪除索引庫(kù)
           */

          @Test
          public void testDelIndex() throws IOException {
              // 操作索引的對(duì)象
              IndicesClient indices = client.indices();
              // 刪除索引的請(qǐng)求
              DeleteIndexRequest deleteIndexRequest = new DeleteIndexRequest("ysx_course");
              // 刪除索引
              DeleteIndexResponse response = indices.delete(deleteIndexRequest);
              // 得到響應(yīng)
              boolean b = response.isAcknowledged();
              System.out.println(b);
          }

          創(chuàng)建索引, 步驟和刪除類似,需要注意的是刪除的時(shí)候需要指定 ES 庫(kù)分片的數(shù)量和副本的數(shù)量,并且在創(chuàng)建索引的時(shí)候可以將映射一起指定了。代碼如下

              public void testAddIndex() throws IOException {
                  // 操作索引的對(duì)象
                  IndicesClient indices = client.indices();
                  // 創(chuàng)建索引的請(qǐng)求
                  CreateIndexRequest request = new CreateIndexRequest("ysx_course");
                  request.settings(Settings.builder().put("number_of_shards""1").put("number_of_replicas""0"));
                  // 創(chuàng)建映射
                  request.mapping("doc""{\n" +
                          "                \"properties\": {\n" +
                          "                    \"description\": {\n" +
                          "                        \"type\": \"text\",\n" +
                          "                        \"analyzer\": \"ik_max_word\",\n" +
                          "                        \"search_analyzer\": \"ik_smart\"\n" +
                          "                    },\n" +
                          "                    \"name\": {\n" +
                          "                        \"type\": \"text\",\n" +
                          "                        \"analyzer\": \"ik_max_word\",\n" +
                          "                        \"search_analyzer\": \"ik_smart\"\n" +
                          "                    },\n" +
                          "\"pic\":{                    \n" +
                          "\"type\":\"text\",                        \n" +
                          "\"index\":false                        \n" +
                          "},                    \n" +
                          "                    \"price\": {\n" +
                          "                        \"type\": \"float\"\n" +
                          "                    },\n" +
                          "                    \"studymodel\": {\n" +
                          "                        \"type\": \"keyword\"\n" +
                          "                    },\n" +
                          "                    \"timestamp\": {\n" +
                          "                        \"type\": \"date\",\n" +
                          "                        \"format\": \"yyyy-MM‐dd HH:mm:ss||yyyy‐MM‐dd||epoch_millis\"\n" +
                          "                    }\n" +
                          "                }\n" +
                          "            }", XContentType.JSON);


                  // 執(zhí)行創(chuàng)建操作
                  CreateIndexResponse response = indices.create(request);
                  // 得到響應(yīng)
                  boolean b = response.isAcknowledged();
                  System.out.println(b);
              }

          Java API操作ES

          準(zhǔn)備數(shù)據(jù)環(huán)境

          創(chuàng)建索引:ysx_course

          創(chuàng)建映射:

          PUT http://localhost:9200/ysx_course/doc/_mapping
          {
          "properties": {
          "description": { // 課程描述
          "type": "text", // String text 類型
          "analyzer": "ik_max_word", // 存入的分詞模式:細(xì)粒度
          "search_analyzer": "ik_smart" // 查詢的分詞模式:粗粒度
          },
          "name": { // 課程名稱
          "type": "text",
          "analyzer": "ik_max_word",
          "search_analyzer": "ik_smart"
          },
          "pic":{ // 圖片地址
          "type":"text",
          "index":false // 地址不用來(lái)搜索,因此不為它構(gòu)建索引
          },
          "price": { // 價(jià)格
          "type": "scaled_float", // 有比例浮點(diǎn)
          "scaling_factor": 100 // 比例因子 100
          },
          "studymodel": {
          "type": "keyword" // 不分詞,全關(guān)鍵字匹配
          },
          "timestamp": {
          "type": "date",
          "format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis"
          }
          }
          }

          加入原始數(shù)據(jù):

          POST http://localhost:9200/ysx_course/doc/1
          {
          "name": "Bootstrap開(kāi)發(fā)",
          "description": "Bootstrap是由Twitter推出的一個(gè)前臺(tái)頁(yè)面開(kāi)發(fā)框架,是一個(gè)非常流行的開(kāi)發(fā)框架,此框架集成了多種頁(yè)面效果。此開(kāi)發(fā)框架包含了大量的CSS、JS程序代碼,可以幫助開(kāi)發(fā)者(尤其是不擅長(zhǎng)頁(yè)面開(kāi)發(fā)的程序人員)輕松的實(shí)現(xiàn)一個(gè)不受瀏覽器限制的精美界面效果。",
          "studymodel": "201002",
          "price":38.6,
          "timestamp":"2018-04-25 19:11:35",
          "pic":"group1/M00/00/00/wKhlQFs6RCeAY0pHAAJx5ZjNDEM428.jpg"
          }

          DSL搜索

          DSL(Domain Specific Language)是ES提出的基于json的搜索方式,在搜索時(shí)傳入特定的json格式的數(shù)據(jù)來(lái)完成不 同的搜索需求。DSL比URI搜索方式功能強(qiáng)大,在項(xiàng)目中建議使用DSL方式來(lái)完成搜索。

          查詢?nèi)?/h5>

          原本我們想要查詢?nèi)康脑?,需要使?GET 請(qǐng)求發(fā)送 _search 命令,如今使用 DSL 方式搜索,可以使用 POST 請(qǐng)求,并在請(qǐng)求體中設(shè)置 JSON 字符串來(lái)構(gòu)建查詢條件

          POST http://localhost:9200/ysx_course/doc/_search

          請(qǐng)求體 JSON


              "query": {
                  "match_all": {} // 查詢?nèi)?/span>
              },
              "_source" : ["name","studymodel"// 查詢結(jié)果包括 課程名 + 學(xué)習(xí)模式兩個(gè)映射
          }

          具體的測(cè)試方法如下:過(guò)程比較繁瑣,好在條理還比較清晰

          // 搜索全部記錄
          @Test
          public void testSearchAll() throws IOException, ParseException {
              // 搜索請(qǐng)求對(duì)象
              SearchRequest searchRequest = new SearchRequest("ysx_course");
              // 指定類型
              searchRequest.types("doc");
              // 搜索源構(gòu)建對(duì)象
              SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
              // 搜索方式
              // matchAllQuery搜索全部
              searchSourceBuilder.query(QueryBuilders.matchAllQuery());
              // 設(shè)置源字段過(guò)慮,第一個(gè)參數(shù)結(jié)果集包括哪些字段,第二個(gè)參數(shù)表示結(jié)果集不包括哪些字段
              searchSourceBuilder.fetchSource(new String[]{"name","studymodel","price","timestamp"},new String[]{});
              // 向搜索請(qǐng)求對(duì)象中設(shè)置搜索源
              searchRequest.source(searchSourceBuilder);
              // 執(zhí)行搜索,向ES發(fā)起http請(qǐng)求
              SearchResponse searchResponse = client.search(searchRequest);
              // 搜索結(jié)果
              SearchHits hits = searchResponse.getHits();
              // 匹配到的總記錄數(shù)
              long totalHits = hits.getTotalHits();
              // 得到匹配度高的文檔
              SearchHit[] searchHits = hits.getHits();
              // 日期格式化對(duì)象
              SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
              for(SearchHit hit:searchHits){
                  // 文檔的主鍵
                  String id = hit.getId();
                  // 源文檔內(nèi)容
                  Map<String, Object> sourceAsMap = hit.getSourceAsMap();
                  String name = (String) sourceAsMap.get("name");
                  // 由于前邊設(shè)置了源文檔字段過(guò)慮,這時(shí)description是取不到的
                  String description = (String) sourceAsMap.get("description");
                  // 學(xué)習(xí)模式
                  String studymodel = (String) sourceAsMap.get("studymodel");
                  // 價(jià)格
                  Double price = (Double) sourceAsMap.get("price");
                  // 日期
                  Date timestamp = dateFormat.parse((String) sourceAsMap.get("timestamp"));
                  System.out.println(name);
                  System.out.println(studymodel);
                  System.out.println("你看不見(jiàn)我,看不見(jiàn)我~" + description);
                  System.out.println(price);
              }

          }
          坑:red>

          執(zhí)行過(guò)程中遇到的問(wèn)題:不能對(duì)這個(gè)值進(jìn)行初始化,導(dǎo)致 Spring 容器無(wú)法初始化

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

          通過(guò)檢查 target 目錄發(fā)現(xiàn),生成的 target 文件包中沒(méi)有將 yml 配置文件帶過(guò)來(lái)... 仔細(xì)對(duì)比發(fā)現(xiàn),我的項(xiàng)目竟然變成了一個(gè)不是 Maven 的項(xiàng)目。重新使用 IDEA 導(dǎo)入 Mavaen 工程之后便能正常運(yùn)行了

          分頁(yè)查詢

          我們來(lái) look 一下 ES 的分頁(yè)查詢參數(shù):


              // from 起始索引
              // size 每頁(yè)顯示的條數(shù)
              "from" : 0"size" : 1,
              "query": {
                 "match_all": {}
               },
              "_source" : ["name","studymodel"]
          }

          堅(jiān)決不給中國(guó)人發(fā)Offer的GitLab成立中國(guó)公司!立志3-5年上市,怕是聞到了韭菜香?

          通過(guò)查詢結(jié)果可以發(fā)現(xiàn),我們?cè)O(shè)置了分頁(yè)參數(shù)之后, hits.total 仍然是 3,表示它找到了 3 條數(shù)據(jù),而按照分頁(yè)規(guī)則,它只會(huì)返回一條數(shù)據(jù),因此 hits.hits 里面只有一條數(shù)據(jù)。這也符合我們的業(yè)務(wù)規(guī)則,在查詢前端頁(yè)面顯示總共的條數(shù)和當(dāng)前的數(shù)據(jù)。

          由此,我們就可以通過(guò) Java API 來(lái)構(gòu)建查詢條件了:對(duì)上面查詢?nèi)康拇a進(jìn)行如下改造:

          // 搜索源構(gòu)建對(duì)象
          SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
          int page = 2// 頁(yè)碼
          int size = 1// 每頁(yè)顯示的條數(shù)
          int index = (page - 1) * size;
          searchSourceBuilder.from(index);
          searchSourceBuilder.size(1);
          // 搜索方式
          // matchAllQuery搜索全部
          searchSourceBuilder.query(QueryBuilders.matchAllQuery());
          精確查詢 TermQuery

          Term Query為精確查詢,在搜索時(shí)會(huì)整體匹配關(guān)鍵字,不再將關(guān)鍵字分詞

          例如:

          {
              "query": {
               "term": { // 查詢的方式為 term 精確查詢
                "name""spring" // 查詢的字段為 name 關(guān)鍵字是 spring
               }
              },
              "_source": [
                  "name",
                  "studymodel"
              ]
          }

          此時(shí)查詢的結(jié)果是:

           "hits": [
               {
                   "_index""ysx_course",
                   "_type""doc",
                   "_id""3",
                   "_score"0.9331132,
                   "_source": {
                       "studymodel""201001",
                       "name""spring開(kāi)發(fā)基礎(chǔ)"
                   }
               }
           ]

          查詢到了上面這條數(shù)據(jù),因?yàn)?spring開(kāi)發(fā)基礎(chǔ) 分完詞后是 spring 開(kāi)發(fā) 基礎(chǔ) ,而查詢關(guān)鍵字是 spring 不分詞,這樣當(dāng)然可以匹配到這條記錄,但是當(dāng)我們修改關(guān)鍵字為 spring開(kāi)發(fā),按照往常的查詢方法,也是可以查詢到的。但是 term 不一樣,它不會(huì)對(duì)關(guān)鍵字分詞。結(jié)果可想而知是查詢不到的

          JavaAPI如下:

          // 搜索方式
          // termQuery 精確查詢
          searchSourceBuilder.query(QueryBuilders.termQuery("studymodel""201002"));
          根據(jù) ID 查詢:

          根據(jù) ID 精確查詢和根據(jù)其他條件精確查詢是一樣的,不同的是 id 字段前面有一個(gè)下劃線注意寫(xiě)上

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

          但是,當(dāng)一次查詢多個(gè) ID 時(shí),相應(yīng)的 API 也應(yīng)該改變,使用 termsQuery 而不是 termQuery。多了一個(gè) s

          全文檢索 MatchQuery

          MatchQuery 即全文檢索,會(huì)對(duì)關(guān)鍵字進(jìn)行分詞后匹配詞條。

          query:搜索的關(guān)鍵字,對(duì)于英文關(guān)鍵字如果有多個(gè)單詞則中間要用半角逗號(hào)分隔,而對(duì)于中文關(guān)鍵字中間可以用 逗號(hào)分隔也可以不用

          operator:設(shè)置查詢的結(jié)果取交集還是并集,并集用 or, 交集用 and

          {
              "query": {
                  "match": {
                      "description": {
                          "query""spring開(kāi)發(fā)",
                          "operator""or"
                      }
                  }
              }
          }

          有時(shí),我們需要設(shè)定一個(gè)量化的表達(dá)方式,例如查詢 spring開(kāi)發(fā)基礎(chǔ),這三個(gè)詞條。我們需求是至少匹配兩個(gè)詞條,這時(shí) operator 屬性就不能滿足要求了,ES 還提供了另外一個(gè)屬性:minimum_should_match 用一個(gè)百分?jǐn)?shù)來(lái)設(shè)定應(yīng)該有多少個(gè)詞條滿足要求。例如查詢:

          “spring開(kāi)發(fā)框架”會(huì)被分為三個(gè)詞:spring、開(kāi)發(fā)、框架 設(shè)置"minimum_should_match": "80%"表示,三個(gè)詞在文檔的匹配占比為80%,即3*0.8=2.4,向下取整得2,表 示至少有兩個(gè)詞在文檔中要匹配成功。

          JavaAPI

          通過(guò) matchQuery.minimumShouldMatch 的方式來(lái)設(shè)置條件

          // matchQuery全文檢索
                  searchSourceBuilder.query(QueryBuilders.matchQuery("description""Spring開(kāi)發(fā)框架").minimumShouldMatch("70%"));
          多字段聯(lián)合搜索 MultiQuery

          上面的 MatchQuery 有一個(gè)短板,假如用戶輸入了某關(guān)鍵字,我們?cè)诓檎业臅r(shí)候并不知道他輸入的是 name 還是 description,這時(shí)我們用什么都不合適,而 MultiQuery 的出現(xiàn)解決了這個(gè)問(wèn)題,他可以通過(guò) fields 屬性來(lái)設(shè)置多個(gè)域聯(lián)合查找:具體用法如下

          {
              "query": {
                  "multi_match": {
                      "query""Spring開(kāi)發(fā)",
                      "minimum_should_match""70%",
                      "fields": ["name""description"]
                  }
              }
          }

          JavaAPI

          searchSourceBuilder.query(QueryBuilders.multiMatchQuery("Spring開(kāi)發(fā)框架""name""description").minimumShouldMatch("70%"));
          提升 boost

          在多域聯(lián)合查詢的時(shí)候,可以通過(guò) boost 來(lái)設(shè)置某個(gè)域在計(jì)算得分時(shí)候的比重,比重越高的域當(dāng)他符合條件時(shí)計(jì)算的得分越高,相應(yīng)的該記錄也更靠前。通過(guò)在 fields 中給相應(yīng)的字段用 ^權(quán)重倍數(shù)來(lái)實(shí)現(xiàn)

          "fields": ["name^10""description"]

          上面的代碼表示給 name 字段提升十倍權(quán)重,查詢到的結(jié)果:

          {
              "_index""ysx_course",
              "_type""doc",
              "_id""3",
              "_score"13.802518// 可以清楚的發(fā)現(xiàn),得分竟然是 13 了
              "_source": {
                  "name""spring開(kāi)發(fā)基礎(chǔ)",
                  "description""spring 在java領(lǐng)域非常流行,java程序員都在用。",
                  "studymodel""201001",
                  "price"88.6,
                  "timestamp""2018-02-24 19:11:35",
                  "pic""group1/M00/00/00/wKhlQFs6RCeAY0pHAAJx5ZjNDEM428.jpg"
              }
          },

          而在 Java 中,仍然可以通過(guò)鏈?zhǔn)骄幊虂?lái)實(shí)現(xiàn)

          searchSourceBuilder.query(QueryBuilders.multiMatchQuery("Spring開(kāi)發(fā)框架""name""description").field("name"10)); // 設(shè)置 name 10倍權(quán)重
          布爾查詢 BoolQuery

          如果我們既要對(duì)一些字段進(jìn)行分詞查詢,同時(shí)要對(duì)另一些字段進(jìn)行精確查詢,就需要使用布爾查詢來(lái)實(shí)現(xiàn)了。布爾查詢對(duì)應(yīng)于Lucene的BooleanQuery查詢,實(shí)現(xiàn)將多個(gè)查詢組合起來(lái),有三個(gè)可選的參數(shù):

          must:文檔必須匹配must所包括的查詢條件,相當(dāng)于 “AND”

          should:文檔應(yīng)該匹配should所包括的查詢條件其中的一個(gè)或多個(gè),相當(dāng)于 "OR"

          must_not:文檔不能匹配must_not所包括的該查詢條件,相當(dāng)于“NOT”

          {
              "query": {
                  "bool": { // 布爾查詢
                      "must": [ // 查詢條件 must 表示數(shù)組中的查詢方式所規(guī)定的條件都必須滿足
                          {
                              "multi_match": {
                                  "query""spring框架",
                                  "minimum_should_match""50%",
                                  "fields": [
                                      "name^10",
                                      "description"
                                  ]
                              }
                          },
                          {
                              "term": {
                                  "studymodel""201001"
                              }
                          }
                      ]
                  }
              }
          }

          JavaAPI

          // 搜索方式
          // 首先構(gòu)造多關(guān)鍵字查詢條件
          MultiMatchQueryBuilder matchQueryBuilder = QueryBuilders.multiMatchQuery("Spring開(kāi)發(fā)框架""name""description").field("name"10);
          // 然后構(gòu)造精確匹配查詢條件
          TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("studymodel""201002");
          // 組合兩個(gè)條件,組合方式為 must 全滿足
          BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
          boolQueryBuilder.must(matchQueryBuilder);
          boolQueryBuilder.must(termQueryBuilder);
          // 將查詢條件封裝給查詢對(duì)象
          searchSourceBuilder.query(boolQueryBuilder);
          過(guò)濾器

          定義過(guò)濾器查詢,是在原本查詢結(jié)果的基礎(chǔ)上對(duì)數(shù)據(jù)進(jìn)行篩選,因此省略了重新計(jì)算的分的步驟,效率更高。并且方便緩存。推薦盡量使用過(guò)慮器去實(shí)現(xiàn)查詢或者過(guò)慮器和查詢共同使用,過(guò)濾器在布爾查詢中使用,下邊是在搜索結(jié)果的基礎(chǔ)上進(jìn)行過(guò)濾:

          {
              "query": {
                  "bool": {
                      "must": [
                          {
                              "multi_match": {
                                  "query""spring框架",
                                  "minimum_should_match""50%",
                                  "fields": [
                                      "name^10",
                                      "description"
                                  ]
                              }
                          }
                      ],
                      "filter": [
                          {
                              // 過(guò)濾條件:studymodel 必須是 201001
                              "term": {"studymodel""201001"}
                          },
                          {
                              // 過(guò)濾條件:價(jià)格 >=60 <=100
                              "range": {"price": {"gte"60,"lte"100}}
                          }
                      ]
                  }
              }
          }

          注意:range和term一次只能對(duì)一個(gè)Field設(shè)置范圍過(guò)慮。

          JavaAPI

          // 首先構(gòu)造多關(guān)鍵字查詢條件
          MultiMatchQueryBuilder matchQueryBuilder = QueryBuilders.multiMatchQuery("Spring框架""name""description").field("name"10);
          // 添加條件到布爾查詢
          BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
          boolQueryBuilder.must(matchQueryBuilder);
          // 通過(guò)布爾查詢來(lái)構(gòu)造過(guò)濾查詢
          boolQueryBuilder.filter(QueryBuilders.termQuery("studymodel""201001"));
          boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").gte(60).lte(100));
          // 將查詢條件封裝給查詢對(duì)象
          searchSourceBuilder.query(boolQueryBuilder);
          排序

          我們可以在查詢的結(jié)果上進(jìn)行二次排序,支持對(duì) keyword、date、float 等類型添加排序,text類型的字段不允許排序。排序使用的 JSON 格式如下:

          {
              "query": {
                  "bool": {
                      "filter": [
                          {
                              "range": {
                                  "price": {
                                      "gte"0,
                                      "lte"100
                                  }
                              }
                          }
                      ]
                  }
              },
              "sort": [ // 注意這里排序是寫(xiě)在 query key 的外面的。這就表示它的API也不是布爾查詢提供
                  {
                      "studymodel""desc" // 對(duì) studymodel(keyword)降序
                  },
                  {
                      "price""asc" // 對(duì) price(double)升序
                  }
              ]
          }

          由上面的 JSON 數(shù)據(jù)可以發(fā)現(xiàn),排序所屬的 API 是和 query 評(píng)級(jí)的,因此在調(diào)用 API 時(shí)也應(yīng)該選擇對(duì)應(yīng)的 SearchSourceBuilder 對(duì)象

          // 排序查詢
          @Test
          public void testSort() throws IOException, ParseException {
              // 搜索請(qǐng)求對(duì)象
              SearchRequest searchRequest = new SearchRequest("ysx_course");
              // 指定類型
              searchRequest.types("doc");
              // 搜索源構(gòu)建對(duì)象
              SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
              // 搜索方式
              // 添加條件到布爾查詢
              BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
              // 通過(guò)布爾查詢來(lái)構(gòu)造過(guò)濾查詢
              boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").gte(0).lte(100));
              // 將查詢條件封裝給查詢對(duì)象
              searchSourceBuilder.query(boolQueryBuilder);
              // 向搜索請(qǐng)求對(duì)象中設(shè)置搜索源
              searchRequest.source(searchSourceBuilder);
              
              // 設(shè)置排序規(guī)則
              searchSourceBuilder.sort("studymodel", SortOrder.DESC); // 第一排序規(guī)則
              searchSourceBuilder.sort("price", SortOrder.ASC); // 第二排序規(guī)則
              
              // 執(zhí)行搜索,向ES發(fā)起http請(qǐng)求
              SearchResponse searchResponse = client.search(searchRequest);
              // 搜索結(jié)果
              SearchHits hits = searchResponse.getHits();
              // 匹配到的總記錄數(shù)
              long totalHits = hits.getTotalHits();
              // 得到匹配度高的文檔
              SearchHit[] searchHits = hits.getHits();
              // 日期格式化對(duì)象
              soutData(searchHits);
          }
          高亮顯示

          高亮顯示可以將搜索結(jié)果一個(gè)或多個(gè)字突出顯示,以便向用戶展示匹配關(guān)鍵字的位置。

          高亮三要素:高亮關(guān)鍵字、高亮前綴、高亮后綴

          {
              "query": {
                  "bool": {
                      "must": [
                          {
                              "multi_match": {
                                  "query""開(kāi)發(fā)框架",
                                  "minimum_should_match""50%",
                                  "fields": [
                                      "name^10",
                                      "description"
                                  ],
                                  "type""best_fields"
                              }
                          }
                      ]
                  }
              },
              "sort": [
                  {
                      "price""asc"
                  }
              ],
              "highlight": {
                  "pre_tags": [
                      "<em>"
                  ],
                  "post_tags": [
                      "</em>"
                  ],
                  "fields": {
                      "name": {},
                      "description": {}
                  }
              }
          }

          查詢結(jié)果的數(shù)據(jù)如下:

          這是人家大一新生開(kāi)發(fā)的工具!網(wǎng)友:我好菜

          Java 代碼如下,注意到上面的 JSON 數(shù)據(jù), highlight 和 sort 和 query 依然是同級(jí)的,所以也需要用 SearchSourceBuilder 對(duì)象來(lái)設(shè)置到搜索條件中

          // 高亮查詢
          @Test
          public void testHighLight() throws IOException, ParseException {
              // 搜索請(qǐng)求對(duì)象
              SearchRequest searchRequest = new SearchRequest("ysx_course");
              // 指定類型
              searchRequest.types("doc");
              // 搜索源構(gòu)建對(duì)象
              SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
              // 搜索方式
              // 首先構(gòu)造多關(guān)鍵字查詢條件
              MultiMatchQueryBuilder matchQueryBuilder = QueryBuilders.multiMatchQuery("Spring框架""name""description").field("name"10);
              // 添加條件到布爾查詢
              BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
              boolQueryBuilder.must(matchQueryBuilder);
              // 通過(guò)布爾查詢來(lái)構(gòu)造過(guò)濾查詢
              boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").gte(60).lte(100));
              // 將查詢條件封裝給查詢對(duì)象
              searchSourceBuilder.query(boolQueryBuilder);
           // ***********************
              
              // 高亮查詢
              HighlightBuilder highlightBuilder = new HighlightBuilder();
              highlightBuilder.preTags("<em>"); // 高亮前綴
              highlightBuilder.postTags("</em>"); // 高亮后綴
              highlightBuilder.fields().add(new HighlightBuilder.Field("name")); // 高亮字段
              // 添加高亮查詢條件到搜索源
              searchSourceBuilder.highlighter(highlightBuilder);
              
           // ***********************
              
              // 設(shè)置源字段過(guò)慮,第一個(gè)參數(shù)結(jié)果集包括哪些字段,第二個(gè)參數(shù)表示結(jié)果集不包括哪些字段
              searchSourceBuilder.fetchSource(new String[]{"name","studymodel","price","timestamp"},new String[]{});
              // 向搜索請(qǐng)求對(duì)象中設(shè)置搜索源
              searchRequest.source(searchSourceBuilder);
              // 執(zhí)行搜索,向ES發(fā)起http請(qǐng)求
              SearchResponse searchResponse = client.search(searchRequest);
              // 搜索結(jié)果
              SearchHits hits = searchResponse.getHits();
              // 匹配到的總記錄數(shù)
              long totalHits = hits.getTotalHits();
              // 得到匹配度高的文檔
              SearchHit[] searchHits = hits.getHits();
              // 日期格式化對(duì)象
              soutData(searchHits);
          }

          根據(jù)查詢結(jié)果的數(shù)據(jù)結(jié)構(gòu)來(lái)獲取高亮的數(shù)據(jù),替換原有的數(shù)據(jù):

          private void soutData(SearchHit[] searchHits) throws ParseException {
              SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
              for (SearchHit hit : searchHits) {
                  // 文檔的主鍵
                  String id = hit.getId();
                  // 源文檔內(nèi)容
                  Map<String, Object> sourceAsMap = hit.getSourceAsMap();
                  String name = (String) sourceAsMap.get("name");

                  // 獲取高亮查詢的內(nèi)容。如果存在,則替換原來(lái)的name
                  Map<String, HighlightField> highlightFields = hit.getHighlightFields();
                  if( highlightFields != null ){
                      HighlightField nameField = highlightFields.get("name");
                      if(nameField!=null){
                          Text[] fragments = nameField.getFragments();
                          StringBuffer stringBuffer = new StringBuffer();
                          for (Text str : fragments) {
                              stringBuffer.append(str.string());
                          }
                          name = stringBuffer.toString();
                      }
                  }

                  // 由于前邊設(shè)置了源文檔字段過(guò)慮,這時(shí)description是取不到的
                  String description = (String) sourceAsMap.get("description");
                  // 學(xué)習(xí)模式
                  String studymodel = (String) sourceAsMap.get("studymodel");
                  // 價(jià)格
                  Double price = (Double) sourceAsMap.get("price");
                  // 日期
                  Date timestamp = dateFormat.parse((String) sourceAsMap.get("timestamp"));
                  System.out.println(name);
                  System.out.println(id);
                  System.out.println(studymodel);
                  System.out.println("你看不見(jiàn)我,看不見(jiàn)我~" + description);
                  System.out.println(price);
              }
          }

          福利進(jìn)行時(shí)當(dāng)當(dāng)羊毛又該剪了!限時(shí)三天!170買400/90買200

          往期推薦

          堅(jiān)決不給中國(guó)人發(fā)Offer的GitLab成立中國(guó)公司!立志3-5年上市,怕是聞到了韭菜香?

          緊隨Java 16,Spring Framework 5.3.5 發(fā)布:涵蓋JDK 16的支持!

          哪家中國(guó)公司為Java 16貢獻(xiàn)最多?Java第一大廠居然不是第一的...

          在 IntelliJ IDEA 中與小姐姐連麥寫(xiě)代碼是什么體驗(yàn)?

          Java都到16了,為什么都還在用8,是越做越爛了么?


          如果你喜歡本文,歡迎關(guān)注我,訂閱更多精彩內(nèi)容
          關(guān)注我回復(fù)「加群」,加入Spring技術(shù)交流群

          免費(fèi)領(lǐng)?。何⑿帕奶旒用艽蠓?/a>

          瀏覽 36
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  97人人操 | 99无码国产成人精品视频 | 亚洲综合13p | 色欲狠狠躁天天躁无码中文字幕 | 黄色操B视频 |