天天看點

elasticsearch DSL指令索引庫操作 文檔操作DSL 查詢文檔

目錄

索引庫操作

1.mapping映射屬性

2.建立索引庫和映射

3.查詢索引庫

 4.修改索引庫

 5.删除索引庫

 文檔操作

1.新增文檔

 2.查詢文檔

 3.删除文檔

4.修改文檔

全量修改

增量修改

DSL 查詢文檔

1.查詢所有

​編輯

2.全文檢索查詢

單字段查詢

多字段查詢

3.精準查詢

term查詢

 rang查詢

 4.地理坐标查詢

矩形範圍查詢

附近查詢

5.複合查詢

相關性算分

6.算分函數查詢

7.布爾查詢(多條件查詢)

8.排序

普通字段排序

地理坐标排序 

9.分頁

分頁深度問題

分頁小結:

10.搜尋結果 -- 高亮

11.資料聚合

Bucket聚合

 聚合結果排序

限定聚合範圍

 Metric聚合文法

12.自動補全查詢

索引庫操作

索引庫就類似資料庫表,mapping映射就類似表的結構。

我們要向es中存儲資料,必須先建立“庫”和“表”。

1.mapping映射屬性

mapping是對索引庫中文檔的限制,常見的mapping屬性包括:

  • type:字段資料類型,常見的簡單類型有:
    • 字元串:text(可分詞的文本)、keyword(精确值,例如:品牌、國家、ip位址)
    • 數值:long、integer、short、byte、double、float、
    • 布爾:boolean
    • 日期:date
    • 對象:object
  • index:是否建立反向索引,預設為true
  • analyzer:使用哪種分詞器
  • properties:該字段的子字段

例如下面的json文檔:

{
    "age": 21,
    "weight": 52.1,
    "isMarried": false,
    "info": "四大美人之一貂蟬",
    "email": "[email protected]",
    "score": [99.1, 99.5, 98.9],
    "name": {
        "firstName": "貂",
        "lastName": "蟬"
    }
}
           

對應的每個字段映射(mapping):

  • age:類型為 integer;參與搜尋,是以需要index為true;無需分詞器
  • weight:類型為float;參與搜尋,是以需要index為true;無需分詞器
  • isMarried:類型為boolean;參與搜尋,是以需要index為true;無需分詞器
  • info:類型為字元串,需要分詞,是以是text;參與搜尋,是以需要index為true;需要分詞器
  • email:類型為字元串,但是不需要分詞,是以是keyword;不參與搜尋,是以需要index為false;無需分詞器
  • score:雖然是數組,但是我們隻看元素的類型,類型為float;參與搜尋,是以需要index為true;無需分詞器(es 沒有數組類型,一個字段可以有多個值)
  • name:類型為object,需要定義多個子屬性
    • name.firstName;類型為字元串,但是不需要分詞,是以是keyword;參與搜尋,是以需要index為true;無需分詞器
    • name.lastName;類型為字元串,但是不需要分詞,是以是keyword;參與搜尋,是以需要index為true;無需分詞器

2.建立索引庫和映射

基本文法:

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

示例:

#建立索引庫
PUT /lb
{
  "mappings": {
    "properties": {
      "info": {
        "type": "text",
        "analyzer": "ik_smart"
      },
      "email": {
        "type": "keyword",
        "index": false
      },
      "name": {
        "properties": {
          "firstName": {
            "type": "keyword"
          },
          "lastName": {
            "type": "keyword"
          }
        }
      }
    }
  }
}
           
elasticsearch DSL指令索引庫操作 文檔操作DSL 查詢文檔

3.查詢索引庫

基本文法:

  • 請求方式:GET
  • 請求路徑:/索引庫名
  • 請求參數:無
GET /索引庫名
           
elasticsearch DSL指令索引庫操作 文檔操作DSL 查詢文檔

 4.修改索引庫

反向索引結構雖然不複雜,但是一旦資料結構改變(比如改變了分詞器),就需要重新建立反向索引,這簡直是災難。是以索引庫一旦建立,無法修改mapping。

雖然無法修改mapping中已有的字段,但是卻允許添加新的字段到mapping中,因為不會對反向索引産生影響。

PUT /索引庫名/_mapping
{
  "properties": {
    "新字段名":{
      "type": "integer"
    }
  }
}
           
elasticsearch DSL指令索引庫操作 文檔操作DSL 查詢文檔

 5.删除索引庫

DELETE /索引庫名
           
elasticsearch DSL指令索引庫操作 文檔操作DSL 查詢文檔

 文檔操作

1.新增文檔

POST /索引庫名/_doc/文檔id
{
    "字段1": "值1",
    "字段2": "值2",
    "字段3": {
        "子屬性1": "值3",
        "子屬性2": "值4"
    },
    // ...
}
           

示例

POST /heima/_doc/1
{
    "info": "黑馬程式員Java講師",
    "email": "[email protected]",
    "name": {
        "firstName": "雲",
        "lastName": "趙"
    }
}
           
elasticsearch DSL指令索引庫操作 文檔操作DSL 查詢文檔

 2.查詢文檔

根據rest風格,新增是post,查詢應該是get,不過查詢一般都需要條件,這裡我們把文檔id帶上。

GET /{索引庫名稱}/_doc/{id}
           

通過kibana檢視資料:

GET /heima/_doc/1
           
elasticsearch DSL指令索引庫操作 文檔操作DSL 查詢文檔

 3.删除文檔

删除使用DELETE請求,同樣,需要根據id進行删除:

DELETE /{索引庫名}/_doc/id值
           
# 根據id删除資料
DELETE /heima/_doc/1
           
elasticsearch DSL指令索引庫操作 文檔操作DSL 查詢文檔

4.修改文檔

 修改有兩種方式:

  • 全量修改:直接覆寫原來的文檔
  • 增量修改:修改文檔中的部分字段

全量修改

全量修改是覆寫原來的文檔,其本質是:

  • 根據指定的id删除文檔
  • 新增一個相同id的文檔

注意:如果根據id删除時,id不存在,第二步的新增也會執行,也就從修改變成了新增操作了。

PUT /{索引庫名}/_doc/文檔id
{
    "字段1": "值1",
    "字段2": "值2",
    // ... 略
}
           
PUT /heima/_doc/1
{
    "info": "黑馬程式員進階Java講師",
    "email": "[email protected]",
    "name": {
        "firstName": "雲",
        "lastName": "趙"
    }
}
           

增量修改

增量修改是隻修改指定id比對的文檔中的部分字段。

POST /{索引庫名}/_update/文檔id
{
    "doc": {
         "字段名": "新的值",
    }
}
           
POST /heima/_update/1
{
  "doc": {
    "email": "[email protected]"
  }
}
           

DSL 查詢文檔

1.查詢所有

// 查詢所有
GET /indexName/_search
{
  "query": {
    "match_all": {}
  }
}
           

elasticsearch DSL指令索引庫操作 文檔操作DSL 查詢文檔

2.全文檢索查詢

全文檢索查詢的基本流程如下:

  • 對使用者搜尋的内容做分詞,得到詞條
  • 根據詞條去反向索引庫中比對,得到文檔id
  • 根據文檔id找到文檔,傳回給使用者

比較常用的場景包括:

  • 商城的輸入框搜尋
  • 百度輸入框搜尋
elasticsearch DSL指令索引庫操作 文檔操作DSL 查詢文檔

 因為是拿着詞條去比對,是以參與搜尋的字段也必須是可分詞的text類型的字段

單字段查詢

  • match查詢:單字段查詢
GET /indexName/_search
{
  "query": {
    "match": {
      "FIELD": "TEXT"
    }
  }
}
           
elasticsearch DSL指令索引庫操作 文檔操作DSL 查詢文檔

 “all”是可分詞字段

多字段查詢

  • multi_match查詢:多字段查詢,任意一個字段符合條件就算符合查詢條件

match查詢文法如下

GET /indexName/_search
{
  "query": {
    "multi_match": {
      "query": "TEXT",
      "fields": ["FIELD1", " FIELD12"]
    }
  }
}
           
elasticsearch DSL指令索引庫操作 文檔操作DSL 查詢文檔

 多字段查詢和利用把這幾個多字段copy_to到all字段,查詢結果一樣,但是,搜尋字段越多,對查詢性能影響越大,是以建議采用copy_to,然後單字段查詢的方式。

elasticsearch DSL指令索引庫操作 文檔操作DSL 查詢文檔

3.精準查詢

精确查詢一般是查找keyword、數值、日期、boolean等類型字段。是以不會對搜尋條件分詞。常見的有:

  • term:根據詞條精确值查詢
  • range:根據值的範圍查詢

term查詢

  • term查詢:根據詞條精确比對,一般搜尋keyword類型、數值類型、布爾類型、日期類型字段

1.查詢分詞字段,則查詢到包含該查詢條件的文檔

2.查詢精準字段,則字段内容必須和查詢條件完全一緻,否則插叙不到

// term查詢
GET /indexName/_search
{
  "query": {
    "term": {
      "FIELD": {
        "value": "VALUE"
      }
    }
  }
}
           
elasticsearch DSL指令索引庫操作 文檔操作DSL 查詢文檔
elasticsearch DSL指令索引庫操作 文檔操作DSL 查詢文檔

 rang查詢

  • range查詢:根據數值範圍查詢,可以是數值、日期的範圍

範圍查詢,一般應用在對數值類型做範圍過濾的時候。比如做價格範圍過濾。

// range查詢
GET /indexName/_search
{
  "query": {
    "range": {
      "FIELD": {
        "gte": 10, // 這裡的gte代表大于等于,gt則代表大于
        "lte": 20 // lte代表小于等于,lt則代表小于
      }
    }
  }
}
           
elasticsearch DSL指令索引庫操作 文檔操作DSL 查詢文檔

 4.地理坐标查詢

所謂的地理坐标查詢,其實就是根據經緯度查詢,官方文檔:Geo queries | Elasticsearch Guide [8.4] | Elastic

常見的使用場景包括:

  • 攜程:搜尋我附近的酒店
  • 滴滴:搜尋我附近的計程車
  • 微信:搜尋我附近的人

矩形範圍查詢

矩形範圍查詢,也就是geo_bounding_box查詢,查詢坐标落在某個矩形範圍的所有文檔:

elasticsearch DSL指令索引庫操作 文檔操作DSL 查詢文檔

查詢時,需要指定矩形的左上、右下兩個點的坐标,然後畫出一個矩形,落在該矩形内的都是符合條件的點。

文法如下:

// geo_bounding_box查詢
GET /indexName/_search
{
  "query": {
    "geo_bounding_box": {
      "FIELD": {
        "top_left": { // 左上點
          "lat": 31.1,
          "lon": 121.5
        },
        "bottom_right": { // 右下點
          "lat": 30.9,
          "lon": 121.7
        }
      }
    }
  }
}
           

附近查詢

附近查詢,也叫做距離查詢(geo_distance):查詢到指定中心點小于某個距離值的所有文檔。

換句話來說,在地圖上找一個點作為圓心,以指定距離為半徑,畫一個圓,落在圓内的坐标都算符合條件:

elasticsearch DSL指令索引庫操作 文檔操作DSL 查詢文檔
// geo_distance 查詢
GET /indexName/_search
{
  "query": {
    "geo_distance": {
      "distance": "15km", // 半徑
      "FIELD": "31.21,121.5" // 圓心
    }
  }
}
           

查詢15km内的酒店

elasticsearch DSL指令索引庫操作 文檔操作DSL 查詢文檔

發現47家酒店,縮小範圍 

elasticsearch DSL指令索引庫操作 文檔操作DSL 查詢文檔

5.複合查詢

複合(compound)查詢:複合查詢可以将其它簡單查詢組合起來,實作更複雜的搜尋邏輯。常見的有兩種:

  • fuction score:算分函數查詢,可以控制文檔相關性算分,控制文檔排名
  • bool query:布爾查詢,利用邏輯關系組合多個其它的查詢,實作複雜搜尋

相關性算分

當我們利用match查詢時,文檔結果會根據與搜尋詞條的關聯度打分(_score),傳回結果時按照分值降序排列。

例如,我們搜尋 "虹橋如家",結果如下:

[
  {
    "_score" : 17.850193,
    "_source" : {
      "name" : "虹橋如家酒店真不錯",
    }
  },
  {
    "_score" : 12.259849,
    "_source" : {
      "name" : "外灘如家酒店真不錯",
    }
  },
  {
    "_score" : 11.91091,
    "_source" : {
      "name" : "迪士尼如家酒店真不錯",
    }
  }
]
           

在elasticsearch中,早期使用的打分算法是TF-IDF算法,公式如下:

elasticsearch DSL指令索引庫操作 文檔操作DSL 查詢文檔

 在後來的5.1版本更新中,elasticsearch将算法改進為BM25算法,公式如下:

elasticsearch DSL指令索引庫操作 文檔操作DSL 查詢文檔

TF-IDF算法有一各缺陷,就是詞條頻率越高,文檔得分也會越高,單個詞條對文檔影響較大。而BM25則會讓單個詞條的算分有一個上限,曲線更加平滑:

elasticsearch DSL指令索引庫操作 文檔操作DSL 查詢文檔

6.算分函數查詢

根據相關度打分是比較合理的需求,但合理的不一定是産品經理需要的。

以XX為例,你搜尋的結果中,并不是相關度越高排名越靠前,而是誰掏的錢多排名就越靠前.

要想認為控制相關性算分,就需要利用elasticsearch中的function score 查詢了。

文法說明

elasticsearch DSL指令索引庫操作 文檔操作DSL 查詢文檔

 function score 查詢中包含四部分内容:

  • 原始查詢條件:query部分,基于這個條件搜尋文檔,并且基于BM25算法給文檔打分,原始算分(query score)
  • 過濾條件:filter部分,符合該條件的文檔才會重新算分
  • 算分函數:符合filter條件的文檔要根據這個函數做運算,得到的函數算分(function score),有四種函數
    • weight:函數結果是常量
    • field_value_factor:以文檔中的某個字段值作為函數結果
    • random_score:以随機數作為函數結果
    • script_score:自定義算分函數算法
  • 運算模式:算分函數的結果、原始查詢的相關性算分,兩者之間的運算方式,包括:
    • multiply:相乘
    • replace:用function score替換query score
    • 其它,例如:sum、avg、max、min

function score的運作流程如下:

  • 1)根據原始條件查詢搜尋文檔,并且計算相關性算分,稱為原始算分(query score)
  • 2)根據過濾條件,過濾文檔
  • 3)符合過濾條件的文檔,基于算分函數運算,得到函數算分(function score)
  • 4)将原始算分(query score)和函數算分(function score)基于運算模式做運算,得到最終結果,作為相關性算分。

是以,其中的關鍵點是:

  • 過濾條件:決定哪些文檔的算分被修改
  • 算分函數:決定函數算分的算法
  • 運算模式:決定最終算分結果

需求:給“如家”這個品牌的酒店排名靠前一些

翻譯一下這個需求,轉換為之前說的四個要點:

  • 原始條件:不确定,可以任意變化
  • 過濾條件:brand = "如家"
  • 算分函數:可以簡單粗暴,直接給固定的算分結果,weight
  • 運算模式:比如求和
是以最終的DSL語句如下:
GET /hotel/_search
{
  "query": {
    "function_score": {
      "query": {  .... }, // 原始查詢,可以是任意條件
      "functions": [ // 算分函數
        {
          "filter": { // 滿足的條件,品牌必須是如家
            "term": {
              "brand": "如家"
            }
          },
          "weight": 2 // 算分權重為2
        }
      ],
      "boost_mode": "sum" // 權重模式,求和
    }
  }
}
           
測試,在未添加算分函數時,如家得分如下:
elasticsearch DSL指令索引庫操作 文檔操作DSL 查詢文檔
添加了算分函數後,如家得分就提升了:
elasticsearch DSL指令索引庫操作 文檔操作DSL 查詢文檔

7.布爾查詢(多條件查詢)

布爾查詢是一個或多個查詢子句的組合,每一個子句就是一個子查詢。子查詢的組合方式有:

  • must:必須比對每個子查詢,類似“與”
  • should:選擇性比對子查詢,類似“或”
  • must_not:必須不比對,不參與算分,類似“非”
  • filter:必須比對,不參與算分

比如在搜尋酒店時,除了關鍵字搜尋外,我們還可能根據品牌、價格、城市等字段做過濾:

elasticsearch DSL指令索引庫操作 文檔操作DSL 查詢文檔

 每一個不同的字段,其查詢的條件、方式都不一樣,必須是多個不同的查詢,而要組合這些查詢,就必須用bool查詢了。

需要注意的是,搜尋時,參與打分的字段越多,查詢的性能也越差。是以這種多條件查詢時,建議這樣做:

  • 搜尋框的關鍵字搜尋,是全文檢索查詢,使用must查詢,參與算分
  • 其它過濾條件,采用filter查詢。不參與算分

1)文法示例:

GET /hotel/_search
{
  "query": {
    "bool": {
      "must": [
        {"term": {"city": "上海" }}
      ],
      "should": [
        {"term": {"brand": "皇冠假日" }},
        {"term": {"brand": "華美達" }}
      ],
      "must_not": [
        { "range": { "price": { "lte": 500 } }}
      ],
      "filter": [
        { "range": {"score": { "gte": 45 } }}
      ]
    }
  }
}
           

2)示例

 需求:搜尋名字包含“如家”,價格不高于400,在坐标31.21,121.5周圍10km範圍内的酒店。

分析:

  • 名稱搜尋,屬于全文檢索查詢,應該參與算分。放到must中
  • 價格不高于400,用range查詢,屬于過濾條件,不參與算分。放到must_not中
  • 周圍10km範圍内,用geo_distance查詢,屬于過濾條件,不參與算分。放到filter中
elasticsearch DSL指令索引庫操作 文檔操作DSL 查詢文檔
GET /hotel/_search
{
  "query": {
    "bool": {
      "must": [{
          "match": {
            "name": "如家"
          }
        }],
      "must_not": [{
          "range": {
            "price": {
              "gt": 400
            }
          }
        }],
      "filter": [{
          "geo_distance": {
            "distance": "10km",
            "location": {
              "lat": 31.21,
              "lon": 121.5
            }
          }
        }]
    }
  }
}
           

8.排序

搜尋的結果可以按照使用者指定的方式去處理或展示。

elasticsearch預設是根據相關度算分(_score)來排序,但是也支援自定義方式對搜尋結果排序。可以排序字段類型有:keyword類型、數值類型、地理坐标類型、日期類型等。

普通字段排序

keyword、數值、日期類型排序的文法基本一緻。

文法:

GET /indexName/_search
{
  "query": {
    "match_all": {}
  },
  "sort": [
    {
      "FIELD": "desc"  // 排序字段、排序方式ASC、DESC
    }
  ]
}
           

排序條件是一個數組,也就是可以寫多個排序條件。按照聲明的順序,當第一個條件相等時,再按照第二個條件排序,以此類推

示例:酒店資料按照使用者評價(score)降序排序,評價相同的按照價格(price)升序排序
GET /hotel/_search
{
  "query": {
    "match_all": {}
  },
  "sort": [
    {
      "score":  "desc"
    },
    {
      "price": "asc"
    }
  ]
}
           

地理坐标排序 

地理坐标排序略有不同。

文法說明:

GET /indexName/_search
{
  "query": {
    "match_all": {}
  },
  "sort": [
    {
      "_geo_distance" : {
          "FIELD" : "緯度,經度", // 文檔中geo_point類型的字段名、目标坐标點
          "order" : "asc", // 排序方式
          "unit" : "km" // 排序的距離機關
      }
    }
  ]
           

這個查詢的含義是:

  • 指定一個坐标,作為目标點
  • 計算每一個文檔中,指定字段(必須是geo_point類型)的坐标 到目标點的距離是多少
  • 根據距離排序

示例: 實作對酒店資料按照到你的位置坐标的距離升序排序

提示:擷取你的位置的經緯度的方式:擷取滑鼠點選經緯度-地圖屬性-示例中心-JS API 2.0 示例 | 高德地圖API

假設我的位置是:31.034661,121.612282,尋找我周圍距離最近的酒店。

GET /hotel/_search
{
  "query": {
    "match_all": {}
  },
  "sort": [
    {
      "_geo_distance": {
        "location": {
          "lat": 31.034661,
          "lon": 121.612282
        },
        "order": "asc",
        "unit": "km"
      }
    }
  ]
}
           
 經緯度 2 種書寫方式都可以
elasticsearch DSL指令索引庫操作 文檔操作DSL 查詢文檔

9.分頁

elasticsearch 預設情況下隻傳回top10的資料。而如果要查詢更多資料就需要修改分頁參數了。elasticsearch中通過修改from、size參數來控制要傳回的分頁結果:

  • from:從第幾個文檔開始
  • size:總共查詢幾個文檔

類似于mysql中的

limit ?, ?

基本文法如下:

GET /hotel/_search
{
  "query": {
    "match_all": {}
  },
  "from": 0, // 分頁開始的位置,預設為0
  "size": 10, // 期望擷取的文檔總數
  "sort": [
    {"price": "asc"}
  ]
}
           

分頁深度問題

現在,我要查詢990~1000的資料,查詢邏輯要這麼寫:

GET /hotel/_search
{
  "query": {
    "match_all": {}
  },
  "from": 990, // 分頁開始的位置,預設為0
  "size": 10, // 期望擷取的文檔總數
  "sort": [
    {"price": "asc"}
  ]
}
           

這裡是查詢990開始的資料,也就是 第990~第1000條 資料。

不過,elasticsearch内部分頁時,必須先查詢 0~1000條,然後截取其中的990 ~ 1000的這10條:

elasticsearch DSL指令索引庫操作 文檔操作DSL 查詢文檔

查詢TOP1000,如果es是單點模式,這并無太大影響。

但是elasticsearch将來一定是叢集,例如我叢集有5個節點,我要查詢TOP1000的資料,并不是每個節點查詢200條就可以了。

因為節點A的TOP200,在另一個節點可能排到10000名以外了。

是以要想擷取整個叢集的TOP1000,必須先查詢出每個節點的TOP1000,彙總結果後,重新排名,重新截取TOP1000。

elasticsearch DSL指令索引庫操作 文檔操作DSL 查詢文檔

那如果我要查詢9900~10000的資料呢?是不是要先查詢TOP10000呢?那每個節點都要查詢10000條?彙總到記憶體中?

當查詢分頁深度較大時,彙總資料過多,對記憶體和CPU會産生非常大的壓力,是以elasticsearch會禁止from+ size 超過10000的請求。

針對深度分頁,ES提供了兩種解決方案,官方文檔:

  • search after:分頁時需要排序,原理是從上一次的排序值開始,查詢下一頁資料。官方推薦使用的方式。
  • scroll:原理将排序後的文檔id形成快照,儲存在記憶體。官方已經不推薦使用。

分頁小結:

分頁查詢的常見實作方案以及優缺點:

  • from + size

    • 優點:支援随機翻頁
    • 缺點:深度分頁問題,預設查詢上限(from + size)是10000
    • 場景:百度、京東、谷歌、淘寶這樣的随機翻頁搜尋
  • after search

    • 優點:沒有查詢上限(單次查詢的size不超過10000)
    • 缺點:隻能向後逐頁查詢,不支援随機翻頁
    • 場景:沒有随機翻頁需求的搜尋,例如手機向下滾動翻頁
  • scroll

    • 優點:沒有查詢上限(單次查詢的size不超過10000)
    • 缺點:會有額外記憶體消耗,并且搜尋結果是非實時的
    • 場景:海量資料的擷取和遷移。從ES7.1開始不推薦,建議用 after search方案。

10.搜尋結果 -- 高亮

什麼是高亮顯示呢?

我們在百度,京東搜尋時,關鍵字會變成紅色,比較醒目,這叫高亮顯示:

elasticsearch DSL指令索引庫操作 文檔操作DSL 查詢文檔

高亮顯示的實作分為兩步:

  • 1)給文檔中的所有關鍵字都添加一個标簽,例如

    <em>

    标簽
  • 2)頁面給

    <em>

    标簽編寫CSS樣式

高亮的文法:

GET /hotel/_search
{
  "query": {
    "match": {
      "FIELD": "TEXT" // 查詢條件,高亮一定要使用全文檢索查詢
    }
  },
  "highlight": {
    "fields": { // 指定要高亮的字段
      "FIELD": {
        "pre_tags": "<em>",  // 用來标記高亮字段的前置标簽
        "post_tags": "</em>" // 用來标記高亮字段的後置标簽
      }
    }
  }
}
           

注意:

  • 高亮是對關鍵字高亮,是以搜尋條件必須帶有關鍵字,而不能是範圍這樣的查詢。
  • 預設情況下,高亮的字段,必須與搜尋指定的字段一緻,否則無法高亮
  • 如果要對非搜尋字段高亮,則需要添加一個屬性:required_field_match=false
GET /hotel/_search
{
  "query": {
    "match": {
      "all": "外灘"
    }
  },
  "highlight": {
    "fields": {
      "name": {
        "require_field_match": "false"
      }
    }
  }
}
           
elasticsearch DSL指令索引庫操作 文檔操作DSL 查詢文檔

11.資料聚合

聚合(aggregations)可以讓我們極其友善的實作對資料的統計、分析、運算。例如:

  • 什麼品牌的手機最受歡迎?
  • 這些手機的平均價格、最高價格、最低價格?
  • 這些手機每月的銷售情況如何?

實作這些統計功能的比資料庫的sql要友善的多,而且查詢速度非常快,可以實作近實時搜尋效果。

聚合的種類:

聚合常見的有三類:

  • 桶(Bucket)聚合:用來對文檔做分組
    • TermAggregation:按照文檔字段值分組,例如按照品牌值分組、按照國家分組
    • Date Histogram:按照日期階梯分組,例如一周為一組,或者一月為一組
  • 度量(Metric)聚合:用以計算一些值,比如:最大值、最小值、平均值等
    • Avg:求平均值
    • Max:求最大值
    • Min:求最小值
    • Stats:同時求max、min、avg、sum等
  • 管道(pipeline)聚合:其它聚合的結果為基礎做聚合
注意:參加聚合的字段必須是keyword、日期、數值、布爾類型

Bucket聚合

GET /hotel/_search
{
  "size": 0,  // 設定size為0,結果中不包含文檔,隻包含聚合結果
  "aggs": { // 定義聚合
    "brandAgg": { //給聚合起個名字
      "terms": { // 聚合的類型,按照品牌值聚合,是以選擇term
        "field": "brand", // 參與聚合的字段
        "size": 20 // 希望擷取的聚合結果數量
      }
    }
  }
}
           

 結果如圖:

elasticsearch DSL指令索引庫操作 文檔操作DSL 查詢文檔

 聚合結果排序

預設情況下,Bucket聚合會統計Bucket内的文檔數量,記為count,并且按照count降序排序。

我們可以指定order屬性,自定義聚合的排序方式:

GET /hotel/_search
{
  "size": 0, 
  "aggs": {
    "brandAgg": {
      "terms": {
        "field": "brand",
        "order": {
          "_count": "asc" // 按照_count升序排列
        },
        "size": 20
      }
    }
  }
}
           

限定聚合範圍

預設情況下,Bucket聚合是對索引庫的所有文檔做聚合,但真實場景下,使用者會輸入搜尋條件,是以聚合必須是對搜尋結果聚合。那麼聚合必須添加限定條件。

我們可以限定要聚合的文檔範圍,隻要添加query條件即可:

GET /hotel/_search
{
  "query": {
    "range": {
      "price": {
        "lte": 200 // 隻對200元以下的文檔聚合
      }
    }
  }, 
  "size": 0, 
  "aggs": {
    "brandAgg": {
      "terms": {
        "field": "brand",
        "size": 20
      }
    }
  }
}
           
elasticsearch DSL指令索引庫操作 文檔操作DSL 查詢文檔

 Metric聚合文法

我們對酒店按照品牌分組,形成了一個個桶。現在我們需要對桶内的酒店做運算,擷取每個品牌的使用者評分的min、max、avg等值。

這就要用到Metric聚合了,例如stat聚合:就可以擷取min、max、avg等結果。

GET /hotel/_search
{
  "size": 0, 
  "aggs": {
    "brandAgg": { 
      "terms": { 
        "field": "brand", 
        "size": 20
      },
      "aggs": { // 是brands聚合的子聚合,也就是分組後對每組分别計算
        "score_stats": { // 聚合名稱
          "stats": { // 聚合類型,這裡stats可以計算min、max、avg等
            "field": "score" // 聚合字段,這裡是score
          }
        }
      }
    }
  }
}
           

這次的score_stats聚合是在brandAgg的聚合内部嵌套的子聚合。因為我們需要在每個桶分别計算。

另外,我們還可以給聚合結果做個排序,例如按照每個桶的酒店平均分做排序:

elasticsearch DSL指令索引庫操作 文檔操作DSL 查詢文檔

12.自動補全查詢

elasticsearch提供了Completion Suggester查詢來實作自動補全功能。這個查詢會比對以使用者輸入内容開頭的詞條并傳回。為了提高補全查詢的效率,對于文檔中字段的類型有一些限制:

  • 參與補全查詢的字段必須是completion類型。
  • 字段的内容一般是用來補全的多個詞條形成的數組。

比如,一個這樣的索引庫:

// 建立索引庫
PUT test
{
  "mappings": {
    "properties": {
      "title":{
        "type": "completion"
      }
    }
  }
}

// 示例資料
POST test/_doc
{
  "title": ["Sony", "WH-1000XM3"]
}
POST test/_doc
{
  "title": ["SK-II", "PITERA"]
}
POST test/_doc
{
  "title": ["Nintendo", "switch"]
}
           
// 自動補全查詢
GET /test/_search
{
  "suggest": {
    "title_suggest": {
      "text": "s", // 關鍵字
      "completion": {
        "field": "title", // 補全查詢的字段
        "skip_duplicates": true, // 跳過重複的
        "size": 10 // 擷取前10條結果
      }
    }
  }
}
           
elasticsearch DSL指令索引庫操作 文檔操作DSL 查詢文檔
elasticsearch DSL指令索引庫操作 文檔操作DSL 查詢文檔

繼續閱讀