elasticsearch筆記_近似匹配_部分匹配(七)
短語匹配
一個被認定為和短語 quick brown fox 匹配的文件,必須滿足以下這些要求:
- quick 、 brown 和 fox 需要全部出現在域中。
- brown 的位置應該比 quick 的位置大 1 。
- fox 的位置應該比 quick 的位置大 2 。
如果以上任何一個選項不成立,則該文件不能認定為匹配。
match_phrase查詢
GET /my_index/my_type/_search
{
"query": {
"match_phrase": {
"title": "quick brown fox"
}
}
}
類似 match 查詢, match_phrase 查詢首先將查詢字串解析成一個詞項列表,然後對這些詞項進行搜尋,但只保留那些包含 全部 搜尋詞項,且 位置 與搜尋詞項相同的文件。
當一個字串被分詞後,這個分析器不但會 返回一個詞項列表,而且還會返回各詞項在原始字串中的 位置 或者順序關係.
GET /_analyze?analyzer=standard Quick brown fox 返回結果 : { "tokens": [ { "token": "quick", "start_offset
": 0, "end_offset": 5, "type": "<ALPHANUM>", "position": 1 //詞條在短語中的位置. }, { "token": "brown", "start_offset": 6, "end_offset": 11, "type": "<ALPHANUM>", "position": 2 }, { "token": "fox", "start_offset": 12, "end_offset": 15, "type": "<ALPHANUM>", "position": 3 } ] }
match_phrase查詢(slop 引數)
如果想要包含 “quick brown fox” 的文件也能夠匹配“quick fox,” 。需要用到slop引數,slop引數的意思是告訴 match_phrase 查詢詞條相隔多遠時仍然能將文件視為匹配 .
GET /my_index/my_type/_search { "query": { "match_phrase": { "title": { "query": "quick fox", "slop": 1 } } } }
match_phrase查詢(多值欄位的小問題)
假設現在有一個文件如下:
{ "names": [ "John Abraham", "Lincoln Smith"] }
執行下面這個查詢
GET /my_index/groups/_search { "query": { "match_phrase": { "names": "Abraham Lincoln" } } }
即使 Abraham 和 Lincoln 在 names 數組裡屬於兩個不同的人名, 我們的文件也匹配了查詢。 這一切的原因在Elasticsearch陣列的索引方式。
在分析 John Abraham 的時候, 產生了如下資訊:
Position 1: john
Position 1: john
Position 2: abraham然後在分析 Lincoln Smith 的時候, 產生了:
Position 3: lincoln
Position 4: smithElasticsearch對以上陣列分析生成了與分析單個字串 John Abraham Lincoln Smith 一樣幾乎完全相同的語彙單元。 我們的查詢示例尋找相鄰的 lincoln 和 abraham , 而且這兩個詞條確實存在,並且它們倆正好相鄰, 所以這個查詢匹配了。解決這個問題的技巧使用position_increment_gap引數 .
position_increment_gap
DELETE /my_index/groups/
PUT /my_index/_mapping/groups
{
"properties": {
"names": {
"type": "string",
"position_increment_gap": 100
}
}
}
position_increment_gap 設定告訴 Elasticsearch 應該為陣列中每個新元素增加當前詞條 position 的指定值。 所以現在當我們再索引 names 陣列時,會產生如下的結果:
Position 1: john
Position 2: abraham
Position 103: lincoln
Position 104: smith現在我們的短語查詢可能無法匹配該文件因為 abraham 和 lincoln 之間的距離為 100 。 為了匹配這個文件你必須新增值為 100 的 slop 。
slop引數的設定會影響對文件的評分,短語的詞條離的越近,評分越高.
例如 : 對 quick dog 的鄰近查詢匹配以下兩個文件 :
{ "hits": [ { "_id": "3", "_score": 0.75, "_source": { "title": "The quick brown fox jumps over the quick dog" } }, { "_id": "2", "_score": 0.28347334, "_source": { "title": "The quick brown fox jumps over the lazy dog" } } ] }
可以看到文件1的評分要高於文件2 , 因為文件1裡面的quick 和 dog 離更近一些 .
小技巧
有時候可能會遇見這樣的情況 : 如果七個詞條中有六個匹配, 那麼這個文件對使用者而言就已經足夠相關了, 但是 match_phrase 查詢可能會將它排除在外。
可以這樣做 :
將一個簡單的 match 查詢作為一個 must 子句。 這個查詢將決定哪些文件需要被包含到結果集中。 我們可以用 minimum_should_match 引數去除長尾。 然後我們可以以 should 子句的形式新增更多特定查詢。 每一個匹配成功的都會增加匹配文件的相關度。
GET /my_index/my_type/_search
{
"query": {
"bool": {
"must": {
"match": {
"title": {
"query": "quick brown fox",
"minimum_should_match": "30%"
}
}
},
"should": {
"match_phrase": {
"title": {
"query": "quick brown fox",
"slop": 50
}
}
}
}
}
}
尋找相關詞
上面所有的查詢都沒法解決這樣一個問題:兩個子句 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"]
這些單詞對(或者 bigrams )被稱為 shingles 。
Shingles 不限於單詞對;你也可以索引三個單詞( trigrams )
["sue ate the", "ate the alligator"]
Trigrams 提供了更高的精度,但是也大大增加了索引中唯一詞項的數量。在大多數情況下,Bigrams 就夠了。
DELETE /my_index PUT /my_index { "settings": { "number_of_shards": 1, "analysis": { "filter": { "my_shingle_filter": { "type": "shingle", "min_shingle_size": 2, //預設最小/最大的 shingle 大小是 2 ,所以實際上不需要設定。 "max_shingle_size": 2, "output_unigrams": false //shingle 語彙單元過濾器預設輸出 unigrams ,但是我們想讓 unigrams 和 bigrams 分開。 } }, "analyzer": { "my_shingle_analyzer": { "type": "custom", "tokenizer": "standard", "filter": [ "lowercase", "my_shingle_filter" //my_shingle_analyzer 使用我們常規的 my_shingles_filter 語彙單元過濾器。 ] } } } } }
部分匹配
WHERE text LIKE "%quick%" AND text LIKE "%brown%" AND text LIKE "%fox%"
為了實現上述sql語句的功能 , elasticsearch提供了三種方式:
prefix字首查詢
GET /my_index/address/_search { "query": { "prefix": { "postcode": "W1" } } }
萬用字元
GET /my_index/address/_search { "query": { "wildcard": { "postcode": "W?F*HW" } } } //它使用標準的 shell 萬用字元查詢: ? 匹配任意字元, * 匹配 0 或多個字元。 //? 可以匹配 1 和 2 , * 可以與空格及 7 和 8 匹配。
正則表達regexp
GET /my_index/address/_search { "query": { "regexp": { "postcode": "W[0-9].+" } } } //這個正則表示式要求詞必須以 W 開頭,緊跟 0 至 9 之間的任何一個數字,然後接一或多個其他字元。