1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        深入理解ElasticSearch(六)排序與相關(guān)性

        共 6120字,需瀏覽 13分鐘

         ·

        2021-04-22 09:39

        排序與相關(guān)性

        默認(rèn)情況下,返回的結(jié)果是按照 相關(guān)性 進(jìn)行排序的——最相關(guān)的文檔排在最前。在本章的后面部分,我們會(huì)解釋 相關(guān)性 意味著什么以及它是如何計(jì)算的, 不過讓我們首先看看 sort 參數(shù)以及如何使用它。

        1、排序

        為了按照相關(guān)性來排序,需要將相關(guān)性表示為一個(gè)數(shù)值。在 Elasticsearch 中, 相關(guān)性得分 由一個(gè)浮點(diǎn)數(shù)進(jìn)行表示,并在搜索結(jié)果中通過 _score 參數(shù)返回, 默認(rèn)排序是 _score 降序。

        有時(shí),相關(guān)性評(píng)分對(duì)你來說并沒有意義。例如,下面的查詢返回所有 user_id 字段包含 1 的結(jié)果:

        1. GET /_search

        2. {

        3. "query" : {

        4. "bool" : {

        5. "filter" : {

        6. "term" : {

        7. "user_id" : 1

        8. }

        9. }

        10. }

        11. }

        12. }

        這里沒有一個(gè)有意義的分?jǐn)?shù):因?yàn)槲覀兪褂玫氖?filter (過濾),這表明我們只希望獲取匹配 user_id: 1 的文檔,并沒有試圖確定這些文檔的相關(guān)性。實(shí)際上文檔將按照隨機(jī)順序返回,并且每個(gè)文檔都會(huì)評(píng)為零分。

        1.1、按照字段的值排序

        在這個(gè)案例中,通過時(shí)間來對(duì) tweets 進(jìn)行排序是有意義的,最新的 tweets 排在最前。我們可以使用 sort 參數(shù)進(jìn)行實(shí)現(xiàn):

        1. GET /_search

        2. {

        3. "query" : {

        4. "bool" : {

        5. "filter" : { "term" : { "user_id" : 1 }}

        6. }

        7. },

        8. "sort": { "date": { "order": "desc" }}

        9. }

        你會(huì)注意到結(jié)果中的兩個(gè)不同點(diǎn):

        1. "hits" : {

        2. "total" : 6,

        3. "max_score" : null,

        4. "hits" : [ {

        5. "_index" : "us",

        6. "_type" : "tweet",

        7. "_id" : "14",

        8. "_score" : null,

        9. "_source" : {

        10. "date": "2014-09-24",

        11. ...

        12. },

        13. "sort" : [ 1411516800000 ]

        14. },

        15. ...

        16. }

        _score 不被計(jì)算, 因?yàn)樗]有用于排序。

        date 字段的值表示為自 epoch (January 1, 1970 00:00:00 UTC)以來的毫秒數(shù),通過 sort 字段的值進(jìn)行返回。

        首先我們?cè)诿總€(gè)結(jié)果中有一個(gè)新的名為 sort 的元素,它包含了我們用于排序的值。在這個(gè)案例中,我們按照 date 進(jìn)行排序,在內(nèi)部被索引為 自 epoch 以來的毫秒數(shù) 。long 類型數(shù) 1411516800000 等價(jià)于日期字符串 2014-09-24 00:00:00 UTC 。

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

        1.2、多級(jí)排序

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

        1. GET /_search

        2. {

        3. "query" : {

        4. "bool" : {

        5. "must": { "match": { "tweet": "manage text search" }},

        6. "filter" : { "term" : { "user_id" : 2 }}

        7. }

        8. },

        9. "sort": [

        10. { "date": { "order": "desc" }},

        11. { "_score": { "order": "desc" }}

        12. ]

        13. }

        排序條件的順序是很重要的。結(jié)果首先按第一個(gè)條件排序,僅當(dāng)結(jié)果集的第一個(gè) sort 值完全相同時(shí)才會(huì)按照第二個(gè)條件進(jìn)行排序,以此類推。

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

        1.3、字段多值的排序

        一種情形是字段有多個(gè)值的排序, 需要記住這些值并沒有固有的順序;一個(gè)多值的字段僅僅是多個(gè)值的包裝,這時(shí)應(yīng)該選擇哪個(gè)進(jìn)行排序呢?

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

        1. "sort": {

        2. "dates": {

        3. "order": "asc",

        4. "mode": "min"

        5. }

        6. }

        2、字符串排序與多字段

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

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

        為了以字符串字段進(jìn)行排序,這個(gè)字段應(yīng)僅包含一項(xiàng):整個(gè) not_analyzed 字符串。但是我們?nèi)孕枰?analyzed 字段,這樣才能以全文進(jìn)行查詢

        一個(gè)簡(jiǎn)單的方法是用兩種方式對(duì)同一個(gè)字符串進(jìn)行索引,這將在文檔中包括兩個(gè)字段:analyzed 用于搜索, not_analyzed 用于排序

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

        該參數(shù)允許你轉(zhuǎn)化一個(gè)簡(jiǎn)單的映射如:

        1. "tweet": {

        2. "type": "string",

        3. "analyzer": "english"

        4. }

        為一個(gè)多字段映射如:

        1. "tweet": {

        2. "type": "string",

        3. "analyzer": "english",

        4. "fields": {

        5. "raw": {

        6. "type": "string",

        7. "index": "not_analyzed"

        8. }

        9. }

        10. }

        tweet 主字段與之前的一樣: 是一個(gè) analyzed 全文字段。

        新的 tweet.raw 子字段是 not_analyzed.

        現(xiàn)在,至少只要我們重新索引了我們的數(shù)據(jù),使用 tweet 字段用于搜索,tweet.raw 字段用于排序:

        1. GET /_search

        2. {

        3. "query": {

        4. "match": {

        5. "tweet": "elasticsearch"

        6. }

        7. },

        8. "sort": "tweet.raw"

        9. }

        3、什么是相關(guān)性?

        我們?cè)?jīng)講過,默認(rèn)情況下,返回結(jié)果是按相關(guān)性倒序排列的。但是什么是相關(guān)性?相關(guān)性如何計(jì)算?

        每個(gè)文檔都有相關(guān)性評(píng)分,用一個(gè)正浮點(diǎn)數(shù)字段 _score 來表示 。_score 的評(píng)分越高,相關(guān)性越高。

        查詢語句會(huì)為每個(gè)文檔生成一個(gè) _score 字段。評(píng)分的計(jì)算方式取決于查詢類型 不同的查詢語句用于不同的目的:fuzzy 查詢會(huì)計(jì)算與關(guān)鍵詞的拼寫相似程度,terms 查詢會(huì)計(jì)算 找到的內(nèi)容與關(guān)鍵詞組成部分匹配的百分比,但是通常我們說的 relevance 是我們用來計(jì)算全文本字段的值相對(duì)于全文本檢索詞相似程度的算法。

        Elasticsearch 的相似度算法 被定義為檢索詞頻率/反向文檔頻率, TF/IDF ,包括以下內(nèi)容:

        • 檢索詞頻率 
          檢索詞在該字段出現(xiàn)的頻率?出現(xiàn)頻率越高,相關(guān)性也越高。字段中出現(xiàn)過 5 次要比只出現(xiàn)過 1 次的相關(guān)性高。

        • 反向文檔頻率 
          每個(gè)檢索詞在索引中出現(xiàn)的頻率?頻率越高,相關(guān)性越低。檢索詞出現(xiàn)在多數(shù)文檔中會(huì)比出現(xiàn)在少數(shù)文檔中的權(quán)重更低。

        • 字段長(zhǎng)度準(zhǔn)則 
          字段的長(zhǎng)度是多少?長(zhǎng)度越長(zhǎng),相關(guān)性越低。檢索詞出現(xiàn)在一個(gè)短的 title 要比同樣的詞出現(xiàn)在一個(gè)長(zhǎng)的 content 字段權(quán)重更大。

        單個(gè)查詢可以聯(lián)合使用 TF/IDF 和其他方式,比如短語查詢中檢索詞的距離或模糊查詢里的檢索詞相似度。

        相關(guān)性并不只是全文本檢索的專利。也適用于 yes|no 的子句,匹配的子句越多,相關(guān)性評(píng)分越高。

        如果多條查詢子句被合并為一條復(fù)合查詢語句 ,比如 bool 查詢,則每個(gè)查詢子句計(jì)算得出的評(píng)分會(huì)被合并到總的相關(guān)性評(píng)分中。

        3.1、理解評(píng)分標(biāo)準(zhǔn)

        當(dāng)調(diào)試一條復(fù)雜的查詢語句時(shí), 想要理解 _score 究竟是如何計(jì)算是比較困難的。Elasticsearch 在 每個(gè)查詢語句中都有一個(gè) explain 參數(shù),將 explain 設(shè)為 true 就可以得到更詳細(xì)的信息。

        1. GET /_search?explain

        2. {

        3. "query" : { "match" : { "tweet" : "honeymoon" }}

        4. }

        explain 參數(shù)可以讓返回結(jié)果添加一個(gè) _score 評(píng)分的得來依據(jù)。

        首先,我們看一下普通查詢返回的元數(shù)據(jù):

        1. {

        2. "_index" : "us",

        3. "_type" : "tweet",

        4. "_id" : "12",

        5. "_score" : 0.076713204,

        6. "_source" : { ... trimmed ... },

        這里加入了該文檔來自于哪個(gè)節(jié)點(diǎn)哪個(gè)分片上的信息,這對(duì)我們是比較有幫助的,因?yàn)樵~頻率和 文檔頻率是在每個(gè)分片中計(jì)算出來的,而不是每個(gè)索引中:

        1. "_shard" : 1,

        2. "_node" : "mzIVYCsqSWCG_M_ZffSs9Q",

        然后它提供了 _explanation 。每個(gè) 入口都包含一個(gè) description 、 value 、 details 字段,它分別告訴你計(jì)算的類型、計(jì)算結(jié)果和任何我們需要的計(jì)算細(xì)節(jié)。

        1. "_explanation": {

        2. "description": "weight(tweet:honeymoon in 0)

        3. [PerFieldSimilarity], result of:",

        4. "value": 0.076713204,

        5. "details": [

        6. {

        7. "description": "fieldWeight in 0, product of:",

        8. "value": 0.076713204,

        9. "details": [

        10. {

        11. "description": "tf(freq=1.0), with freq of:",

        12. "value": 1,

        13. "details": [

        14. {

        15. "description": "termFreq=1.0",

        16. "value": 1

        17. }

        18. ]

        19. },

        20. {

        21. "description": "idf(docFreq=1, maxDocs=1)",

        22. "value": 0.30685282

        23. },

        24. {

        25. "description": "fieldNorm(doc=0)",

        26. "value": 0.25,

        27. }

        28. ]

        29. }

        30. ]

        31. }

        第一部分是關(guān)于計(jì)算的總結(jié)。告訴了我們 honeymoon 在 tweet 字段中的檢索詞頻率/反向文檔頻率或 TF/IDF, (這里的文檔 0 是一個(gè)內(nèi)部的 ID,跟我們沒有關(guān)系,可以忽略。)

        然后它提供了權(quán)重是如何計(jì)算的細(xì)節(jié):

        檢索詞頻率:

        檢索詞 honeymoon 在這個(gè)文檔的 tweet 字段中的出現(xiàn)次數(shù)。

        反向文檔頻率:

        檢索詞 honeymoon 在索引上所有文檔的 tweet 字段中出現(xiàn)的次數(shù)。

        字段長(zhǎng)度準(zhǔn)則:

        在這個(gè)文檔中, tweet 字段內(nèi)容的長(zhǎng)度 – 內(nèi)容越長(zhǎng),值越小。

        復(fù)雜的查詢語句解釋也非常復(fù)雜,但是包含的內(nèi)容與上面例子大致相同。通過這段信息我們可以了解搜索結(jié)果是如何產(chǎn)生的。

        3.2、理解文檔是如何被匹配到的

        當(dāng) explain 選項(xiàng)加到某一文檔上時(shí), explain api 會(huì)幫助你理解為何這個(gè)文檔會(huì)被匹配,更重要的是,一個(gè)文檔為何沒有被匹配。

        請(qǐng)求路徑為 /index/type/id/_explain ,如下所示:

        1. GET /us/tweet/12/_explain

        2. {

        3. "query" : {

        4. "bool" : {

        5. "filter" : { "term" : { "user_id" : 2 }},

        6. "must" : { "match" : { "tweet" : "honeymoon" }}

        7. }

        8. }

        9. }

        不只是我們之前看到的充分解釋 ,我們現(xiàn)在有了一個(gè) description 元素,它將告訴我們:

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

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

        4、Doc Values 介紹

        本章的最后一個(gè)話題是關(guān)于 Elasticsearch 內(nèi)部的一些運(yùn)行情況。在這里我們先不介紹新的知識(shí)點(diǎn),所以我們應(yīng)該意識(shí)到,Doc Values 是我們需要反復(fù)提到的一個(gè)重要話題。

        當(dāng)你對(duì)一個(gè)字段進(jìn)行排序時(shí),Elasticsearch 需要訪問每個(gè)匹配到的文檔得到相關(guān)的值。倒排索引的檢索性能是非??斓模窃谧侄沃蹬判驎r(shí)卻不是理想的結(jié)構(gòu)。

        • 在搜索的時(shí)候,我們能通過搜索關(guān)鍵詞快速得到結(jié)果集。

        • 當(dāng)排序的時(shí)候,我們需要倒排索引里面某個(gè)字段值的集合。換句話說,我們需要 倒置 倒排索引。

        倒置 結(jié)構(gòu)在其他系統(tǒng)中經(jīng)常被稱作 列存儲(chǔ) 。實(shí)質(zhì)上,它將所有單字段的值存儲(chǔ)在單數(shù)據(jù)列中,這使得對(duì)其進(jìn)行操作是十分高效的,例如排序。

        在 Elasticsearch 中,doc values 就是一種列式存儲(chǔ)結(jié)構(gòu),默認(rèn)情況下每個(gè)字段的 doc values 都是激活的,doc values 是在索引時(shí)創(chuàng)建的,當(dāng)字段索引時(shí),Elasticsearch 為了能夠快速檢索,會(huì)把字段的值加入倒排索引中,同時(shí)它也會(huì)存儲(chǔ)該字段的 doc values。

        Elasticsearch 中的 doc vaules 常被應(yīng)用到以下場(chǎng)景:

        • 對(duì)一個(gè)字段進(jìn)行排序

        • 對(duì)一個(gè)字段進(jìn)行聚合

        • 某些過濾,比如地理位置過濾

        • 某些與字段相關(guān)的腳本計(jì)算

        因?yàn)槲臋n值被序列化到磁盤,我們可以依靠操作系統(tǒng)的幫助來快速訪問。當(dāng) working set 遠(yuǎn)小于節(jié)點(diǎn)的可用內(nèi)存,系統(tǒng)會(huì)自動(dòng)將所有的文檔值保存在內(nèi)存中,使得其讀寫十分高速;當(dāng)其遠(yuǎn)大于可用內(nèi)存,操作系統(tǒng)會(huì)自動(dòng)把 doc values 加載到系統(tǒng)的頁緩存中,從而避免了 jvm 堆內(nèi)存溢出異常。


        瀏覽 83
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評(píng)論
        圖片
        表情
        推薦
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            天天爽天天爽 | 日日摸夜夜添狠狠添 | 国产91探花在线观看 | 日韩毛片中文字幕 | 男人天堂网最新地址 | 凸凹日日摸日日碰夜夜爽 | 亚洲一区翔田千里无码 | 亚洲午夜福利 | 久久一级婬A片AAA毛片古代 | 国产下药迷倒白嫩丰满美女j9 |