ElasticSearch-排序

引用自ElaticSearch權威指南node

1、排序

相關性排序

默認狀況下,結果集會按照相關性進行排序 -- 相關性越高,排名越靠前。 這一章咱們會講述相關性是什麼以及它是如何計算的。 在此以前,咱們先看一下sort參數的使用方法。算法

排序方式

爲了使結果能夠按照相關性進行排序,咱們須要一個相關性的值。在ElasticSearch的查詢結果中, 相關性分值會用_score字段來給出一個浮點型的數值,因此默認狀況下,結果集以_score進行倒序排列。api

有時,即使如此,你仍是沒有一個有意義的相關性分值。好比,如下語句返回全部tweets中 user_id 是否 包含值 1app

複製代碼
GET /_search
{
    "query" : {
        "filtered" : {
            "filter" : {
                "term" : {
                    "user_id" : 1
                }
            }
        }
    }
}
複製代碼

過濾語句與 _score 沒有關係,可是有隱含的查詢條件 match_all 爲全部的文檔的 _score 設值爲 1。 也就至關於全部的文檔相關性是相同的。elasticsearch

字段值排序

下面例子中,對結果集按照時間排序,這也是最多見的情形,將最新的文檔排列靠前。 咱們使用 sort 參數進行排序:工具

複製代碼
GET /_search
{
    "query" : {
        "filtered" : {
            "filter" : { "term" : { "user_id" : 1 }}
        }
    },
    "sort": { "date": { "order": "desc" }}
}
複製代碼

你會發現這裏有兩個不一樣點:性能

複製代碼
"hits" : {
    "total" :           6,
    "max_score" :       null, <1>
    "hits" : [ {
        "_index" :      "us",
        "_type" :       "tweet",
        "_id" :         "14",
        "_score" :      null, <1>
        "_source" :     {
             "date":    "2014-09-24",
             ...
        },
        "sort" :        [ 1411516800000 ] <2>
    },
    ...
}
複製代碼

_score字段沒有通過計算,由於它沒有用做排序。date 字段被轉爲毫秒看成排序依據。spa

首先,在每一個結果中增長了一個 sort 字段,它所包含的值是用來排序的。調試

在這個例子當中 date字段在內部被轉爲毫秒,即長整型數字1411516800000等同於日期字符串 2014-09-24 00:00:00 UTC。

其次就是 _score 和 max_score 字段都爲 null。

計算 _score 是比較消耗性能的, 並且一般主要用做排序 -- 咱們不是用相關性進行排序的時候,就不須要統計其相關性。 若是你想強制計算其相關性,能夠設置track_scores爲 true。

默認排序 

你能夠只指定要排序的字段名稱:

"sort": "number_of_children"

字段值默認以順序排列,而 _score 默認以倒序排列。

多級排序

若是咱們想要合併一個查詢語句,而且展現全部匹配的結果集使用第一排序是date,第二排序是 _score:

複製代碼
GET /_search 
{
    "query": {
        "filtered": {
            "query": {
                "match": {
                    "tweet": "manage text search"
                }
            },
            "filter": {
                "term": {
                    "user_id": 2
                }
            }
        }
    },
    "sort": [
        {
            "date": {
                "order": "desc"
            }
        },
        {
            "_score": {
                "order": "desc"
            }
        }
    ]
}
複製代碼

 

排序是很重要的。結果集會先用第一排序字段來排序,當用用做第一字段排序的值相同的時候, 而後再用第二字段對第一排序值相同的文檔進行排序,以此類推。

多級排序不須要包含 _score 

你可使用幾個不一樣的字段,如位置距離或者自定義數值。

字符串參數排序

字符查詢也支持自定義排序,在查詢字符串使用sort參數就能夠:

GET /_search?sort=date:desc&sort=_score&q=search

爲多值字段排序

在爲一個字段的多個值進行排序的時候, 其實這些值原本是沒有固定的排序的-- 一個擁有多值的字段就是一個集合, 你準備以哪個做爲排序依據呢?

對於數字和日期,你能夠從多個值中取出一個來進行排序,你可使用min, max, avg 或 sum這些模式。

比說你能夠在 dates 字段中用最先的日期來進行排序: 

"sort": { "dates": { "order": "asc", "mode": "min" } } 

2、字符串排序

多值字段字符串排序

譯者注: 多值字段是指同一個字段在ES索引中能夠有多個含義,便可使用多個分析器(analyser)進行分詞與排序,也能夠不添加分析器,保留原值。

被分析器(analyser)處理過的字符稱爲analyzed field(譯者注:即已被分詞並排序的字段,全部寫入ES中的字段默認圴會被analyzed), analyzed字符串字段同時也是多值字段,在這些字段上排序每每得不到你想要的值。 好比你分析一個字符 "fine old art",它最終會獲得三個值。例如咱們想要按照第一個詞首字母排序, 若是第一個單詞相同的話,再用第二個詞的首字母排序,以此類推,惋惜 ElasticSearch 在進行排序時 是得不到這些信息的。

固然你可使用 min 和 max 模式來排(默認使用的是 min 模式)但它是依據art 或者 old排序, 而不是咱們所指望的那樣

爲了使一個string字段能夠進行排序,它必須只包含一個詞:即完整的not_analyzed字符串(譯者注:未經分析器分詞並排序的原字符串)。

固然咱們須要對字段進行全文本搜索的時候還必須使用被 analyzed 標記的字段。

在 _source 下相同的字符串上排序兩次會形成沒必要要的資源浪費。 而咱們想要的是同一個字段中同時包含這兩種索引方式,咱們只須要改變索引(index)的mapping便可。 方法是在全部核心字段類型上,使用通用參數 fields對mapping進行修改。 好比,咱們原有mapping以下:

"tweet": {
    "type":     "string",
    "analyzer": "english"
}

改變後的多值字段mapping以下:

複製代碼
"tweet": { <1>
    "type":     "string",
    "analyzer": "english",
    "fields": {
        "raw": { <2>
            "type":  "string",
            "index": "not_analyzed"
        }
    }
}
複製代碼

<1> tweet 字段用於全文本的 analyzed 索引方式不變。

<2> 新增的 tweet.raw 子字段索引方式是 not_analyzed

如今,在給數據重建索引後,咱們既可使用 tweet 字段進行全文本搜索,也能夠用tweet.raw字段進行排序:

複製代碼
GET /_search
{
    "query": {
        "match": {
            "tweet": "elasticsearch"
        }
    },
    "sort": "tweet.raw"
}
複製代碼

警告:

對 analyzed 字段進行強制排序會消耗大量內存。 詳情請查閱《字段類型簡介》相關內容。

3、 相關性

相關性簡介

咱們曾經講過,默認狀況下,返回結果是按相關性倒序排列的。 可是什麼是相關性? 相關性如何計算?

每一個文檔都有相關性評分,用一個相對的浮點數字段 _score 來表示 -- _score 的評分越高,相關性越高。

查詢語句會爲每一個文檔添加一個 _score 字段。評分的計算方式取決於不一樣的查詢類型 -- 不一樣的查詢語句用於不一樣的目的:fuzzy 查詢會計算與關鍵詞的拼寫類似程度,terms查詢會計算 找到的內容與關鍵詞組成部分匹配的百分比,可是通常意義上咱們說的全文本搜索是指計算內容與關鍵詞的相似程度。

ElasticSearch的類似度算法被定義爲 TF/IDF,即檢索詞頻率/反向文檔頻率,包括如下內容:

  • 檢索詞頻率::檢索詞在該字段出現的頻率?出現頻率越高,相關性也越高。 字段中出現過5次要比只出現過1次的相關性高。
  • 反向文檔頻率::每一個檢索詞在索引中出現的頻率?頻率越高,相關性越低。 檢索詞出如今多數文檔中會比出如今少數文檔中的權重更低, 即檢驗一個檢索詞在文檔中的廣泛重要性。
  • 字段長度準則::字段的長度是多少?長度越長,相關性越低。 檢索詞出如今一個短的 title 要比一樣的詞出如今一個長的 content 字段。

單個查詢可使用TF/IDF評分標準或其餘方式,好比短語查詢中檢索詞的距離或模糊查詢裏的檢索詞類似度。

相關性並不僅是全文本檢索的專利。也適用於yes|no的子句,匹配的子句越多,相關性評分越高。

若是多條查詢子句被合併爲一條複合查詢語句,好比 bool 查詢,則每一個查詢子句計算得出的評分會被合併到總的相關性評分中。

理解評分標準

當調試一條複雜的查詢語句時,想要理解相關性評分 _score 是比較困難的。ElasticSearch 在 每一個查詢語句中都有一個explain參數,將 explain 設爲 true 就能夠獲得更詳細的信息。

GET /_search?explain <1>
{
   "query"   : { "match" : { "tweet" : "honeymoon" }}
}

<1> explain 參數可讓返回結果添加一個 _score 評分的得來依據。


增長一個 explain 參數會爲每一個匹配到的文檔產生一大堆額外內容,可是花時間去理解它是頗有意義的。 若是如今看不明白也不要緊 -- 等你須要的時候再來回顧這一節就行。下面咱們來一點點的瞭解這塊知識點。


首先,咱們看一下普通查詢返回的元數據:

複製代碼
{
    "_index" :      "us",
    "_type" :       "tweet",
    "_id" :         "12",
    "_score" :      0.076713204,
    "_source" :     { ... trimmed ... },
}
複製代碼

這裏加入了該文檔來自於哪一個節點哪一個分片上的信息,這對咱們是比較有幫助的,由於詞頻率文檔頻率是在每一個分片中計算出來的,而不是每一個索引中:

    "_shard" :      1,
    "_node" :       "mzIVYCsqSWCG_M_ZffSs9Q",

而後返回值中的 _explanation 會包含在每個入口,告訴你採用了哪一種計算方式,並讓你知道計算的結果以及其餘詳情:

複製代碼
"_explanation": { <1>
   "description": "weight(tweet:honeymoon in 0)
                  [PerFieldSimilarity], result of:",
   "value":       0.076713204,
   "details": [
      {
         "description": "fieldWeight in 0, product of:",
         "value":       0.076713204,
         "details": [
            {  <2>
               "description": "tf(freq=1.0), with freq of:",
               "value":       1,
               "details": [
                  {
                     "description": "termFreq=1.0",
                     "value":       1
                  }
               ]
            },
            { <3>
               "description": "idf(docFreq=1, maxDocs=1)",
               "value":       0.30685282
            },
            { <4>
               "description": "fieldNorm(doc=0)",
               "value":        0.25,
            }
         ]
      }
   ]
}
複製代碼

<1> honeymoon 相關性評分計算的總結

<2> 檢索詞頻率

<3> 反向文檔頻率

<4> 字段長度準則

重要

  輸出 explain 結果代價是十分昂貴的,它只能用做調試工具 --千萬不要用於生產環境。

第一部分是關於計算的總結。告訴了咱們 "honeymoon" 在 tweet字段中的檢索詞頻率/反向文檔頻率或 TF/IDF, (這裏的文檔 0 是一個內部的ID,跟咱們沒有關係,能夠忽略。)

而後解釋了計算的權重是如何計算出來的:

檢索詞頻率:

檢索詞 `honeymoon` 在 `tweet` 字段中的出現次數。

反向文檔頻率:

檢索詞 `honeymoon` 在 `tweet` 字段在當前文檔出現次數與索引中其餘文檔的出現總數的比率。

字段長度準則:

文檔中 `tweet` 字段內容的長度 -- 內容越長,值越小。

複雜的查詢語句解釋也很是複雜,可是包含的內容與上面例子大體相同。 經過這段描述咱們能夠了解搜索結果是如何產生的。

提示

  JSON形式的explain描述是難以閱讀的 可是轉成 YAML 會好不少,只須要在參數中加上 format=yaml

Explain Api

文檔是如何被匹配到的

explain選項加到某一文檔上時,它會告訴你爲什麼這個文檔會被匹配,以及一個文檔爲什麼沒有被匹配。

請求路徑爲 /index/type/id/_explain, 以下所示:

複製代碼
GET /us/tweet/12/_explain
{
   "query" : {
      "filtered" : {
         "filter" : { "term" :  { "user_id" : 2           }},
         "query" :  { "match" : { "tweet" :   "honeymoon" }}
      }
   }
}
複製代碼

除了上面咱們看到的完整描述外,咱們還能夠看到這樣的描述:

"failure to match filter: cache(user_id:[2 TO 2])"

也就是說咱們的 user_id 過濾子句使該文檔不能匹配到。

4、數據字段

本章的目的在於介紹關於ElasticSearch內部的一些運行狀況。在這裏咱們先不介紹新的知識點, 數據字段是咱們要常常查閱的內容之一,但咱們使用的時候沒必要太在乎。

當你對一個字段進行排序時,ElasticSearch 須要進入每一個匹配到的文檔獲得相關的值。 倒排索引在用於搜索時是很是卓越的,但卻不是理想的排序結構

  • 當搜索的時候,咱們須要用檢索詞去遍歷全部的文檔。

  • 當排序的時候,咱們須要遍歷文檔中全部的值,咱們須要作反倒序排列操做。

爲了提升排序效率,ElasticSearch 會將全部字段的值加載到內存中,這就叫作"數據字段"。

重要: ElasticSearch將全部字段數據加載到內存中並非匹配到的那部分數據。 而是索引下全部文檔中的值,包括全部類型

將全部字段數據加載到內存中是由於從硬盤反向倒排索引是很是緩慢的。儘管你此次請求須要的是某些文檔中的部分數據, 但你下個請求卻須要另外的數據,因此將全部字段數據一次性加載到內存中是十分必要的。

ElasticSearch中的字段數據常被應用到如下場景:

  • 對一個字段進行排序
  • 對一個字段進行聚合
  • 某些過濾,好比地理位置過濾
  • 某些與字段相關的腳本計算

毫無疑問,這會消耗掉不少內存,尤爲是大量的字符串數據 -- string字段可能包含不少不一樣的值,好比郵件內容。 值得慶幸的是,內存不足是能夠經過橫向擴展解決的,咱們能夠增長更多的節點到集羣。

如今,你只須要知道字段數據是什麼,和何時內存不足就能夠了。 稍後咱們會講述字段數據到底消耗了多少內存,如何限制ElasticSearch可使用的內存,以及如何預加載字段數據以提升用戶體驗。

相關文章
相關標籤/搜索