1. 程式人生 > >elasticsearch(6) 對映和分析

elasticsearch(6) 對映和分析

類似關係型資料庫中每個欄位都有對應的資料型別,例如nvarchar、int、date等等,elasticsearch也會將文件中的欄位對映成對應的資料型別,這一對映可以使ES自動生成的,也是可以由我們自定義的。不同的對映關係是會影響到我們的搜尋查詢功能。

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 !

分析上面的查詢語句,我們可以發現一個很奇怪的現象。為什麼通過q=2014可以查到12條結果,但通過date欄位查詢年份q=date:2014卻沒有結果呢?我們推測就是因為不同的欄位對映的型別不同而導致的。

  • 使用分析工具

通過_mapping api可以分析型別的對映

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" } } } } } }

我們可以看到date欄位是date型別的,而q=2014查詢的是_all欄位,我們知道這個欄位是String型別的。我們可能會猜測 date 欄位和 string 欄位 索引方式不同,所以搜尋結果也不一樣。

但實際上雖然不同的資料型別的索引方式會有所不同,但最大的區別是一個是代表精確值的欄位(包括String型別),另一個是代表全文的欄位。這個區別非常重要,這正是ES和其他關係型資料庫最大的不同。ES會將全文域的所有詞條形成倒排索引,這也是ES搜尋的基礎。

 

  • 倒排索引

elsaticsearch使用一種稱為倒排索引的結構,它適用於快速的全文搜尋。一個倒排索引由文件中所有不重複詞的列表構成,對於其中每個詞,有一個包含它的文件列表。

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

  1. The quick brown fox jumped over the lazy dog
  2. 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

由上圖可得知,匹配的文件有兩個,但是第一個文件要比第二個匹配度更好。但這也引發出了一些問題。

對於一個搜尋操作來說:

1、使用者可能不關心詞條的大小寫,Quick和quick可能是一樣的

2、由於詞法的變化,例如單複數的變化,對於使用者來說可能也是不關心的,例如dog和dogs

3、同義詞,一些同義詞儘管不同,但實際上表達的含義是相同的,我們希望能夠匹配出來。

解決方案:

對於輸入和輸出事先做預處理,也就是在倒排索引前對文件統一處理,例如把Quick統一成quick,把dogs統一成dog。搜尋時,對搜尋的內容也做統一處理,例如Quick dogs轉化成quick dog。這樣通過倒排索引就可以解決上面幾個問題,優化搜尋。這個過程就是分析。

 

  • 分析和分析器

所謂分析器實際上是把三個功能封裝在了一個包裡

1、字元過濾器

首先,字串按順序通過每個字元過濾器。他們的任務是在分詞前整理字串。一個字元過濾器可以用來去掉HTML,或者將 & 轉化成 `and`。

2、分詞器

字串被分詞器分為單個的詞條。一個簡單的分詞器遇到空格和標點的時候,可能會將文字拆分成詞條。

3、Token過濾器

詞條按順序通過每個Token過濾器。這個過程可能會改變詞條(例如,小寫化 Quick ),刪除詞條(例如, 像 a,and,the 等無用詞),或者增加詞條(例如,像 jump 和 leap 這種同義詞)

 

  • 內建分析器

ES附帶了可以直接使用的預包裝的分析器

示例文字

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

 

1、標準分析器

標準分析器是Elasticsearch預設使用的分析器。它是分析各種語言文字最常用的選擇。它根據Unicode聯盟定義的單詞邊界劃分文字。刪除絕大部分標點。最後,將詞條小寫。

set, the, shape, to, semi, transparent, by, calling, set_trans, 5

 

2、簡單分析器

簡單分析器在任何不是字母的地方分隔文字,將詞條小寫。

set, the, shape, to, semi, transparent, by, calling, set, trans

 

3、空格分析器

空格分析器在任何不適字母的地方分割文字,將詞條小寫。

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

 

4、語言分析器

特定語言分析器可用於很多語言。它們可以考慮指定語言的特點。例如, 英語分析器附帶了一組英語無用詞(常用單詞,例如 and 或者the,它們對相關性沒有多少影響),它們會被刪除。 由於理解英語語法的規則,這個分詞器可以提取英語單詞的詞幹

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

這裡set_trans變為了set_tran,calling變為了call

 

  • 測試分析器

有些時候很難理解分詞的過程和實際被儲存到索引中的詞條,你可以使用 analyze API 來看文字是如何被分析的。在訊息體裡,指定分析器和要分析的文字:

 GET 127.0.0.1:9200/_analyze

{
  "analyzer": "standard",
  "text": "our Fingertips owns the power of changing the world"
}

響應

{
  "tokens": [
    {
      "token": "our",
      "start_offset": 0,
      "end_offset": 3,
      "type": "<ALPHANUM>",
      "position": 0
    },
    {
      "token": "fingertips",
      "start_offset": 4,
      "end_offset": 14,
      "type": "<ALPHANUM>",
      "position": 1
    },
    {
      "token": "owns",
      "start_offset": 15,
      "end_offset": 19,
      "type": "<ALPHANUM>",
      "position": 2
    },
    {
      "token": "the",
      "start_offset": 20,
      "end_offset": 23,
      "type": "<ALPHANUM>",
      "position": 3
    },
    {
      "token": "power",
      "start_offset": 24,
      "end_offset": 29,
      "type": "<ALPHANUM>",
      "position": 4
    },
    {
      "token": "of_changing",
      "start_offset": 30,
      "end_offset": 41,
      "type": "<ALPHANUM>",
      "position": 5
    },
    {
      "token": "the",
      "start_offset": 42,
      "end_offset": 45,
      "type": "<ALPHANUM>",
      "position": 6
    },
    {
      "token": "world",
      "start_offset": 46,
      "end_offset": 51,
      "type": "<ALPHANUM>",
      "position": 7
    }
  ]
}

從中我們可以看到分析器是怎麼幫我們分詞的。token 是實際儲存到索引中的詞條。 position 指明詞條在原始文字中出現的位置。 start_offset 和end_offset 指明字元在原始字串中的位置。

 

  • 核心簡單域型別

elasticsearch支援一下簡單域型別

1、字串:string

2、整數:byte,short,integer,long

3、浮點型:float,double

4、布林型:boolean

5、日期:date

當你索引一個包含新域的文件(之前未曾出現), Elasticsearch 會使用動態對映,通過JSON中基本資料型別,嘗試猜測域型別,使用如下規則:

JSON type

域 type

布林型: true 或者 false

boolean

整數: 123

long

浮點數: 123.45

double

字串,有效日期: 2014-09-15

date

字串: foo bar

string

 

這意味著如果通過“123”索引一個數字,那麼它會被對映成為string,但如果這個域已經是long,那麼ES會嘗試將這個字串轉化為long,如果無法轉化,則丟擲一個異常。

 

  • 自定義域對映

自定義對映可以做到

1、全文字串域和精確值字串域的區別

2、使用特定的語言分析器

3、優化域以適應部分匹配

4、指定自定義資料格式

 

域屬性:

  • type

域最重要的屬性是type.對於不是string的域,一般只需要設定type

{
    "number_of_clicks": {
        "type": "integer"
    }
}

預設, string 型別域會被認為包含全文。就是說,它們的值在索引前,會通過 一個分析器,針對於這個域的查詢在搜尋前也會經過一個分析器。

  • index

index屬性控制了怎樣所以字串,值可以為:

1、analyzed

首先分析字串,然後索引它。換句話說,以全文索引這個域。

2、not_analyzed

索引這個域,所以可以搜尋到它,但索引指定的精確值。不對它進行分析。

3、no

這個域不會被搜尋到。

{
    "tag": {
        "type":     "string",
        "index":    "not_analyzed"
    }
}
  • analyzer

對於 analyzed 字串域,用 analyzer 屬性指定在搜尋和索引時使用的分析器。預設, Elasticsearch 使用 standard 分析器, 但你可以指定一個內建的分析器替代它,例如 whitespace 、 simple 和 english:

{
    "tweet": {
        "type":     "string",
        "analyzer": "english"
    }
}

 

更新對映

首次 建立一個索引的時候,可以指定型別的對映。你也可以使用 /_mapping 為新型別(或者為存在的型別更新對映)增加對映。

注意:儘管你可以增加一個已經存在的對映,但無法修改存在的域對映。如果一個域的對映已經存在,那麼該域的資料可能已經被索引。如果你意圖修改這個域的對映,索引的資料可能會出錯,不能被正常的搜尋。

  • 建立索引時指定對映
PUT /gb
{
  "mappings": {
    "tweet" : {
      "properties" : {
        "tweet" : {
          "type" :    "string",
          "analyzer": "english"
        },
        "date" : {
          "type" :   "date"
        },
        "name" : {
          "type" :   "string"
        },
        "user_id" : {
          "type" :   "long"
        }
      }
    }
  }
}

這段程式碼指定了tweet型別的對映,把tweet屬性使用english分析器,是一個全文string型別。date為日期型別,name為全文string型別使用standard分析器,user_id是長整型。

 

  • 新增一個屬性,並指定對映
PUT /gb/_mapping/tweet
{
  "properties" : {
    "tag" : {
      "type" :    "string",
      "index":    "not_analyzed"
    }
  }
}

新增了一個tag屬性,為一個精確值string型別,即不對這個欄位作分析

 

我們來測試一下

GET /gb/_analyze?field=tweet&text=Black-cats

GET /gb/_analyze?field=tag&text=Black-cats

可以看到對於tweet屬性來說,索引為black和cat,而對於Black-cat 來說索引為Black-cat