亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

SpringBoot整合ES高級(jí)查詢方式

 更新時(shí)間:2022年05月05日 11:05:14   作者:芒果無(wú)憂  
這篇文章主要介紹了SpringBoot整合ES高級(jí)查詢方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
  • springboot版本:2.0.5.RELEASE
  • elasticsearch版本:7.9.1

1、配置

引入依賴:

<dependency>
    <groupId>org.elasticsearch.client</groupId>
    <artifactId>elasticsearch-rest-high-level-client</artifactId>
    <version>7.9.1</version>
</dependency>
<dependency>
    <groupId>org.elasticsearch</groupId>
    <artifactId>elasticsearch</artifactId>
    <version>7.9.1</version>
</dependency>

application.properties 配置文件:

elasticsearch.schema=http
elasticsearch.address=192.168.80.130:9200,192.168.80.131:9200,192.168.80.132:9200
elasticsearch.connectTimeout=10000
elasticsearch.socketTimeout=60000
elasticsearch.connectionRequestTimeout=10000
elasticsearch.maxConnectNum=200
elasticsearch.maxConnectPerRoute=200
# 無(wú)密碼可忽略
elasticsearch.userName=elastic
elasticsearch.password=123456

連接配置:

import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestClientBuilder;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.annotation.PreDestroy;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
@Configuration
public class ElasticSearchConfig {
    /**
     * 協(xié)議
     */
    @Value("${elasticsearch.schema:http}")
    private String schema;
    /**
     * 集群地址,如果有多個(gè)用“,”隔開
     */
    @Value("${elasticsearch.address}")
    private String address;
    /**
     * 集群地址,如果有多個(gè)用“,”隔開
     */
    @Value("${elasticsearch.userName}")
    private String userName;
    /**
     * 集群地址,如果有多個(gè)用“,”隔開
     */
    @Value("${elasticsearch.password}")
    private String password;
    /**
     * 連接超時(shí)時(shí)間
     */
    @Value("${elasticsearch.connectTimeout:5000}")
    private int connectTimeout;
    /**
     * Socket 連接超時(shí)時(shí)間
     */
    @Value("${elasticsearch.socketTimeout:10000}")
    private int socketTimeout;
    /**
     * 獲取連接的超時(shí)時(shí)間
     */
    @Value("${elasticsearch.connectionRequestTimeout:5000}")
    private int connectionRequestTimeout;
    /**
     * 最大連接數(shù)
     */
    @Value("${elasticsearch.maxConnectNum:100}")
    private int maxConnectNum;
    /**
     * 最大路由連接數(shù)
     */
    @Value("${elasticsearch.maxConnectPerRoute:100}")
    private int maxConnectPerRoute;
    private RestHighLevelClient restHighLevelClient;
    @Bean
    public RestHighLevelClient restHighLevelClient() {
        final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
        UsernamePasswordCredentials elastic = new UsernamePasswordCredentials(userName, password);
        credentialsProvider.setCredentials(AuthScope.ANY,elastic);
        // 拆分地址
        List<HttpHost> hostLists = new ArrayList<>();
        String[] hostList = address.split(",");
        for (String addr : hostList) {
            String host = addr.split(":")[0];
            String port = addr.split(":")[1];
            hostLists.add(new HttpHost(host, Integer.parseInt(port), schema));
        }
        // 轉(zhuǎn)換成 HttpHost 數(shù)組
        HttpHost[] httpHost = hostLists.toArray(new HttpHost[]{});
        // 構(gòu)建連接對(duì)象
        RestClientBuilder builder = RestClient.builder(httpHost);
        // 異步連接延時(shí)配置
        builder.setRequestConfigCallback(requestConfigBuilder -> {
            requestConfigBuilder.setConnectTimeout(connectTimeout);
            requestConfigBuilder.setSocketTimeout(socketTimeout);
            requestConfigBuilder.setConnectionRequestTimeout(connectionRequestTimeout);
            return requestConfigBuilder;
        });
        // 異步連接數(shù)配置
        builder.setHttpClientConfigCallback(httpClientBuilder -> {
            httpClientBuilder.setMaxConnTotal(maxConnectNum);
            httpClientBuilder.setMaxConnPerRoute(maxConnectPerRoute);
            httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider);
            return httpClientBuilder;
        });
        restHighLevelClient = new RestHighLevelClient(builder);
        return restHighLevelClient;
    }
    @PreDestroy
    public void clientClose() {
        try {
            this.restHighLevelClient.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

2、API操作ES

2.1 查詢索引列表

可以模糊匹配索引名稱

@Test
public void tset() throws IOException {
    GetIndexRequest getIndexRequest = new GetIndexRequest("log*");
    // 獲取es前綴過(guò)濾下所有索引
    GetIndexResponse getIndexResponse = restHighLevelClient.indices().get(getIndexRequest, RequestOptions.DEFAULT);
    // 將es查出的索引轉(zhuǎn)換為list
    List<String> elasticsearchList = new ArrayList<>(getIndexResponse.getMappings().keySet());
    elasticsearchList.forEach(System.out::println);
}

2.2 TermsQuery

es 的 trem query 做的是精確匹配查詢,關(guān)于這里早 serviceName 字段后面加的 .keyword 說(shuō)明如下:

1.es5.0 及以后的版本取消了 String 類型,將原先的 String 類型拆分為 text 和 keyword 兩種類型。它們的區(qū)別在于 text 會(huì)對(duì)字段進(jìn)行分詞處理而 keyword 則不會(huì)。

2.當(dāng)沒有為索引字段預(yù)先指定 mapping 的話,es 就會(huì)使用 Dynamic Mapping ,通過(guò)推斷你傳入的文檔中字段的值對(duì)字段進(jìn)行動(dòng)態(tài)映射。例如傳入的文檔中字段 total 的值為12,那么 total 將被映射為 long 類型;字段 addr 的值為"192.168.0.1",那么 addr 將被映射為 ip 類型。然而對(duì)于不滿足 ip 和 long 格式的普通字符串來(lái)說(shuō),情況有些不同:ES 會(huì)將它們映射為 text 類型,但為了保留對(duì)這些字段做精確查詢以及聚合的能力,又同時(shí)對(duì)它們做了 keyword 類型的映射,作為該字段的 fields 屬性寫到 _mapping 中。例如,我這里使用的字段 “serviceName”,用來(lái)存儲(chǔ)服務(wù)名稱字符串類型,會(huì)對(duì)它做如下的 Dynamic Mapping:

"serviceName" : {
    "type" : "text",
    "fields" : {
        "keyword" : {
            "type" : "keyword",
            "ignore_above" : 256
        }
    }
}

在之后的查詢中使用 serviceName 是將 serviceName 作為 text 類型查詢,而使用 serviceName.keyword 則是將 serviceName 作為 keyword 類型查詢。前者會(huì)對(duì)查詢內(nèi)容做分詞處理之后再匹配,而后者則是直接對(duì)查詢結(jié)果做精確匹配。

3.es 的 trem query 做的是精確匹配而不是分詞查詢,因此對(duì) text 類型的字段做 term 查詢將是查不到結(jié)果的(除非字段本身經(jīng)過(guò)分詞器處理后不變,未被轉(zhuǎn)換或分詞)。此時(shí),必須使用 serviceName.keyword 來(lái)對(duì) serviceName 字段以 keyword 類型進(jìn)行精確匹配。

GET logdata-log-center-2021.05.06/_search
{
  "query": {
    "terms": {
      "serviceName.keyword": [
        "log-center-user-portal",
        "log-center-collect-manage"
      ]
    }
  }
}

Java API

@Test
public void test() throws IOException {
    //構(gòu)建查詢?cè)礃?gòu)建器
    SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
//  termQuery只能匹配一個(gè)值,第一個(gè)入?yún)樽侄蚊Q,第二個(gè)參數(shù)為傳入的值,相當(dāng)于sql中的=
//  searchSourceBuilder.query(QueryBuilders.termQuery("serviceName.keyword", "log-center-user-portal-web"));
    //termsQuery可以一次性匹配多個(gè)值,相當(dāng)于sql中的in
    searchSourceBuilder.query(QueryBuilders.termsQuery("serviceName.keyword", "log-center-user-portal-web", "log-center-collect-manage"));
    //構(gòu)建查詢請(qǐng)求對(duì)象,入?yún)樗饕?
    SearchRequest searchRequest = new SearchRequest("log-web-up-log-center-2021.10.30");
    //向搜索請(qǐng)求對(duì)象中配置搜索源
    searchRequest.source(searchSourceBuilder);
    // 執(zhí)行搜索,向ES發(fā)起http請(qǐng)求
    SearchResponse response = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
    if (RestStatus.OK.equals(response.status())) {
        long total = response.getHits().getTotalHits().value; //檢索到符合條件的總數(shù)
        SearchHit[] hits = response.getHits().getHits();
        //未指定size,默認(rèn)查詢的是10條
        for (SearchHit hit : hits) {
            String index = hit.getIndex();//索引名稱
            String id = hit.getId(); //文檔id
            JSONObject jsonObject = JSON.parseObject(hit.getSourceAsString(), JSONObject.class); //文檔內(nèi)容
            System.out.println(jsonObject);
        }
    }
}

2.3 WildcardQuery

es的 wildcard query 做的是模糊匹配查詢,類似 sql 中的 like,而 value 值前后的 “*” 號(hào)類似與 sql 中的 ”%“ 。

GET logdata-log-center-2021.05.06/_search
{
  "query": {
    "wildcard": {
      "serviceName.keyword": {
        "value": "*user-portal*"
      }
    }
  }
}

Java API

searchSourceBuilder.query(QueryBuilders.wildcardQuery("serviceName.keyword", "*" + "user-portal" + "*"));

2.4 RangeQuery

es 的 range query 做的是范圍查詢,相當(dāng)于 sql 中的 between … and …

GET log-web-up-log-center-2021.10.30/_search
{
  "query": {
    "range": {
      "timestamp": {
        "gte": "2021-10-30 15:00:00",
        "lte": "2021-10-30 16:00:00",
        "format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd HH:mm:ss.SSS"
      }
    }
  }
}

Java API

searchSourceBuilder.query(QueryBuilders.rangeQuery("timestamp")
                              .gte("2021-10-30 15:00:00") //起始值
                              .lte("2021-10-30 16:00:00")   //結(jié)束值
                              .format("yyyy-MM-dd HH:mm:ss||yyyy-MM-dd HH:mm:ss.SSS"));//可以指定多個(gè)格式化標(biāo)準(zhǔn),使用||隔開

2.5 MatchQuery

es的 match query 做的是全文檢索,會(huì)對(duì)關(guān)鍵字進(jìn)行分詞后匹配詞條。

GET log-web-up-log-center-2021.10.30/_search
{
  "query": {
    "match": {
      "orgName": {
        "query": "有限公司"
      }
    }
  }
}

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

Java API

//全文檢索,支持分詞匹配
searchSourceBuilder.query(QueryBuilders.matchQuery("orgName", "有限公司");

2.6 MultiMatchQuery

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

GET log-web-up-log-center-2021.10.30/_search
{
  "query": {
    "multi_match": {
      "query": "user-portal",
      "fields": ["serviceName", "systemName"]
    }
  }
}

Java API

//全文檢索,支持分詞匹配,支持多字段檢索
searchSourceBuilder.query(QueryBuilders.multiMatchQuery("user-portal", "serviceName", "systemName", "description"));

2.7 ExistsQuery

es的 exists query 做的是檢索某個(gè)字段存在的數(shù)據(jù),即不為 null 的數(shù)據(jù)。其中指定的 field 可以是一個(gè)具體的字段,也可以是一個(gè) json 結(jié)構(gòu)。

GET logdata-log-center-2021.05.06/_search
{
  "query": {
    "exists": {
      "field": "networkLogDetailInfo"
    }
  }
}

Java API

//查詢networkLogDetailInfo不為null的數(shù)據(jù)
searchSourceBuilder.query(QueryBuilders.existsQuery("networkLogDetailInfo"));

2.8 BoolQuery

es的 bool query 做的是將多個(gè)查詢組合起來(lái)去檢索數(shù)據(jù),主要的組合參數(shù)有 must、should、mustNot 等。

  • must:數(shù)據(jù)必須匹配 must 所包含的查詢條件,相當(dāng)于 ”AND“
  • should:數(shù)據(jù)匹配 should 包含的一個(gè)或多個(gè)查詢條件,相當(dāng)于 ”OR“
  • mustNot:數(shù)據(jù)必須不匹配 mustNot 所包含的查詢條件,相當(dāng)于 ”NOT“
GET logdata-log-center-2021.05.06/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "exists": {
            "field": "networkLogDetailInfo"
          }
        },
        {
          "range": {
            "timestamp": {
              "gte": "2021-05-05 00:00:00",
              "lte": "2021-05-07 00:00:00",
              "format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd HH:mm:ss.SSS"
            }
          }
        }
      ],
      "must_not": [
        {
          "exists": {
            "field": "serviceLogDetailInfo"
          }
        }
      ]
    }
  }
}

Java API

@Test
public void test() throws IOException {
    //構(gòu)建查詢?cè)礃?gòu)建器
    SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
    //構(gòu)建bool類型查詢器
    BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
    //使用must連接,相當(dāng)于and,構(gòu)建第一個(gè)查詢條件existsQuery必須包含此字段
    boolQueryBuilder.must(QueryBuilders.existsQuery("networkLogDetailInfo"));
    //使用must連接第二個(gè)條件,rangeQuery范圍查找,相當(dāng)于between...and...
    boolQueryBuilder.must(QueryBuilders.rangeQuery("timestamp")
                          .from("2021-05-05 00:00:00") //起始值
                          .to("2021-05-07 00:00:00")   //結(jié)束值
                          .includeLower(true)          //是否等于起始值
                          .includeUpper(false)         //是否等于結(jié)束值
                          .format("yyyy-MM-dd HH:mm:ss||yyyy-MM-dd HH:mm:ss.SSS")); //格式化時(shí)間
    //使用mustNot連接第三個(gè)條件
    boolQueryBuilder.mustNot(QueryBuilders.existsQuery("serviceLogDetailInfo"));
    searchSourceBuilder.query(boolQueryBuilder);
    //構(gòu)建查詢請(qǐng)求對(duì)象,入?yún)樗饕?
    SearchRequest searchRequest = new SearchRequest("logdata-log-center-2021.05.06");
    //向搜索請(qǐng)求對(duì)象中配置搜索源
    searchRequest.source(searchSourceBuilder);
    // 執(zhí)行搜索,向ES發(fā)起http請(qǐng)求
    SearchResponse response = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
    if (RestStatus.OK.equals(response.status())) {
        long total = response.getHits().getTotalHits().value; //檢索到符合條件的總數(shù)
        SearchHit[] hits = response.getHits().getHits();
        for (SearchHit hit : hits) {
            String index = hit.getIndex();//索引名稱
            String id = hit.getId(); //文檔id
            JSONObject jsonObject = JSON.parseObject(hit.getSourceAsString(), JSONObject.class); //文檔內(nèi)容
            System.out.println(jsonObject);
        }
    }
}

2.9 排序

es 使用 sort 進(jìn)行排序,可以多個(gè)字段聯(lián)合排序。

GET logdata-log-center-2021.05.06/_search
{
  "query": {
    "bool": {
      "must_not": [
        {
          "exists": {
            "field": "serviceLogDetailInfo"
          }
        }
      ]
    }
  },
  "sort": [
    {
      "serviceName.keyword": {
        "order": "asc"
      },
      "timestamp": {
        "order": "desc"
      }
    }
  ]
}

先按照第一個(gè)字段排序,第一個(gè)字段相同時(shí)按照第二個(gè)字段排序。

Java API

//升序
searchSourceBuilder.sort("serviceName.keyword", SortOrder.ASC);
//降序
searchSourceBuilder.sort("timestamp", SortOrder.DESC);

2.10 結(jié)果字段過(guò)濾

檢索數(shù)據(jù),有時(shí)只需要其中的幾個(gè)字段,es 也支持對(duì)結(jié)果集進(jìn)行字段篩選過(guò)濾。字段可以使用 “*” 進(jìn)行模糊匹配。

GET logdata-log-center-2021.05.06/_search
{
  "_source": {
    "includes": ["messageId", "system*", "service*", "timestamp"],
    "excludes": []
  }
}

Java API

//篩選字段,第一個(gè)參數(shù)為需要的字段,第二個(gè)參數(shù)為不需要的字段
searchSourceBuilder.fetchSource(new String[] {"messageId", "system*", "service*", "timestamp"}, new String[] {});

2.11 分頁(yè)

es 的分頁(yè)方式有三種:from+ size、scroll、search_after, 默認(rèn)采用的分頁(yè)方式是 from+ size 的形式。

2.11.1 from+ size

GET logdata-log-center-2021.05.06/_search
{
  "from": 0, 
  "size": 2, 
  "query": {
    "exists": {
      "field": "networkLogDetailInfo"
    }
  },
  "_source": {
    "includes": ["messageId", "system*", "service*", "timestamp"],
    "excludes": []
  }
}

在這里插入圖片描述

通過(guò)查詢結(jié)果可以發(fā)現(xiàn),我們?cè)O(shè)置了分頁(yè)參數(shù)之后, hits.total 返回的是數(shù)據(jù)總數(shù)7149,而按照分頁(yè)規(guī)則,我們?cè)O(shè)置的size=2,因此 hits.hits 里面只有兩條數(shù)據(jù)。

Java API

@Test
public void test() throws IOException {
    //構(gòu)建查詢?cè)礃?gòu)建器
    SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
    //查詢條件
    searchSourceBuilder.query(QueryBuilders.existsQuery("networkLogDetailInfo"));
    int page = 1; // 頁(yè)碼
    int size = 2; // 每頁(yè)顯示的條數(shù)
    int index = (page - 1) * size;
    searchSourceBuilder.from(index); //設(shè)置查詢起始位置
    searchSourceBuilder.size(size); //結(jié)果集返回的數(shù)據(jù)條數(shù)
    //篩選字段,第一個(gè)參數(shù)為需要的字段,第二個(gè)參數(shù)為不需要的字段
    searchSourceBuilder.fetchSource(new String[] {"messageId", "system*", "service*", "timestamp"}, new String[] {});
    //構(gòu)建查詢請(qǐng)求對(duì)象,入?yún)樗饕?
    SearchRequest searchRequest = new SearchRequest("logdata-log-center-2021.05.06");
    //向搜索請(qǐng)求對(duì)象中配置搜索源
    searchRequest.source(searchSourceBuilder);
    // 執(zhí)行搜索,向ES發(fā)起http請(qǐng)求
    SearchResponse response = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
    if (RestStatus.OK.equals(response.status())) {
        long total = response.getHits().getTotalHits().value; //檢索到符合條件的總數(shù)
        SearchHit[] hits = response.getHits().getHits();
        //未指定size,默認(rèn)查詢的是10條
        for (SearchHit hit : hits) {
            String index = hit.getIndex();//索引名稱
            String id = hit.getId(); //文檔id
            JSONObject jsonObject = JSON.parseObject(hit.getSourceAsString(), JSONObject.class); //文檔內(nèi)容
            System.out.println(jsonObject);
        }
    }
}

2.11.2 scroll

一種可滿足深度分頁(yè)的方式,es 提供了 scroll 的方式進(jìn)行分頁(yè)讀取。原理上是對(duì)某次查詢生成一個(gè)游標(biāo) scroll_id , 后續(xù)的查詢只需要根據(jù)這個(gè)游標(biāo)去取數(shù)據(jù),每次只能拿到下一頁(yè)的數(shù)據(jù),直到結(jié)果集中返回的 hits 字段為空,就表示遍歷結(jié)束。這里scroll=1m是scroll_id的有效期,表示1分鐘,過(guò)期后會(huì)被es自動(dòng)清理,每次查詢會(huì)更新此值。

GET logdata-log-center-2021.05.06/_search?scroll=1m
{
  "size": 2, 
  "query": {
    "exists": {
      "field": "networkLogDetailInfo"
    }
  },
  "_source": {
    "includes": ["messageId", "system*", "service*", "timestamp"],
    "excludes": []
  }
}

在這里插入圖片描述

后續(xù)的查詢中查詢條件不需要指定,只需要攜帶 scroll_id 即可它會(huì)按照首次查詢條件進(jìn)行分頁(yè)展示,下一次查詢(兩種方式):

POST /_search/scroll
{
  "scroll": "1m",
  "scroll_id": "FGluY2x1ZGVfY29udGV4dF91dWlkDXF1ZXJ5QW5kRmV0Y2gBFFp0bGhXbjBCQU55Q3EtSDcxaWF4AAAAAACF-OYWV0liWUNLUHVTN09DS1ZtUl9SSHhVdw=="
}
GET /_search/scroll?scroll=1m&scroll_id=FGluY2x1ZGVfY29udGV4dF91dWlkDXF1ZXJ5QW5kRmV0Y2gBFFp0bGhXbjBCQU55Q3EtSDcxaWF4AAAAAACF-OYWV0liWUNLUHVTN09DS1ZtUl9SSHhVdw==

Java API

public void testScroll(String scrollId) throws IOException {
    //查詢?cè)礃?gòu)建器
    SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
    //每頁(yè)顯示2條
    searchSourceBuilder.size(2);
    //查詢條件
    searchSourceBuilder.query(QueryBuilders.existsQuery("networkLogDetailInfo"));
    //篩選字段,第一個(gè)參數(shù)為需要的字段,第二個(gè)參數(shù)為不需要的字段
    searchSourceBuilder.fetchSource(new String[] {"messageId", "system*", "service*", "timestamp"}, new String[] {});
    SearchRequest request = new SearchRequest("logdata-log-center-2021.05.06");
    request.source(searchSourceBuilder);
    Scroll scroll = new Scroll(TimeValue.timeValueMinutes(1L));
    request.scroll(scroll);//滾動(dòng)翻頁(yè)
    SearchResponse response;
    if (!StringUtils.isBlank(scrollId)) {
        //Scroll查詢
        SearchScrollRequest scrollRequest = new SearchScrollRequest(scrollId);
        scrollRequest.scroll(scroll);
        response = restHighLevelClient.scroll(scrollRequest, RequestOptions.DEFAULT);
    } else {
        //首次查詢使用普通查詢
        response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
    }
    //更新scrollId
    scrollId = response.getScrollId();
    System.out.println(scrollId);
    if (RestStatus.OK.equals(response.status())) {
        //設(shè)置查詢總量
        SearchHit[] hits = response.getHits().getHits();
        for (SearchHit hit : hits) {
            String index = hit.getIndex();
            String id = hit.getId();
            JSONObject jsonObject = JSON.parseObject(hit.getSourceAsString(), JSONObject.class);
            System.out.println(jsonObject);
        }
    }
}

2.11.3 search_after

search_after 是 ES5.0 及之后版本提供的新特性,search_after查詢時(shí)需要指定sort排序字段,可以指定多個(gè)排序字段,后續(xù)查詢有點(diǎn)類似 scroll ,但是和 scroll 又不一樣,它提供一個(gè)活動(dòng)的游標(biāo),通過(guò)上一次查詢的最后一條數(shù)據(jù)的來(lái)進(jìn)行下一次查詢。 這里需要說(shuō)明一下,使用search_after查詢需要將from設(shè)置為0或-1,當(dāng)然你也可以不寫

第一次查詢:

POST logdata-log-center-2021.05.06/_search
{
  "size": 2, 
  "query": {
    "exists": {
      "field": "networkLogDetailInfo"
    }
  },
  "_source": {
    "includes": ["messageId", "system*", "service*", "timestamp"],
    "excludes": []
  },
  "sort": [
    {
      "timestamp": {
        "order": "desc"
      }
    }
  ]
}

查詢結(jié)果:可以看到每一條數(shù)據(jù)都有一個(gè)sort部分,而下一頁(yè)的查詢需要本次查詢結(jié)果最后一條的sort值作為游標(biāo),實(shí)現(xiàn)分頁(yè)查詢

在這里插入圖片描述

第二次查詢:

POST logdata-log-center-2021.05.06/_search
{
  "search_after": [
    1620374316433
  ],
  "size": 2, 
  "query": {
    "exists": {
      "field": "networkLogDetailInfo"
    }
  },
  "_source": {
    "includes": ["messageId", "system*", "service*", "timestamp"],
    "excludes": []
  },
  "sort": [
    {
      "timestamp": {
        "order": "desc"
      }
    }
  ]
}

Java API

public void testSearchAfter(Object[] values) throws IOException {
    //查詢?cè)礃?gòu)建器
    SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
    searchSourceBuilder.size(2);
    searchSourceBuilder.from(0); //searchAfter需要將from設(shè)置為0或-1,當(dāng)然也可以不寫
    //查詢條件
    searchSourceBuilder.query(QueryBuilders.existsQuery("networkLogDetailInfo"));
    //篩選字段,第一個(gè)參數(shù)為需要的字段,第二個(gè)參數(shù)為不需要的字段
    searchSourceBuilder.fetchSource(new String[] {"messageId", "system*", "service*", "timestamp"}, new String[] {});
    //以時(shí)間戳排序
    searchSourceBuilder.sort("timestamp", SortOrder.DESC);
    if (values != null)
        searchSourceBuilder.searchAfter(values);
    SearchRequest request = new SearchRequest("logdata-log-center-2021.05.06");
    request.source(searchSourceBuilder);
    SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
    if (RestStatus.OK.equals(response.status())) {
        //設(shè)置查詢總量
        SearchHit[] hits = response.getHits().getHits();
        for(int i = 0; i < hits.length; i++) {
            String index = hits[i].getIndex();
            String id = hits[i].getId();
            JSONObject jsonObject = JSON.parseObject(hits[i].getSourceAsString(), JSONObject.class);
            System.out.println(jsonObject);
            if (i == hits.length-1) {
                //最后一條數(shù)據(jù)的sortValue作為下一次查詢的游標(biāo)值
                values = hits[i].getSortValues();
                System.out.println(Arrays.toString(values));
            }
        }
    }
}

2.11.4 三種分頁(yè)方式特點(diǎn)

  • from+size 比較適合淺分頁(yè)模式,在深度分頁(yè)的情況下,這種使用方式效率是非常低的,隨著分頁(yè)頁(yè)碼的不斷增大,查詢的效率會(huì)直線下降。比如from = 5000, size=20, es 需要在各個(gè)分片上匹配排序并得到5000*20 條有效數(shù)據(jù),然后在結(jié)果集中取最后20條。除了效率上的問題,還有一個(gè)無(wú)法解決的問題是,es 目前支持最大的 skip 值是 max_result_window ,默認(rèn)為 10000 。也就是當(dāng) from + size > max_result_window 時(shí),es 將返回錯(cuò)誤。
  • scroll 是一種滾屏形式的分頁(yè)檢索,滿足深度分頁(yè)的場(chǎng)景。查詢的時(shí)候生成一個(gè)游標(biāo) scroll_id,有效期內(nèi)每次返回的值是一樣的,后續(xù)的查詢只需要根據(jù)這個(gè)游標(biāo)去取數(shù)據(jù)即可。scroll查詢是很耗性能的方式,scroll_id 的生成可以理解為建立了一個(gè)臨時(shí)的歷史快照, 系統(tǒng)會(huì)耗費(fèi)大量的資源來(lái)保存一份當(dāng)前查詢結(jié)果集映像,并且會(huì)占用文件描述符,在此之后的增刪改查等操作不會(huì)影響到這個(gè)快照的結(jié)果,因此不建議在實(shí)時(shí)查詢中運(yùn)用。這種方式往往用于非實(shí)時(shí)處理大量數(shù)據(jù)的情況,比如要進(jìn)行數(shù)據(jù)遷移或者索引變更之類的。
  • search_after 適用于深度分頁(yè)+ 排序,分頁(yè)是根據(jù)上一頁(yè)最后一條數(shù)據(jù)來(lái)定位下一頁(yè)的位置,所以無(wú)法跳頁(yè)請(qǐng)求,同時(shí)在分頁(yè)請(qǐng)求的過(guò)程中,如果有索引數(shù)據(jù)的增刪改,這些變更也會(huì)實(shí)時(shí)的反映到游標(biāo)上。在選擇search_after的排序字段時(shí)盡量使用比如文檔的id或者時(shí)間戳等具有唯一性的字段。search_after 相比 from+size 的淺分頁(yè)以及 scroll 滾屏查詢會(huì)有很大的性能提升。

2.22 聚合

es 的 aggs 對(duì)數(shù)據(jù)進(jìn)行聚合查詢統(tǒng)計(jì),查詢方式如下: 

## 統(tǒng)計(jì)各系統(tǒng)一個(gè)月的日志采集數(shù)量
POST log*/_search
{
  "size": 0,
  "query": {
		"range": {
			"timestamp": {
				"gte": "2021-10-24 00:00:00",
				"lte": "2021-11-24 00:00:00",
				"format": "yyyy-MM-dd HH:mm:ss"
			}
		}
	},
	"aggs": {
	  "allLog": {
	    "terms": {
	      "field": "systemName.keyword",
	      "size": 10
	    }
	  }
	}
}

在這里插入圖片描述

Java API

@Test
public void test() throws IOException {
    //按照systemName字段聚合統(tǒng)計(jì)各個(gè)系統(tǒng)的日志數(shù)量
    TermsAggregationBuilder bySystemName = AggregationBuilders.terms("allLog").field("systemName.keyword");
    RangeQueryBuilder timestamp = QueryBuilders.rangeQuery("timestamp")
        .gte("2021-10-24 00:00:00")
        .lte("2021-11-24 00:00:00")
        .format("yyyy-MM-dd HH:mm:ss");
    //查詢?cè)礃?gòu)建器
    SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
    //配置聚合條件
    searchSourceBuilder.aggregation(bySystemName);
    //配置查詢條件
    searchSourceBuilder.query(timestamp);
    //設(shè)置查詢結(jié)果不返回,只返回聚合結(jié)果
    searchSourceBuilder.size(0);
    //創(chuàng)建查詢請(qǐng)求對(duì)象,將查詢條件配置到其中
    SearchRequest request = new SearchRequest("log*");
    request.source(searchSourceBuilder);
    // 執(zhí)行搜索,向ES發(fā)起http請(qǐng)求
    SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
    Aggregations aggregations = response.getAggregations();
    if (aggregations != null) {
        Terms terms = aggregations.get("allLog");
        //解析桶
        for (Terms.Bucket bucket : terms.getBuckets()) {
            System.out.print("系統(tǒng)名稱:" + bucket.getKeyAsString());
            System.out.println("\t總?cè)罩緮?shù)量:" + bucket.getDocCount());
        }
    }
}

多層嵌套聚合

## 統(tǒng)計(jì)各個(gè)系統(tǒng)的總?cè)罩緮?shù)量,按系統(tǒng)統(tǒng)計(jì)各種類型日志數(shù)量
POST log*/_search
{
	"size": 0,
	"query": {
		"range": {
			"timestamp": {
				"gte": "2021-10-24 00:00:00",
				"lte": "2021-11-24 00:00:00",
				"format": "yyyy-MM-dd HH:mm:ss"
			}
		}
	},
	"aggs": {
		"allLog": {
			"terms": {
				"field": "systemName.keyword",
				"size": 10
			},
			"aggs": {
				"errorLogNum": {
					"filter": {
						"terms": {
							"level.keyword": [
								"ERROR",
								"FATAL"
							]
						}
					}
				},
				"dbLogNum": {
					"filter": {
						"exists": {
							"field": "dataLogDetailInfo"
						}
					}
				},
				"interfaceLogNum": {
					"filter": {
						"exists": {
							"field": "networkLogDetailInfo"
						}
					}
				},
				"serviceLogNum": {
					"filter": {
						"exists": {
							"field": "serviceLogDetailInfo"
						}
					}
				},
				"webLogNum": {
					"filter": {
						"exists": {
							"field": "browserModel"
						}
					}
				}
			}
		}
	}
}

Java API

@Test
public void test() throws IOException {
    //錯(cuò)誤日志聚合條件
    FilterAggregationBuilder errorLogNum = AggregationBuilders.filter("errorLogNum", QueryBuilders.termsQuery("level.keyword", "ERROR", "FATAL"));
    //數(shù)據(jù)庫(kù)日志聚合條件
    FilterAggregationBuilder dataLogNum = AggregationBuilders.filter("dbLogNum", QueryBuilders.existsQuery("dataLogDetailInfo"));
    //接口日志聚合條件
    FilterAggregationBuilder networkLogNum = AggregationBuilders.filter("interfaceLogNum", QueryBuilders.existsQuery("networkLogDetailInfo"));
    //應(yīng)用日志聚合條件
    FilterAggregationBuilder serviceLogNum = AggregationBuilders.filter("serviceLogNum", QueryBuilders.existsQuery("serviceLogDetailInfo"));
    //前端日志聚合條件
    FilterAggregationBuilder webUpLogNum = AggregationBuilders.filter("webLogNum", QueryBuilders.existsQuery("browserModel"));
    //最外層聚合條件,第一次聚合的條件
    TermsAggregationBuilder bySystemName = AggregationBuilders.terms("allLog").field("systemName.keyword").size(10);
    //內(nèi)部多個(gè)條件的子聚合,在系統(tǒng)聚合后的結(jié)果上二次聚合
    bySystemName.subAggregation(errorLogNum)
        .subAggregation(dataLogNum).
        subAggregation(networkLogNum).
        subAggregation(serviceLogNum).
        subAggregation(webUpLogNum);
    RangeQueryBuilder timestamp = QueryBuilders.rangeQuery("timestamp")
        .gte("2021-10-24 00:00:00")
        .lte("2021-11-24 00:00:00")
        .format("yyyy-MM-dd HH:mm:ss");
    //查詢?cè)礃?gòu)建器
    SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
    //配置聚合條件
    searchSourceBuilder.aggregation(bySystemName);
    //配置查詢條件
    searchSourceBuilder.query(timestamp);
    //設(shè)置查詢結(jié)果不返回,只返回聚合結(jié)果
    searchSourceBuilder.size(0);
    //創(chuàng)建查詢請(qǐng)求對(duì)象,將查詢條件配置到其中
    SearchRequest request = new SearchRequest("log*");
    request.source(searchSourceBuilder);
    // 執(zhí)行搜索,向ES發(fā)起http請(qǐng)求
    SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
    Aggregations aggregations = response.getAggregations();
    if (aggregations != null) {
        Terms terms = aggregations.get("allLog");
        for (Terms.Bucket bucket : terms.getBuckets()) {
            ParsedFilter dbFilter = bucket.getAggregations().get("dbLogNum");
            ParsedFilter serviceFilter = bucket.getAggregations().get("serviceLogNum");
            ParsedFilter webFilter = bucket.getAggregations().get("webLogNum");
            ParsedFilter interfaceFilter = bucket.getAggregations().get("interfaceLogNum");
            ParsedFilter errorFilter = bucket.getAggregations().get("errorLogNum");
            System.out.print("系統(tǒng)名稱:" + bucket.getKeyAsString());
            System.out.print("\t總?cè)罩荆? + bucket.getDocCount());
            System.out.print("\t數(shù)據(jù)庫(kù)日志:" + dbFilter.getDocCount());
            System.out.print("\t服務(wù)執(zhí)行日志:" + serviceFilter.getDocCount());
            System.out.print("\t前端操作日志:" + webFilter.getDocCount());
            System.out.print("\t接口日志:" + interfaceFilter.getDocCount());
            System.out.println("\t錯(cuò)誤日志:" + errorFilter.getDocCount());
        }
    }
}

聚合查詢還提供了許多查詢規(guī)則,按時(shí)間date聚合、count聚合、avg聚合、sum聚合、min聚合、max聚合等等,這里就不一一列舉了。

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。 

相關(guān)文章

  • Java日志框架之logback使用詳解

    Java日志框架之logback使用詳解

    這篇文章主要介紹了Java日志框架之logback使用詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-07-07
  • vscode 配置java環(huán)境并調(diào)試運(yùn)行的詳細(xì)過(guò)程

    vscode 配置java環(huán)境并調(diào)試運(yùn)行的詳細(xì)過(guò)程

    這篇文章主要介紹了vscode 配置java環(huán)境并調(diào)試運(yùn)行的詳細(xì)過(guò)程,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-05-05
  • MyBatisPlus 主鍵策略的實(shí)現(xiàn)(4種)

    MyBatisPlus 主鍵策略的實(shí)現(xiàn)(4種)

    MyBatis Plus 集成了多種主鍵策略,幫助用戶快速生成主鍵,本文主要介紹了MyBatisPlus主鍵策略的實(shí)現(xiàn),具有一定的參考價(jià)值,感興趣的可以了解一下
    2023-10-10
  • Java中SimpleDateFormat 格式化日期的使用

    Java中SimpleDateFormat 格式化日期的使用

    本文主要介紹了Java中SimpleDateFormat 格式化日期的使用,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-03-03
  • MyBatis詳細(xì)講解DAO代理的使用

    MyBatis詳細(xì)講解DAO代理的使用

    MyBatis允許只聲明一個(gè)dao接口,而無(wú)需寫dao實(shí)現(xiàn)類的方式實(shí)現(xiàn)數(shù)據(jù)庫(kù)操作。前提是必須保證Mapper文件中的<mapper>標(biāo)簽的namespace屬性值必須要和dao接口的類路徑一致,MyBatis容器會(huì)自動(dòng)通過(guò)動(dòng)態(tài)代理生成接口的實(shí)現(xiàn)類
    2022-04-04
  • java進(jìn)制轉(zhuǎn)換工具類實(shí)現(xiàn)減少參數(shù)長(zhǎng)度

    java進(jìn)制轉(zhuǎn)換工具類實(shí)現(xiàn)減少參數(shù)長(zhǎng)度

    這篇文章主要為大家介紹了java進(jìn)制轉(zhuǎn)換工具類實(shí)現(xiàn)減少參數(shù)長(zhǎng)度示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-02-02
  • Java實(shí)現(xiàn)List反轉(zhuǎn)的方法總結(jié)

    Java實(shí)現(xiàn)List反轉(zhuǎn)的方法總結(jié)

    在Java中,反轉(zhuǎn)一個(gè)List意味著將其元素的順序顛倒,使得第一個(gè)元素變成最后一個(gè),最后一個(gè)元素變成第一個(gè),依此類推,這一操作在處理數(shù)據(jù)集合時(shí)非常有用,所以本文給大家總結(jié)了Java實(shí)現(xiàn)List反轉(zhuǎn)的方法,需要的朋友可以參考下
    2024-04-04
  • 淺談Java注解和動(dòng)態(tài)代理

    淺談Java注解和動(dòng)態(tài)代理

    這篇文章主要介紹了Java中有關(guān)注解和動(dòng)態(tài)代理的一些知識(shí),涉及了Annotation、數(shù)據(jù)類型等相關(guān)內(nèi)容,需要的朋友可以參考下。
    2017-09-09
  • 用JAVA實(shí)現(xiàn)單鏈表,檢測(cè)字符串是否是回文串

    用JAVA實(shí)現(xiàn)單鏈表,檢測(cè)字符串是否是回文串

    這篇文章主要介紹了使用JAVA實(shí)現(xiàn)單鏈表,檢測(cè)字符串是否是回文串,幫助大家更好的理解和使用Java,感興趣的朋友可以了解下
    2020-11-11
  • Java圖形用戶界面設(shè)計(jì)(Swing)的介紹

    Java圖形用戶界面設(shè)計(jì)(Swing)的介紹

    看到多數(shù)人提到 Java 就以為是網(wǎng)絡(luò)開發(fā),其實(shí)不是這樣的,Java 也可以開發(fā)應(yīng)用程序,而且可以開發(fā)出漂亮的圖形用戶界面的應(yīng)用程序,因此,我寫下這篇文章,希望能帶你進(jìn)入 Java 圖形用戶界面設(shè)計(jì)之門。
    2016-07-07

最新評(píng)論