elasticsearch學習筆記高級篇(十)——多字段搜索(上)

只有一個簡單的match子句的查詢是不多見的。咱們常常須要在一個或者多個字段中查詢相同的或者不一樣的查詢字符串,意味着咱們須要可以組合多個子查詢以及使它們的相關性得分有意義。網站

一、best fields

假設咱們有一個讓用戶搜索博客文章的網站。其中有兩個文檔以下:ui

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

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

進行查詢:code

GET /test_index/_search
{
    "query": {
        "bool": {
            "should": [
                { "match": { "title": "Brown fox" }},
                { "match": { "body":  "Brown fox" }}
            ]
        }
    }
}

輸出結果:ip

{
  "took" : 326,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 2,
      "relation" : "eq"
    },
    "max_score" : 0.90425634,
    "hits" : [
      {
        "_index" : "test_index",
        "_type" : "_doc",
        "_id" : "1",
        "_score" : 0.90425634,
        "_source" : {
          "title" : "Quick brown rabbits",
          "body" : "Brown rabbits are commonly seen."
        }
      },
      {
        "_index" : "test_index",
        "_type" : "_doc",
        "_id" : "2",
        "_score" : 0.77041256,
        "_source" : {
          "title" : "Keeping pets healthy",
          "body" : "My quick brown fox eats rabbits on a regular basis."
        }
      }
    ]
  }
}

咱們發現文檔1的相關度分數更高,排在了前面。要理解緣由,能夠相像一下bool查詢是如何計算相關度分數的
(1)運行should子句中的兩個查詢
(2)相加查詢返回的分值
(3)將相加獲得的分值乘以匹配的查詢子句的數量
(4)除以總的查詢子句的數量
文檔1在兩個字段中都包含了brown,所以兩個match查詢都匹配成功並擁有了一個分值。文檔2在body字段中包含了brown以及fox,可是在title字段中沒有出現任何搜索的單詞。所以對body字段查詢獲得的高分加上對title字段查詢獲得的零分,而後在乘以匹配的查詢子句數量1,最後除以總的查詢子句數量2,致使總體分數值比文檔1的低。文檔

解決方法:

相比使用bool查詢,咱們可使用dis_max查詢(Disjuction Max Query),意思就是返回匹配了任何查詢的文檔,而且分值是產生了最佳匹配的查詢所對應的分值:字符串

GET /test_index/_search
{
  "query": {
    "dis_max": {
      "queries": [
        {
          "match": {
            "title": "Brown fox"
          }
        },
        {
          "match": {
            "body": "Brown fox"
          }
        }
      ]
    }
  }
}

輸出結果:博客

{
  "took" : 11,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 2,
      "relation" : "eq"
    },
    "max_score" : 0.77041256,
    "hits" : [
      {
        "_index" : "test_index",
        "_type" : "_doc",
        "_id" : "2",
        "_score" : 0.77041256,
        "_source" : {
          "title" : "Keeping pets healthy",
          "body" : "My quick brown fox eats rabbits on a regular basis."
        }
      },
      {
        "_index" : "test_index",
        "_type" : "_doc",
        "_id" : "1",
        "_score" : 0.6931472,
        "_source" : {
          "title" : "Quick brown rabbits",
          "body" : "Brown rabbits are commonly seen."
        }
      }
    ]
  }
}

此時就獲得了咱們想要的結果,文檔2排在了文檔1的前面it

二、針對best fields查詢進行調優

上面的例子中,假設咱們搜索的是「quick pets」,兩份文檔中都包含了單詞quick,可是隻有文檔2包含了pets。兩份文檔都沒能在一個字段中同時包含搜索的兩個單詞。
進行查詢:io

GET /test_index/_search
{
  "query": {
    "dis_max": {
      "queries": [
        {
          "match": {
            "title": "Quick pets"
          }
        },
        {
          "match": {
            "body": "Quick pets"
          }
        }
      ]
    }
  }
}

輸出結果:test

{
  "took" : 2,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 2,
      "relation" : "eq"
    },
    "max_score" : 0.6931472,
    "hits" : [
      {
        "_index" : "test_index",
        "_type" : "_doc",
        "_id" : "1",
        "_score" : 0.6931472,
        "_source" : {
          "title" : "Quick brown rabbits",
          "body" : "Brown rabbits are commonly seen."
        }
      },
      {
        "_index" : "test_index",
        "_type" : "_doc",
        "_id" : "2",
        "_score" : 0.6931472,
        "_source" : {
          "title" : "Keeping pets healthy",
          "body" : "My quick brown fox eats rabbits on a regular basis."
        }
      }
    ]
  }
}

此時能夠發現兩份文檔的分值是同樣的。可是咱們指望的是同時匹配了title字段和body字段的文檔可以擁有更高的排名。
注意:dis_max查詢只是簡單的使用最佳匹配查詢子句獲得的相關度分數。

解決方法:

要想獲得咱們指望的結果,此時能夠經過指定tie_breaker參數:
查詢以下:

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

輸出結果:

{
  "took" : 1,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 2,
      "relation" : "eq"
    },
    "max_score" : 0.87613803,
    "hits" : [
      {
        "_index" : "test_index",
        "_type" : "_doc",
        "_id" : "2",
        "_score" : 0.87613803,
        "_source" : {
          "title" : "Keeping pets healthy",
          "body" : "My quick brown fox eats rabbits on a regular basis."
        }
      },
      {
        "_index" : "test_index",
        "_type" : "_doc",
        "_id" : "1",
        "_score" : 0.6931472,
        "_source" : {
          "title" : "Quick brown rabbits",
          "body" : "Brown rabbits are commonly seen."
        }
      }
    ]
  }
}

此時就是咱們指望的結果了,文檔2的分數比文檔1的要高了
tie_breaker參數會讓dis_max查詢的行爲更像是dis_max和bool的一種折中。它會經過下面的方式改變分值計算過程:
(1)取得最佳匹配查詢子句的_score
(2)將其它每一個匹配的子句的分值乘以tie_breaker
(3)將以上獲得的分值進行累加並規範化
經過tie_breaker參數匹配的全部子句都會起做用,只不過最佳匹配子句的做用更大
注意:tie_breaker的取值範圍是0到1之間的浮點數,取0時即爲僅使用最佳匹配子句。取1則會將全部匹配的子句一視同仁。它的確切值須要根據你的數據和查詢進行調整,可是一個合理的值會靠近0,來確保不會壓倒dis_max查詢具備的最佳匹配性質。

三、multi_match查詢

multi_match查詢提供了一個簡便的方法用來對多個字段執行相同的查詢。
默認狀況下,該查詢以best_fields類型執行,它會爲每一個字段生成一個match查詢,而後將這些查詢包含在一個dis_max查詢中。下面的dis_max查詢:

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

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

GET /test_index/_search
{
  "query": {
      "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查詢中
針對multi_match,還可使用通配符匹配字段名,以及針對個別字段進行加權
通配符:

"fields": "*_title"

加權:

"fields": ["*_title", "chapter_title^2"] # 此時chapter_title字段的boost值爲2,而book_title和section_title字段的boost值爲默認的1
相關文章
相關標籤/搜索