1. 程式人生 > >elasticsearch 關聯單詞查詢以及Shingles

elasticsearch 關聯單詞查詢以及Shingles

Shingle Token Filter

A token filter of type shingle that constructs shingles (token n-grams) from a token stream. In other words, it creates combinations of tokens as a single token. For example, the sentence "please divide this sentence into shingles" might be tokenized into shingles "please divide", "divide this", "this sentence", "sentence into", and "into shingles".

This filter handles position increments > 1 by inserting filler tokens (tokens with termtext "_"). It does not handle a position increment of 0.

The following are settings that can be set for a shingle token filter type:

Setting Description

max_shingle_size

The maximum shingle size. Defaults to 2

.

min_shingle_size

The minimum shingle size. Defaults to 2.

output_unigrams

If true the output will contain the input tokens (unigrams) as well as the shingles. Defaults to true.

output_unigrams_if_no_shingles

If output_unigrams is false

 the output will contain the input tokens (unigrams) if no shingles are available. Note if output_unigrams is set to true this setting has no effect. Defaults to false.

token_separator

The string to use when joining adjacent tokens to form a shingle. Defaults to " ".

filler_token

The string to use as a replacement for each position at which there is no actual token in the stream. For instance this string is used if the position increment is greater than one when a stop filter is used together with the shingle filter. Defaults to "_"

The index level setting index.max_shingle_diff controls the maximum allowed difference between max_shingle_size and min_shingle_size.

尋找關聯的單詞(Finding Associated Words)

儘管短語和鄰近度查詢很管用,它們還是有一個缺點。它們過於嚴格了:所有的在短語查詢中的詞條都必須出現在文件中,即使使用了slop。

通過slop獲得的能夠調整單詞順序的靈活性也是有代價的,因為你失去了單詞之間的關聯。儘管你能夠識別文件中的sue,alligator和ate出現在一塊,但是你不能判斷是Sue ate還是alligator ate。

當單詞結合在一起使用時,它們表達的意思比單獨使用時要豐富。"I’m not happy I’m working"和"I’m happy I’m not working"含有相同的單詞,也擁有相近的鄰近度,但是它們的意思大相徑庭。

如果我們索引單詞對,而不是索引獨立的單詞,那麼我們就能夠保留更多關於單詞使用的上下文資訊。

對於句子"Sue ate the alligator",我們不僅索引每個單詞(或者Unigram)為一個詞條:

["sue", "ate", "the", "alligator"]

我們同時會將每個單詞和它的鄰近單詞一起索引成一個詞條:

["sue ate", "ate the", "the alligator"]

這些單詞對(也叫做Bigram)就是所謂的Shingle。

TIP

Shingle不限於只是單詞對;你也可以索引三個單詞(Word Triplet,也被稱為Trigram)作為一個詞條:

["sue ate the", "ate the alligator"]

Trigram能夠給你更高的精度,但是也大大地增加了索引的不同詞條的數量。在多數情況下,Bigram就足夠了。

當然,只有當用戶輸入查詢的順序和原始文件的順序一致,Shingle才能夠起作用;一個針對sue alligator的查詢會匹配單獨的單詞,但是不會匹配任何Shingle。

幸運的是,使用者會傾向於使用和他們正在搜尋的資料中相似的結構來表達查詢。但是這是很重要的一點:僅使用Bigram是不夠的;我們仍然需要Unigram,我們可以將匹配Bigram作為訊號(Signal)來增加相關度分值。

產生Shingle

Shingle需要在索引期間,作為分析過程的一部分被建立。我們可以將Unigram和Bigram都索引到一個欄位中,但是將它們放在不同的欄位中會更加清晰,也能夠讓它們能夠被獨立地查詢。Unigram欄位形成了我們搜尋的基礎部分,而Bigram欄位則用來提升相關度。

首先,我們需要使用shingle詞條過濾器來建立解析器:

DELETE /my_index

PUT /my_index
{
    "settings": {
        "number_of_shards": 1,  
        "analysis": {
            "filter": {
                "my_shingle_filter": {
                    "type":             "shingle",
                    "min_shingle_size": 2, 
                    "max_shingle_size": 2, 
                    "output_unigrams":  false   
                }
            },
            "analyzer": {
                "my_shingle_analyzer": {
                    "type":             "custom",
                    "tokenizer":        "standard",
                    "filter": [
                        "lowercase",
                        "my_shingle_filter" 
                    ]
                }
            }
        }
    }
}

預設Shingle的min/max值就是2,因此我們也可以不顯式地指定它們。 output_unigrams被設定為false,用來避免將Unigram和Bigram索引到相同欄位中。

讓我們使用analyze API來測試該解析器:

GET /my_index/_analyze?analyzer=my_shingle_analyzer
Sue ate the alligator

不出所料,我們得到了3個詞條:

  • sue ate
  • ate the
  • the alligator

現在我們就可以建立一個使用新解析器的欄位了。

多欄位(Multifields)

將Unigram和Bigram分開索引會更加清晰,因此我們將title欄位建立成一個多欄位(Multifield)(參見字串排序和多欄位(String Sorting and Multifields)):

PUT /my_index/_mapping/my_type
{
    "my_type": {
        "properties": {
            "title": {
                "type": "string",
                "fields": {
                    "shingles": {
                        "type":     "string",
                        "analyzer": "my_shingle_analyzer"
                    }
                }
            }
        }
    }
}

有了上述對映,JSON文件中的title欄位會以Unigram(title欄位)和Bigram(title.shingles欄位)的方式索引,從而讓我們可以獨立地對這兩個欄位進行查詢。

最後,我們可以索引示例文件:

POST /my_index/my_type/_bulk
{ "index": { "_id": 1 }}
{ "title": "Sue ate the alligator" }
{ "index": { "_id": 2 }}
{ "title": "The alligator ate Sue" }
{ "index": { "_id": 3 }}
{ "title": "Sue never goes anywhere without her alligator skin purse" }

搜尋Shingles

為了理解新增的shingles欄位的好處,讓我們首先看看一個針對"The hungry alligator ate Sue"的簡單match查詢的返回結果:

GET /my_index/my_type/_search
{
   "query": {
        "match": {
           "title": "the hungry alligator ate sue"
        }
   }
}

該查詢會返回所有的3份文件,但是注意文件1和文件2擁有相同的相關度分值,因為它們含有相同的單詞:

{
  "hits": [
     {
        "_id": "1",
        "_score": 0.44273707, 
        "_source": {
           "title": "Sue ate the alligator"
        }
     },
     {
        "_id": "2",
        "_score": 0.44273707, 
        "_source": {
           "title": "The alligator ate Sue"
        }
     },
     {
        "_id": "3", 
        "_score": 0.046571054,
        "_source": {
           "title": "Sue never goes anywhere without her alligator skin purse"
        }
     }
  ]
}

現在讓我們將shingles欄位也新增到查詢中。記住我們會將shingle欄位作為訊號 - 以增加相關度分值 - 我們仍然需要將主要的title欄位包含到查詢中:

GET /my_index/my_type/_search
{
   "query": {
      "bool": {
         "must": {
            "match": {
               "title": "the hungry alligator ate sue"
            }
         },
         "should": {
            "match": {
               "title.shingles": "the hungry alligator ate sue"
            }
         }
      }
   }
}

我們仍然匹配了3分文件,但是文件2現在排在了第一位,因為它匹配了Shingle詞條"ate sue":

{
  "hits": [
     {
        "_id": "2",
        "_score": 0.4883322,
        "_source": {
           "title": "The alligator ate Sue"
        }
     },
     {
        "_id": "1",
        "_score": 0.13422975,
        "_source": {
           "title": "Sue ate the alligator"
        }
     },
     {
        "_id": "3",
        "_score": 0.014119488,
        "_source": {
           "title": "Sue never goes anywhere without her alligator skin purse"
        }
     }
  ]
}

即使在查詢中包含了沒有在任何文件中出現的單詞hungry,我們仍然通過使用單詞鄰近度得到了最相關的文件。

效能

Shingle不僅比短語查詢更靈活,它們的效能也更好。相比每次搜尋需要為短語查詢付出的代價,對Shingle的查詢和簡單match查詢一樣的高效。只是在索引期間會付出一點小代價,因為更多的詞條需要被索引,意味著使用了Shingle的欄位也會佔用更多的磁碟空間。但是,多數應用是寫入一次讀取多次的,因此在索引期間花費一點代價來讓查詢更迅速是有意義的。

這是一個你在ES中經常會碰到的主題:讓你在搜尋期間能夠做很多事情,而不需要任何預先的設定。一旦更好地瞭解了你的需求,就能夠通過在索引期間正確地建模來得到更好的結果和更好的效能。