乾貨 Elasticsearch 知識點整理 一

極力推薦: 官網地址: https://www.elastic.co/guide/en/elasticsearch/reference/6.0html

肺腑之言,學ES先學原生的語法,SpringData封裝的是太好用了,可是沒玩過原生的語法,可能不知道Spring提供的API在幹什麼java

核心概念:

Near Realtime (NRT)

在ES中進行搜索是近實時的,意思是數據從寫入ES到能夠被searchable僅僅須要1秒鐘,所以說基於ES執行的搜索和分析能夠達到秒級node

Cluster

集羣 , 集羣是一個或多個node的集合,他們一塊兒保存你存放進去的數據,用戶能夠在全部的node之間進行檢索,通常的每一個集羣都會有一個惟一的名稱標識,默認的名稱標識爲 elasticsearch , 這個名字很重要,由於node想加入cluster時,須要這個名稱信息mysql

確保別在不一樣的環境中使用相同的集羣名稱,進而避免node加錯集羣的狀況,一顆考慮下面的集羣命名風格logging-stagelogging-devlogging-pro算法

Node

單臺server就是一個node,他和 cluster同樣,也存在一個默認的名稱,可是它的名稱是經過UUID生成的隨機串,固然用戶也能夠定製不一樣的名稱,可是這個名字最好別重複,這個名稱對於管理來講很在意要,由於須要肯定,當前網絡中的哪臺服務器,對應這個集羣中的哪一個節點sql

node存在一個默認的設置,默認的,當每個node在啓動時都會自動的去加入一個叫elasticsearch的節點,這就意味着,若是用戶在網絡中啓動了多個node,他們會彼此發現,而後組成集羣數據庫

在單個的cluster中,你能夠擁有任意多的node,假如說你的網絡上沒有有其餘正在運行的節點,而後你啓動一個新的節點,這個新的節點本身會組件一個集羣json

Index

Index是一類擁有類似屬性的document的集合,好比你能夠爲消費者的數據建立一個index,爲產品建立一個index,爲訂單建立一個indexapi

index名稱(必須是小寫的字符), 當須要對index中的文檔執行索引,搜索,更新,刪除,等操做時,都須要用到這個index數組

一個集羣中理論上你能夠建立任意數量的index

Type

Type能夠做爲index中的邏輯類別,爲了更細的劃分,好比用戶數據type,評論數據type,博客數據type

在設計時,盡最大努力讓擁有更多相同field的document會分爲同一個type下

Document

document就是ES中存儲的一條數據,就像mysql中的一行記錄同樣,能夠是一條用戶的記錄,一個商品的記錄等等

一個不嚴謹的小結:

爲何說這是不嚴謹的小結呢? 就是說下面三個對應關係只能說的從表面上看起來比較類似,可是ES中的type實際上是一個邏輯上的劃分,數據在存儲是時候依然是混在一塊兒存儲的(往下看下文中有寫,),然而mysql中的不一樣表的兩個列是絕對沒有關係的

Elasticsearch 關係型數據庫
Document
type
index 數據庫

Shards & Replicas

問題引入:

若是讓一個Index本身存儲1TB的數據,響應的速度就會降低爲了解決這個問題,ES提供了一種將用戶的Index進行subdivide的騷操做,就是將index分片, 每一片都叫一個Shards,實現了將總體龐大的數據分佈在不一樣的服務器上存儲

什麼是shard?

shard分紅replica shard和primary shard,顧名思義一個是主shard一個是備份shard, 負責容錯以及承擔部分讀請求

shard能夠理解成是ES中最小的工做單元,全部shard中的數據之和,纔是整個ES中存儲的數據, 能夠把shard理解成是一個luncene的實現,擁有完成的建立索引,處理請求的能力

下圖是兩個node,6個shard的組成的集羣的劃分狀況

兩個節點的分佈狀況

你們能夠看到,這時不管java應用程序訪問的是node1仍是node2,其實都能獲取到數據

shard的默認數量

新建立的節點會存在5個primary shard,後續否則能再改動primary shard的值,若是每個primary shard都對應一個replica shard,按理說單臺es啓動就會存在10個分片,可是現實是,同一個節點的replica shard和primary shard不能存在於一個server中,所以單臺es默認啓動後的分片數量仍是5個

如何拓容Cluster

首先明確一點: 一旦index建立完成了,primary shard的數量就不可能再發生變化

所以橫向拓展就得添加replica的數量, 由於replica shard的數量後續是能夠改動的, 也就是說,若是後續咱們將他的數量改爲了2, 就意味着讓每一個primary shard都擁有了兩個replica shard, 計算一下: 5+5*2=15 集羣就會拓展成15個節點

若是想讓每個shard都有最多的系統的資源,就增長服務器的數量,讓每個shard獨佔一個服務器,

舉個例子:

shard和replica入門圖

上圖中存在上下兩個node,每個node,每一個node中都有一個 本身的primary shard其餘節點的replica shard,爲何是強調本身和其餘呢? 由於ES中規定,同一個節點的replica shard和primary shard不能存在於一個server中,可是不一樣節點的primary shard能夠存在於同一個server上

當primary shard宕機時,它對應的replicas在其餘的server不會受到影響,能夠繼續響應用戶的讀請求,經過這種分片的機制,而且分片的地位至關,假設單個shard能夠處理2000/s的請求,經過橫向拓展能夠在此基礎上成倍提高系統的吞吐量,天生分佈式,高可用

此外:每個document確定存在於一個primary shard和這個primary shard 對應的replica shard中, 絕對不會出現同一個document同時存在於多個primary shard中的狀況

入門探索:

集羣的健康情況

GET /_cat/health?v

執行結果以下:

epoch      timestamp cluster       status node.total node.data shards pri relo init unassign pending_tasks max_task_wait_time active_shards_percent
1572595632 16:07:12  elasticsearch yellow          1         1      5   5    0    0        5             0                  -                 50.0%

解讀上面的信息,默認的集羣名是elasticsearch,當前集羣的status是yellow,後續列出來的是集羣的分片信息,最後一個active_shards_percent表示當前集羣中僅有一半shard是可用的

狀態

存在三種狀態分別是red green yellow

  • green : 表示當前集羣全部的節點所有可用
  • yellow: 表示全部的數據是能夠訪問的,可是並非全部的replica shard都是可使用的(我如今是默認啓動一個node,而ES又不容許同一個node的primary shard和replica shard共存,所以我當前的node中僅僅存在5個primary shard,爲status爲黃色)
  • red: 集羣宕機,數據不可訪問

集羣的索引信息

GET /_cat/indices?v

結果:

health status index              uuid                   pri rep docs.count docs.deleted store.size pri.store.size
yellow open   ai_answer_question cl_oJNRPRV-bdBBBLLL05g   5   1     203459            0    172.3mb        172.3mb

顯示,狀態yellow表示存在replica shard不可用, 存在5個primary shard,而且每個primary shard都有一個replica shard , 一共20多萬條文檔,未刪除過文檔,文檔佔用的空間狀況爲172.3兆

建立index

PUT /customer?pretty

ES 使用的RestfulAPI,新增使用put,這是個很親民的舉動

添加 or 修改

若是是ES中沒有過下面的數據則添加進去,若是存在了id=1的元素就修改(全量替換)

  • 格式:PUT /index/type/id

全量替換時,原來的document是沒有被刪除的,而是被標記爲deleted,被標記成的deleted是不會被檢索出來的,當ES中數據愈來愈多時,纔會刪除它

PUT /customer/_doc/1?pretty
{
  "name": "John Doe"
}

響應:

{
  "_index": "customer",
  "_type": "_doc",
  "_id": "1",
  "_version": 1,
  "result": "created",
  "_shards": {
    "total": 2,
    "successful": 1,
    "failed": 0
  },
  "_seq_no": 0,
  "_primary_term": 1
}

強制建立,加添_create或者?op_type=create

PUT /customer/_doc/1?op_type=create
PUT /customer/_doc/1/_create
  • 局部更新(Partial Update)

不指定id則新增document

POST /customer/_doc?pretty
{
  "name": "Jane Doe"
}

指定id則進行doc的局部更新操做

POST /customer/_doc/1?pretty
{
  "name": "Jane Doe"
}

而且POST相對於上面的PUT而言,不管是否存在相同內容的doc,只要不指定id,都會使用一個隨機的串當成id,完成doc的插入

Partial Update先獲取document,再將傳遞過來的field更新進document的json中,將老的doc標記爲deleted,再將建立document,相對於全量替換中間會省去兩次網絡請求

檢索

格式: GET /index/type/

GET /customer/_doc/1?pretty

響應:

{
  "_index": "customer",
  "_type": "_doc",
  "_id": "1",
  "_version": 1,
  "found": true,
  "_source": {
    "name": "John Doe"
  }
}

刪除

刪除一條document

大部分狀況下,原來的document不會被當即刪除,而是被標記爲deleted,被標記成的deleted是不會被檢索出來的,當ES中數據愈來愈多時,纔會刪除它

DELETE /customer/_doc/1

響應:

{
  "_index": "customer",
  "_type": "_doc",
  "_id": "1",
  "_version": 2,
  "result": "deleted",
  "_shards": {
    "total": 2,
    "successful": 1,
    "failed": 0
  },
  "_seq_no": 1,
  "_primary_term": 1
}

刪除index

DELETE /index1
DELETE /index1,index2
DELETE /index*
DELETE /_all

能夠在elasticsearch.yml中將下面這個設置置爲ture,表示禁止使用 DELETE /_all
action.destructive_required_name:true

響應

{
  "acknowledged": true
}

更新文檔

上面說了POST關鍵字,能夠實現不指定id就完成document的插入, POST + _update關鍵字能夠實現更新的操做

POST /customer/_doc/1/_update?pretty
{
  "doc": { "name": "changwu" }
}

**POST+_update進行更新的動做依然須要執行id, 可是它相對於PUT來講,當使用POST進行更新時,id不存在的話會報錯,而PUT則會認爲這是在新增**

此外: 針對這種更新操做,ES會先刪除原來的doc,而後插入這個新的doc

document api

multi-index & multi-type

  • 檢索全部索引下面的全部數據
/_search
  • 搜索指定索引下的全部數據
/index/_search
  • 更多模式
/index1/index2/_search
/*1/*2/_search
/index1/index2/type1/type2/_search
/_all/type1/type2/_search

_mget api 批量查詢

  • 在docs中指定_index,_type,_id
GET /_mget
{
    "docs" : [
        {
            "_index" : "test",
            "_type" : "_doc",
            "_id" : "1"
        },
        {
            "_index" : "test",
            "_type" : "_doc",
            "_id" : "2"
        }
    ]
}
  • 在URL中指定index
GET /test/_mget
{
    "docs" : [
        {
            "_type" : "_doc",
            "_id" : "1"
        },
        {
            "_type" : "_doc",
            "_id" : "2"
        }
    ]
}
  • 在URL中指定 index和type
GET /test/type/_mget
{
    "docs" : [
        {
            "_id" : "1"
        },
        {
            "_id" : "2"
        }
  • 在URL中指定index和type,並使用ids指定id範圍
GET /test/type/_mget
{
    "ids" : ["1", "2"]
}
  • 爲不一樣的doc指定不一樣的過濾規則
GET /_mget
{
    "docs" : [
        {
            "_index" : "test",
            "_type" : "_doc",
            "_id" : "1",
            "_source" : false
        },
        {
            "_index" : "test",
            "_type" : "_doc",
            "_id" : "2",
            "_source" : ["field3", "field4"]
        },
        {
            "_index" : "test",
            "_type" : "_doc",
            "_id" : "3",
            "_source" : {
                "include": ["user"],
                "exclude": ["user.location"]
            }
        }
    ]
}

_bulk api 批量增刪改

基本語法

{"action":{"metadata"}}\n
{"data"}\n

存在哪些類型的操做能夠執行呢?

  • delete: 刪除文檔

  • create: _create 強制建立

  • index: 表示普通的put操做,能夠是建立文檔也能夠是全量替換文檔

  • update: 局部替換

上面的語法中並非人們習慣閱讀的json格式,可是這種單行形式的json更具有高效的優點

ES如何處理普通的json以下:

  • 將json數組轉換爲JSONArray對象,這就意味着內存中會出現一份如出一轍的拷貝,一份是json文本,一份是JSONArray對象

可是若是上面的單行JSON,ES直接進行切割使用,不會在內存中整一個數據拷貝出來

delete

delete比較好看僅僅須要一行json就ok

{ "delete" : { "_index" : "test", "_type" : "_doc", "_id" : "2" } }

create

兩行json,第一行指明咱們要建立的json的index,type以及id

第二行指明咱們要建立的doc的數據

{ "create" : { "_index" : "test", "_type" : "_doc", "_id" : "3" } }
{ "field1" : "value3" }

index

至關因而PUT,能夠實現新建或者是全量替換,一樣是兩行json

第一行表示將要新建或者是全量替換的json的index type 以及 id

第二行是具體的數據

{ "index" : { "_index" : "test", "_type" : "_doc", "_id" : "1" } }
{ "field1" : "value1" }

update

表示 parcial update,局部替換

他能夠指定一個retry_on_conflict的特性,表示能夠重試3次

POST _bulk
{ "update" : {"_id" : "1", "_type" : "_doc", "_index" : "index1", "retry_on_conflict" : 3} }
{ "doc" : {"field" : "value"} }
{ "update" : { "_id" : "0", "_type" : "_doc", "_index" : "index1", "retry_on_conflict" : 3} }
{ "script" : { "source": "ctx._source.counter += params.param1", "lang" : "painless", "params" : {"param1" : 1}}, "upsert" : {"counter" : 1}}
{ "update" : {"_id" : "2", "_type" : "_doc", "_index" : "index1", "retry_on_conflict" : 3} }
{ "doc" : {"field" : "value"}, "doc_as_upsert" : true }
{ "update" : {"_id" : "3", "_type" : "_doc", "_index" : "index1", "_source" : true} }
{ "doc" : {"field" : "value"} }
{ "update" : {"_id" : "4", "_type" : "_doc", "_index" : "index1"} }
{ "doc" : {"field" : "value"}, "_source": true}

滾動查詢技術

滾動查詢技術和分頁技術在使用場景方面仍是存在出入的,這裏的滾動查詢技術一樣適用於系統在海量數據中進行檢索,好比過一次性存在10條數據被命中能夠被檢索出來,那麼性能必定會不好,這時能夠選擇使用滾動查詢技術,一批一批的查詢,直到全部的數據被查詢完成他能夠先搜索一批數據再搜索一批數據

採用基於_doc的排序方式會得到較高的性能

每次發送scroll請求,咱們還須要指定一個scroll參數,指定一個時間窗口,每次搜索只要在這個時間窗口內完成就ok

示例

GET /index/type/_search?scroll=1m
{
    "query":{
        "match_all":{}
    },
    "sort":["_doc"],
    "size":3
}

響應

{
  "_scroll_id": "DnF1ZXJ5VGhlbkZldGNoBQAAAAAAAACNFlJmWHZLTkFhU0plbzlHX01LU2VzUXcAAAAAAAAAkRZSZlh2S05BYVNKZW85R19NS1Nlc1F3AAAAAAAAAI8WUmZYdktOQWFTSmVvOUdfTUtTZXNRdwAAAAAAAACQFlJmWHZLTkFhU0plbzlHX01LU2VzUXcAAAAAAAAAjhZSZlh2S05BYVNKZW85R19NS1Nlc1F3",
  "took": 9,
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": 2,
    "max_score": null,
    "hits": [
      {
        "_index": "my_index",
        "_type": "_doc",
        "_id": "2",
        "_score": null,
        "_source": {
          "title": "This is another document",
          "body": "This document has a body"
        },
        "sort": [
          0
        ]
      },
      {
        "_index": "my_index",
        "_type": "_doc",
        "_id": "1",
        "_score": null,
        "_source": {
          "title": "This is a document"
        },
        "sort": [
          0
        ]
      }
    ]
  }
}

再次滾動查詢

GET /_search/scroll
{
    "scroll":"1m",
    "_scroll_id": "DnF1ZXJ5VGhlbkZldGNoBQAAAAAAAACNFlJmWHZLTkFhU0plbzlHX01LU2VzUXcAAAAAAAAAkRZSZlh2S05BYVNKZW85R19NS1Nlc1F3AAAAAAAAAI8WUmZYdktOQWFTSmVvOUdfTUtTZXNRdwAAAAAAAACQFlJmWHZLTkFhU0plbzlHX01LU2VzUXcAAAAAAAAAjhZSZlh2S05BYVNKZW85R19NS1Nlc1F3"
}

_search api 搜索api

_searchAPI + 將請求寫在URI中

GET /bank/_search?q=*&sort=account_number:asc&pretty

一樣使用的是RestfulAPI, q=* ,表示匹配index=bank的下的全部doc,sort=account_number:asc表示告訴ES,結果按照account_number字段升序排序,pretty是告訴ES,返回一個漂亮的json格式的數據

上面的q還能夠寫成下面這樣

GET /bank/_search?q=自定義field:指望的值
GET /bank/_search?q=+自定義field:指望的值
GET /bank/_search?q=-自定義field:指望的值

響應:

{
  "took" : 63,    // 耗費的時間
  "timed_out" : false,  // 是否超時了
  "_shards" : {   // 分片信息
    "total" : 5, // 總共5個分片,它的搜索請求會被打到5個分片上去,而且都成功了
    "successful" : 5,  // 
    "skipped" : 0, // 跳過了0個
    "failed" : 0 // 失敗了0個
  },
  "hits" : {  //命中的狀況
    "total" : 1000,  // 命中率 1000個
    "max_score" : null,  // 相關性得分,越相關就越匹配
    "hits" : [ {   
      "_index" : "bank",  // 索引
      "_type" : "_doc",   // type
      "_id" : "0",  // id 
      "sort": [0], 
      "_score" : null, // 相關性得分
                    // _source裏面存放的是數據
      "_source" : {"account_number":0,"balance":16623,"firstname":"Bradshaw","lastname":"Mckenzie","age":29,"gender":"F","address":"244 Columbus Place","employer":"Euron","email":"bradshawmckenzie@euron.com","city":"Hobucken","state":"CO"}
    }, {
      "_index" : "bank",
      "_type" : "_doc",
      "_id" : "1",
      "sort": [1],
      "_score" : null,
      "_source" : {"account_number":1,"balance":39225,"firstname":"Amber","lastname":"Duke","age":32,"gender":"M","address":"880 Holmes Lane","employer":"Pyrami","email":"amberduke@pyrami.com","city":"Brogan","state":"IL"}
    }, ...
    ]
  }
}

指定超時時間: GET /_search?timeout=10ms 在進行優化時,能夠考慮使用timeout, 好比: 正常來講咱們能夠在10s內獲取2000條數據,可是指定了timeout,發生超時後咱們能夠獲取10ms中獲取到的 100條數據

query dsl (domain specified language)

下面我僅僅列出來了一點點, 更多的示例,參見官網 點擊進入官網

_searchAPI +將請求寫在請求體中

GET /bank/_search
{
  "query": { "match_all": {} }, # 查詢所有
  "query": { "match": {"name":"changwu zhu"} }, # 全文檢索,戶將輸入的字符串拆解開,去倒排索引中一一匹配, 哪怕匹配上了一個也會將結果返回
  # 實際上,上面的操做會被ES轉換成下面的格式
  #
  # {
  #    "bool":{
  #        "should":[
  #         {"term":{"title":"changwu"}},
  #         {"term":{"title":"zhu"}}
  #     ]
  #  }
  # }
  #
   "query": { 
     "match": { # 手動控制全文檢索的精度,
        "name":{
            "query":"changwu zhu",
            "operator":"and",  # and表示,只有同時出現changwu zhu 兩個詞的doc纔會被命中
            "minimum_should_match":"75%" # 去長尾,控制至少命中3/4纔算是真正命中
        }
     }
    }, # 全文檢索,operator 表示
  # 添加上operator 操做會被ES轉換成下面的格式,將上面的should轉換成must
  #
  # {
  #    "bool":{
  #        "must":[
  #         {"term":{"title":"changwu"}},
  #         {"term":{"title":"zhu"}}
  #     ]
  #  }
  # }
  #
  # 添加上 minimum_should_match 操做會被ES轉換成下面的格式 
  #
  # {
  #    "bool":{
  #        "should":[
  #         {"term":{"title":"changwu"}},
  #         {"term":{"title":"zhu"}}
  #     ],
  #       "minimum_should_match":3
  #  }
  # }
  #  
   "query": { 
     "match": { #控制權重, 
        "name":{
            "query":"changwu zhu",
            "boost":3  # 將name字段的權重提高成3,默認狀況下,全部字段的權重都是樣的,都是1
        }
     }
    },
   "query": { 
   # 這種用法不容忽略
     "dis_max": { # 直接取下面多個query中得分最高的query當成最終得分
        "queries":[
           {"match":{"name":"changwu zhu"}},
           {"match":{"content":"changwu"}}
        ]
     }
    },
    
    # best field策略
    "query": { # 基於 tie_breaker 優化dis_max
    # tie_breaker可使dis_max考慮其餘field的得分影響
       "multi_match":{
           "query":"用於去匹配的字段",
           "type":"most_fields",# 指定檢索的策略most_fields
           "fields":["field1","field2","field3"]
       }
    },
    
    # most field 策略, 優先返回命中更多關鍵詞的doc, (忽略從哪一個,從多少個field中命中的,只要命中就行)
    "query": { # 基於 tie_breaker 優化dis_max
    # tie_breaker可使dis_max考慮其餘field的得分影響
     "dis_max": { # 直接取下面多個query中得分最高的query當成最終得分, 這也是best field策略
        "queries":[
           {"match":{"name":"changwu zhu"}},
           {"match":{"content":"changwu"}}
        ],
        "tie_breaker":0.4
     }
    },
    
    
  "query": { "match_none": {} }
  "query": { "term": {"test_field":"指定值"} } # 精確匹配
  "query": { "exits": {"field":"title"} } # title不爲空(可是這時ES2.0中的用法,如今再也不提供了)
  "query": {  # 短語檢索 
              # 順序的保證是經過 term position來保證的
              # 精準度很高,可是召回率低
         "match_phrase": { # 只有address字段中包含了完整的 mill lane (相連,順序也不能變) 時,這個doc纔算命中
             "address": "mill lane"
             } 
  },
   "query": {  # 短語檢索 
         "match_phrase": { 
             "address": "mill lane",
             # 指定了slop就再也不要求搜索term之間必須相鄰,而是能夠最多間隔slop距離
             # 在指定了slop參數的狀況下,離關鍵詞越近,移動的次數越少, relevance score 越高
             # match_phrase +  slop 和 proximity match 近似匹配做用相似
             # 平衡精準度和召回率
             "slop":1 # 指定搜索文本中的幾個term通過幾回移動後能夠匹配到一個doc
             } 
  },
  
  # 混合使用match和match_phrase 平衡精準度和召回率
   "query": { 
      "bool": {  
      "must":  {
          # 全文檢索雖然能夠匹配到大量的文檔,可是它不能控制詞條之間的距離
          # 可能java elasticsearch在Adoc中距離很近,可是它卻被ES排在結果集的後面
          # 它的性能比match_phrase高10倍,比proximity高20倍
         "match": {
            "address": "java elasticsearch" 
            } 
      },
      "should": {
         # 藉助match_phrase+slop能夠感知term position的功能,爲距離相近的doc貢獻分數,讓它們靠前排列
          "match_phrase":{
              "title":{
                  "query":"java elasticsearch",
                  "slop":50
              }
          }
      }
  },
  
  # 重打分機制
   "query": { 
       "match":{
           "title":{
               "query":"java elasticsearch",
               "minimum_should_match":"50%"
           }
       },
       "rescore":{ # 對全文檢索的結果進行從新打分
           "window_size":50,  # 對全文檢索的前50條進行從新打分
           "query": { 
               "rescore_query":{ # 關鍵字
                    "match_phrase":{ # match_phrase + slop 感知 term persition,貢獻分數
                       "title":{
                           "query":"java elasticsearch",
                           "slop":50
                  }
              }
          }
       }
   }
  
  # 前綴匹配, 相對於全文檢索,前綴匹配是不會進行分詞的,並且每次匹配都會掃描整個倒排索引,直到掃描完一遍纔會停下來
  # 不會計算相關性得分,前綴越短拼配到的越多,性能越很差
  "query": { # 查詢多個, 在下面指定的兩個字段中檢索含有 `this is a test`的doc
    "multi_match" : {
      "query":    "this is a test", 
      "fields": [ "subject", "message" ] 
    }
  },
  "query": { # 前綴搜索,搜索 user字段以ki開頭的 doc
    "prefix" : { "user" : "ki" }
  },
  "query": { # 前綴搜索 + 添加權重
    "prefix" : { "user" :  { "value" : "ki", "boost" : 2.0 } }
  },
  
  # 通配符搜索
   "query": {
        "wildcard" : { "user" : "ki*y" }
    },
   "query": {
        "wildcard" : { "user" : { "value" : "ki*y", "boost" : 2.0 } }
    }
  # 正則搜索
   "query": {
        "regexp":{
            "name.first": "s.*y"
        }
    },
   "query": {# 正則搜索
        "regexp":{
            "name.first":{
                "value":"s.*y",
                "boost":1.2
            }
        }
    },
  # 搜索推薦, 相似於百度,當用戶輸入一個詞條後,將其餘符合條件的詞條的選項推送出來
  # 原理和match_pharse類似,可是惟一的區別就是會將最後一個term看成前綴去搜索
  # 下例中: 使用quick brown進行match 使用f進行前綴搜索,使用slop調整term persition,貢獻得分
   "query": {
      "match_phrase_prefix" : {# 前綴匹配
        "message" : {
                "query" : "quick brown f",
                "max_expansions" : 10, # 指定前綴最多匹配多少個term,超過這個數量就不在倒排索引中檢索了,提高性能
                "slop":10
            }
       } 
  },
  # Function Score Query
  # 用戶能夠自定義一個function_secore 函數,而後將某個field的值和ES計算出來的分數進行運算
  # 最終實現對本身指定的field進行分數的加強功能
  "query": {
        "function_score": {
            "query": { "match_all": {} },
            "boost": "5",
            "random_score": {}, 
            "boost_mode":"multiply"
        }
    }, 
  
  # Fuzzy Query 模糊查詢會提供容錯的處理
   "query": {
        "fuzzy" : {
            "user" : {
                "value": "ki",
                "boost": 1.0,
                "fuzziness": 2, # 作大的糾錯數量
                "prefix_length": 0,# 不會被「模糊化」的初始字符數。這有助於減小必須檢查的術語的數量。默認值爲0。
                "max_expansions": 100 # 模糊查詢將擴展到的最大項數。默認值爲50
                transpositions:true # 是否支持模糊變換(ab→ba)。默認的是假的
            }
        }
    }
  
  "query": {
    "bool": {  # 布爾查詢, 最終經過將它內置must,should等查詢的得分加起來/should,must的總數, 獲得最終的得分
      "must": [ # 必須匹配到XXX, 而且會得出相關性得分
        { "match": { "address": "mill" } }, # address中必須包含mill
      ],
      # 在知足must的基礎上,should條件不知足也能夠,可是若是也匹配上了,相關性得分會增長
      # 若是沒有must的話,should中的條件必須知足一個
      "should": [ # 指定能夠包含的值, should是能夠影響相關性得分的
        { "match": { "address": "lane" } }
      ],
      "must_not": [ # 必定不包含誰
        { "match": { "address": "mill" } },
      ],
      "filter": { # 對數據進行過濾
        "range": { # 按照範圍過濾
          "balance": { # 指定過濾的字段
            "gte": 20000, # 高於20000
            "lte": 30000  # 低於30000
          }
        }
      }
    }
  }

在上面的組合查詢中,每個子查詢都會計算一下他的相關性分數,而後由最外層的bool綜合合併一個得分,可是 filter是不會計算分數的

默認的排序規則是按照score降序排序,但像上面說的那樣,若是所有都是filter的話他就不會計算得分,也就是說全部的得分全是1,這時候就須要定製排序規則,定義的語法我在上面寫了

其餘輔助API

好比下面的高亮,排序,分頁,以及_source 指定須要的字段均可以進一步做用在query的結果上

"highlight":{ # 高亮顯示
    "fields":{  # 指定高亮的字段
      "balance":{}
  },
  "sort": [  # 指定排序條件
    { "account_number": "asc" } # 按照帳戶餘額降序
  ],
  "from": 0, # 分頁
  "size": 10, # 每頁的大小4,經過執行size=0,能夠實現僅顯示聚合結果而不顯示命中的信息詳情
  "_source": ["account_number", "balance"], # 默認狀況下,ES會返回全文JSON,經過_source能夠指定返回的字段

聚合分析

聚合分析是基於doc value這樣一個數據結果進行的,前面有說過,這個doc value 其實就是正排索引, 聚合分析就是根據某一個字段進行分組,要求這個字段是不能被分詞的,若是被聚合的字段被分詞,按照倒排索引的方式去索引的話,就不得不去掃描整個倒排索引(纔可能將被聚合的字段找全,效率很低)

更多Aggregate 點擊進入ES官網

三個概念:

  • 什麼是bucket?

bucket就是聚合獲得的結果

  • 什麼是metric?

metric就是對bucket進行分析,如最最大值,最小值,平均值

  • 什麼是下鑽?

下鑽就是在現有的分好組的bucket繼續分組,好比一個先按性別分組,再按年齡分組

聚合的關鍵字: aggsquery地位並列

# 使用聚合時,自然存在一個metric,就是當前bucket的count
  "aggs": { # 聚合
    "group_by_state": { # 自定義的名字
      "term": {
        "field": "balance" # 指定聚合的字段, 意思是 group by balance
      },
       "terms": { # terms
        "field": {"value1","value2","value3"} # 指定聚合的字段, 意思是 group by balance
      }
    }
  },    
  "aggs": { # 聚合中嵌套聚合
    "group_by_state": {
      "terms": {
        "field": "field1"
      },
      "aggs": { # 聚合中嵌套聚合
        "average_balance": {
          "avg": {
            "field": "field2"
          }
        }
      }
    }
  },
   "aggs": { #嵌套聚合,而且使用內部聚合的結果集
    "group_by_state": {
      "terms": {
        "field": "state.keyword",
        "order": {
          "average_balance": "desc" # 使用的下面聚合的結果集
        }
      },
      "aggs": {
        "average_balance": {
          "avg": {  # avg 求平均值  metric
            "field": "balance"
          }
        },
         "min_price": {
          "min": {  # metric 求最小值
            "field": "price"
          }
        },
         "max_price": {
          "max": {  # metric 求最大值
            "field": "price"
          }
        },
         "sum_price": {
          "sum": {  #  metric 計算總和
            "field": "price"
          }
        },
      }
    }
  },
   "aggs": { # 先按照年齡分組,在按照性別分組,再按照平均工資聚合
             # 最終的結果就獲得了每一個年齡段,每一個性別的平均帳戶餘額
    "group_by_age": {
      "range": {
        "field": "age",
        "ranges": [
          {
            "from": 20,
            "to": 30
          }
        ]
      },
      "aggs": {
        "group_by_gender": {
          "terms": {
            "field": "gender.keyword"
          },
          "aggs": {
            "average_balance": {
              "avg": {
                "field": "balance"
              }
            }
          }
        }
      },
      # histogram,相似於terms, 一樣會進行bucket分組操做,接受一個field,按照這個field的值的各個範圍區間進行分組操做
      # 好比咱們指定爲2000, 它會劃分紅這樣 0-2000  2000-4000  4000-6000 ...
      "aggs": { # 聚合中嵌套聚合  
         "group_by_price": {
              "histogram": {
                 "field": "price",
                 "interval":2000
             },
         "aggs": { # 聚合中嵌套聚合
             "average_price": {
               "avg": {
                  "field": "price"
               }
           }
        }
    }
  },
  "aggs" : {
        "sales_over_time" : { # 根據日期進行聚合
            "date_histogram" : {
                "field" : "date",
                "interval" : "1M",# 一個月爲一個跨度
                "format" : "yyyy-MM-dd",
                "min_doc_count":0 #即便這個區間中一條數據都沒有,這個區間也要返回
            } 
        }
    }
    }
  }
}

filter aggregate

過濾加聚合,統計type=t-shirt的平均價格

POST /sales/_search?size=0
{
    "aggs" : {
        "t_shirts" : {
            "filter" : { "term": { "type": "t-shirt" } },
            "aggs" : {
                "avg_price" : { "avg" : { "field" : "price" } }
            }
        }
    }
}

嵌套聚合-廣度優先

說一個應用於場景: 咱們檢索電影的評論, 可是咱們先按照演員分組聚合,在按照評論的數量進行聚合

分析: 若是咱們選擇深度優先的話, ES在構建演員電影相關信息時,會順道計算出電影下面評論數的信息,假如說有10萬個演員的話, 10萬*10=100萬個電影 每一個電影下又有不少影評,接着處理影評, 就這樣內存中可能會存在幾百萬條數據,可是咱們最終就須要50條,這種開銷是很大的

廣度優先的話,是咱們先處理電影數,而無論電影的評論數的聚合狀況,先從10萬演員中幹掉99990條數據,剩下10個演員再聚合

"aggs":{
            "target_actors":{
                "terms":{
                    "field":"actors",
                    "size":10,
                    "collect_mode":"breadth_first" # 廣度優先
                }
            }
        }

global aggregation

全局聚合,下面先使用query進行全文檢索,而後進行聚合, 下面的聚合其實是針對兩個不一樣的結果進行聚合,第一個聚合添加了global關鍵字,意思是ES中存在的全部doc進行聚合計算得出t-shirt的平均價格

第二個聚合針對全文檢索的結果進行聚合

POST /sales/_search?size=0
{
    "query" : {
        "match" : { "type" : "t-shirt" }
    },
    "aggs" : {
        "all_products" : {
            "global" : {}, 
            "aggs" : { 
                "avg_price" : { "avg" : { "field" : "price" } }
            }
        },
        "t_shirts": { "avg" : { "field" : "price" } }
    }
}

Cardinality Aggregate 基數聚合

做用相似於count(distcint),會對每個bucket中指定的field進行去重,而後取去重後的count

雖然她會存在5%左右的錯誤率,可是性能特別好

POST /sales/_search?size=0
{
    "aggs" : {
        "type_count" : {
            "cardinality" : { # 關鍵字
                "field" : "type"
            }
        }
    }
}

對Cardinality Aggregate的性能優化, 添加 precision_threshold 優化準確率和內存的開銷

下面的示例中將precision_threshold的值調整到100意思是當 type的類型小於100時,去重的精準度爲100%, 此時內存的佔用狀況爲 100*8=800字節

加入咱們將這個值調整爲1000,意思是當type的種類在1000個之內時,去重的精準度100%,內存的佔用率爲1000*8=80KB

官方給出的指標是, 當將precision_threshold設置爲5時,錯誤率會被控制在5%之內

POST /sales/_search?size=0
{
    "aggs" : {
        "type_count" : {
            "cardinality" : { # 關鍵字
                "field" : "type",
                "precision_threshold":100
            }
        }
    }
}

進一步優化,Cardinality底層使用的算法是 HyperLogLog++, 能夠針對這個算法的特性進行進一步的優化,由於這個算法的底層會對全部的 unique value取hash值,利用這個hash值去近似的求distcint count, 所以咱們能夠在建立mapping時,將這個hash的求法設置好,添加doc時,一併計算出這個hash值,這樣 HyperLogLog++ 就無需再計算hash值,而是直接使用

PUT /index/
{
    "mappings":{
        "my_type":{
            "properties":{
                "my_field":{
                    "type":"text",
                    "fields":{
                        "hash":{
                            "type":"murmu3"
                        }
                    }
                }
            }
        }
    }
}

控制聚合的升降序

先按照顏色聚合,在聚合的結果上,再根據價格進行聚合, 最終的結果中,按照價格聚合的分組中升序排序, 這算是個在下轉分析時的排序技巧

GET /index/type/_search
{
    "size":0,
     "aggs":{
         "group_by_color":{
             "term":{
                 "field":"color",
                 "order":{ # 
                     "avg_price":"asc"
                 }
             }
         },
         "aggs":{
             "avg_price":{
                 "avg":{
                     "field":"price"
                 }
             }
         }
     }
}

Percentiles Aggregation

計算百分比, 經常使用它計算如,在200ms內成功訪問網站的比率,在500ms內成功訪問網站的比例,在1000ms內成功訪問網站的比例, 或者是銷售價爲1000元的商品,佔總銷售量的比例, 銷售價爲2000元的商品佔總銷售量的比例等等

示例: 針對doc中的 load_time字段, 計算出在不一樣百分比下面的 load_time_outliner狀況

GET latency/_search
{
    "size": 0,
    "aggs" : {
        "load_time_outlier" : {
            "percentiles" : {
                "field" : "load_time" 
            }
        }
    }
}

響應 : 解讀: 在百分之50的加載請求中,平均load_time的時間是在445.0, 在99%的請求中,平均加載時間980.1

{
    ...

   "aggregations": {
      "load_time_outlier": {
         "values" : {
            "1.0": 9.9,
            "5.0": 29.500000000000004,
            "25.0": 167.5,
            "50.0": 445.0,
            "75.0": 722.5,
            "95.0": 940.5,
            "99.0": 980.1000000000001
         }
      }
   }
}

還能夠本身指定百分比跨度間隔

GET latency/_search
{
    "size": 0,
    "aggs" : {
        "load_time_outlier" : {
            "percentiles" : {
                "field" : "load_time",
                "percents" : [95, 99, 99.9] 
            }
        }
    }
}

優化: percentile底層使用的是 TDigest算法,用不少個節點執行百分比計算,近似估計,有偏差,節點越多,越精準

能夠設置compression的值, 默認是100 , ES限制節點的最可能是 compression*20 =2000個node去計算 , 由於節點越多,性能就越差

一個節點佔用 32字節, 1002032 = 64KB

GET latency/_search
{
    "size": 0,
    "aggs" : {
        "load_time_outlier" : {
            "percentiles" : {
                "field" : "load_time",
                "percents" : [95, 99, 99.9],
                "compression":100 # 默認值100
            }
        }
    }
}

優化相關性得分

  • 第一種方式:

在content字段中全文檢索 java elasticsearch時,給title中同時出現java elasticsearch的doc的權重加倍

"query": {
      "bool" : {# 前綴匹配
         "match":{
            "content":{
                 "query":"java elasticsearch"
            }
         },
         "should":[
             "match":{
                 "title":{
                     "query":"java elasticsearch",
                     "boost":2
                 }
             }
         ]
       } 
  }
  • 第二種: 更換寫法,改變佔用的權重比例
GET my_index/_doc/_search
{
  "query":{
     "should":[
      { "match":{"title":"this is"}},  # 1/3
      { "match":{"title":"this is"}},  # 1/3
      {
        "bool":{
         "should":[
           {"match":{"title":"this is"}}, # 1/6
           {"match":{"title":"this is"}}  # 1/6
           ]
       }
     }
   ] 
 }
}
  • 第三種: 若是不但願使用相關性得分,使用下面的語法
GET my_index/_doc/_search
{
    "query": {
        "constant_score" : {
            "filter" : {
              "term" : { "title" : "this"} #
            },
            "boost" : 1.2
        }
    }
}
  • 第四種: 靈活的查詢

查詢必須包含XXX,必須不包含YYY的doc

GET my_index/_doc/_search
{
  "query":{
    "bool": {
      "must":{
        "match":{
          "title":"this is a "
        }
      },
      "must_not":{
        "match":{
           "title":"another"
         }
       }
    }
  }
}
  • 第五種: 查詢必須包含XXX,能夠包含YYY,可是包含了YYY後它的權重就會減小指定的值
GET my_index/_doc/_search
{
  "query":{
    "boosting": {
      "positive":{
        "match":{
          "title":"this is a "
        }
      },
      "negative":{
        "match":{
           "title":"another"
         }
       },
       "negative_boost": 0.2
    }
  }
}
  • 第六種: 重打分機制
"query": { 
       "match":{
           "title":{
               "query":"java elasticsearch",
               "minimum_should_match":"50%"
           }
       },
       "rescore":{ # 對全文檢索的結果進行從新打分
           "window_size":50,  # 對全文檢索的前50條進行從新打分
           "query": { 
               "rescore_query":{ # 關鍵字
                    "match_phrase":{ # match_phrase + slop 感知 term persition,貢獻分數
                       "title":{
                           "query":"java elasticsearch",
                           "slop":50
                  }
              }
          }
       }
   }
  • 第七種: 混用match和match_phrase提升召回率
"query": { 
      "bool": {  
      "must":  {
          # 全文檢索雖然能夠匹配到大量的文檔,可是它不能控制詞條之間的距離
          # 可能java elasticsearch在Adoc中距離很近,可是它卻被ES排在結果集的後面
          # 它的性能比match_phrase高10倍,比proximity高20倍
         "match": {
            "address": "java elasticsearch" 
            } 
      },
      "should": {
         # 藉助match_phrase+slop能夠感知term position的功能,爲距離相近的doc貢獻分數,讓它們靠前排列
          "match_phrase":{
              "title":{
                  "query":"java elasticsearch",
                  "slop":50
              }
          }
      }
  }

歡迎你們點贊支持

相關文章
相關標籤/搜索