1. 程式人生 > >Elasticsearch的停用詞(stopwords)

Elasticsearch的停用詞(stopwords)

query tle IT cut 情況下 要求 true 可能 利用

1、問題

在使用搜索引擎(Elasticsearch或Solr)作為應用的後臺搜索平臺的時候,會遇到停用詞(stopwords)的問題。

在信息檢索中,停用詞是為節省存儲空間和提高搜索效率,處理文本時自動過濾掉某些字或詞,這些字或詞即被稱為Stop Words(停用詞)。停用詞大致分為兩類。一類是語言中的功能詞,這些詞極其普遍而無實際含義,比如“the”、“is“、“which“、“on”等。另一類是詞匯詞,比如‘want‘等,這些詞應用廣泛,但搜索引擎無法保證能夠給出真正相關的搜索結果,難以縮小搜索範圍,還會降低搜索效率。實踐中,通常把這些詞從問題中過濾,從而節省索引的存儲空間、提高搜索性能。

但是在實際語言環境中,停用詞有時也有用的。比如,莎士比亞的名句:“To be or not to be.”所有的詞都是停用詞。特別當停用詞和通配符(*)同時使用的時候,問題就來了:“the”、“is“、“on”還是停用詞碼?

2、解決方案

實際運用中,沒有一個解決方案是100%完美的。很多時候需要我們根據實際用例作相應的調整和折中,來達到期望的結果。在這個時候,需要用80/20原則,把目標專著在提高用戶體驗上。

2.1、對不同的搜索對象區別對待

過濾停用詞是為節省存儲空間和提高搜索效率。實踐中,不同的應用場景和對象對存儲空間和搜索效率的需求不一樣。比如,文章的標題,一般都很短,而且有大量的限定詞區別詞的定義,它對節省存儲空間和效率的要求不高,但是常常需要停用詞來限定名詞的意義。我們可以考慮保留停用詞。而對於文章體的全文本,存儲空間和效率的要求很高,使用停用詞過濾可以大大減少存儲空間,提高搜索效率。

對Elasticsearch,下面是我們用到的索引定義:消息標題是text類型,沒有使用停用詞,而消息文本是standard_text類型,這個類型在設置裏定義了使用英語標準的停用詞過濾。

{
  "demo": {
    "settings": {
      "index": {
        "number_of_shards": "5",
        "number_of_replicas": "1",
        "analysis": {
          "analyzer": {
            "standard_text": {
              
"type": "standard", "stopwords": "_english_" } } } }, "mappings": { "msg": { "_routing": { "required": true }, "properties": { "title": { "type": "text" }, "body": { "type": "text", "analyzer": "standard_text" } } } } } }

2.2、match查詢

考慮一個例子:“and”。作為停用詞,在“and”會在索引創建的時候被過濾掉:POST store/_analyze { "field": "body", "text": ["and"] }

得到的分析結果是:{ "tokens": [] }

但是,如果我們用title字段來分析的時候,結果會得到保存:POST store/_analyze { "field": "title", "text": ["and"] }

{ "tokens": [{
      "token": "and",
      "start_offset": 0,
      "end_offset": 3,
      "type": "<ALPHANUM>",
      "position": 0
    }
  ]
}

但是當我們需要搜索文本的時候,會出現很多不如意的地方。比如,如果我們需要查消息體內chris && and && john這三個詞的時候,因為and被過濾了,而查詢條件又是與操作,導致沒有任何信息符合。有人說,能不能把and從查詢條件中去除啊?可以,雖然有點麻煩,總是可以做。但是,有幾個新問題需要解決:

  • 你需要拿到所有語言的停用詞才能做這個預處理。
  • 萬一這些語言的停用詞變了呢?我們還需要及時更正。

幸運的是Elasticsearch的match查詢提供了一個功能解決這個問題,同時我們不需要在應用程序中預處理停用詞:zero_terms_query和cutoff_frequency。

  • zero_terms_query

如果使用的分析器刪除查詢中的所有標記(如停用詞),默認行為完全不匹配任何文檔(none)。 可以使用zero_terms_query選項改變默認,none(默認),或all對應於match_all查詢。

當查詢使用"operator" : "and"的時候,需要把zero_terms_query設置為all。如果"operator" : "or",默認選項是我們需要的:

GET demo/msg/_search
{
    "query": {
        "match" : {
            "body" : {
                "query" : "chris and john",
                "operator" : "and",
                "zero_terms_query": "all"
            }
        }
    }
}
  • cutoff_frequency

match查詢支持cutoff_frequency,允許指定絕對或相對的文檔頻率:

    • OR:高頻單詞被放入“或許有”的類別,僅在至少有一個低頻(低於截斷)單詞滿足條件時才積分;
    • AND:高頻單詞被放入“或許有”的類別,僅在所有低頻(低於截斷)單詞滿足條件時才積分。

該查詢允許在運行時動態地處理停用詞,相對領域獨立,並且不需要停用詞文件。它防止評分/叠代高頻詞,只在更重要(更低頻率)的詞與文檔匹配時才考慮。但是,如果所有查詢條件都高於給定的cutoff_frequency,查詢會自動轉換為純聯合(和)查詢以確保快速執行。

cutoff_frequency可以是相對於文檔的總數的小數[0..1),也可以是絕對值[1, +)。

GET demo/msg/_search
{
    "query": {
        "match" : {
            "body" : {
                "query" : "chris and john",
                "cutoff_frequency" : 0.001
            }
        }
    }
}

2.3、common 查詢

大致說,common查詢會分析查詢文本,確定哪些單詞“重要”,並使用這些單詞進行搜索。 只有在文件與重要文字相匹配後才考慮“不重要”的字眼。“common查詢”背後的動機是充分利用停用詞清除的功能(更快的搜索),而不會完全消除停用詞(因為它們有時可能有助於得分)。

執行此查詢時會分幾步:

  1. 查詢會被發送到索引的每個shard;
  2. 在每個shard,Elasticsearch都會查看每個術語的文檔頻率
  3. 如果一個詞的文檔頻率低於0.1%(0.001),那麽它被認為是“低頻”。 否則,它將被移到次要的“高頻”列表中
  4. “低頻”列表被重寫為(邏輯AND)。 在這個例子中,它會包含“bonsai”,“cool”
  5. 然後將任何高頻的文檔分到剩余的高頻列表中(“this”,“is”)

看看下面例子:

{
  "common": {
    "body": {
      "query":            "this is bonsai cool",
      "cutoff_frequency": 0.001
    }
  }
}

在系統內,它被重寫為:

{
  "bool": {
    "must": [
      { "term": { "body": "bonsai"}},
      { "term": { "body": "cool"}}
    ],
    "should": [
      { "term": { "body": "this"}}
      { "term": { "body": "is"}}
    ]
  }
}

3、通配符

我們還有一個問題,match查詢不支持通配符。Elasticsearch對通配符支持包括兩個情況:

  • keyword:wildcard,prefix
  • text:wildcard,prefix,match_phrase_prefix

第一種情況下,keyword的字段不會對索引和查詢時文本做預處理。因此,在對該字段索引和查詢的時候,應用程序必須做簡單的一致性處理 ,比如把單詞字母都變成小寫。

第二種情況,text在索引時是通過分析器處理和過濾的,比如每個詞都會正規化。而查詢時,wildcard,match_prefix,match_phrase_prefix每個方法對查詢文本的處理都不一樣,需要分開對待:

  • wildcard:查詢時Elasticsearch不會通過分析器處理,因此,應用程序必須對查詢文本做簡單的一致性處理。
  • prefix,match_phrase_prefix:查詢時Elasticsearch會通過分析器處理。這時候停用詞會被過濾掉。

參考文獻

[1] https://www.elastic.co/blog/stop-stopping-stop-words-a-look-at-common-terms-query

Elasticsearch的停用詞(stopwords)