Elasticsearch核心詳解

3.1、文檔

在Elasticsearch中,文檔以JSON格式進行存儲,可以是複雜的結構,如:

{
  "_index": "haoke",
  "_type": "user",
  "_id": "1005",
  "_version": 1,
  "_score": 1,
  "_source": {
    "id": 1005,
    "name": "孫七",
    "age": 37,
    "sex": "女",
    "card": {
      "card_number": "123456789"
   }
 }
}

其中,card是一個複雜對象,嵌套的Card對象。

元數據(metadata)

一個文檔不只有數據。它還包含了元數據(metadata)——關於文檔的信息。三個必須的元數據節點是:

節點 說明
_index 文檔存儲的地方
_type 文檔代表的對象的類
_id 文檔的唯一標識

 _index

索引(index)類似於關係型數據庫裏的“數據庫”——它是我們存儲和索引關聯數據的地方。

    提示:
    事實上,我們的數據被存儲和索引在分片(shards)中,索引只是一個把一個或多個分片分組在一起的邏輯空
間。然而,這只是一些內部細節——我們的程序完全不用關心分片。對於我們的程序而言,文檔存儲在索引
(index)中。剩下的細節由Elasticsearch關心既可。

_type

        在應用中,我們使用對象表示一些“事物”,例如一個用戶、一篇博客、一個評論,或者一封郵件。每個對象都屬於一
個類(class),這個類定義了屬性或與對象關聯的數據。 user 類的對象可能包含姓名、性別、年齡和Email地址。
在關係型數據庫中,我們經常將相同類的對象存儲在一個表裏,因爲它們有着相同的結構。同理,在Elasticsearch
中,我們使用相同類型(type)的文檔表示相同的“事物”,因爲他們的數據結構也是相同的。
每個類型(type)都有自己的映射(mapping)或者結構定義,就像傳統數據庫表中的列一樣。所有類型下的文檔被存儲
在同一個索引下,但是類型的映射(mapping)會告訴Elasticsearch不同的文檔如何被索引。
_type 的名字可以是大寫或小寫,不能包含下劃線或逗號。我們將使用 blog 做爲類型名。

_id
id僅僅是一個字符串,它與 _index 和 _type 組合時,就可以在Elasticsearch中唯一標識一個文檔。當創建一個文
檔,你可以自定義 _id ,也可以讓Elasticsearch幫你自動生成(32位長度)。

3.2、查詢響應

3.2.1、pretty

可以在查詢url後面添加pretty參數,使得返回的json更易查看。

 3.2.2、指定響應字段

在響應的數據中,如果我們不需要全部的字段,可以指定某些需要的字段進行返回。

GET /haoke/user/1005?_source=id,name
#響應
{
  "_index": "haoke",
  "_type": "user",
  "_id": "1005",
  "_version": 1,
  "found": true,
  "_source": {
    "name": "孫七",
    "id": 1005
 }
}

如不需要返回元數據,僅僅返回原始數據,可以這樣:

GET /haoke/user/1005/_source 

還可以這樣:

GET /haoke/user/1005/_source?_source=id,name 

3.3、判斷文檔是否存在

如果我們只需要判斷文檔是否存在,而不是查詢文檔內容,那麼可以這樣:

HEAD /haoke/user/1005 

 當然,這隻表示你在查詢的那一刻文檔不存在,但並不表示幾毫秒後依舊不存在。另一個進程在這期間可能創
建新文檔。

3.4、批量操作

有些情況下可以通過批量操作以減少網絡請求。如:批量查詢、批量插入數據。

3.4.1、批量查詢

POST /haoke/user/_mget
{
 "ids" : [ "1001", "1003" ]
}

結果:

 

 如果,某一條數據不存在,不影響整體響應,需要通過found的值進行判斷是否查詢到數據。

POST /haoke/user/_mget
{
 "ids" : [ "1001", "1006" ]
}

結果:

 3.4.2、_bulk操作

在Elasticsearch中,支持批量的插入、修改、刪除操作,都是通過_bulk的api完成的。
請求格式如下:(請求格式不同尋常)

{ action: { metadata }}\n
{ request body    }\n
{ action: { metadata }}\n
{ request body    }\n
...

批量插入數據:

{"create":{"_index":"haoke","_type":"user","_id":2001}}
{"id":2001,"name":"name1","age": 20,"sex": "男"}
{"create":{"_index":"haoke","_type":"user","_id":2002}}
{"id":2002,"name":"name2","age": 20,"sex": "男"}
{"create":{"_index":"haoke","_type":"user","_id":2003}}
{"id":2003,"name":"name3","age": 20,"sex": "男"}

注意最後一行的回車

 響應結果:

{
  "took": 17,
  "errors": false,
  "items": [
   {
      "create": {
        "_index": "haoke",
        "_type": "user",
        "_id": "2001",
        "_version": 1,
        "result": "created",
        "_shards": {
          "total": 1,
          "successful": 1,
          "failed": 0
       },
        "_seq_no": 24,
        "_primary_term": 1,
        "status": 201
     }
   },
   {
      "create": {
        "_index": "haoke",
        "_type": "user",
        "_id": "2002",
        "_version": 1,
        "result": "created",
        "_shards": {
          "total": 1,
          "successful": 1,
          "failed": 0
       },
        "_seq_no": 0,
        "_primary_term": 1,
        "status": 201
     }
   },
   {
      "create": {
        "_index": "haoke",
        "_type": "user",
        "_id": "2003",
        "_version": 1,
        "result": "created",
        "_shards": {
          "total": 1,
          "successful": 1,
          "failed": 0
       },
        "_seq_no": 1,
        "_primary_term": 1,
        "status": 201
     }
   }
 ]
}

批量刪除:

{"delete":{"_index":"haoke","_type":"user","_id":2001}}
{"delete":{"_index":"haoke","_type":"user","_id":2002}}
{"delete":{"_index":"haoke","_type":"user","_id":2003}}

由於delete沒有請求體,所以,action的下一行直接就是下一個action。

 

{
  "took": 3,
  "errors": false,
  "items": [
   {
      "delete": {
        "_index": "haoke",
        "_type": "user",
        "_id": "2001",
        "_version": 2,
        "result": "deleted",
        "_shards": {
          "total": 1,
          "successful": 1,
          "failed": 0
       },
        "_seq_no": 25,
        "_primary_term": 1,
        "status": 200
     }
   },
   {
      "delete": {
        "_index": "haoke",
        "_type": "user",
        "_id": "2002",
        "_version": 2,
        "result": "deleted",
        "_shards": {
          "total": 1,
          "successful": 1,
          "failed": 0
       },
        "_seq_no": 2,
        "_primary_term": 1,
        "status": 200
     }
   },
   {
      "delete": {
        "_index": "haoke",
        "_type": "user",
        "_id": "2003",
        "_version": 2,
        "result": "deleted",
        "_shards": {
          "total": 1,
          "successful": 1,
          "failed": 0
       },
        "_seq_no": 3,
        "_primary_term": 1,
        "status": 200
     }
   }
 ]
}

其他操作就類似了。
一次請求多少性能最高?

  • 整個批量請求需要被加載到接受我們請求節點的內存裏,所以請求越大,給其它請求可用的內存就越小。有一個最佳的bulk請求大小。超過這個大小,性能不再提升而且可能降低。
  • 最佳大小,當然並不是一個固定的數字。它完全取決於你的硬件、你文檔的大小和複雜度以及索引和搜索的負載。
  • 幸運的是,這個最佳點(sweetspot)還是容易找到的:試着批量索引標準的文檔,隨着大小的增長,當性能開始降低,說明你每個批次的大小太大了。開始的數量可以在1000~5000個文檔之間,如果你的文檔非常大,可以使用較小的批次。
  • 通常着眼於你請求批次的物理大小是非常有用的。一千個1kB的文檔和一千個1MB的文檔大不相同。一個好的批次最好保持在5-15MB大小間。

3.5、分頁

和SQL使用 LIMIT 關鍵字返回只有一頁的結果一樣,Elasticsearch接受 from 和 size 參數:

size: 結果數,默認10
from: 跳過開始的結果數,默認0

如果你想每頁顯示5個結果,頁碼從1到3,那請求如下:

GET /_search?size=5
GET /_search?size=5&from=5
GET /_search?size=5&from=10

應該當心分頁太深或者一次請求太多的結果。結果在返回前會被排序。但是記住一個搜索請求常常涉及多個分
片。每個分片生成自己排好序的結果,它們接着需要集中起來排序以確保整體排序正確。

GET /haoke/user/_search?size=1&from=2 

 在集羣系統中深度分頁
爲了理解爲什麼深度分頁是有問題的,讓我們假設在一個有5個主分片的索引中搜索。當我們請求結果的第一
頁(結果1到10)時,每個分片產生自己最頂端10個結果然後返回它們給請求節點(requesting node),它再
排序這所有的50個結果以選出頂端的10個結果。
現在假設我們請求第1000頁——結果10001到10010。工作方式都相同,不同的是每個分片都必須產生頂端的
10010個結果。然後請求節點排序這50050個結果並丟棄50040個!

你可以看到在分佈式系統中,排序結果的花費隨着分頁的深入而成倍增長。這也是爲什麼網絡搜索引擎中任何
語句不能返回多於1000個結果的原因。

3.6、映射

           前面我們創建的索引以及插入數據,都是由Elasticsearch進行自動判斷類型,有些時候我們是需要進行明確字段類型
的,否則,自動判斷的類型和實際需求是不相符的。
自動判斷的規則如下:

JSON type  Field type
Boolean: true or false  "boolean"
Whole number: 123  "long"
Floating point: 123.45  "double"
String, valid date: "2014-09-15"  "date"
String: "foo bar"  "string"

Elasticsearch中支持的類型如下:

類型  表示的數據類型
String  string , text , keyword
Whole number  byte , short , integer , long
Floating point  float , double
Boolean  boolean
Date date
  • string類型在ElasticSearch 舊版本中使用較多,從ElasticSearch 5.x開始不再支持string,由text和keyword類型替代。
  • text 類型,當一個字段是要被全文搜索的,比如Email內容、產品描述,應該使用text類型。設置text類型以後,字段內容會被分析,在生成倒排索引以前,字符串會被分析器分成一個一個詞項。text類型的字段不用於排序,很少用於聚合。
  • keyword類型適用於索引結構化的字段,比如email地址、主機名、狀態碼和標籤。如果字段需要進行過濾(比如查找已發佈博客中status屬性爲published的文章)、排序、聚合。keyword類型的字段只能通過精確值搜索到。

創建明確類型的索引:

PUT /admin
{
  "settings": {
    "index": {
      "number_of_shards": "2",
      "number_of_replicas": "0"
   }
 },
  "mappings": {
    "person": {
      "properties": {
        "name": {
          "type": "text"
       },
        "age": {
          "type": "integer"
       },
        "mail": {
          "type": "keyword"
       },
        "hobby": {
          "type": "text"
       }
     }
   }
 }
}

查看映射:

#自己的用戶名路徑
GET /admin/_mapping 

插入數據:

POST /admin/_bulk
{"index":{"_index":"admin","_type":"person"}}
{"name":"張三","age": 20,"mail": "[email protected]","hobby":"羽毛球、乒乓球、足球"}
{"index":{"_index":"admin","_type":"person"}}
{"name":"李四","age": 21,"mail": "[email protected]","hobby":"羽毛球、乒乓球、足球、籃球"}
{"index":{"_index":"admin","_type":"person"}}
{"name":"王五","age": 22,"mail": "[email protected]","hobby":"羽毛球、籃球、游泳、聽音樂"}
{"index":{"_index":"admin","_type":"person"}}
{"name":"趙六","age": 23,"mail": "[email protected]","hobby":"跑步、游泳"}
{"index":{"_index":"admin","_type":"person"}}
{"name":"孫七","age": 24,"mail": "[email protected]","hobby":"聽音樂、看電影"}

測試搜索:

POST /admin/person/_search
{
  "query" : {
    "match" : { 
      "hobby" : "音樂"
   }
 }
}

3.7、結構化查詢

3.7.1、term查詢

term 主要用於精確匹配哪些值,比如數字,日期,布爾值或 not_analyzed 的字符串(未經分析的文本數據類型):

{ "term": { "age":   26      }}
 { "term": { "date":  "2014-09-01" }}
 { "term": { "public": true     }}
 { "term": { "tag":   "full_text" }}

示例:

POST /admin/person/_search
{
  "query" : {
    "term" : { 
      "age" : 20
   }
 }
}

3.7.2、terms查詢

terms 跟 term 有點類似,但 terms 允許指定多個匹配條件。 如果某個字段指定了多個值,那麼文檔需要一起去做匹配:

{
  "terms": {
    "tag": [ "search", "full_text", "nosql" ]
   }
}
POST /admin/person/_search
{
  "query" : {
    "terms" : { 
      "age" : [20,21]
   }
 }
}

 3.7.3、range查詢

range 過濾允許我們按照指定範圍查找一批數據:

{
  "range": {
    "age": {
      "gte":  20,
      "lt":  30
   }
 }
}

範圍操作符包含:
gt :: 大於
gte :: 大於等於
lt :: 小於
lte :: 小於等於
示例:

POST /admin/person/_search
{
  "query": {
    "range": {
      "age": {
        "gte": 20,
        "lte": 22
     }
   }
 }
}

 3.7.4、exists 查詢

exists 查詢可以用於查找文檔中是否包含指定字段或沒有某個字段,類似於SQL語句中的 IS_NULL 條件

{
  "exists":  {
    "field":   "title"
 }
}

這兩個查詢只是針對已經查出一批數據來,但是想區分出某個字段是否存在的時候使用。
示例:

POST /haoke/user/_search
{
  "query": {
    "exists": {  #必須包含
      "field": "card"
   }
 }
}

 3.6.5、match查詢

match 查詢是一個標準查詢,不管你需要全文本查詢還是精確查詢基本上都要用到它。
如果你使用 match 查詢一個全文本字段,它會在真正查詢之前用分析器先分析 match 一下查詢字符:

{
  "match": {
    "tweet": "About Search"
 }
}

如果用 match 下指定了一個確切值,在遇到數字,日期,布爾值或者 not_analyzed 的字符串時,它將爲你搜索你給定的值:

3.7.6、bool查詢

bool 查詢可以用來合併多個條件查詢結果的布爾邏輯,它包含一下操作符:
must :: 多個查詢條件的完全匹配,相當於 and 。
must_not :: 多個查詢條件的相反匹配,相當於 not 。
should :: 至少有一個查詢條件匹配, 相當於 or 。
這些參數可以分別繼承一個查詢條件或者一個查詢條件的數組:

{
  "bool": {
    "must":   { "term": { "folder": "inbox" }},
    "must_not": { "term": { "tag":   "spam" }},
    "should": [
         { "term": { "starred": true  }},
         { "term": { "unread":  true  }}
   ]
 }
}

3.8、過濾查詢

前面講過結構化查詢,Elasticsearch也支持過濾查詢,如term、range、match等。

示例:查詢年齡爲20歲的用戶。

POST /admin/person/_search
{
  "query": {
    "bool": {
      "filter": {
        "term": {
          "age": 20
       }
     }
   }
 }
}

結果:

 查詢和過濾的對比

  • 一條過濾語句會詢問每個文檔的字段值是否包含着特定值。
  • 查詢語句會詢問每個文檔的字段值與特定值的匹配程度如何。 

              一條查詢語句會計算每個文檔與查詢語句的相關性,會給出一個相關性評分 _score,並且 按照相關性對匹配到的文檔           進行排序。 這種評分方式非常適用於一個沒有完全配置結果的全文本搜索。

  • 一個簡單的文檔列表,快速匹配運算並存入內存是十分方便的, 每個文檔僅需要1個字節。這些緩存的過濾結果集與後續請求的結合使用是非常高效的。
  • 查詢語句不僅要查找相匹配的文檔,還需要計算每個文檔的相關性,所以一般來說查詢語句要比 過濾語句更耗時,並且查詢結果也不可緩存。

建議:
做精確匹配搜索時,最好用過濾語句,因爲過濾語句可以緩存數據。

 

4、中文分詞

4.1、什麼是分詞

分詞就是指將一個文本轉化成一系列單詞的過程,也叫文本分析,在Elasticsearch中稱之爲Analysis。
舉例:我是中國人 --> 我/是/中國人

4.2、分詞api

指定分詞器進行分詞

POST /_analyze
{
  "analyzer":"standard",
  "text":"hello world"
}

結果:

 在結果中不僅可以看出分詞的結果,還返回了該詞在文本中的位置。
        指定索引分詞

POST /admin/_analyze
{
  "analyzer": "standard",
  "field": "hobby",
  "text": "聽音樂"
}

4.4、中文分詞

中文分詞的難點在於,在漢語中沒有明顯的詞彙分界點,如在英語中,空格可以作爲分隔符,如果分隔不正確就會造
成歧義。
如:
我/愛/炒肉絲
我/愛/炒/肉絲
常用中文分詞器,IK、jieba、THULAC等,推薦使用IK分詞器。

IK Analyzer是一個開源的,基於java語言開發的輕量級的中文分詞工具包。從2006年12月推出1.0版開始,
IKAnalyzer已經推出了3個大版本。最初,它是以開源項目Luence爲應用主體的,結合詞典分詞和文法分析算
法的中文分詞組件。新版本的IK Analyzer 3.0則發展爲面向Java的公用分詞組件,獨立於Lucene項目,同時提
供了對Lucene的默認優化實現。
採用了特有的“正向迭代最細粒度切分算法“,具有80萬字/秒的高速處理能力 採用了多子處理器分析模式,支
持:英文字母(IP地址、Email、URL)、數字(日期,常用中文數量詞,羅馬數字,科學計數法),中文詞彙
(姓名、地名處理)等分詞處理。 優化的詞典存儲,更小的內存佔用。

 IK分詞器 Elasticsearch插件地址:https://github.com/medcl/elasticsearch-analysis-ik

#安裝方法:將下載到的elasticsearch-analysis-ik-6.5.4.zip解壓到/elasticsearch/plugins/ik目錄下
即可。
mkdir es/plugins/ik
cp elasticsearch-analysis-ik-6.5.4.zip ./es/plugins/ik
#解壓
unzip elasticsearch-analysis-ik-6.5.4.zip
#重啓
./bin/elasticsearch

測試:

POST /_analyze
{
  "analyzer": "ik_max_word",
  "text": "我是中國人"
}

結果:

{
  "tokens": [
   {
      "token": "我",
      "start_offset": 0,
      "end_offset": 1,
      "type": "CN_CHAR",
      "position": 0
   },
   {
      "token": "是",
      "start_offset": 1,
      "end_offset": 2,
      "type": "CN_CHAR",
      "position": 1
   },
   {
      "token": "中國人",
      "start_offset": 2,
      "end_offset": 5,
      "type": "CN_WORD",
      "position": 2
   },
   {
      "token": "中國",
      "start_offset": 2,
      "end_offset": 4,
      "type": "CN_WORD",
      "position": 3
   },
   {
      "token": "國人",
      "start_offset": 3,
      "end_offset": 5,
      "type": "CN_WORD",
      "position": 4
   }
 ]
}

可以看到,已經對中文進行了分詞。

 

 

5、全文搜索

全文搜索兩個最重要的方面是:

  • 相關性(Relevance) 它是評價查詢與其結果間的相關程度,並根據這種相關程度對結果排名的一種能力,這種計算方式可以是 TF/IDF 方法、地理位置鄰近、模糊相似,或其他的某些算法。
  • 分詞(Analysis) 它是將文本塊轉換爲有區別的、規範化的 token 的一個過程,目的是爲了創建倒排索引以及查詢倒排索引。

5.1、構造數據

PUT /admin
{
  "settings": {
    "index": {
      "number_of_shards": "1",
      "number_of_replicas": "0"
   }
 },
  "mappings": {
    "person": {
      "properties": {
        "name": {
          "type": "text"
       },
        "age": {
          "type": "integer"
       },
        "mail": {
          "type": "keyword"
       },
        "hobby": {
          "type": "text",
          "analyzer":"ik_max_word"
       }
     }
   }
 }
}

 批量插入數據:

POST http://172.16.55.185:9200/admin/_bulk
{"index":{"_index":"admin","_type":"person"}}
{"name":"張三","age": 20,"mail": "[email protected]","hobby":"羽毛球、乒乓球、足球"}
{"index":{"_index":"admin","_type":"person"}}
{"name":"李四","age": 21,"mail": "[email protected]","hobby":"羽毛球、乒乓球、足球、籃球"}
{"index":{"_index":"admin","_type":"person"}}
{"name":"王五","age": 22,"mail": "[email protected]","hobby":"羽毛球、籃球、游泳、聽音樂"}
{"index":{"_index":"admin","_type":"person"}}
{"name":"趙六","age": 23,"mail": "[email protected]","hobby":"跑步、游泳、籃球"}
{"index":{"_index":"admin","_type":"person"}}
{"name":"孫七","age": 24,"mail": "[email protected]","hobby":"聽音樂、看電影、羽毛球"}

結果:

 5.2、單詞搜索

POST /admin/person/_search
{
"query":{
"match":{
"hobby":"音樂"
}
},
"highlight": {
    "fields": {
      "hobby": {}
   }
 }
}

結果:

{
  "took": 9,
  "timed_out": false,
  "_shards": {
    "total": 1,
    "successful": 1,
    "skipped": 0,
    "failed": 0
 },
  "hits": {
    "total": 2,
    "max_score": 0.6841192,
    "hits": [
     {
        "_index": "admin",
        "_type": "person",
        "_id": "Uv0cDWgBR-bSw8-LpdkZ",
        "_score": 0.6841192,
        "_source": {
          "name": "王五",
          "age": 22,
          "mail": "[email protected]",
          "hobby": "羽毛球、籃球、游泳、聽音樂"
       },
        "highlight": {
          "hobby": [
            "羽毛球、籃球、游泳、聽<em>音樂</em>"
         ]
       }
     },
     {
        "_index": "admin",
        "_type": "person",
        "_id": "VP0cDWgBR-bSw8-LpdkZ",
        "_score": 0.6841192,
        "_source": {
          "name": "孫七",
          "age": 24,
          "mail": "[email protected]",
          "hobby": "聽音樂、看電影、羽毛球"
       },
        "highlight": {
          "hobby": [
            "聽<em>音樂</em>、看電影、羽毛球"
         ]
       }
     }
   ]
 }
}

過程說明:
1. 檢查字段類型
   愛好 hobby 字段是一個 text 類型( 指定了IK分詞器),這意味着查詢字符串本身也應該被分詞。
2. 分析查詢字符串 。
   將查詢的字符串 “音樂” 傳入IK分詞器中,輸出的結果是單個項 音樂。因爲只有一個單詞項,所以 match 查詢執行的是單個底層 term 查詢。
3. 查找匹配文檔 。
    用 term 查詢在倒排索引中查找 “音樂” 然後獲取一組包含該項的文檔,本例的結果是文檔:3 、5 。

4. 爲每個文檔評分 。
     用 term 查詢計算每個文檔相關度評分 _score ,這是種將 詞頻(term frequency,即詞 “音樂” 在相關文檔的
hobby 字段中出現的頻率)和 反向文檔頻率(inverse document frequency,即詞 “音樂” 在所有文檔的
hobby 字段中出現的頻率),以及字段的長度(即字段越短相關度越高)相結合的計算方式。

5.3、多詞搜索

POST /admin/person/_search
{
"query":{
"match":{
"hobby":"音樂 籃球"
}
},
"highlight": {
    "fields": {
      "hobby": {}
   }
 }
}

結果:

{
  "took": 3,
  "timed_out": false,
  "_shards": {
    "total": 1,
    "successful": 1,
    "skipped": 0,
    "failed": 0
 },
  "hits": {
    "total": 4,
    "max_score": 1.3192271,
    "hits": [
     {
        "_index": "admin",
        "_type": "person",
        "_id": "Uv0cDWgBR-bSw8-LpdkZ",
        "_score": 1.3192271,
        "_source": {
          "name": "王五",
          "age": 22,
          "mail": "[email protected]",
          "hobby": "羽毛球、籃球、游泳、聽音樂"
       },
        "highlight": {
          "hobby": [
            "羽毛球、<em>籃球</em>、游泳、聽<em>音樂</em>"
         ]
        }
     },
     {
        "_index": "admin",
        "_type": "person",
        "_id": "VP0cDWgBR-bSw8-LpdkZ",
        "_score": 0.81652206,
        "_source": {
          "name": "孫七",
          "age": 24,
          "mail": "[email protected]",
          "hobby": "聽音樂、看電影、羽毛球"
       },
        "highlight": {
          "hobby": [
            "聽<em>音樂</em>、看電影、羽毛球"
         ]
       }
     },
     {
        "_index": "admin",
        "_type": "person",
        "_id": "Vf0gDWgBR-bSw8-LOdm_",
        "_score": 0.6987338,
        "_source": {
          "name": "趙六",
          "age": 23,
          "mail": "[email protected]",
          "hobby": "跑步、游泳、籃球"
       },
        "highlight": {
          "hobby": [
            "跑步、游泳、<em>籃球</em>"
         ]
       }
     },
     {
        "_index": "admin",
        "_type": "person",
        "_id": "Uf0cDWgBR-bSw8-LpdkZ",
        "_score": 0.50270504,
        "_source": {
          "name": "李四",
          "age": 21,
          "mail": "[email protected]",
          "hobby": "羽毛球、乒乓球、足球、籃球"
       },
        "highlight": {
          "hobby": [
            "羽毛球、乒乓球、足球、<em>籃球</em>"
         ]
       }
     }
   ]
 }
}

結果:

 可以看到結果符合預期。

前面我們測試了“OR” 和 “AND”搜索,這是兩個極端,其實在實際場景中,並不會選取這2個極端,更有可能是選取這
種,或者說,只需要符合一定的相似度就可以查詢到數據,在Elasticsearch中也支持這樣的查詢,通過
minimum_should_match來指定匹配度,如:70%;

示例:

{
"query":{
"match":{
"hobby":{
"query":"游泳 羽毛球",
"minimum_should_match":"80%"
}
}
},
"highlight": {
    "fields": {
      "hobby": {}
   }
 }
}
#結果:省略顯示
"hits": {
    "total": 4, #相似度爲80%的情況下,查詢到4條數據
    "max_score": 1.621458,
    "hits": [
   .........
   }
#設置40%進行測試:
{
"query":{
"match":{
"hobby":{
"query":"游泳 羽毛球",
"minimum_should_match":"40%"
}
}
},
"highlight": {
    "fields": {
      "hobby": {}
   }
 }
}
#結果:
"hits": {
    "total": 5,  #相似度爲40%的情況下,查詢到5條數據
    "max_score": 1.621458,
    "hits": [
   ........
 }

相似度應該多少合適,需要在實際的需求中進行反覆測試,纔可得到合理的值。

 5.4、組合搜索

在搜索時,也可以使用過濾器中講過的bool組合查詢,示例:

POST /admin/person/_search
{
"query":{
"bool":{
"must":{
"match":{
"hobby":"籃球"
}
},
"must_not":{
"match":{
"hobby":"音樂"
}
},
"should":[
{
"match": {
"hobby":"游泳"
}
}
]
}
},
"highlight": {
    "fields": {
      "hobby": {}
   }
 }
}

上面搜索的意思是:
搜索結果中必須包含籃球,不能包含音樂,如果包含了游泳,那麼它的相似度更高。

 結果:

 評分的計算規則
bool 查詢會爲每個文檔計算相關度評分 _score , 再將所有匹配的 must 和 should 語句的分數 _score 求和,
最後除以 must 和 should 語句的總數。
must_not 語句不會影響評分; 它的作用只是將不相關的文檔排除。

 默認情況下,should中的內容不是必須匹配的,如果查詢語句中沒有must,那麼就會至少匹配其中一個。當然了,
也可以通過minimum_should_match參數進行控制,該值可以是數字也可以的百分比。
示例:

POST /admin/person/_search
{
"query":{
"bool":{
"should":[
        {
    "match": {
"hobby":"游泳"
}
},
{
"match": {
"hobby":"籃球"
}
},
{
"match": {
"hobby":"音樂"
}
}
],
"minimum_should_match":2
}
},
"highlight": {
    "fields": {
      "hobby": {}
   }
 }
}

minimum_should_match爲2,意思是should中的三個詞,至少要滿足2個。

 結果:

 5.5、權重

有些時候,我們可能需要對某些詞增加權重來影響該條數據的得分。如下:

搜索關鍵字爲“游泳籃球”,如果結果中包含了“音樂”權重爲10,包含了“跑步”權重爲2。

 

POST /admin/person/_search
{
  "query": {
    "bool": {
      "must": {
        "match": {
          "hobby": {
            "query": "游泳籃球",
            "operator": "and"
         }
       }
     },
      "should": [
       {
          "match": {
            "hobby": {
              "query": "音樂",
              "boost": 10
           }
         }
       },
       {
          "match": {
            "hobby": {
              "query": "跑步",
              "boost": 2
           }
         }
       }
     ]
   }
 },
  "highlight": {
    "fields": {
      "hobby": {}
   }
 }
}

結果:

 如果不設置權重的查詢結果是這樣:

 

參考資料:

 https://www.bilibili.com/video/av67957955/?p=56&t=49

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章