elasticsearch 學習筆記3

1.分頁

image.png

舉個例子,上面的_search查詢中,total給出數據的總量,但是,實際顯示出來的只有10條。那么,如果我們顯示更多的數據呢
和 SQL 使用 LIMIT 關鍵字返回單個 page 結果的方法相同,Elasticsearch 接受 fromsize 參數:
size顯示應該返回的結果數量,默認是10
from 顯示應該跳過的初始結果數量,默認是0
如果每頁展示 5 條結果,可以用下面方式請求得到 1 到 3 頁的結果:

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

理解為什么深度分頁是有問題的,我們可以假設在一個有 5 個主分片的索引中搜索。 當我們請求結果的第一頁(結果從 1 到 10 ),每一個分片產生前 10 的結果,并且返回給協調節點,協調節點對 50 個結果排序得到全部結果的前 10 個。

現在假設我們請求第 1000 頁--結果從 10001 到 10010 。所有都以相同的方式工作除了每個分片不得不產生前10010個結果以外。 然后協調節點對全部 50050 個結果排序最后丟棄掉這些結果中的 50040 個結果。

可以看到,在分布式系統中,對結果排序的成本隨分頁的深度成指數上升。這就是 web 搜索引擎對任何查詢都不要返回超過 1000 個結果的原因

2. 映射和分析

當擺弄索引里面的數據時,我們發現一些奇怪的事情。一些事情看起來被打亂了:在我們的索引中有12條推文,其中只有一條包含日期 2014-09-15 ,但是看一看下面查詢命中的 總數 (total):

GET /_search?q=2014              # 12 results
GET /_search?q=2014-09-15        # 12 results !
GET /_search?q=date:2014-09-15   # 1  result
GET /_search?q=date:2014         # 0  results !

為什么在_all字段查詢日期返回所有推文,而在 date 字段只查詢年份卻沒有返回結果?為什么我們在 _all 字段和 date 字段的查詢結果有差別?
推測起來,這是因為數據在 _all 字段與 date 字段的索引方式不同。所以,通過請求 gb 索引中 tweet 類型的映射(或模式定義),讓我們看一看 Elasticsearch 是如何解釋我們文檔結構的:

GET /gb/_mapping/tweet

結果:

{
   "gb": {
      "mappings": {
         "tweet": {
            "properties": {
               "date": {
                  "type": "date",
                  "format": "strict_date_optional_time||epoch_millis"
               },
               "name": {
                  "type": "string"
               },
               "tweet": {
                  "type": "string"
               },
               "user_id": {
                  "type": "long"
               }
            }
         }
      }
   }
}

基于對字段類型的猜測, Elasticsearch 動態為我們產生了一個映射。這個響應告訴我們date字段被認為是date類型的。由于_all是默認字段,所以沒有提及它。但是我們知道_all字段是string類型的。所以date字段和string字段索引方式不同,因此搜索結果也不一樣。這完全不令人吃驚。你可能會認為核心數據類型 strings、numbers、Booleans 和 dates 的索引方式有稍許不同。沒錯,他們確實稍有不同。

但是,到目前為止,最大的差異在于代表精確值(它包括string字段)的字段和代表全文的字段。這個區別非常重要——它將搜索引擎和所有其他數據庫區別開來。

3.精確值和全文

Elasticsearch 中的數據可以概括的分為兩類:精確值和全文。 精確值 如它們聽起來那樣精確。 例如日期或者用戶 ID,但字符串也可以表示精確值,例如用戶名或郵箱地址。對于精確值來講,Foofoo是不同的,20142014-09-15也是不同的。另一方面,全文是指文本數據(通常以人類容易識別的語言書寫),例如一個推文的內容或一封郵件的內容.
精確值很容易查詢。結果是二進制的:要么匹配查詢,要么不匹配。查詢全文數據要微妙的多。我們問的不只是“這個文檔匹配查詢嗎”,而是“該文檔匹配查詢的程度有多大?”換句話說,該文檔與給定查詢的相關性如何?我們很少對全文類型的域做精確匹配。相反,我們希望在文本類型的域中搜索。為了促進這類在全文域中的查詢,Elasticsearch 首先 分析 文檔,之后根據結果創建 倒排索引 。

4.倒排索引

例如,假設我們有兩個文檔,每個文檔的content域包含如下內容:

    1. The quick brown fox jumped over the lazy dog
    1. Quick brown foxes leap over lazy dogs in summer

為了創建倒排索引,我們首先將每個文檔的content域拆分成單獨的詞(我們稱它為詞條tokens),創建一個包含所有不重復詞條的排序列表,然后列出每個詞條出現在哪個文檔。結果如下所示:

Term      Doc_1  Doc_2
-------------------------
Quick   |       |  X
The     |   X   |
brown   |   X   |  X
dog     |   X   |
dogs    |       |  X
fox     |   X   |
foxes   |       |  X
in      |       |  X
jumped  |   X   |
lazy    |   X   |  X
leap    |       |  X
over    |   X   |  X
quick   |   X   |
summer  |       |  X
the     |   X   |
------------------------

現在,如果我們想搜索 quick brown ,我們只需要查找包含每個詞條的文檔:

Term      Doc_1  Doc_2
-------------------------
brown   |   X   |  X
quick   |   X   |
------------------------
Total   |   2   |  1

兩個文檔都匹配,但是第一個文檔比第二個匹配度更高。如果我們使用僅計算匹配詞條數量的簡單相似性算法,那么,我們可以說,對于我們查詢的相關性來講,第一個文檔比第二個文檔更佳。但是,我們目前的倒排索引有一些問題:

  • Quick 和 quick 以獨立的詞條出現,然而用戶可能認為它們是相同的詞。
  • fox 和 foxes 非常相似, 就像 dog 和 dogs ;他們有相同的詞根。
  • jumped 和 leap, 盡管沒有相同的詞根,但他們的意思很相近。他們是同義詞。

使用前面的索引搜索 +Quick +fox 不會得到任何匹配文檔。(記住,+ 前綴表明這個詞必須存在。)只有同時出現 Quick 和 fox 的文檔才滿足這個查詢條件,但是第一個文檔包含 quick fox ,第二個文檔包含 Quick foxes 。我們的用戶可以合理的期望兩個文檔與查詢匹配。我們可以做的更好。如果我們將詞條規范為標準模式,那么我們可以找到與用戶搜索的詞條不完全一致,但具有足夠相關性的文檔。例如:

  • Quick 可以小寫化為 quick 。
  • foxes 可以 詞干提取 --變為詞根的格式-- 為 fox 。類似的, dogs 可以為提取為 dog 。
  • jumped 和 leap 是同義詞,可以索引為相同的單詞 jump 。

現在索引看上去像這樣:

Term      Doc_1  Doc_2
-------------------------
brown   |   X   |  X
dog     |   X   |  X
fox     |   X   |  X
in      |       |  X
jump    |   X   |  X
lazy    |   X   |  X
over    |   X   |  X
quick   |   X   |  X
summer  |       |  X
the     |   X   |  X
------------------------

這還遠遠不夠。我們搜索 +Quick +fox 仍然 會失敗,因為在我們的索引中,已經沒有 Quick 了。但是,如果我們對搜索的字符串使用與 content 域相同的標準化規則,會變成查詢 +quick +fox ,這樣兩個文檔都會匹配!

5.分析和分析器

分析包含下面的過程:

  • 首先,將一塊文本分成適合于倒排索引的獨立的 詞條
  • 之后,將這些詞條統一化為標準格式以提高它們的“可搜索性”,或者 recall

分析器執行上面的工作。分析器實際上是將三個功能封裝到了一個包里:
字符過濾器
首先,字符串按順序通過每個 字符過濾器 。他們的任務是在分詞前整理字符串。一個字符過濾器可以用來去掉HTML,或者將 & 轉化成 and
分詞器
其次,字符串被 分詞器 分為單個的詞條。一個簡單的分詞器遇到空格和標點的時候,可能會將文本拆分成詞條。
Token 過濾器
最后,詞條按順序通過每個 token 過濾器 。這個過程可能會改變詞條(例如,小寫化 Quick ),刪除詞條(例如, 像 aandthe 等無用詞),或者增加詞條(例如,像 jump 和 leap 這種同義詞)。
Elasticsearch提供了開箱即用的字符過濾器、 分詞器和token 過濾器。 這些可以組合起來形成自定義的分析器以用于不同的目的。我們會在
自定義分析器 章節詳細討論。

5.1 內置的分析器

但是, Elasticsearch還附帶了可以直接使用的預包裝的分析器。 接下來我們會列出最重要的分析器。為了證明它們的差異,我們看看每個分析器會從下面的字符串得到哪些詞條:
"Set the shape to semi-transparent by calling set_trans(5)"
標準分析器
標準分析器是Elasticsearch默認使用的分析器。它是分析各種語言文本最常用的選擇。它根據Unicode 聯盟定義的單詞邊界劃分文本。刪除絕大部分標點。最后,將詞條小寫。它會產生
set, the, shape, to, semi, transparent, by, calling, set_trans, 5
簡單分析器
簡單分析器在任何不是字母的地方分隔文本,將詞條小寫。它會產生
set, the, shape, to, semi, transparent, by, calling, set, trans
空格分析器
空格分析器在空格的地方劃分文本。它會產生
Set, the, shape, to, semi-transparent, by, calling, set_trans(5)
語言分析器
特定語言分析器可用于很多語言。它們可以考慮指定語言的特點。例如,英語分析器附帶了一組英語無用詞(常用單詞,例and或者the,它們對相關性沒有多少影響),它們會被刪除。 由于理解英語語法的規則,這個分詞器可以提取英語單詞的詞干英語分詞器會產生下面的詞條:
set, shape, semi, transpar, call, set_tran, 5
注意看
transparentcallingset_trans已經變為詞根格式。

5.2什么時候使用分析器

當我們索引一個文檔,它的全文域被分析成詞條以用來創建倒排索引。但是,當我們在全文域搜索的時候,我們需要將查詢字符串通過
相同的分析過程,以保證我們搜索的詞條格式與索引中的詞條格式一致。全文查詢,理解每個域是如何定義的,因此它們可以做正確的事:

  • 當你查詢一個全文 域時, 會對查詢字符串應用相同的分析器,以產生正確的搜索詞條列表。
  • 當你查詢一個精確值 域時,不會分析查詢字符串,而是搜索你指定的精確值。

現在你可以理解在開始的查詢為什么返回那樣的結果:

  • date 域包含一個精確值:單獨的詞條 2014-09-15
  • _all 域是一個全文域,所以分詞進程將日期轉化為三個詞條: 201409, 和 15

當我們在_all域查詢2014,它匹配所有的12條推文,因為它們都含有 2014
當我們在 _all 域查詢 2014-09-15,它首先分析查詢字符串,產生匹配201409, 或15 中 任意 詞條的查詢。這也會匹配所有12條推文,因為它們都含有 2014 ,
當我們在 date 域查詢 2014-09-15,它尋找 精確 日期,只找到一個推文,
當我們在 date 域查詢 2014,它找不到任何文檔,因為沒有文檔含有這個精確日志

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。