ElasticSearch 結構化搜索全文


  1、介紹

 

    上篇介紹了搜索結構化數據的簡單應用示例,現在來探尋 全文搜索(full-text search) :怎樣在全文字段中搜索到最相關的文檔。

    全文搜索兩個最重要的方面是:

    相關性(Relevance)
   它是評價查詢與其結果間的相關程度,並根據這種相關程度對結果排名的一種能力,這種計算方式可以是 TF/IDF 方法(參見  相關性的介紹)、地理位置鄰近、模糊相似,或其他的某些算法。
    分析(Analysis)
   它是將文本塊轉換為有區別的、規范化的 token 的一個過程,(參見  分析的介紹) 目的是為了(a)創建倒排索引以及(b)查詢倒排索引。

    一旦談論相關性或分析這兩個方面的問題時,我們所處的語境是關於查詢的而不是過濾。

 

  2、基於詞項和基於全文

 

    所有查詢會或多或少的執行相關度計算,但不是所有查詢都有分析階段。 和一些特殊的完全不會對文本進行操作的查詢(如 bool 或 function_score )不同,文本查詢可以划分成兩大家族:

    基於詞項的查詢
      如  term 或  fuzzy 這樣的底層查詢不需要分析階段,它們對單個詞項進行操作。用  term 查詢詞項  Foo 只要在倒排索引中查找  准確詞項 ,並且用 TF/IDF 算法為每個包含該詞項的文檔計算相關度評分 _score 。

     記住 term 查詢只對倒排索引的詞項精確匹配,這點很重要,它不會對詞的多樣性進行處理(如, foo或 FOO )。這里,無須考慮詞項是如何存入索引的。

     如果是將 ["Foo","Bar"] 索引存入一個不分析的( not_analyzed )包含精確值的字段,或者將 Foo Bar 索引到一個帶有 whitespace 空格分析器的字段,兩者的結果都會是在倒排索引中有 Foo 和 Bar 這兩個詞。

    基於全文的查詢
      像  match 或  query_string 這樣的查詢是高層查詢,它們了解字段映射的信息:

   1、如果查詢 日期(date) 或 整數(integer) 字段,它們會將查詢字符串分別作為日期或整數對待。

   2、如果查詢一個( not_analyzed )未分析的精確值字符串字段,它們會將整個查詢字符串作為單個詞項對待。

   3、但如果要查詢一個( analyzed )已分析的全文字段,它們會先將查詢字符串傳遞到一個合適的分析器,然后生成一個供查詢的詞項列表。

  一旦組成了詞項列表,這個查詢會對每個詞項逐一執行底層的查詢,再將結果合並,然后為每個文檔生成一個最終的相關度評分。

 

    很少直接使用基於詞項的搜索,通常情況下都是對全文進行查詢,而非單個詞項,這只需要簡單的執行一個高層全文查詢(進而在高層查詢內部會以基於詞項的底層查詢完成搜索)。

    

    當我們想要查詢一個具有精確值的 not_analyzed 未分析字段之前, 需要考慮,是否真的采用評分查詢,或者非評分查詢會更好。

    單詞項查詢通常可以用是、非這種二元問題表示,所以更適合用過濾, 而且這樣做可以有效利用緩存

 

  3、匹配查詢

 

    匹配查詢 match 是個 核心 查詢。無論需要查詢什么字段, match 查詢都應該會是首選的查詢方式。 它是一個高級 全文查詢 ,這表示它既能處理全文字段,又能處理精確字段。

    這就是說, match 查詢主要的應用場景就是進行全文搜索,我們以下面一個簡單例子來說明全文搜索是如何工作的:

    索引一些數據

    首先,我們使用 bulk API 創建一些新的文檔和索引:

DELETE /my_index 

PUT /my_index
{ "settings": { "number_of_shards": 1 }} 

POST /my_index/my_type/_bulk
{ "index": { "_id": 1 }}
{ "title": "The quick brown fox" }
{ "index": { "_id": 2 }}
{ "title": "The quick brown fox jumps over the lazy dog" }
{ "index": { "_id": 3 }}
{ "title": "The quick brown fox jumps over the quick dog" }
{ "index": { "_id": 4 }}
{ "title": "Brown fox brown dog" }

    3.1、單個詞查詢

    用第一個示例來解釋使用 match 查詢搜索全文字段中的單個詞:

GET /my_index/my_type/_search
{
    "query": {
        "match": {
            "title": "QUICK!"
        }
    }
}

    

    Elasticsearch 執行上面這個 match 查詢的步驟是:

    1、檢查字段類型 。

      標題 title 字段是一個 string 類型( analyzed )已分析的全文字段,這意味着查詢字符串本身也應該被分析。

    2、分析查詢字符串 。

      將查詢的字符串 QUICK! 傳入標准分析器中,輸出的結果是單個項 quick 。因為只有一個單詞項,所以 match 查詢執行的是單個底層 term 查詢。

    3、查找匹配文檔 。

      用 term 查詢在倒排索引中查找 quick 然后獲取一組包含該項的文檔,本例的結果是文檔:1、2 和 3 。

    4、為每個文檔評分 。

      用 term 查詢計算每個文檔相關度評分 _score ,這是種將 詞頻(term frequency,即詞 quick 在相關文檔的 title 字段中出現的頻率)和

      反向文檔頻率(inverse document frequency,即詞 quick 在所有文檔的 title 字段中出現的頻率),以及字段的長度(即字段越短相關度越高)相結合的計算方式。參見 相關性的介紹 。

    這個過程給我們以下(經縮減)結果:

    

    文檔 1 最相關,因為它的 title 字段更短,即 quick 占據內容的一大部分。

    文檔 3 比 文檔 2 更具相關性,因為在文檔 2 中 quick 出現了兩次

 

    3.2、多詞查詢

    如果我們一次只能搜索一個詞,那么全文搜索就會不太靈活,幸運的是 match 查詢讓多詞查詢變得簡單

GET /my_index/my_type/_search
{
    "query": {
        "match": {
            "title": "BROWN DOG!"
        }
    }
}

    

    文檔 4 最相關,因為它包含詞 "brown" 兩次以及 "dog" 一次。

    文檔 2、3 同時包含 brown 和 dog 各一次,而且它們 title 字段的長度相同,所以具有相同的評分。

    文檔 1 也能匹配,盡管它只有 brown 沒有 dog 。

    因為 match 查詢必須查找兩個詞( ["brown","dog"] ),它在內部實際上先執行兩次 term 查詢,然后將兩次查詢的結果合並作為最終結果輸出。為了做到這點,

    它將兩個 term 查詢包入一個 bool 查詢中,詳細信息見 布爾查詢

    以上示例告訴我們一個重要信息:即任何文檔只要 title 字段里包含 指定詞項中的至少一個詞 就能匹配,被匹配的詞項越多,文檔就越相關。

 

    提高精度

    用 任意 查詢詞項匹配文檔可能會導致結果中出現不相關的長尾。 這是種散彈式搜索。可能我們只想搜索包含 所有 詞項的文檔,也就是說,不去匹配 brown OR dog ,而通過匹配 brown AND dog 找到所有文檔。

    match 查詢還可以接受 operator 操作符作為輸入參數,默認情況下該操作符是 or 。我們可以將它修改成 and 讓所有指定詞項都必須匹配:

GET /my_index/my_type/_search
{
    "query": {
        "match": {
            "title": {      
                "query":    "BROWN DOG!",
                "operator": "and"
            }
        }
    }
}

    

 

    match 查詢的結構需要做稍許調整才能使用 operator 操作符參數。

    這個查詢可以把文檔 1 排除在外,因為它只包含兩個詞項中的一個。

 

  4、組合查詢

 

    在 組合過濾器 中,討論過如何使用 bool 過濾器通過 and 、 or 和 not 邏輯組合將多個過濾器進行組合。在查詢中, bool 查詢有類似的功能,只有一個重要的區別。

    過濾器做二元判斷:文檔是否應該出現在結果中?但查詢更精妙,它除了決定一個文檔是否應該被包括在結果中,還會計算文檔的 相關程度 。

    與過濾器一樣, bool 查詢也可以接受 must 、 must_not 和 should 參數下的多個查詢語句。比如:

GET /my_index/my_type/_search
{
  "query": {
    "bool": {
      "must":     { "match": { "title": "quick" }},
      "must_not": { "match": { "title": "lazy"  }},
      "should": [
                  { "match": { "title": "brown" }},
                  { "match": { "title": "dog"   }}
      ]
    }
  }
}

    

    

    以上的查詢結果返回 title 字段包含詞項 quick 但不包含 lazy 的任意文檔。目前為止,這與 bool 過濾器的工作方式非常相似。

    區別就在於兩個 should 語句,也就是說:一個文檔不必包含 brown 或 dog 這兩個詞項,但如果一旦包含,我們就認為它們 更相關 

    文檔 3 會比文檔 1 有更高評分是因為它同時包含 brown 和 dog 。

    評分計算

    bool 查詢會為每個文檔計算相關度評分 _score , 再將所有匹配的 must 和 should 語句的分數 _score求和,最后除以 must 和 should 語句的總數。

    must_not 語句不會影響評分; 它的作用只是將不相關的文檔排除。

    控制精度

    所有 must 語句必須匹配,所有 must_not 語句都必須不匹配,但有多少 should 語句應該匹配呢? 默認情況下,沒有 should 語句是必須匹配的,

    只有一個例外:那就是當沒有 must 語句的時候,至少有一個 should 語句必須匹配。

    可以通過 minimum_should_match 參數控制需要匹配的 should 語句的數量, 它既可以是一個絕對的數字,又可以是個百分比:

GET /my_index/my_type/_search
{
  "query": {
    "bool": {
      "should": [
        { "match": { "title": "brown" }},
        { "match": { "title": "fox"   }},
        { "match": { "title": "dog"   }}
      ],
      "minimum_should_match": 2 
    }
  }
}

    這個查詢結果會將所有滿足以下條件的文檔返回: title 字段包含 "brown" AND "fox" 、 "brown" AND "dog" 或 "fox" AND "dog" 。如果有文檔包含所有三個條件,它會比只包含兩個的文檔更相關。

 

  5、布爾匹配

 

    多詞匹配查詢只是簡單地將生成的 term 查詢包裹 在一個 bool 查詢中。如果使用默認的 or 操作符,每個 term 查詢都被當作 should 語句,這樣就要求必須至少匹配一條語句。以下兩個查詢是等價的:

GET /my_index/my_type/_search
{
  "query": {
    "match": { "title": "brown fox"}
  }
}



GET /my_index/my_type/_search
{
  "query": {
    "bool": {
    "should": [
      { "term": { "title": "brown" }},
      { "term": { "title": "fox"   }}
    ]
  }
  }
}

    如果使用 and 操作符,所有的 term 查詢都被當作 must 語句,所以 所有(all) 語句都必須匹配。以下兩個查詢是等價的:

GET /my_index/my_type/_search
{
  "query": {
     "match": {
        "title": {
            "query":    "brown fox",
            "operator": "and"
        }
    }
  }
}



GET /my_index/my_type/_search
{
  "query": {
    "bool": {
    "must": [
      { "term": { "title": "brown" }},
      { "term": { "title": "fox"   }}
    ]
  }
  }
}

    如果指定參數 minimum_should_match ,它可以通過 bool 查詢直接傳遞,使以下兩個查詢等價:

GET /my_index/my_type/_search
{
  "query": {
     "match": {
        "title": {
            "query": "quick brown fox",
            "minimum_should_match": "75%"
        }
    }
  }
}



GET /my_index/my_type/_search
{
  "query": {
    "bool": {
    "should": [
      { "term": { "title": "brown" }},
      { "term": { "title": "fox"   }},
      { "term": { "title": "quick" }}
    ],
    "minimum_should_match": 2 
  }
  }
}

    因為只有三條語句,match 查詢的參數 minimum_should_match 值 75% 會被截斷成 2 。即三條 should 語句中至少有兩條必須匹配。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM