[Elasticsearch] 多字段搜索 (三) - multi_match查詢和多數字段

multi_match查詢

multi_match查詢提供了一個簡便的方法用來對多個字段執行相同的查詢。html

NOTEjson

存在幾種類型的multi_match查詢,其中的3種正好和在"瞭解你的數據"一節中提到的幾種類型相同:best_fields,most_fields以及cross_fields。app

默認狀況下,該查詢以best_fields類型執行,它會爲每一個字段生成一個match查詢,而後將這些查詢包含在一個dis_max查詢中。下面的dis_max查詢:elasticsearch

{
  "dis_max": {
    "queries":  [
      {
        "match": {
          "title": {
            "query": "Quick brown fox",
            "minimum_should_match": "30%"
          }
        }
      },
      {
        "match": {
          "body": {
            "query": "Quick brown fox",
            "minimum_should_match": "30%"
          }
        }
      },
    ],
    "tie_breaker": 0.3
  }
}

能夠經過multi_match簡單地重寫以下:ide

{
    "multi_match": {
        "query":                "Quick brown fox",
        "type":                 "best_fields", 
        "fields":               [ "title", "body" ],
        "tie_breaker":          0.3,
        "minimum_should_match": "30%" 
    }
}

注意到以上的type屬性爲best_fields。 minimum_should_match和operator參數會被傳入到生成的match查詢中。ui

在字段名中使用通配符

字段名能夠經過通配符指定:任何匹配了通配符的字段都會被包含在搜索中。你能夠經過下面的查詢來匹配book_title,chapter_title以及section_title字段:spa

{
    "multi_match": {
        "query":  "Quick brown fox",
        "fields": "*_title"
    }
}

提高個別字段

個別字段能夠經過caret語法(^)進行提高:僅須要在字段名後添加^boost,其中的boost是一個浮點數:code

{
    "multi_match": {
        "query":  "Quick brown fox",
        "fields": [ "*_title", "chapter_title^2" ] 
    }
}

chapter_title字段的boost值爲2,而book_title和section_title字段的boost值爲默認的1。htm

多數字段(Most Fields)

全文搜索是一場召回率(Recall) - 返回全部相關的文檔,以及準確率(Precision) - 不返回無關文檔,之間的戰鬥。目標是在結果的第一頁給用戶呈現最相關的文檔。排序

爲了提升召回率,咱們會廣撒網 - 不只包括精確匹配了用戶搜索詞條的文檔,還包括了那些咱們認爲和查詢相關的文檔。若是一個用戶搜索了"quick brown fox",一份含有fast foxes的文檔也能夠做爲一個合理的返回結果。

若是咱們擁有的相關文檔僅僅是含有fast foxes的文檔,那麼它會出如今結果列表的頂部。可是若是咱們有100份含有quick brown fox的文檔,那麼含有fast foxes的文檔的相關性就會變低,咱們但願它出如今結果列表的後面。在包含了許多可能的匹配後,咱們須要確保相關度高的文檔出如今頂部。

一個用來調優全文搜索相關性的經常使用技術是將一樣的文本以多種方式索引,每一種索引方式都提供了不一樣相關度的信號(Signal)。主要字段(Main field)中含有的詞條的形式是最寬泛的(Broadest-matching),用來儘量多的匹配文檔。好比,咱們能夠這樣作:

  • 使用一個詞幹提取器來將jumps,jumping和jumped索引成它們的詞根:jump。而後當用戶搜索的是jumped時,咱們仍然可以匹配含有jumping的文檔。
  • 包含同義詞,好比jump,leap和hop。
  • 移除變音符號或者聲調符號:好比,ésta,está和esta都會以esta被索引。

可是,若是咱們有兩份文檔,其中之一含有jumped,而另外一份含有jumping,那麼用戶會但願第一份文檔的排序會靠前,由於它含有用戶輸入的精確值

咱們能夠經過將相同的文本索引到其它字段來提供更加精確的匹配。一個字段能夠包含未被提取詞幹的版本,另外一個則是含有變音符號的原始單詞,而後第三個使用了shingles,用來提供和單詞鄰近度相關的信息。這些其它字段扮演的角色就是信號(Signals),它們用來增長每一個匹配文檔的相關度分值。可以匹配的字段越多,相關度就越高。

若是一份文檔可以匹配具備最寬泛形式的主要字段(Main field),那麼它就會被包含到結果列表中。若是它同時也匹配了信號字段,它會獲得一些額外的分值用來將它移動到結果列表的前面。

咱們會在本書的後面討論同義詞,單詞鄰近度,部分匹配以及其餘可能的信號,可是咱們會使用提取了詞幹和未提取詞幹的字段的簡單例子來解釋這個技術。

多字段映射(Multifield Mapping)

第一件事就是將咱們的字段索引兩次:一次是提取了詞幹的形式,一次是未提取詞幹的形式。爲了實現它,咱們會使用多字段(Multifields),在字符串排序和多字段中咱們介紹過:

DELETE /my_index

PUT /my_index
{
    "settings": { "number_of_shards": 1 }, 
    "mappings": {
        "my_type": {
            "properties": {
                "title": { 
                    "type":     "string",
                    "analyzer": "english",
                    "fields": {
                        "std":   { 
                            "type":     "string",
                            "analyzer": "standard"
                        }
                    }
                }
            }
        }
    }
}

title字段使用了english解析器進行詞幹提取。 title.std字段則使用的是standard解析器,所以它沒有進行詞幹提取。

下一步,咱們會索引一些文檔:

PUT /my_index/my_type/1
{ "title": "My rabbit jumps" }

PUT /my_index/my_type/2
{ "title": "Jumping jack rabbits" }

如下是一個簡單的針對title字段的match查詢,它查詢jumping rabbits:

GET /my_index/_search
{
   "query": {
        "match": {
            "title": "jumping rabbits"
        }
    }
}

它會變成一個針對兩個提幹後的詞條jump和rabbit的查詢,這要得益於english解析器。兩份文檔的title字段都包含了以上兩個詞條,所以兩份文檔的分值是相同的

{
  "hits": [
     {
        "_id": "1",
        "_score": 0.42039964,
        "_source": {
           "title": "My rabbit jumps"
        }
     },
     {
        "_id": "2",
        "_score": 0.42039964,
        "_source": {
           "title": "Jumping jack rabbits"
        }
     }
  ]
}

若是咱們只查詢title.std字段,那麼只有文檔2會匹配。可是,當咱們查詢兩個字段並將它們的分值經過bool查詢進行合併的話,兩份文檔都可以匹配(title字段也匹配了),而文檔2的分值會更高一些(匹配了title.std字段):

GET /my_index/_search
{
   "query": {
        "multi_match": {
            "query":  "jumping rabbits",
            "type":   "most_fields", 
            "fields": [ "title", "title.std" ]
        }
    }
}

在上述查詢中,因爲咱們想合併全部匹配字段的分值,所以使用的類型爲most_fields。這會讓multi_match查詢將針對兩個字段的查詢子句包含在一個bool查詢中,而不是包含在一個dis_max查詢中

{
  "hits": [
     {
        "_id": "2",
        "_score": 0.8226396, 
        "_source": {
           "title": "Jumping jack rabbits"
        }
     },
     {
        "_id": "1",
        "_score": 0.10741998, 
        "_source": {
           "title": "My rabbit jumps"
        }
     }
  ]
}

此時,文檔2的分值比文檔1的高許多。

咱們使用了擁有寬泛形式的title字段來匹配儘量多的文檔 - 來增長召回率(Recall),同時也使用了title.std字段做爲信號來讓最相關的文檔可以擁有更靠前的排序(譯註:增長了準確率(Precision))。

每一個字段對最終分值的貢獻能夠經過指定boost值進行控制。好比,咱們能夠提高title字段來讓該字段更加劇要,這也減少了其它信號字段的影響:

GET /my_index/_search
{
   "query": {
        "multi_match": {
            "query":       "jumping rabbits",
            "type":        "most_fields",
            "fields":      [ "title^10", "title.std" ] 
        }
    }
}
相關文章
相關標籤/搜索