Elasticsearch 6.x 倒排索引與分詞

倒排索引

  • 正排索引:文檔id到單詞的關聯關係
  • 倒排索引:單詞到文檔id的關聯關係

示例: 對如下三個文檔去除停用詞後構造倒排索引 html

image

倒排索引-查詢過程

查詢包含「搜索引擎」的文檔git

  1. 經過倒排索引得到「搜索引擎」對應的文檔id列表,有1,3
  2. 經過正排索引查詢1和3的完整內容
  3. 返回最終結果

倒排索引-組成

  • 單詞詞典(Term Dictionary)
  • 倒排列表(Posting List)

單詞詞典(Term Dictionary)

單詞詞典的實現通常用B+樹,B+樹構造的可視化過程網址: B+ Tree Visualizationgithub

關於B樹和B+樹正則表達式

  1. 維基百科-B樹
  2. 維基百科-B+樹
  3. B樹和B+樹的插入、刪除圖文詳解

image

倒排列表(Posting List)

  • 倒排列表記錄了單詞對應的文檔集合,有倒排索引項(Posting)組成
  • 倒排索引項主要包含以下信息:
    1. 文檔id用於獲取原始信息
    2. 單詞頻率(TF,Term Frequency),記錄該單詞在該文檔中出現的次數,用於後續相關性算分
    3. 位置(Posting),記錄單詞在文檔中的分詞位置(多個),用於作詞語搜索(Phrase Query)
    4. 偏移(Offset),記錄單詞在文檔的開始和結束位置,用於高亮顯示

image

B+樹內部結點存索引,葉子結點存數據,這裏的 單詞詞典就是B+樹索引,倒排列表就是數據,整合在一塊兒後以下所示算法

note: B+樹索引中文和英文怎麼比較大小呢?unicode比較仍是拼音呢?bash

image

ES存儲的是一個JSON格式的文檔,其中包含多個字段,每一個字段會有本身的倒排索引微信

分詞

分詞是將文本轉換成一系列單詞(Term or Token)的過程,也能夠叫文本分析,在ES裏面稱爲Analysis網絡

image

分詞器

分詞器是ES中專門處理分詞的組件,英文爲Analyzer,它的組成以下:app

  • Character Filters:針對原始文本進行處理,好比去除html標籤
  • Tokenizer:將原始文本按照必定規則切分爲單詞
  • Token Filters:針對Tokenizer處理的單詞進行再加工,好比轉小寫、刪除或增新等處理

分詞器調用順序 elasticsearch

image

Analyze API

ES提供了一個能夠測試分詞的API接口,方便驗證分詞效果,endpoint是_analyze

  • 能夠直接指定analyzer進行測試

image

  • 能夠直接指定索引中的字段進行測試
POST test_index/doc
{
  "username": "whirly",
  "age":22
}

POST test_index/_analyze
{
  "field": "username",
  "text": ["hello world"]
}
複製代碼
  • 能夠自定義分詞器進行測試
POST _analyze
{
  "tokenizer": "standard",
  "filter": ["lowercase"],
  "text": ["Hello World"]
}

複製代碼

預約義的分詞器

ES自帶的分詞器有以下:

  • Standard Analyzer
    • 默認分詞器
    • 按詞切分,支持多語言
    • 小寫處理
  • Simple Analyzer
    • 按照非字母切分
    • 小寫處理
  • Whitespace Analyzer
    • 空白字符做爲分隔符
  • Stop Analyzer
    • 相比Simple Analyzer多了去除請用詞處理
    • 停用詞指語氣助詞等修飾性詞語,如the, an, 的, 這等
  • Keyword Analyzer
    • 不分詞,直接將輸入做爲一個單詞輸出
  • Pattern Analyzer
    • 經過正則表達式自定義分隔符
    • 默認是\W+,即非字詞的符號做爲分隔符
  • Language Analyzer
    • 提供了30+種常見語言的分詞器

示例:停用詞分詞器

POST _analyze
{
  "analyzer": "stop",
  "text": ["The 2 QUICK Brown Foxes jumped over the lazy dog's bone."]
}

複製代碼

結果

{
  "tokens": [
    {
      "token": "quick",
      "start_offset": 6,
      "end_offset": 11,
      "type": "word",
      "position": 1
    },
    {
      "token": "brown",
      "start_offset": 12,
      "end_offset": 17,
      "type": "word",
      "position": 2
    },
    {
      "token": "foxes",
      "start_offset": 18,
      "end_offset": 23,
      "type": "word",
      "position": 3
    },
    {
      "token": "jumped",
      "start_offset": 24,
      "end_offset": 30,
      "type": "word",
      "position": 4
    },
    {
      "token": "over",
      "start_offset": 31,
      "end_offset": 35,
      "type": "word",
      "position": 5
    },
    {
      "token": "lazy",
      "start_offset": 40,
      "end_offset": 44,
      "type": "word",
      "position": 7
    },
    {
      "token": "dog",
      "start_offset": 45,
      "end_offset": 48,
      "type": "word",
      "position": 8
    },
    {
      "token": "s",
      "start_offset": 49,
      "end_offset": 50,
      "type": "word",
      "position": 9
    },
    {
      "token": "bone",
      "start_offset": 51,
      "end_offset": 55,
      "type": "word",
      "position": 10
    }
  ]
}
複製代碼

中文分詞

  • 難點
    • 中文分詞指的是將一個漢字序列切分爲一個一個的單獨的詞。在英文中,單詞之間以空格做爲天然分界詞,漢語中詞沒有一個形式上的分界符
    • 上下文不一樣,分詞結果迥異,好比交叉歧義問題
  • 常見分詞系統
    • IK:實現中英文單詞的切分,可自定義詞庫,支持熱更新分詞詞典
    • jieba:支持分詞和詞性標註,支持繁體分詞,自定義詞典,並行分詞等
    • Hanlp:由一系列模型與算法組成的Java工具包,目標是普及天然語言處理在生產環境中的應用
    • THUAC:中文分詞和詞性標註

安裝ik中文分詞插件

# 在Elasticsearch安裝目錄下執行命令,而後重啓es
bin/elasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v6.3.0/elasticsearch-analysis-ik-6.3.0.zip

# 若是因爲網絡慢,安裝失敗,能夠先下載好zip壓縮包,將下面命令改成實際的路徑,執行,而後重啓es
bin/elasticsearch-plugin install file:///path/to/elasticsearch-analysis-ik-6.3.0.zip
複製代碼
  • ik測試 - ik_smart
POST _analyze
{
  "analyzer": "ik_smart",
  "text": ["公安部:各地校車將享最高路權"]
}

# 結果
{
  "tokens": [
    {
      "token": "公安部",
      "start_offset": 0,
      "end_offset": 3,
      "type": "CN_WORD",
      "position": 0
    },
    {
      "token": "各地",
      "start_offset": 4,
      "end_offset": 6,
      "type": "CN_WORD",
      "position": 1
    },
    {
      "token": "校車",
      "start_offset": 6,
      "end_offset": 8,
      "type": "CN_WORD",
      "position": 2
    },
    {
      "token": "將",
      "start_offset": 8,
      "end_offset": 9,
      "type": "CN_CHAR",
      "position": 3
    },
    {
      "token": "享",
      "start_offset": 9,
      "end_offset": 10,
      "type": "CN_CHAR",
      "position": 4
    },
    {
      "token": "最高",
      "start_offset": 10,
      "end_offset": 12,
      "type": "CN_WORD",
      "position": 5
    },
    {
      "token": "路",
      "start_offset": 12,
      "end_offset": 13,
      "type": "CN_CHAR",
      "position": 6
    },
    {
      "token": "權",
      "start_offset": 13,
      "end_offset": 14,
      "type": "CN_CHAR",
      "position": 7
    }
  ]
}
複製代碼
  • ik測試 - ik_max_word
POST _analyze
{
  "analyzer": "ik_max_word",
  "text": ["公安部:各地校車將享最高路權"]
}

# 結果
{
  "tokens": [
    {
      "token": "公安部",
      "start_offset": 0,
      "end_offset": 3,
      "type": "CN_WORD",
      "position": 0
    },
    {
      "token": "公安",
      "start_offset": 0,
      "end_offset": 2,
      "type": "CN_WORD",
      "position": 1
    },
    {
      "token": "部",
      "start_offset": 2,
      "end_offset": 3,
      "type": "CN_CHAR",
      "position": 2
    },
    {
      "token": "各地",
      "start_offset": 4,
      "end_offset": 6,
      "type": "CN_WORD",
      "position": 3
    },
    {
      "token": "校車",
      "start_offset": 6,
      "end_offset": 8,
      "type": "CN_WORD",
      "position": 4
    },
    {
      "token": "將",
      "start_offset": 8,
      "end_offset": 9,
      "type": "CN_CHAR",
      "position": 5
    },
    {
      "token": "享",
      "start_offset": 9,
      "end_offset": 10,
      "type": "CN_CHAR",
      "position": 6
    },
    {
      "token": "最高",
      "start_offset": 10,
      "end_offset": 12,
      "type": "CN_WORD",
      "position": 7
    },
    {
      "token": "路",
      "start_offset": 12,
      "end_offset": 13,
      "type": "CN_CHAR",
      "position": 8
    },
    {
      "token": "權",
      "start_offset": 13,
      "end_offset": 14,
      "type": "CN_CHAR",
      "position": 9
    }
  ]
}
複製代碼
  • ik兩種分詞模式ik_max_word 和 ik_smart 什麼區別?
    • ik_max_word: 會將文本作最細粒度的拆分,好比會將「中華人民共和國國歌」拆分爲「中華人民共和國,中華人民,中華,華人,人民共和國,人民,人,民,共和國,共和,和,國國,國歌」,會窮盡各類可能的組合;

    • ik_smart: 會作最粗粒度的拆分,好比會將「中華人民共和國國歌」拆分爲「中華人民共和國,國歌」。

自定義分詞

當自帶的分詞沒法知足需求時,能夠自定義分詞,經過定義Character Filters、Tokenizer和Token Filters實現

Character Filters

  • 在Tokenizer以前對原始文本進行處理,好比增長、刪除或替換字符等
  • 自帶的以下:
    • HTML Strip Character Filter:去除HTML標籤和轉換HTML實體
    • Mapping Character Filter:進行字符替換操做
    • Pattern Replace Character Filter:進行正則匹配替換
  • 會影響後續tokenizer解析的position和offset信息

Character Filters測試

POST _analyze
{
  "tokenizer": "keyword",
  "char_filter": ["html_strip"],
  "text": ["<p>I&apos;m so <b>happy</b>!</p>"]
}

# 結果
{
  "tokens": [
    {
      "token": """ I'm so happy! """,
      "start_offset": 0,
      "end_offset": 32,
      "type": "word",
      "position": 0
    }
  ]
}
複製代碼

Tokenizers

  • 將原始文本按照必定規則切分爲單詞(term or token)
  • 自帶的以下:
    • standard 按照單詞進行分割
    • letter 按照非字符類進行分割
    • whitespace 按照空格進行分割
    • UAX URL Email 按照standard進行分割,但不會分割郵箱和URL
    • Ngram 和 Edge NGram 連詞分割
    • Path Hierarchy 按照文件路徑進行分割

Tokenizers 測試

POST _analyze
{
  "tokenizer": "path_hierarchy",
  "text": ["/path/to/file"]
}

# 結果
{
  "tokens": [
    {
      "token": "/path",
      "start_offset": 0,
      "end_offset": 5,
      "type": "word",
      "position": 0
    },
    {
      "token": "/path/to",
      "start_offset": 0,
      "end_offset": 8,
      "type": "word",
      "position": 0
    },
    {
      "token": "/path/to/file",
      "start_offset": 0,
      "end_offset": 13,
      "type": "word",
      "position": 0
    }
  ]
}
複製代碼

Token Filters

  • 對於tokenizer輸出的單詞(term)進行增長、刪除、修改等操做
  • 自帶的以下:
    • lowercase 將全部term轉爲小寫
    • stop 刪除停用詞
    • Ngram 和 Edge NGram 連詞分割
    • Synonym 添加近義詞的term

Token Filters測試

POST _analyze
{
  "text": [
    "a Hello World!"
  ],
  "tokenizer": "standard",
  "filter": [
    "stop",
    "lowercase",
    {
      "type": "ngram",
      "min_gram": 4,
      "max_gram": 4
    }
  ]
}

# 結果
{
  "tokens": [
    {
      "token": "hell",
      "start_offset": 2,
      "end_offset": 7,
      "type": "<ALPHANUM>",
      "position": 1
    },
    {
      "token": "ello",
      "start_offset": 2,
      "end_offset": 7,
      "type": "<ALPHANUM>",
      "position": 1
    },
    {
      "token": "worl",
      "start_offset": 8,
      "end_offset": 13,
      "type": "<ALPHANUM>",
      "position": 2
    },
    {
      "token": "orld",
      "start_offset": 8,
      "end_offset": 13,
      "type": "<ALPHANUM>",
      "position": 2
    }
  ]
}
複製代碼

自定義分詞

自定義分詞須要在索引配置中設定 char_filter、tokenizer、filter、analyzer等

自定義分詞示例:

  • 分詞器名稱:my_custom\
  • 過濾器將token轉爲大寫
PUT test_index_1
{
  "settings": {
    "analysis": {
      "analyzer": {
        "my_custom_analyzer": {
          "type":      "custom",
          "tokenizer": "standard",
          "char_filter": [
            "html_strip"
          ],
          "filter": [
            "uppercase",
            "asciifolding"
          ]
        }
      }
    }
  }
}
複製代碼

自定義分詞器測試

POST test_index_1/_analyze
{
  "analyzer": "my_custom_analyzer",
  "text": ["<p>I&apos;m so <b>happy</b>!</p>"]
}

# 結果
{
  "tokens": [
    {
      "token": "I'M",
      "start_offset": 3,
      "end_offset": 11,
      "type": "<ALPHANUM>",
      "position": 0
    },
    {
      "token": "SO",
      "start_offset": 12,
      "end_offset": 14,
      "type": "<ALPHANUM>",
      "position": 1
    },
    {
      "token": "HAPPY",
      "start_offset": 18,
      "end_offset": 27,
      "type": "<ALPHANUM>",
      "position": 2
    }
  ]
}
複製代碼

分詞使用說明

分詞會在以下兩個時機使用:

  • 建立或更新文檔時(Index Time),會對相應的文檔進行分詞處理
  • 查詢時(Search Time),會對查詢語句進行分詞
    • 查詢時經過analyzer指定分詞器
    • 經過index mapping設置search_analyzer實現
    • 通常不須要特別指定查詢時分詞器,直接使用索引分詞器便可,不然會出現沒法匹配的狀況

分詞使用建議

  • 明確字段是否須要分詞,不須要分詞的字段就將type設置爲keyword,能夠節省空間和提升寫性能
  • 善用_analyze API,查看文檔的分詞結果

更多內容請訪問個人我的網站: laijianfeng.org
參考文檔:

  1. elasticsearch 官方文檔
  2. 慕課網 Elastic Stack從入門到實踐

歡迎關注個人微信公衆號

小旋鋒微信公衆號
相關文章
相關標籤/搜索