多字符串對應多字段
多字符串是最簡單的一種,例如,搜索標題是 War and Peace ,作者是Leo Tolstoy ,直接用bool連接兩個match查詢即可.(這裏面War and Peace和Leo Tolstoy就是多字符串,title和author是多字段)
GET /_search
{
"query": {
"bool": {
"should": [
{ "match": { "title": "War and Peace" }},
{ "match": { "author": "Leo Tolstoy" }}
]
}
}
}
單符串(多關鍵詞)對應多字段
當輸入單個字符串搜索時候,通常有下面三種需求:
1.最佳字段(best_fields) : 字符串整體的匹配度最高,而不是將字符創拆分成分詞以後分詞的匹配量最高 .(例如: “brown fox” ,詞組比各自獨立的單詞更有意義。文檔在 相同字段 中包含的詞越多越好,評分也來自於 最匹配字段 。)
2.多數字段(most_fields) : 字符串在被拆分成分詞以後,與每一個字段的匹配數量的和最大時被認爲是匹配度最高.(這種情況多用於相關度微調技術 : 將相同的數據索引到不同的字段,它們各自具有獨立的分析鏈.比如,一個字段被索引(動詞)的是原詞,另一個字段被索引(動詞)的是這個詞的變形詞,口音,去掉過去式或複數形式等)
3.混合字段(cross_fields)
對多個字段的組合進行匹配.例如:
(1) Person: first_name 和 last_name (人:名和姓)
(2) Book: title 、 author 和 description (書:標題、作者、描述)
(3) Address: street 、 city 、 country 和 postcode (地址:街道、市、國家和郵政編碼)
最佳字段(best_fields)
1 . 第一種搜索情況 : 輸入”Brown fox”對title和body進行搜索.(注意:doc1裏面title和body都只含有brown ; doc2裏面的title不含任何關鍵字 , body裏面含有Brown fox整體.)
//以下面兩個文檔爲例:
{
"title": "Quick brown rabbits",
"body": "Brown rabbits are commonly seen."
}
{
"title": "Keeping pets healthy",
"body": "My quick brown fox eats rabbits on a regular basis."
}
//輸入"Brown fox"對title和body進行搜索.
{
"query": {
"bool": {
"should": [
{ "match": { "title": "Brown fox" }},
{ "match": { "body": "Brown fox" }}
]
}
}
}
分析 : 根據肉眼觀察,”Brown fox”明顯和文檔2更加匹配 , 但是搜索結果卻是文檔1的評分更高 . 這與bool的評分方式有關 . bool的評分方式是:(1)將每一個should的查詢結果評分進行相加. (2)乘以匹配語句的總數 . (3)除以全部語句的總數.
很明顯,上面的評分結果並不是我們想要的 . 最大化查詢就是爲了解決上面的搜索情況的.
分離最大化查詢(Disjunction Max Query)
分離最大化查詢(dis_max):將任何與任一查詢匹配的文檔作爲結果返回,但只將最佳匹配的評分作爲查詢的評分結果返回 .
{
"query": {
"dis_max": {
"queries": [
{ "match": { "title": "Brown fox" }},
{ "match": { "body": "Brown fox" }}
]
}
}
}
2.第二種搜索情況 : 輸入”Quick pets”對title和body進行搜索 . (注意 : 兩個文檔都包含詞 quick ,但是隻有文檔 2 包含詞 pets)
{
"query": {
"dis_max": {
"queries": [
{ "match": { "title": "Quick pets" }},
{ "match": { "body": "Quick pets" }}
]
}
}
}
此時會發現兩個文檔的用dis_max查詢的評分是相同的 . 也是明顯不符合我們想要的結果,這種情況就要用到tie_breaker參數了.
tie_breaker參數
{
"query": {
"dis_max": {
"queries": [
{ "match": { "title": "Quick pets" }},
{ "match": { "body": "Quick pets" }}
],
"tie_breaker": 0.3
}
}
}
分析過程 :
1 . 獲得最佳匹配語句的評分 _score 。
2 . 將其他匹配語句的評分結果與 tie_breaker 相乘。
3 . 對以上評分求和並規範化。注意 :tie_breaker 可以是 0 到 1 之間的浮點數,其中 0 代表使用 dis_max 最佳匹配語句的普通邏輯, 1 表示所有匹配語句同等重要。最佳的精確值需要根據數據與查詢調試得出,但是合理值應該與零接近(處於 0.1 - 0.4 之間),這樣就不會顛覆 dis_max 最佳匹配性質的根本。
multi_match查詢
multi_match是爲了簡化多個match的情況 . 例如 :默認情況下,查詢的類型是 best_fields(最佳字段) , 而dis_max是最佳字段最常用的查詢方式 , dis_max的底層實現是:
{
"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", //best_fields 類型是默認值,可以不指定。
"fields": [ "title", "body" ],
"tie_breaker": 0.3,
"minimum_should_match": "30%"
}
}
multi_match還支持字段模糊查詢 .
//匹配 book_title 、 chapter_title 和 section_title (書名、章名、節名)這三個字段:
{
"multi_match": {
"query": "Quick brown fox",
"fields": "*_title"
}
}
提升單個字段的權重:
{
"multi_match": {
"query": "Quick brown fox",
"fields": [ "*_title", "chapter_title^2" ]
}
}
多數字段(most_fields)
提高全文相關性精度的常用方式是爲同一文本建立多種方式的索引, 每種方式都提供了一個不同的相關度信號 signal 。主字段會包括最廣匹配(broadest-matching)形式的詞去儘可能的匹配更多的文檔。舉個例子,我們可以進行以下操作:
1.使用詞幹提取來索引 jumps 、 jumping 和 jumped 樣的詞,將 jump 作爲它們的詞根形式。這樣即使用戶搜索 jumped ,也還是能找到包含 jumping 的匹配的文檔。
將同義詞包括其中,如 jump 、 leap 和 hop 。2.移除變音或口音詞:如 ésta 、 está 和 esta 都會以無變音形式 esta 來索引。
儘管如此,如果我們有兩個文檔,其中一個包含詞 jumped ,另一個包含詞 jumping ,用戶很可能期望前者能排的更高,因爲它正好與輸入的搜索條件一致。
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"
}
}
}
}
}
}
}
//添加兩個新的文檔.
PUT /my_index/my_type/1
{ "title": "My rabbit jumps" }
PUT /my_index/my_type/2
{ "title": "Jumping jack rabbits" }
查詢title字段,結果是兩個文檔的評分相同,因爲title用的是english分析器.如果只是查詢 title.std 字段,那麼只有文檔 2 是匹配的。
GET /my_index/_search
{
"query": {
"match": {
"title": "jumping rabbits"
}
}
}
如果同時查詢兩個字段,然後使用 bool 查詢將評分結果 合併 ,那麼兩個文檔都是匹配的( title 字段的作用),而且文檔 2 的相關度評分更高( title.std 字段的作用):
GET /my_index/_search
{
"query": {
"multi_match": {
"query": "jumping rabbits",
"type": "most_fields",
"fields": [ "title", "title.std" ]
}
}
}
每個字段對於最終評分的貢獻可以通過自定義值 boost 來控制。比如,使 title 字段更爲重要,這樣同時也降低了其他信號字段的作用:
GET /my_index/_search
{
"query": {
"multi_match": {
"query": "jumping rabbits",
"type": "most_fields",
"fields": [ "title^10", "title.std" ]
}
}
}
混合字段(cross_fields)
現在想要查詢 “Poland Street W1V” 這個地址 , 地址的索引(名詞)是這樣建立的:
{
"street": "5 Poland Street",
"city": "London",
"country": "United Kingdom",
"postcode": "W1V 3DG"
}
最簡單的方式是這樣的:
{
"query": {
"bool": {
"should": [
{ "match": { "street": "Poland Street W1V" }},
{ "match": { "city": "Poland Street W1V" }},
{ "match": { "country": "Poland Street W1V" }},
{ "match": { "postcode": "Poland Street W1V" }}
]
}
}
}
另一種形式:
{
"query": {
"multi_match": {
"query": "Poland Street W1V",
"type": "most_fields", //注意,不是cross_fields
"fields": [ "street", "city", "country", "postcode" ]
}
}
}
注意:most_fields 這種方式搜索也存在某些問題.
問題1. 它是爲多數字段匹配 任意 詞設計的,而不是在 所有字段 中找到最匹配的。
問題2. 它不能使用 operator或and 操作符 或 minimum_should_match 參數來降低次相關結果造成的長尾效應。
問題3. 詞頻對於每個字段是不一樣的,而且它們之間的相互影響會導致不好的排序結果。
通過 validate-query API 查看查詢的解釋 :
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 文檔的評分高。(這就是問題1)
{
"query": {
"multi_match": {
"query": "Poland Street W1V",
"type": "most_fields",
"operator": "and",
"fields": [ "street", "city", "country", "postcode" ]
}
}
}
注意 : 使用 and 操作符要求所有詞都必須存在於 相同字段 ,這顯然是不對的!可能就不存在能與這個查詢匹配的文檔。(這就是問題2)
{
"query": {
"multi_match": {
"query": "Peter Smith",
"type": "most_fields",
"fields": [ "*_name" ]
}
}
}
注意 : 用字段 first_name 和 last_name 查詢 “Peter Smith” 時, Peter 是個平常的名 Smith 也是平常的姓,這兩者都具有較低的 IDF 值。但當索引中有另外一個人的名字是 “Smith Williams” 時,姓 Smith 就會非常的不平常,以致它有一個較高的 IDF 值!
這個查詢可能會在結果中將 “Smith Williams” 置於 “Peter Smith” 之上,儘管事實上是第二個人比第一個人更爲匹配。(這就是問題3)
解決方案
1.創建一個full_name , 將搜索轉化爲搜索full_name , 缺點就是存儲冗餘數據.
{
"first_name": "Peter",
"last_name": "Smith",
"full_name": "Peter Smith"
}
2.自定義_all字段,利用copy_to來實現,copy_to 設置對multi-field無效。如果嘗試這樣配置映射,Elasticsearch 會拋異常。爲什麼呢?多字段只是以不同方式簡單索引“主”字段;它們沒有自己的數據源。也就是說沒有可供 copy_to 到另一字段的數據源。
PUT /my_index
{
"mappings": {
"person": {
"properties": {
"first_name": {
"type": "string",
"copy_to": "full_name"
},
"last_name": {
"type": "string",
"copy_to": "full_name"
},
"full_name": {
"type": "string"
}
}
}
}
}
3.cross_field詞中心匹配 , 爲了讓 cross_fields 查詢以最優方式工作,所有的字段都須使用相同的分析器, 具有相同分析器的字段會被分組在一起作爲混合字段使用。
GET /_validate/query?explain
{
"query": {
"multi_match": {
"query": "peter smith",
"type": "cross_fields",
"operator": "and",
"fields": [ "first_name", "last_name" ]
}
}
}
搜索過程 : 它會同時在 first_name 和 last_name 兩個字段中查找 smith 的 IDF ,然後用兩者的最小值作爲兩個字段的 IDF 。結果實際上就是 smith 會被認爲既是個平常的姓,也是平常的名。
爲了讓 cross_fields 查詢以最優方式工作,所有的字段都須使用相同的分析器, 具有相同分析器的字段會被分組在一起作爲混合字段使用。
cross_fields 查詢與 自定義 _all 字段 相比,其中一個優勢就是它可以在搜索時爲單個字段提升權重。
GET /books/_search
{
"query": {
"multi_match": {
"query": "peter smith",
"type": "cross_fields",
"fields": [ "title^2", "description" ]
}
}
}