Elasticsearch 中的類型 type 和映射 mapping

Neil Zhu,簡書ID Not_GOD,University AI 創(chuàng)始人 & Chief Scientist,致力于推進世界人工智能化進程。制定并實施 UAI 中長期增長戰(zhàn)略和目標,帶領(lǐng)團隊快速成長為人工智能領(lǐng)域最專業(yè)的力量。
作為行業(yè)領(lǐng)導者,他和UAI一起在2014年創(chuàng)建了TASA(中國最早的人工智能社團), DL Center(深度學習知識中心全球價值網(wǎng)絡),AI growth(行業(yè)智庫培訓)等,為中國的人工智能人才建設輸送了大量的血液和養(yǎng)分。此外,他還參與或者舉辦過各類國際性的人工智能峰會和活動,產(chǎn)生了巨大的影響力,書寫了60萬字的人工智能精品技術(shù)內(nèi)容,生產(chǎn)翻譯了全球第一本深度學習入門書《神經(jīng)網(wǎng)絡與深度學習》,生產(chǎn)的內(nèi)容被大量的專業(yè)垂直公眾號和媒體轉(zhuǎn)載與連載。曾經(jīng)受邀為國內(nèi)頂尖大學制定人工智能學習規(guī)劃和教授人工智能前沿課程,均受學生和老師好評。

類型表示類似的文檔的類。類型包含一個名字——例如 user 或者 blogpost —— 和一個 mapping。映射,就像數(shù)據(jù)庫的模式一樣,描述了對應類型的文檔可能擁有的字段或者屬性,每個字段的數(shù)據(jù)類型——如 string integer 或者 date —— 還有這些字段會被 Lucene 如何索引和存儲。

what is a document? 中,我們說類型就像關(guān)系型數(shù)據(jù)庫中的表。盡管這樣的類比在開始熟悉 elasticsearch 時便于理解,但是我們這里還是更加細化對類型究竟是什么以及在 Lucene 之上如何實現(xiàn)的認知。

Lucene 如何看待文檔

Lucene 中的文檔包含 字段-值 對的簡單列表。字段必須有至少一個值,但是任何的字段都可以包含多個值。類似地,單個字符串值可能會被分析過程轉(zhuǎn)換成多個值。Lucene 并不管這些值是字符串或者數(shù)字或者日期——所有值都會被看做是 opaque bytes

當我們在 Lucene 中索引文檔時,每個字段的值被加入到關(guān)聯(lián)字段的倒排索引中??蛇x擇的是,原始值可能也會不作變化就存儲起來,這樣以后就可以對這些信息進行檢索了。

類型如何實現(xiàn)

Elasticsearch 類型在這個簡單的基礎(chǔ)上實現(xiàn)的。索引可能有多個類型,每個有自己的映射,不同類型的文檔可以存放在同一個索引中。

因為 Lucene 并沒有文檔類型的概念,每個文檔的類型名會使用一個稱為 _type 的元數(shù)據(jù)字段進行存放。當我們搜索特定類型的文檔時,Elasticsearch 可以輕易地在 _type 字段上使用過濾器限制在該類型文檔上進行檢索。

Lucene 同樣也沒有映射的概念。映射是 Elasticsearch 用來映射復雜的 JSON 文檔到簡單的 Lucene 期待接受的扁平文檔的層。

例如,在 user 類型中的 name 的映射可能聲明這個字段是 string 字段,并且他的值在索引到倒排索引 name 中之前必須使用 whitespace 分析器進行分析:

"name": {
    "type":     "string",
    "analyzer": "whitespace"
}

避免類型的出錯

不同類型的文檔可以被加入同樣的索引中也帶來了一些意料之外的復雜性。

假設我們在索引中有兩個類型:blog_en 對英文博客,blog_es 對西班牙文博客。兩個類型都有一個 title 字段,但是一個采用的是 english 分析器,另一個則是 spanish 分析器。

下面的查詢就會產(chǎn)生一個問題:

GET /_search
{
    "query": {
        "match": {
            "title": "The quick brown fox"
        }
    }
}

我們在兩個類型中搜索 title 字段。查詢字符串需要被分析,但是使用哪一個分析器呢,english 還是 spanish?它會使用首先發(fā)現(xiàn)的 title 所使用的分析器,所以這對于有些文檔就是正確的,而對其他一些文檔就是錯誤的。

我們可以避免這個問題,通過對字段采取不同的命名——例如,title_entitle_es ——或者顯式地包含類型的名字在字段名字中,分開查詢每個字段:

GET /_search
{
    "query": {
        "multi_match": { 
            "query":    "The quick brown fox",
            "fields": [ "blog_en.title", "blog_es.title" ]
        }
    }
}
  • multi_match 查詢執(zhí)行一個 match 查詢在多個字段上,并對結(jié)果進行合并

我們新的查詢就會使用 english 分析器在字段 blog_en.title 上,而使用 spanish 分析器在字段 blog_es.title 上,合并的結(jié)果會得到一個總體的相關(guān)分數(shù)。

這個解決方案可以在兩個字段都有相同的數(shù)據(jù)類型的時候起作用,但是如何你索引下面兩個文檔到同一個索引中時:

  • 類型:user
{ "login": "john_smith" }
  • 類型:event
{ "login": "2014-06-01" }

Lucene 不管一個字段包含字符串另一個字段包含一個日期。它很樂意從兩個字段索引字節(jié)值。

然而,如果我們試著去按照 event.login 字段排序,Elasticsearch 需要將 login 字段的值載入到內(nèi)存中。正如我們在 Fielddata 所說,他會載入在這個索引下的所有文檔而不顧其類型。

所以,他會嘗試或者按照字符串或者日期來載入這些值,取決于它首先看到的 login 字段。這個會得到無法預期的結(jié)果或者出錯。

為了確保你不會出現(xiàn)這樣的沖突,建議確保在一個索引中的每個類型的擁有同樣名字的這些字段按照同樣的映射方式進行配置。


什么是文檔?

在大多數(shù)應用中的大多數(shù)實體或者對象可以被序列化為一個 JSON 對象,包含 key 和 value。key 就是字段或者屬性的名稱,value 可以是一個字符串、數(shù)字、布爾值、另一個對象、值的數(shù)組或者其他特定類型的(如表示日期的字符串或者表示位置的對象):

{
    "name":         "John Smith",
    "age":          42,
    "confirmed":    true,
    "join_date":    "2014-06-01",
    "home": {
        "lat":      51.5,
        "lon":      0.1
    },
    "accounts": [
        {
            "type": "facebook",
            "id":   "johnsmith"
        },
        {
            "type": "twitter",
            "id":   "johnsmith"
        }
    ]
}

我們互換地使用對象或者文檔,這兩者是一個東西。然而,還是有個差異。對象就是一個 JSON 對象——類似于我們熟知的 hash、hashmap、dictionary 或者 associative array。對象可能包含其他的對象。在 Elasticsearch 中,術(shù)語 文檔 有一個特定的含義——文檔代表最頂層、根對象,序列化成 JSON 并在 Elasticsearch 中以唯一個 ID 進行存儲。


字段數(shù)據(jù)

關(guān)于 Elasticsearch 內(nèi)部的視角。盡管我們沒有展示任何新技術(shù),字段數(shù)據(jù)是一個我們會反復提到的重要的話題,這也是你需要注意的事項。

當你對一個字段進行排序時,Elasticsearch 需要獲得每個已經(jīng)匹配的文檔相應字段的值。倒排索引,在搜索的時候的表現(xiàn)很好,但是在進行排序并非理想的選擇:

  • 搜索的時候,我們需要將一個 term 映射到文檔的列表
  • 排序的時候,我們需要將一個 文檔映射到其 term上,換言之,我們需要“翻轉(zhuǎn)”倒排索引。

為了讓排序更加高效,Elasticsearch 將那個想要進行排序的字段所有的值都載入到了內(nèi)存中。這個就稱為字段數(shù)據(jù)

Elasticsearch 不會僅僅載入匹配了特定查詢的文檔的值。它會載入在索引中每個文檔的值,不管對應的 type

Elasticsearch 載入所有值到內(nèi)存中的原因就是從磁盤翻轉(zhuǎn)索引非常緩慢。即使你可能需要當前請求的部分文檔的值,你可能會需要對下一個請求獲取另外的文檔的值,所以將這些值一次性載入到內(nèi)存中也很合理了。

字段數(shù)據(jù)用在了 Elasticsearch 中的好幾個地方:

  • 對一個字段進行排序
  • 對字段進行聚合
  • 特定的過濾器(例如,地理位置過濾器)
  • 關(guān)聯(lián)的 script

顯然,這樣會消耗很多內(nèi)存,特別是較大字符串的字段,其值包含眾多唯一的值——就像郵件的正文。幸運的是,內(nèi)存不夠的問題可以通過水平擴展來解決,增加更多的節(jié)點。

現(xiàn)在,所有你需要知道的就是字段數(shù)據(jù)是什么,注意可能會出現(xiàn)的消耗內(nèi)存的問題。后面,我們會告訴你如何確定字段數(shù)據(jù)使用的內(nèi)存的數(shù)量,如果去限制其可用的內(nèi)存,如何去提前載入來提升用戶體驗過。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容