Multi_field_Search

多字符串查詢

GET /_search
{
  "query": {
    "bool": {
      "should": [
        { "match": { "title":  "War and Peace" }},
        { "match": { "author": "Leo Tolstoy"   }},
        { "bool":  {
          "should": [
            { "match": { "translator": "Constance Garnett" }},
            { "match": { "translator": "Louise Maude"      }}
          ]
        }}
      ]
    }
  }
}

4個match查詢,為什么后兩個使用bool包裹?
同一層查詢每條語句具有相同的權重!!!

語句的優先級

boost

單字符串查詢

用戶期望將所有的搜索項堆積到單個字段中。
1、最佳字段
例如我們搜索“es reindex”,對于title和body這樣的兩個字段,“es reindex”同時出現在一個字段時的文檔評分更高
2、多數字段
為了對相關度進行微調,常用的一個技術就是將相同的數據索引到不同的字段,使他們具有獨立的分析鏈。
主字段可能包括它們的詞源、同義詞以及變音詞或口音詞等,用來匹配盡可能多的文檔。
相同的文本被索引到其他字段,提供更精確的匹配。一個字段可以包括未經詞干提取的原詞;另一個字段包括口音,還有一個字段提供詞語相似性。
其他字段是作為匹配每個文檔時提高相關度評分的信號。
3、混合字段
對于某些實體,需要在多個字段中確認其信息,單個字段都只能作為整體的一部分:
Person: first_name、last_name
如同在Persion這個大字段中搜索 first_name和last_name這兩個字段

最佳字段

以兩個文檔為例

PUT /my_index/my_type/1
{
    "title": "Quick brown rabbits",
    "body":  "Brown rabbits are commonly seen."
}

PUT /my_index/my_type/2
{
    "title": "Keeping pets healthy",
    "body":  "My quick brown fox eats rabbits on a regular basis."
}

bool查詢,想要匹配Brown fox

{
    "query": {
        "bool": {
            "should": [
                { "match": { "title": "Brown fox" }},
                { "match": { "body":  "Brown fox" }}
            ]
        }
    }
}

我們想要的結果是文檔2,但是文檔1的評分更高
bool如何計算評分:
1、執行should語句中的兩個查詢。
2、兩個查詢的評分相加
3、乘以匹配語句的總數
4、除以所有語句總數(這里為2,指的應該兩個字段???)
文檔1中兩個字段都包含brown,所以兩個match語句都有一個評分。
文檔2中body字段包含兩個詞,獲得較高分,但title字段沒有包含任何詞,平均一下文檔2的整體分就比文檔1低。
可以使用dis_max分離最大化查詢Disjunction Max Query,大致意思是,將任何與任一查詢匹配的文檔作為結果返回,但只將最匹配的評分作為查詢的評分結果返回:

{
    "query": {
        "dis_max": {
            "queries": [
                { "match": { "title": "Brown fox" }},
                { "match": { "body":  "Brown fox" }}
            ]
        }
    }
}

最佳字段查詢調優

dis_max查詢會單個匹配最佳字段,而忽略其他的匹配。(單個最佳匹配語句的評分作為整體評分)
可以通過指定tie_breaker字段將其他語句的評分也考慮其中。

{
    "query": {
        "dis_max": {
            "queries": [
                { "match": { "title": "Quick pets" }},
                { "match": { "body":  "Quick pets" }}
            ],
            "tie_breaker": 0.3
        }
    }
}

結果如下:

{
  "hits": [
     {
        "_id": "2",
        "_score": 0.14757764, (1)
        "_source": {
           "title": "Keeping pets healthy",
           "body": "My quick brown fox eats rabbits on a regular basis."
        }
     },
     {
        "_id": "1",
        "_score": 0.124275915, (1)
        "_source": {
           "title": "Quick brown rabbits",
           "body": "Brown rabbits are commonly seen."
        }
     }
   ]
}

不帶有tie_breaker參數時,評分1等于評分2
tie_breaker的評分方式:
1、獲得最佳匹配語句的評分
2、其他語句的評分結果乘以tie_breaker
3、上述相加即為最后評分
tie_breaker的合理值應該處于0.1-0.4之間

multi_match多匹配查詢

在多個字段上反復執行相同的查詢。
multi_match多匹配查詢的類型有多種,其中三種與前文的最佳字段(best_fields)、多數字段(most_fields)、跨字段相匹配(cross_fields)
默認情況查詢的類型是best_fields,例如:

{
  "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重寫成更簡潔的形式

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

minimum_should_match、operator 這樣的參數會被傳遞到生成的match查詢中
匹配的字段可以使用模糊匹配的方式給出,例如:

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

可以使用^為單個字段提升權重

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

多數字段

召回率:返回所有的相關文檔
精確率:不返回無關文檔
全文搜索的兩個名詞,目的是在結果的第一頁為用戶呈現最為相關的文檔
多字段映射,首先要做的事情就是對我們的字段索引兩次,一次使用詞干模式,一次使用非詞干模式

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

簡單的查詢,兩個文檔的評分相同;只查詢title.std那么只有文檔2是匹配的

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

如果同時查詢兩個字段,然后使用bool查詢將結果合并,那么兩個文檔都是匹配的,文檔2的分更高

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

用廣度匹配字段title匹配盡可能多的文檔,提升召回率
同時又使用title.std作為信號將相關度更高的文檔置于結果頂部

跨字段實體搜索

多字符串查詢中,我們為每個字段使用不同的字符串,
而跨字段查詢是使用單個字符串在多個字段中進行搜索。
例如我們搜索Poland Street W1V這個地址,它需要匹配多個字段
假如使用motst_fields會出現如下問題:
1、它是為多數字段匹配任意詞設計的,而不是在所有字段中找到最匹配的
2、它不能使用operator或minimum_should_match參數來降低相關結果造成的長尾效應
3、詞頻對于每個字段是不一樣的,而且它們之間的相互影響會導致不好的排序結果。

字段中心式查詢

most_fields(best_fields)出現的問題都是因為它是字段中心式查詢,而不是詞中心式的。
詞中心式:當真正感興趣的事匹配詞的時候,它為我們查找的是最匹配的字段
問題1:在多個字段中匹配相同的詞

GET /_validate/query?explain
{
  "query": {
    "multi_match": {
      "query":   "Poland Street W1V",
      "type":    "most_fields",
      "fields":  [ "street", "city", "country", "postcode" ]
    }
  }
}

生成的explanation解釋:

(street:poland   street:street   street:w1v)
(city:poland     city:street     city:w1v)
(country:poland  country:street  country:w1v)
(postcode:poland postcode:street postcode:w1v)

可以發現兩個字段都與poland匹配的文檔要比一個字段同時匹配poland和street文檔的評分高(可能是順序,得出這樣的結論)
問題2:剪掉長尾
在精度匹配中,我們可以使用operator和minimum_should_match參數來消除結果中幾乎不相關的長尾,但是對于best_fields和most_fields中,這些參數會在match查詢生成時被傳入,例如操作符and,就會要求所有詞必須存在與相同字段中,這顯然是不對的
問題3:詞頻

{
    "query": {
        "multi_match": {
            "query":       "Peter Smith",
            "type":        "most_fields",
            "fields":      [ "*_name" ]
        }
    }
}

可能在結果中將“Smith Williams” 置于 “Peter Smith” 之上。
簡單來說就是smith在名字段中具有較高的IDF,他會削弱“Peter”作為名和“smith”作為姓時低IDF的所起作用。

解決方案

存在這些問題僅僅是因為我們在處理著多個字段,如果將這些字段組合成單個字段,問題就會消失。

{
    "first_name":  "Peter",
    "last_name":   "Smith",
    "full_name":   "Peter Smith"
}

這樣只要查詢full_name剛才的問題就會消失,但是數據卻會冗余。取而代之的是另外兩種方案,一個是在索引時,另一個是在搜索時

自定義_all字段

_all字段的索引方式是將所有其他字段的值作為一個大字符串索引的。然而這么做并不靈活,為了靈活,我們可以給人名添加一個自定義_all字段,也為地址添加另一個_all字段
可以使用copy_to參數來實現這個功能

PUT /my_index
{
    "mappings": {
        "person": {
            "properties": {
                "first_name": {
                    "type":     "string",
                    "copy_to":  "full_name" (1)
                },
                "last_name": {
                    "type":     "string",
                    "copy_to":  "full_name" (1)
                },
                "full_name": {
                    "type":     "string"
                }
            }
        }
    }
}

first_name和last_name字段的值會被復制到full_name中
copy_to設置對multi_field無效。
多字段只是以不同方式簡單索引“主”字段,他們沒有自己的數據源,只要對主字段copy_to就能輕而易舉的達到相同的效果

PUT /my_index
{
    "mappings": {
        "person": {
            "properties": {
                "first_name": {
                    "type":     "string",
                    "copy_to":  "full_name", (1)
                    "fields": {
                        "raw": {
                            "type": "string",
                            "index": "not_analyzed"
                        }
                    }
                },
                "full_name": {
                    "type":     "string"
                }
            }
        }
    }
}

跨字段查詢

搜索時相應的解決方案:使用cross_fields進行multi_match查詢。
cross_fields是詞中心式的查詢,它將所有字段當成一個大字段,并在每個字段中查詢每個詞

GET /_validate/query?explain
{
    "query": {
        "multi_match": {
            "query":       "peter smith",
            "type":        "most_fields",
            "operator":    "and", (1)
            "fields":      [ "first_name", "last_name" ]
        }
    }
}

得到結果表示peter和smith都必須同時出現在相同字段中,要么是first_name,要么last_name

(+first_name:peter +first_name:smith)
(+last_name:peter  +last_name:smith)

然而詞中心式會使用以下邏輯,意味著peter和smith都必須出現,但是可以出現在任意字段中

+(first_name:peter last_name:peter)
+(first_name:smith last_name:smith)

cross_fields類型首先分析查詢字符串并生成一個詞列表,然后它從所有字段中依次搜索每個詞。
這種不同的查詢方式自然就解決了前兩個問題。
至于IDF問題,它通過混合不同字段逆向索引文檔頻率的方式解決了詞頻的問題。

GET /_validate/query?explain
{
    "query": {
        "multi_match": {
            "query":       "peter smith",
            "type":        "cross_fields", (1)
            "operator":    "and",
            "fields":      [ "first_name", "last_name" ]
        }
    }
}
+blended("peter", fields: [first_name, last_name])
+blended("smith", fields: [first_name, last_name])

換句話說,它會在兩個字段字段中查找smith的IDF,然后使用最小值作為兩個字段的IDF。
Tip:為了讓cross_fields查詢以最優的方式工作,所有的字段都必須使用相同的分析器,具有相同分析器的字段會被分組在一起作為混合字段使用。
如果包括了不同分析器的字段,它們會以best_fields的相同方式加入到查詢結果中。
例如將title字段加入到之前的查詢,explanation解釋如下:

(+title:peter +title:smith)
(
  +blended("peter", fields: [first_name, last_name])
  +blended("smith", fields: [first_name, last_name])
)

當在使用minimum_should_match和operator參數時,這點尤為重要

按字段提高權重

使用cross_fields和_all相比,其中一個優勢就是它可以在搜索時為單個字段提升權重
正如前文提到的^
自定義單字段查詢能否優于多字段查詢,取決于在多字段與單字段自定義_all之間代價的權衡。

Exact-Value精確值字段

將not_analyzed字段與 multi_match中analyzed字段混在一起沒有多大用處。
如前文的查詢,假如title是not_analyzed

title:peter smith
(
    blended("peter", fields: [first_name, last_name])
    blended("smith", fields: [first_name, last_name])
)

es會將peter smith完整的字符串作為查詢條件來搜索,所以需要在multi_match查詢中避免使用not_analyzed字段

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,030評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,310評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,951評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,796評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,566評論 6 407
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,055評論 1 322
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,142評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,303評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,799評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,683評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,899評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,409評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,135評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,520評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,757評論 1 282
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,528評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,844評論 2 372

推薦閱讀更多精彩內容