最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Elasticsearch安裝?+?CRUD?+?查詢實(shí)戰(zhàn)指南

 更新時(shí)間:2026年03月16日 10:21:01   作者:阿杰真不會(huì)敲代碼  
Elasticsearch是基于Lucene的分布式全文搜索引擎,支持近實(shí)時(shí)搜索、海量數(shù)據(jù)存儲(chǔ)、高可用集群、RESTful?API,廣泛用于日志分析、商品搜索、數(shù)據(jù)可視化等場(chǎng)景,本文給大家介紹Elasticsearch安裝+CRUD+查詢實(shí)戰(zhàn)指南,感興趣的朋友跟隨小編一起看看吧

一、Elasticsearch 是什么??

Elasticsearch(簡(jiǎn)稱 ES) 是基于 Lucene 的分布式全文搜索引擎,支持近實(shí)時(shí)搜索、海量數(shù)據(jù)存儲(chǔ)、高可用集群、RESTful API,廣泛用于日志分析、商品搜索、數(shù)據(jù)可視化等場(chǎng)景。?

在搜索時(shí)為什么我們要用 Elasticsearch,而不是 MySQL 模糊匹配?

在實(shí)際業(yè)務(wù)中,商品搜索、日志檢索、全文搜索 這些場(chǎng)景,MySQL 根本扛不住。

1. MySQL 的like '%關(guān)鍵詞%'為什么慢?

  • 無(wú)法走索引,必須全表掃描
  • 數(shù)據(jù)量越大,速度越慢
  • 不支持分詞,不支持相關(guān)性打分
  • 不支持高亮、同義詞、拼音搜索

簡(jiǎn)單說(shuō):數(shù)據(jù)量一大,like 查詢直接拖垮數(shù)據(jù)庫(kù)。

2. Elasticsearch 為什么快?—— 倒排索引原理

ES 快的核心原因只有一個(gè):它用了 倒排索引(Inverted Index)。

我用最簡(jiǎn)單的方式講清楚:

正常數(shù)據(jù)庫(kù)(正排索引)

文檔1 → 我喜歡華為手機(jī)

文檔2 → 華為手機(jī)信號(hào)好

文檔3 → 蘋果手機(jī)也不錯(cuò)

倒排索引(關(guān)鍵詞 → 文檔)

華為 → 文檔1、文檔2

手機(jī) → 文檔1、文檔2、文檔3

蘋果 → 文檔3 喜歡 → 文檔1

當(dāng)你搜索:“華為”

ES 不需要掃全表,直接查倒排索引:華為 → 文檔 1、文檔 2直接返回,毫秒級(jí)響應(yīng)。

3. 倒排索引三大優(yōu)勢(shì)

  1. 檢索極快:不需要全表掃描
  2. 支持分詞:把一句話切成詞語(yǔ)
  3. 相關(guān)性打分:最匹配的排前面

MySQL 適合存數(shù)據(jù),Elasticsearch 適合搜數(shù)據(jù)。

MySQL 用 B+ 樹,適合精確查詢。

Elasticsearch 用倒排索引,適合全文檢索。

Elasticsearch核心概念?

  • 索引(Index):對(duì)應(yīng) MySQL 的數(shù)據(jù)庫(kù)?
  • 文檔(Document):對(duì)應(yīng) MySQL 的行數(shù)據(jù)?
  • 映射(Mapping):對(duì)應(yīng) MySQL 的表結(jié)構(gòu)?
  • 分片 / 副本:保證分布式存儲(chǔ)與高可用

文檔數(shù)據(jù)會(huì)被序列化為json格式后存儲(chǔ)在elasticsearch

我們統(tǒng)一的把mysql與elasticsearch的概念做一下對(duì)比

Kibana是elastic公司提供的用于操作Elasticsearch的可視化控制臺(tái)。它的功能非常強(qiáng)大,包括:

  • 對(duì)Elasticsearch數(shù)據(jù)的搜索、展示
  • 對(duì)Elasticsearch數(shù)據(jù)的統(tǒng)計(jì)、聚合,并形成圖形化報(bào)表、圖形
  • 對(duì)Elasticsearch的集群狀態(tài)監(jiān)控
  • 它還提供了一個(gè)開(kāi)發(fā)控制臺(tái)(DevTools),在其中對(duì)Elasticsearch的Restful的API接口提供了語(yǔ)法提示

elasticsearch安裝

通過(guò)下面的Docker命令即可安裝單機(jī)版本的elasticsearch:

docker run -d \
  --name es \
  -e "ES_JAVA_OPTS=-Xms512m -Xmx512m" \
  -e "discovery.type=single-node" \
  -v es-data:/usr/share/elasticsearch/data \
  -v es-plugins:/usr/share/elasticsearch/plugins \
  --privileged \
  --network hm-net \
  -p 9200:9200 \
  -p 9300:9300 \
  elasticsearch:7.12.1

訪問(wèn)9200端口,即可看到響應(yīng)的Elasticsearch服務(wù)的基本信息:

安裝kibana

docker run -d \
--name kibana \
-e ELASTICSEARCH_HOSTS=http://es:9200 \
--network=hm-net \
-p 5601:5601  \
kibana:7.12.1

訪問(wèn)5601端口,即可看到控制臺(tái)頁(yè)面:

ik分詞器

Elasticsearch的關(guān)鍵就是倒排索引,而倒排索引依賴于對(duì)文檔內(nèi)容的分詞,而分詞則需要高效、精準(zhǔn)的分詞算法,IK分詞器就是這樣一個(gè)中文分詞算法。

安裝:

docker exec -it es ./bin/elasticsearch-plugin  install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.12.1/elasticsearch-analysis-ik-7.12.1.zip

IK分詞器包含兩種模式:

  • ik_smart:智能語(yǔ)義切分
  • ik_max_word:最細(xì)粒度切分

詞典:隨著互聯(lián)網(wǎng)的發(fā)展出現(xiàn)了很多新的詞語(yǔ),在原有的詞匯列表中并不存在,IK分詞器無(wú)法對(duì)這些詞匯分詞,要想正確分詞,IK分詞器的詞庫(kù)也需要不斷的更新,IK分詞器提供了擴(kuò)展詞匯的功能

在分詞器的配置目錄下

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
        <comment>IK Analyzer 擴(kuò)展配置</comment>
        <!--用戶可以在這里配置自己的擴(kuò)展字典 *** 添加擴(kuò)展詞典-->
        <entry key="ext_dict">ext.dic</entry>
</properties>

改目錄下目錄新建一個(gè) ext.dic,然后在里面添加新的詞匯,重啟后ik分詞器就能正確識(shí)別ext.dic的這些單詞

CRUD操作:

由于Elasticsearch采用的是Restful風(fēng)格的API,因此其請(qǐng)求方式和路徑相對(duì)都比較規(guī)范,而且請(qǐng)求參數(shù)也都采用JSON風(fēng)格。

我們直接基于Kibana的DevTools來(lái)編寫請(qǐng)求做測(cè)試,由于有語(yǔ)法提示,會(huì)非常方便。

.創(chuàng)建索引庫(kù)和映射

基本語(yǔ)法

  • 請(qǐng)求方式:PUT
  • 請(qǐng)求路徑:/索引庫(kù)名,可以自定義
  • 請(qǐng)求參數(shù):mapping映射
PUT /索引庫(kù)名稱
{
  "mappings": {
    "properties": {
      "字段名":{
        "type": "text",
        "analyzer": "ik_smart"
      },
      "字段名2":{
        "type": "keyword",
        "index": "false"
      },
      "字段名3":{
        "properties": {
          "子字段": {
            "type": "keyword"
          }
        }
      },
      // ...略
    }
  }
}

查詢索引庫(kù)

基本語(yǔ)法

  • 請(qǐng)求方式:GET
  • 請(qǐng)求路徑:/索引庫(kù)名
  • 請(qǐng)求參數(shù):無(wú)

比如:GET /item

修改索引庫(kù)

倒排索引結(jié)構(gòu)雖然不復(fù)雜,但是一旦數(shù)據(jù)結(jié)構(gòu)改變(比如改變了分詞器),就需要重新創(chuàng)建倒排索引,這簡(jiǎn)直是災(zāi)難。因此索引庫(kù)一旦創(chuàng)建,無(wú)法修改mapping

雖然無(wú)法修改mapping中已有的字段,但是卻允許添加新的字段到mapping中,因?yàn)椴粫?huì)對(duì)倒排索引產(chǎn)生影響。因此修改索引庫(kù)能做的就是向索引庫(kù)中添加新字段,或者更新索引庫(kù)的基礎(chǔ)屬性。

語(yǔ)法:

PUT /索引庫(kù)名/_mapping
{
  "properties": {
    "新字段名":{
      "type": "integer"
    }
  }
}

刪除索引庫(kù)

語(yǔ)法:

  • 請(qǐng)求方式:DELETE
  • 請(qǐng)求路徑:/索引庫(kù)名
  • 請(qǐng)求參數(shù):無(wú)

文檔操作

有了索引庫(kù),接下來(lái)就可以向索引庫(kù)中添加數(shù)據(jù)了。

4.1 新增文檔(指定 ID)?

PUT /product/_doc/1?
{?
"id": 1,?
"name": "華為Mate70",?
"price": 5999,?
"category": "手機(jī)"?
}?

新增文檔(自動(dòng) ID)

POST /product/_doc?
{?
  "id": 2,?
  "name": "小米14",?
  "price": 4299,?
  "category": "手機(jī)"?
}

4.3 查詢文檔(根據(jù) ID)

GET /product/_doc/1

全量更新文檔(相當(dāng)于覆蓋原文檔)

PUT /product/_doc/1
{
  "id": 1,
  "name": "華為Mate70 Pro",
  "price": 6999,
  "category": "手機(jī)"
}

局部更新文檔(推薦)

POST /product/_update/1
{
  "doc": {
    "price": 6799
  }
}

刪除文檔

DELETE /product/_doc/1

批處理

POST _bulk
{ "index" : { "_index" : "test", "_id" : "1" } }
{ "field1" : "value1" }
{ "delete" : { "_index" : "test", "_id" : "2" } }
{ "create" : { "_index" : "test", "_id" : "3" } }
{ "field1" : "value3" }
{ "update" : {"_id" : "1", "_index" : "test"} }
{ "doc" : {"field2" : "value2"} }
  • index代表新增操作

    • _index:指定索引庫(kù)名

    • _id指定要操作的文檔id

    • { "field1" : "value1" }:則是要新增的文檔內(nèi)容

  • delete代表刪除操作

    • _index:指定索引庫(kù)名

    • _id指定要操作的文檔id

  • update代表更新操作

    • _index:指定索引庫(kù)名

    • _id指定要操作的文檔id

    • { "doc" : {"field2" : "value2"} }:要更新的文檔字段

總結(jié)

  • 創(chuàng)建文檔:POST /{索引庫(kù)名}/_doc/文檔id { json文檔 }

  • 查詢文檔:GET /{索引庫(kù)名}/_doc/文檔id

  • 刪除文檔:DELETE /{索引庫(kù)名}/_doc/文檔id

  • 修改文檔:

    • 全量修改:PUT /{索引庫(kù)名}/_doc/文檔id { json文檔 }

    • 局部修改:POST /{索引庫(kù)名}/_update/文檔id { "doc": {字段}}

在java中訪問(wèn)客戶端

引入依賴

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

因?yàn)镾pringBoot默認(rèn)的ES版本是7.17.10,所以我們需要覆蓋默認(rèn)的ES版本

  <properties>
      <maven.compiler.source>11</maven.compiler.source>
      <maven.compiler.target>11</maven.compiler.target>
      <elasticsearch.version>7.12.1</elasticsearch.version>
  </properties>

初始化RestHighLevelClient:

RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(
        HttpHost.create("http://192.168.150.101:9200")
));
package com.hmall.item.es;

import org.apache.http.HttpHost;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.io.IOException;

public class IndexTest {

    private RestHighLevelClient client;

    @BeforeEach
    void setUp() {
        this.client = new RestHighLevelClient(RestClient.builder(
                HttpHost.create("http://192.168.150.101:9200")
        ));
    }

    @Test
    void testConnect() {
        System.out.println(client);
    }

    @AfterEach
    void tearDown() throws IOException {
        this.client.close();
    }
}

RestClient操作索引庫(kù)

代碼分為三步:

  • 1)創(chuàng)建Request對(duì)象。

    • 因?yàn)槭莿?chuàng)建索引庫(kù)的操作,因此Request是CreateIndexRequest。

  • 2)添加請(qǐng)求參數(shù)

    • 其實(shí)就是Json格式的Mapping映射參數(shù)。因?yàn)閖son字符串很長(zhǎng),這里是定義了靜態(tài)字符串常量MAPPING_TEMPLATE,讓代碼看起來(lái)更加優(yōu)雅。

  • 3)發(fā)送請(qǐng)求

    • client.indices()方法的返回值是IndicesClient類型,封裝了所有與索引庫(kù)操作有關(guān)的方法。例如創(chuàng)建索引、刪除索引、判斷索引是否存在等

RestClient操作文檔

新增文檔

  // 1.準(zhǔn)備Request對(duì)象
    IndexRequest request = new IndexRequest("items").id(itemDoc.getId());
    // 2.準(zhǔn)備Json文檔
    request.source(doc, XContentType.JSON);
    // 3.發(fā)送請(qǐng)求
    client.index(request, RequestOptions.DEFAULT);
  • 1)創(chuàng)建Request對(duì)象,這里是IndexRequest,因?yàn)樘砑游臋n就是創(chuàng)建倒排索引的過(guò)程

  • 2)準(zhǔn)備請(qǐng)求參數(shù),本例中就是Json文檔

  • 3)發(fā)送請(qǐng)求

查詢文檔

刪除文檔

@Test
void testDeleteDocument() throws IOException {
    // 1.準(zhǔn)備Request,兩個(gè)參數(shù),第一個(gè)是索引庫(kù)名,第二個(gè)是文檔id
    DeleteRequest request = new DeleteRequest("item", "100002644680");
    // 2.發(fā)送請(qǐng)求
    client.delete(request, RequestOptions.DEFAULT);
}

修改文檔

修改我們講過(guò)兩種方式:

  • 全量修改:本質(zhì)是先根據(jù)id刪除,再新增

  • 局部修改:修改文檔中的指定字段值

在RestClient的API中,全量修改與新增的API完全一致,判斷依據(jù)是ID:

  • 如果新增時(shí),ID已經(jīng)存在,則修改

  • 如果新增時(shí),ID不存在,則新增

這里不再贅述,我們主要關(guān)注局部修改的API即可。

.批量導(dǎo)入文檔

@Test
void testBulk() throws IOException {
    // 1.創(chuàng)建Request
    BulkRequest request = new BulkRequest();
    // 2.準(zhǔn)備請(qǐng)求參數(shù)
    request.add(new IndexRequest("items").id("1").source("json doc1", XContentType.JSON));
    request.add(new IndexRequest("items").id("2").source("json doc2", XContentType.JSON));
    // 3.發(fā)送請(qǐng)求
    client.bulk(request, RequestOptions.DEFAULT);
}

總結(jié)

  • 初始化RestHighLevelClient

  • 創(chuàng)建XxxRequest。

    • XXX是Index、GetUpdate、Delete、Bulk

  • 準(zhǔn)備參數(shù)(IndexUpdate、Bulk時(shí)需要)

  • 發(fā)送請(qǐng)求。

    • 調(diào)用RestHighLevelClient#.xxx()方法,xxx是indexget、update、delete、bulk

  • 解析結(jié)果(Get時(shí)需要)

DSL查詢

在 Elasticsearch 中,所有復(fù)雜查詢都是通過(guò) DSL(Domain Specific Language) 來(lái)實(shí)現(xiàn)的。簡(jiǎn)單說(shuō):DSL = 用 JSON 寫查詢語(yǔ)句

所有查詢都分為兩類:

(1)查詢上下文(Query Context)

會(huì)計(jì)算相關(guān)性分?jǐn)?shù)(_score)用于:全文搜索、模糊匹配例:match、match_phrase、multi_match

(2)過(guò)濾上下文(Filter Context)

不計(jì)算分?jǐn)?shù),只判斷 是 / 否速度極快,會(huì)自動(dòng)緩存用于:精確匹配、范圍、布爾過(guò)濾例:term、range、bool filter

最常用 DSL 查詢

match 全文檢索

對(duì)字段分詞后匹配

{
  "query": {
    "match": {
      "name": "拉桿箱"
    }
  }
}

查詢結(jié)果一般格式

term 精確匹配(不分詞)

用于 keyword、數(shù)字、布爾

{
  "query": {
    "term": {
      "category": "手機(jī)"
    }
  }
}

range 范圍查詢

{
  "query": {
    "range": {
      "price": {
        "gte": 3000,
        "lte": 8000
      }
    }
  }
}

搜索的關(guān)鍵字高亮

GET /{索引庫(kù)名}/_search
{
  "query": {
    "match": {
      "搜索字段": "搜索關(guān)鍵字"
    }
  },
  "highlight": {
    "fields": {
      "高亮字段名稱": {
        "pre_tags": "<em>",
        "post_tags": "</em>"
      }
    }
  }
}

bool :組合查詢包含四個(gè)子句:

  • must:必須滿足(算分)
  • should:滿足任意一個(gè)
  • must_not:必須不滿足
  • filter:必須滿足(過(guò)濾,不計(jì)分

示例

{
  "query": {
    "bool": {
      "must": [
        { "match": { "name": "手機(jī)" } }
      ],
      "filter": [
        { "range": { "price": { "gte": 2000 } } }
      ]
    }
  }
}

分頁(yè)、排序、指定返回字段

{
  "from": 0,
  "size": 10,
  "_source": ["name", "price"],
  "sort": [
    { "price": { "order": "desc" } }
  ],
  "query": {
    "match_all": {}
  }
}

總結(jié)

查詢的DSL是一個(gè)大的JSON對(duì)象,包含下列屬性:

  • query:查詢條件

  • fromsize:分頁(yè)條件

  • sort:排序條件

  • highlight:高亮條件

Java中RestClient查詢

  • 第一步,創(chuàng)建SearchRequest對(duì)象,指定索引庫(kù)名

  • 第二步,利用request.source()構(gòu)建DSL,DSL中可以包含查詢、分頁(yè)、排序、高亮等

    • query():代表查詢條件,利用QueryBuilders.matchAllQuery()構(gòu)建一個(gè)match_all查詢的DSL

  • 第三步,利用client.search()發(fā)送請(qǐng)求,得到響應(yīng)

request.source(),它構(gòu)建的就是DSL中的完整JSON參數(shù)。其中包含了query、sortfrom、size、highlight等所有功能

QueryBuilders,其中包含了葉子查詢、復(fù)合查詢

解析響應(yīng)結(jié)果

elasticsearch返回的結(jié)果是一個(gè)JSON字符串,結(jié)構(gòu)包含:

  • hits:命中的結(jié)果

    • total:總條數(shù),其中的value是具體的總條數(shù)值

    • max_score:所有結(jié)果中得分最高的文檔的相關(guān)性算分

    • hits:搜索結(jié)果的文檔數(shù)組,其中的每個(gè)文檔都是一個(gè)json對(duì)象

      • _source:文檔中的原始數(shù)據(jù),也是json對(duì)象

因此,我們解析響應(yīng)結(jié)果,就是逐層解析JSON字符串,流程如下:

  • SearchHits:通過(guò)response.getHits()獲取,就是JSON中的最外層的hits,代表命中的結(jié)果

    • SearchHits#getTotalHits().value:獲取總條數(shù)信息

    • SearchHits#getHits():獲取SearchHit數(shù)組,也就是文檔數(shù)組

      • SearchHit#getSourceAsString():獲取文檔結(jié)果中的_source,也就是原始的json文檔數(shù)據(jù)

葉子查詢

所有的查詢條件都是由QueryBuilders來(lái)構(gòu)建的,葉子查詢也不例外。因此整套代碼中變化的部分僅僅是query條件構(gòu)造的方式,其它不動(dòng)。

match查詢:

@Test
void testMatch() throws IOException {
    // 1.創(chuàng)建Request
    SearchRequest request = new SearchRequest("items");
    // 2.組織請(qǐng)求參數(shù)
    request.source().query(QueryBuilders.matchQuery("name", "脫脂牛奶"));
    // 3.發(fā)送請(qǐng)求
    SearchResponse response = client.search(request, RequestOptions.DEFAULT);
    // 4.解析響應(yīng)
    handleResponse(response);
}

multi_match查詢

@Test
void testMultiMatch() throws IOException {
    // 1.創(chuàng)建Request
    SearchRequest request = new SearchRequest("items");
    // 2.組織請(qǐng)求參數(shù)
    request.source().query(QueryBuilders.multiMatchQuery("脫脂牛奶", "name", "category"));
    // 3.發(fā)送請(qǐng)求
    SearchResponse response = client.search(request, RequestOptions.DEFAULT);
    // 4.解析響應(yīng)
    handleResponse(response);
}

還有range查詢:

@Test
void testRange() throws IOException {
    // 1.創(chuàng)建Request
    SearchRequest request = new SearchRequest("items");
    // 2.組織請(qǐng)求參數(shù)
    request.source().query(QueryBuilders.rangeQuery("price").gte(10000).lte(30000));
    // 3.發(fā)送請(qǐng)求
    SearchResponse response = client.search(request, RequestOptions.DEFAULT);
    // 4.解析響應(yīng)
    handleResponse(response);
}

term查詢:

@Test
void testTerm() throws IOException {
    // 1.創(chuàng)建Request
    SearchRequest request = new SearchRequest("items");
    // 2.組織請(qǐng)求參數(shù)
    request.source().query(QueryBuilders.termQuery("brand", "華為"));
    // 3.發(fā)送請(qǐng)求
    SearchResponse response = client.search(request, RequestOptions.DEFAULT);
    // 4.解析響應(yīng)
    handleResponse(response);
}

復(fù)合查詢

復(fù)合查詢也是由QueryBuilders來(lái)構(gòu)建,我們以bool查詢?yōu)槔?,DSL和JavaAPI的對(duì)比如圖:

@Test
void testBool() throws IOException {
    // 1.創(chuàng)建Request
    SearchRequest request = new SearchRequest("items");
    // 2.組織請(qǐng)求參數(shù)
    // 2.1.準(zhǔn)備bool查詢
    BoolQueryBuilder bool = QueryBuilders.boolQuery();
    // 2.2.關(guān)鍵字搜索
    bool.must(QueryBuilders.matchQuery("name", "脫脂牛奶"));
    // 2.3.品牌過(guò)濾
    bool.filter(QueryBuilders.termQuery("brand", "德亞"));
    // 2.4.價(jià)格過(guò)濾
    bool.filter(QueryBuilders.rangeQuery("price").lte(30000));
    request.source().query(bool);
    // 3.發(fā)送請(qǐng)求
    SearchResponse response = client.search(request, RequestOptions.DEFAULT);
    // 4.解析響應(yīng)
    handleResponse(response);
}

排序和分頁(yè)

@Test
void testPageAndSort() throws IOException {
    int pageNo = 1, pageSize = 5;

    // 1.創(chuàng)建Request
    SearchRequest request = new SearchRequest("items");
    // 2.組織請(qǐng)求參數(shù)
    // 2.1.搜索條件參數(shù)
    request.source().query(QueryBuilders.matchQuery("name", "脫脂牛奶"));
    // 2.2.排序參數(shù)
    request.source().sort("price", SortOrder.ASC);
    // 2.3.分頁(yè)參數(shù)
    request.source().from((pageNo - 1) * pageSize).size(pageSize);
    // 3.發(fā)送請(qǐng)求
    SearchResponse response = client.search(request, RequestOptions.DEFAULT);
    // 4.解析響應(yīng)
    handleResponse(response);
}

高亮

@Test
void testHighlight() throws IOException {
    // 1.創(chuàng)建Request
    SearchRequest request = new SearchRequest("items");
    // 2.組織請(qǐng)求參數(shù)
    // 2.1.query條件
    request.source().query(QueryBuilders.matchQuery("name", "脫脂牛奶"));
    // 2.2.高亮條件
    request.source().highlighter(
            SearchSourceBuilder.highlight()
                    .field("name")
                    .preTags("<em>")
                    .postTags("</em>")
    );
    // 3.發(fā)送請(qǐng)求
    SearchResponse response = client.search(request, RequestOptions.DEFAULT);
    // 4.解析響應(yīng)
    handleResponse(response);
}
private void handleResponse(SearchResponse response) {
    SearchHits searchHits = response.getHits();
    // 1.獲取總條數(shù)
    long total = searchHits.getTotalHits().value;
    System.out.println("共搜索到" + total + "條數(shù)據(jù)");
    // 2.遍歷結(jié)果數(shù)組
    SearchHit[] hits = searchHits.getHits();
    for (SearchHit hit : hits) {
        // 3.得到_source,也就是原始json文檔
        String source = hit.getSourceAsString();
        // 4.反序列化
        ItemDoc item = JSONUtil.toBean(source, ItemDoc.class);
        // 5.獲取高亮結(jié)果
        Map<String, HighlightField> hfs = hit.getHighlightFields();
        if (CollUtils.isNotEmpty(hfs)) {
            // 5.1.有高亮結(jié)果,獲取name的高亮結(jié)果
            HighlightField hf = hfs.get("name");
            if (hf != null) {
                // 5.2.獲取第一個(gè)高亮結(jié)果片段,就是商品名稱的高亮值
                String hfName = hf.getFragments()[0].string();
                item.setName(hfName);
            }
        }
        System.out.println(item);
    }
}

數(shù)據(jù)聚合

聚合 = 對(duì)數(shù)據(jù)進(jìn)行統(tǒng)計(jì)、分組、求和、平均值、最大值、最小值、計(jì)數(shù)。

你可以把它理解為:MySQL 的 group by + sum + avg + count 超級(jí)加強(qiáng)版。

聚合的基本結(jié)構(gòu)

{
  "size": 0,       // 不返回原始數(shù)據(jù),只返回統(tǒng)計(jì)結(jié)果
  "aggs": {        // 聚合固定寫 aggs
    "自定義名稱": {
      "聚合類型": {
        "field": "字段名"
      }
    }
  }
}

聚合常見(jiàn)的有三類:

  • 桶(Bucket聚合:用來(lái)對(duì)文檔做分組

    • TermAggregation:按照文檔字段值分組,例如按照品牌值分組、按照國(guó)家分組

    • Date Histogram:按照日期階梯分組,例如一周為一組,或者一月為一組

  • 度量(Metric聚合:用以計(jì)算一些值,比如:最大值、最小值、平均值等

    • Avg:求平均值

    • Max:求最大值

    • Min:求最小值

    • Stats:同時(shí)求maxminavg、sum

  • 管道(pipeline聚合:其它聚合的結(jié)果為基礎(chǔ)做進(jìn)一步運(yùn)算

注意:參加聚合的字段必須是keyword、日期、數(shù)值、布爾類型

.Bucket聚合

例如我們要統(tǒng)計(jì)所有商品中共有哪些商品分類,其實(shí)就是以分類(category)字段對(duì)數(shù)據(jù)分組。category值一樣的放在同一組,屬于Bucket聚合中的Term聚合。

GET /items/_search
{
  "size": 0, 
  "aggs": {
    "category_agg": {
      "terms": {
        "field": "category",
        "size": 20
      }
    }
  }
}
  • size:設(shè)置size為0,就是每頁(yè)查0條,則結(jié)果中就不包含文檔,只包含聚合

  • aggs:定義聚合

    • category_agg:聚合名稱,自定義,但不能重復(fù)

      • terms:聚合的類型,按分類聚合,所以用term

        • field:參與聚合的字段名稱

        • size:希望返回的聚合結(jié)果的最大數(shù)量

查詢的結(jié)果:

嵌套聚合

先按分類分組 → 再求每組的平均價(jià)格

GET /product/_search
{
  "size": 0,
  "aggs": {
    "group_by_category": {
      "terms": {
        "field": "category"
      },
      "aggs": {
        "avg_price": {
          "avg": {
            "field": "price"
          }
        }
      }
    }
  }
}

結(jié)果

手機(jī) → 平均價(jià)格 5800

電腦 → 平均價(jià)格 7200

按價(jià)格區(qū)間統(tǒng)計(jì)(range 聚合)

電商最常用:價(jià)格分段統(tǒng)計(jì)

GET /product/_search
{
  "size": 0,
  "aggs": {
    "price_range": {
      "range": {
        "field": "price",
        "ranges": [
          { "to": 3000 },
          { "from": 3000, "to": 6000 },
          { "from": 6000 }
        ]
      }
    }
  }
}

對(duì)搜索結(jié)果聚合。

例如,我想知道價(jià)格高于3000元的手機(jī)品牌有哪些

GET /items/_search
{
  "query": {
    "bool": {
      "filter": [
        {
          "term": {
            "category": "手機(jī)"
          }
        },
        {
          "range": {
            "price": {
              "gte": 300000
            }
          }
        }
      ]
    }
  }, 
  "size": 0, 
  "aggs": {
    "brand_agg": {
      "terms": {
        "field": "brand",
        "size": 20
      }
    }
  }
}

求平均值(avg)

GET /product/_search
{
  "size": 0,
  "aggs": {
    "avg_price": {
      "avg": {
        "field": "price"
      }
    }
  }
}

求和(sum)

GET /product/_search
{
  "size": 0,
  "aggs": {
    "total_price": {
      "sum": {
        "field": "price"
      }
    }
  }
}

最大值、最小值(max /min)

GET /product/_search
{
  "size": 0,
  "aggs": {
    "max_price": {
      "max": {
        "field": "price"
      }
    },
    "min_price": {
      "min": {
        "field": "price"
      }
    }
  }
}

例:

統(tǒng)計(jì)價(jià)格高于3000的手機(jī)品牌,形成一個(gè)個(gè)桶。對(duì)桶內(nèi)的商品做運(yùn)算,獲取每個(gè)品牌價(jià)格的最小值、最大值、平均值。

GET /items/_search
{
  "query": {
    "bool": {
      "filter": [
        {
          "term": {
            "category": "手機(jī)"
          }
        },
        {
          "range": {
            "price": {
              "gte": 300000
            }
          }
        }
      ]
    }
  }, 
  "size": 0, 
  "aggs": {
    "brand_agg": {
      "terms": {
        "field": "brand",
        "size": 20
      },
      "aggs": {
        "stats_meric": {
          "stats": {
            "field": "price"
          }
        }
      }
    }
  }
}

stat聚合,可以同時(shí)獲取minmax、avg的結(jié)果。

.RestClient實(shí)現(xiàn)聚合

可以看到在DSL中,aggs聚合條件與query條件是同一級(jí)別,都屬于查詢JSON參數(shù)。因此依然是利用request.source()方法來(lái)設(shè)置。

不過(guò)聚合條件的要利用AggregationBuilders這個(gè)工具類來(lái)構(gòu)造。DSL與JavaAPI的語(yǔ)法對(duì)比如下:

聚合結(jié)果與搜索文檔同一級(jí)別,因此需要單獨(dú)獲取和解析。具體解析語(yǔ)法如下:

@Test
void testAgg() throws IOException {
    // 1.創(chuàng)建Request
    SearchRequest request = new SearchRequest("items");
    // 2.準(zhǔn)備請(qǐng)求參數(shù)
    BoolQueryBuilder bool = QueryBuilders.boolQuery()
            .filter(QueryBuilders.termQuery("category", "手機(jī)"))
            .filter(QueryBuilders.rangeQuery("price").gte(300000));
    request.source().query(bool).size(0);
    // 3.聚合參數(shù)
    request.source().aggregation(
            AggregationBuilders.terms("brand_agg").field("brand").size(5)
    );
    // 4.發(fā)送請(qǐng)求
    SearchResponse response = client.search(request, RequestOptions.DEFAULT);
    // 5.解析聚合結(jié)果
    Aggregations aggregations = response.getAggregations();
    // 5.1.獲取品牌聚合
    Terms brandTerms = aggregations.get("brand_agg");
    // 5.2.獲取聚合中的桶
    List<? extends Terms.Bucket> buckets = brandTerms.getBuckets();
    // 5.3.遍歷桶內(nèi)數(shù)據(jù)
    for (Terms.Bucket bucket : buckets) {
        // 5.4.獲取桶內(nèi)key
        String brand = bucket.getKeyAsString();
        System.out.print("brand = " + brand);
        long count = bucket.getDocCount();
        System.out.println("; count = " + count);
    }
}

到此這篇關(guān)于Elasticsearch安裝 + CRUD + 查詢實(shí)戰(zhàn)指南的文章就介紹到這了,更多相關(guān)Elasticsearch 入門到實(shí)戰(zhàn)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論

潢川县| 延长县| 潜江市| 东光县| 资阳市| 凤翔县| 丹阳市| 涞源县| 邯郸市| 青海省| 揭西县| 鲁甸县| 伊川县| 富蕴县| 嘉义市| 丰顺县| 友谊县| 师宗县| 弋阳县| 兴宁市| 尚义县| 翁牛特旗| 大余县| 郴州市| 绥江县| 彰化县| 泰来县| 杨浦区| 新巴尔虎右旗| 宣武区| 湾仔区| 沈阳市| 晋城| 三门县| 汤原县| 读书| 乌鲁木齐县| 揭东县| 嘉祥县| 安义县| 天长市|