Elasticsearch: 權威指南(四)

排序與相關性

默認情況下,返回的結果是按照 相關性 進行排序的——最相關的文檔排在最前。
在 Elasticsearch 中, 相關性得分 由一個浮點數進行表示,并在搜索結果中通過 _score 參數返回, 默認排序是 _score 降序。
有時,相關性評分對你來說并沒有意義。例如,下面的查詢返回所有 user_id 字段包含 1 的結果:

GET /_search
{
    "query" : {
        "bool" : {
            "filter" : {
                "term" : {
                    "user_id" : 1
                }
            }
        }
    }
}

這里沒有一個有意義的分數:因為我們使用的是 filter (過濾),這表明我們只希望獲取匹配 user_id: 1 的文檔,并沒有試圖確定這些文檔的相關性。 實際上文檔將按照隨機順序返回,并且每個文檔都會評為零分。

按照字段的值排序

在這個案例中,通過時間來對 tweets 進行排序是有意義的,最新的 tweets 排在最前。 我們可以使用 sort 參數進行實現:

GET /_search
{
    "query" : {
        "bool" : {
            "filter" : { "term" : { "user_id" : 1 }}
        }
    },
    "sort": { "date": { "order": "desc" }}
}

你會注意到結果中的兩個不同點:

"hits" : {
    "total" :           6,
    "max_score" :       null, 
    "hits" : [ {
        "_index" :      "us",
        "_type" :       "tweet",
        "_id" :         "14",
        "_score" :      null, 
        "_source" :     {
             "date":    "2014-09-24",
             ...
        },
        "sort" :        [ 1411516800000 ] 
    },
    ...
}
  1. _score 不被計算, 因為它并沒有用于排序。
  2. date 字段的值表示為自 epoch (January 1, 1970 00:00:00 UTC)以來的毫秒數,通過 sort 字段的值進行返回。

首先我們在每個結果中有一個新的名為 sort 的元素,它包含了我們用于排序的值。 在這個案例中,我們按照 date 進行排序,在內部被索引為 自 epoch 以來的毫秒數 。 long 類型數 1411516800000 等價于日期字符串 2014-09-24 00:00:00 UTC 。

其次 _score 和 max_score 字段都是 null 。計算 _score 的花銷巨大,通常僅用于排序; 我們并不根據相關性排序,所以記錄 _score 是沒有意義的。如果無論如何你都要計算 _score , 你可以將 track_scores 參數設置為 true 。

多級排序

假定我們想要結合使用 date 和 _score 進行查詢,并且匹配的結果首先按照日期排序,然后按照相關性排序:

GET /_search
{
    "query" : {
        "bool" : {
            "must":   { "match": { "tweet": "manage text search" }},
            "filter" : { "term" : { "user_id" : 2 }}
        }
    },
    "sort": [
        { "date":   { "order": "desc" }},
        { "_score": { "order": "desc" }}
    ]
}

排序條件的順序是很重要的。結果首先按第一個條件排序,僅當結果集的第一個 sort 值完全相同時才會按照第二個條件進行排序,以此類推。

多級排序并不一定包含 _score 。你可以根據一些不同的字段進行排序,如地理距離或是腳本計算的特定值。

多值字段的排序

一種情形是字段有多個值的排序, 需要記住這些值并沒有固有的順序;一個多值的字段僅僅是多個值的包裝,這時應該選擇哪個進行排序呢?

對于數字或日期,你可以將多值字段減為單值,這可以通過使用 min 、 max 、 avg 或是 sum 排序模式 。 例如你可以按照每個 date 字段中的最早日期進行排序,通過以下方法:

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

字符串排序與多字段

被解析的字符串字段也是多值字段, 但是很少會按照你想要的方式進行排序。如果你想分析一個字符串,如 fine old art , 這包含 3 項。我們很可能想要按第一項的字母排序,然后按第二項的字母排序,諸如此類,但是 Elasticsearch 在排序過程中沒有這樣的信息。

你可以使用 min 和 max 排序模式(默認是 min ),但是這會導致排序以 art 或是 old ,任何一個都不是所希望的。

為了以字符串字段進行排序,這個字段應僅包含一項: 整個 not_analyzed 字符串。 但是我們仍需要 analyzed 字段,這樣才能以全文進行查詢

一個簡單的方法是用兩種方式對同一個字符串進行索引,這將在文檔中包括兩個字段: analyzed 用于搜索, not_analyzed 用于排序

但是保存相同的字符串兩次在 _source 字段是浪費空間的。 我們真正想要做的是傳遞一個 單字段 但是卻用兩種方式索引它。所有的 _core_field 類型 (strings, numbers, Booleans, dates) 接收一個 fields 參數

該參數允許你轉化一個簡單的映射如:

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

為一個多字段映射如:

"tweet": { 
    "type":     "string",
    "analyzer": "english",
    "fields": {
        "raw": { 
            "type":  "string",
            "index": "not_analyzed"
        }
    }
}

現在,至少只要我們重新索引了我們的數據,使用 tweet 字段用于搜索,tweet.raw 字段用于排序:

GET /_search
{
    "query": {
        "match": {
            "tweet": "elasticsearch"
        }
    },
    "sort": "tweet.raw"
}

執行分布式檢索

查詢滿足條件的前N項,在協調服務器進行判定需要取回哪些數據,并從分片獲取并返回。

Bouncing Results

想象一下有兩個文檔有同樣值的時間戳字段,搜索結果用 timestamp 字段來排序。 由于搜索請求是在所有有效的分片副本間輪詢的,那就有可能發生主分片處理請求時,這兩個文檔是一種順序, 而副本分片處理請求時又是另一種順序。
這就是所謂的 bouncing results 問題: 每次用戶刷新頁面,搜索結果表現是不同的順序。 讓同一個用戶始終使用同一個分片,這樣可以避免這種問題, 可以設置 preference 參數為一個特定的任意值比如用戶會話ID來解決。

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。