1. 程式人生 > >ElasticSearch權威指南學習(對映和分析)

ElasticSearch權威指南學習(對映和分析)

概念

  1. 對映(mapping)機制用於進行欄位型別確認,將每個欄位匹配為一種確定的資料型別(string, number, booleans, date等)。+
  2. 分析(analysis)機制用於進行全文文字(Full Text)的分詞,以建立供搜尋用的反向索引。

資料型別差異

  1. 在索引中有12個tweets,只有一個包含日期2014-09-15,但是我們看看下面查詢中的total hits。
GET /_search?q=2014              # 12 個結果
GET /_search?q=2014-09-15        # 還是 12 個結果 !
GET /_search?q=date:2014-09-15   # 1  一個結果
GET /_search?q=date:2014         # 0  個結果 !
  1. 解讀我們的文件結構
GET /gb/_mapping/tweet

{
   "gb": {
      "mappings": {
         "tweet": {
            "properties": {
               "date": {
                  "type": "date",
                  "format": "dateOptionalTime"
               },
               "name": {
                  "type": "string"
               },
               "tweet": {
                  "type": "string"
               },
               "user_id": {
                  "type": "long"
               }
            }
         }
      }
   }
}
  1. Elasticsearch為對欄位型別進行猜測,動態生成了欄位和型別的對映關係。返回的資訊顯示了date欄位被識別為date型別。
  2. date型別的欄位和string型別的欄位的索引方式是不同的,因此導致查詢結果的不同

確切值(Exact values) vs. 全文文字(Full text)

  1. Elasticsearch中的資料可以大致分為兩種型別:確切值 及 全文文字。
  2. 確切值是確定的。確切值"Foo"和"foo"就並不相同。確切值2014和2014-09-15也不相同。
  3. 全文文字,從另一個角度來說是文字化的資料,比如一篇推文(Twitter的文章)或郵件正文。
  4. 為了方便在全文文字欄位中進行這些型別的查詢,Elasticsearch首先對文字分析(analyzes),然後使用結果建立一個倒排索引

倒排索引

  1. Elasticsearch使用一種叫做倒排索引(inverted index)的結構來做快速的全文搜尋。倒排索引由在文件中出現的唯一的單詞列表,以及對於每個單詞在文件中的位置組成。
  2. 我們有兩個文件,每個文件content欄位包含:
The quick brown fox jumped over the lazy dog
Quick brown foxes leap over lazy dogs in summer
  1. 為了建立倒排索引,我們首先切分每個文件的content欄位為單獨的單詞,我們把它們叫做詞(terms)或者表徵(tokens)
  2. 把所有的唯一詞放入列表並排序,結果是這個樣子的
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
  1. 現在,如果我們想搜尋"quick brown",我們只需要找到每個詞在哪個文件中出現即可:
Term Doc_1 Doc_2
brown X X
quick X
----- ------- -----
Total 2 1
  1. 兩個文件都匹配,但是第一個比第二個有更多的匹配項。 如果我們加入簡單的相似度演算法(similarity algorithm),計算匹配單詞的數目,這樣我們就可以說第一個文件比第二個匹配度更高——對於我們的查詢具有更多相關性。

  2. 但是這樣我們仍舊查不到像Quick,Dog這樣的詞
  3. 不過,如果我們使用相同的標準化規則處理查詢字串的content欄位,查詢將變成"+quick +fox",這樣就可以匹配到兩個文件。
  4. 這個標記化和標準化的過程叫做分析(analysis)

分析和分析器

  1. 分析(analysis)是這樣一個過程:
  • 首先,標記化一個文字塊為適用於倒排索引單獨的詞(term)
  • 然後標準化這些詞為標準形式,提高它們的“可搜尋性”或“查全率”
  1. 字元過濾器
    • 首先字串經過字元過濾器(character filter),它們的工作是在標記化前處理字串。字元過濾器能夠去除HTML標記,或者轉換"&"為"and"。
  2. 分詞器
    • 下一步,分詞器(tokenizer)被標記化成獨立的詞。一個簡單的分詞器(tokenizer)可以根據空格或逗號將單詞分開
  3. 標記過濾
    • 最後,每個詞都通過所有標記過濾(token filters),它可以修改詞(例如將"Quick"轉為小寫),去掉詞(例如停用詞像"a"、"and"、"the"等等),或者增加詞(例如同義詞像"jump"和"leap")
  4. 內建的分析器
    • 下面我們列出了最重要的幾個分析器,來演示這個字串分詞後的表現差異

    "Set the shape to semi-transparent by calling set_trans(5)"

    • 標準分析器
      • 它根據Unicode Consortium的定義的單詞邊界(word boundaries)來切分文字,然後去掉大部分標點符號。最後,把所有詞轉為小寫。產生的結果為:

      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)

    • 語言分析器
      • 特定語言分析器適用於很多語言。它們能夠考慮到特定語言的特性。
      • english分析器將會產生以下結果:

        set, shape, semi, transpar, call, set_tran, 5

    • 測試分析器
      • 為了更好的理解如何進行,你可以使用analyze API來檢視文字是如何被分析的。在查詢字串引數中指定要使用的分析器,被分析的文字做為請求體:

        GET /_analyze?analyzer=standard&text=Text to analyze

      • 結果中每個節點在代表一個詞:
      {
         "tokens": [
            {
               "token":        "text",
               "start_offset": 0,
               "end_offset":   4,
               "type":         "<ALPHANUM>",
               "position":     1
            },
            {
               "token":        "to",
               "start_offset": 5,
               "end_offset":   7,
               "type":         "<ALPHANUM>",
               "position":     2
            },
            {
               "token":        "analyze",
               "start_offset": 8,
               "end_offset":   15,
               "type":         "<ALPHANUM>",
               "position":     3
            }
         ]
      }
      • token是一個實際被儲存在索引中的詞。position指明詞在原文字中是第幾個出現的。start_offset和end_offset表示詞在原文字中佔據的位置。

對映

  1. 為了能夠把日期欄位處理成日期,把數字欄位處理成數字,把字串欄位處理成全文字(Full-text)或精確的字串值,Elasticsearch需要知道每個欄位裡面都包含了什麼型別。這些型別和欄位的資訊儲存(包含)在對映(mapping)中。
  2. 核心簡單欄位型別
型別 表示的資料型別
String string
Whole number byte, short, integer, long
Floating point float, double
Boolean boolean
Date date
  1. 當你索引一個包含新欄位的文件——一個之前沒有的欄位——Elasticsearch將使用動態對映猜測欄位型別,這型別來自於JSON的基本資料型別,使用以下規則:
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"
  1. 檢視對映
    • 我們可以使用_mapping字尾來檢視Elasticsearch中的對映。在本章開始我們已經找到索引gb型別tweet中的對映:
    GET /gb/_mapping/tweet
    • 欄位的對映(叫做屬性(properties)),這些對映是Elasticsearch在建立索引時動態生成的:
        {
       "gb": {
          "mappings": {
             "tweet": {
                "properties": {
                   "date": {
                      "type": "date",
                      "format": "strict_date_optional_time||epoch_millis"
                   },
                   "name": {
                      "type": "string"
                   },
                   "tweet": {
                      "type": "string"
                   },
                   "user_id": {
                      "type": "long"
                   }
                }
             }
          }
       }
    }
    • ps:錯誤的對映,例如把age欄位對映為string型別而不是integer型別,會造成查詢結果混亂。
      要檢查對映型別,而不是假設它是正確的!
  2. 自定義欄位對映
    • 對映中最重要的欄位引數是type
    {
        "number_of_clicks": {
            "type": "integer"
        }
    }
  3. index
    • index引數控制字串以何種方式被索引。它包含以下三個值當中的一個
    解釋
    analyzed 首先分析這個字串,然後索引。換言之,以全文形式索引此欄位。
    not_analyzed 索引這個欄位,使之可以被搜尋,但是索引內容和指定值一樣。不分析此欄位。
    no 不索引這個欄位。這個欄位不能為搜尋到。
    • 如果我們想對映欄位為確切值,我們需要設定它為not_analyzed:
    {
        "tag": {
            "type":     "string",
            "index":    "not_analyzed"
        }
    }
  4. 分析
    • 對於analyzed型別的字串欄位,使用analyzer引數來指定哪一種分析器將在搜尋和索引的時候使用。預設的,Elasticsearch使用standard分析器,但是你可以通過指定一個內建的分析器來更改它,例如whitespace、simple或english。
    {
        "tweet": {
            "type":     "text",
            "analyzer": "english"
        }
    }
  5. 更新對映
    • 你可以在第一次建立索引的時候指定對映的型別。此外,你也可以晚些時候為新型別新增對映
    • ps:你可以向已有對映中增加欄位,但你不能修改它。如果一個欄位在對映中已經存在,這可能意味著那個欄位的資料已經被索引。如果你改變了欄位對映,那已經被索引的資料將錯誤並且不能被正確的搜尋到。
    • 建立一個新索引,指定tweet欄位的分析器為english:
    PUT /gb 
    {
      "mappings": {
        "tweet" : {
          "properties" : {
            "tweet" : {
              "type" :    "text",
              "analyzer": "english"
            },
            "date" : {
              "type" :   "date"
            },
            "name" : {
              "type" :   "text"
            },
            "user_id" : {
              "type" :   "long"
            }
          }
        }
      }
    }
    • 再後來,我們決定在tweet的對映中增加一個新的not_analyzed型別的文字欄位,叫做tag,使用_mapping字尾:
    PUT /gb/_mapping/tweet
    {
      "properties" : {
        "tag" : {
          "type" :    "string",
          "index":    "not_analyzed"
        }
      }
    }

版本問題

  1. 如果你發現報錯
#! Deprecation: Expected a boolean [true/false] for property [index] but got [not_analyzed]
  1. 原因:該版本以後index這個只能用true或者false了,如果想要不被分詞就把資料型別設定為keyword,只能說優化了,使用更方便,更易理解了

複合核心欄位型別

  1. 多值欄位
    • 我們可以索引一個標籤陣列來代替單一字串:
    • { "tag": [ "search", "nosql" ]}
    • 對於陣列不需要特殊的對映。任何一個欄位可以包含零個、一個或多個值,同樣對於全文欄位將被分析併產生多個詞。
    • 言外之意,這意味著陣列中所有值必須為同一型別。你不能把日期和字元竄混合。如果你建立一個新欄位,這個欄位索引了一個數組,Elasticsearch將使用第一個值的型別來確定這個新欄位的型別。
  2. 空欄位
    • 陣列可以是空的。這等價於有零個值。事實上,Lucene沒法存放null值,所以一個null值的欄位被認為是空欄位。
    • 這四個欄位將被識別為空欄位而不被索引:
    "empty_string":             "",
    "null_value":               null,
    "empty_array":              [],
    "array_with_null_value":    [ null ]
  3. 多層物件
    • 內部物件(inner objects)經常用於在另一個物件中嵌入一個實體或物件。例如,做為在tweet文件中user_name和user_id的替代,我們可以這樣寫:
    {
        "tweet":            "Elasticsearch is very flexible",
        "user": {
            "id":           "@johnsmith",
            "gender":       "male",
            "age":          26,
            "name": {
                "full":     "John Smith",
                "first":    "John",
                "last":     "Smith"
            }
        }
    }
  4. 內部物件的對映
    • Elasticsearch 會動態的檢測新物件的欄位,並且對映它們為 object 型別,將每個欄位加到 properties 欄位下
    {
      "gb": {
        "tweet": { //根物件
          "properties": {
            "tweet":            { "type": "string" },
            "user": { //內部物件
              "type":             "object",
              "properties": {
                "id":           { "type": "string" },
                "gender":       { "type": "string" },
                "age":          { "type": "long"   },
                "name":   { //內部物件
                  "type":         "object",
                  "properties": {
                    "full":     { "type": "string" },
                    "first":    { "type": "string" },
                    "last":     { "type": "string" }
                  }
                }
              }
            }
          }
        }
      }
    }
  5. 內部物件是怎樣被索引的
    • Lucene 並不瞭解內部物件。 一個 Lucene 檔案包含一個鍵-值對應的扁平表單。 為了讓 Elasticsearch 可以有效的索引內部物件,將檔案轉換為以下格式:
    {
        "tweet":            [elasticsearch, flexible, very],
        "user.id":          [@johnsmith],
        "user.gender":      [male],
        "user.age":         [26],
        "user.name.full":   [john, smith],
        "user.name.first":  [john],
        "user.name.last":   [smith]
    }
  6. 物件-陣列
    • 內部物件陣列
    {
        "followers": [
            { "age": 35, "name": "Mary White"},
            { "age": 26, "name": "Alex Jones"},
            { "age": 19, "name": "Lisa Smith"}
        ]
    }
    • 此檔案會如我們以上所說的被扁平化,但其結果會像如此
    {
        "followers.age":    [19, 26, 35],
        "followers.name":   [alex, jones, lisa, smith, mary, white]
    }
    • {age: 35}與{name: Mary White}之間的關聯會消失,因每個多值的欄位會變成一個值集合,而非有序的陣列。