1. 程式人生 > >elasticsearch筆記_近似匹配_部分匹配(七)

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: smith

Elasticsearch對以上陣列分析生成了與分析單個字串 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 或多個字元。
    //? 可以匹配 12 , * 可以與空格及 78 匹配。
  • 正則表達regexp

    GET /my_index/address/_search
    {
        "query": {
            "regexp": {
                "postcode": "W[0-9].+" 
            }
        }
    }
    
    //這個正則表示式要求詞必須以 W 開頭,緊跟 09 之間的任何一個數字,然後接一或多個其他字元。