iT邦幫忙

2023 iThome 鐵人賽

DAY 19
0
Software Development

30 天學習 Elasticsearch系列 第 19

Day 19 Elasticsearch - Mapping 以及基礎資料類型

  • 分享至 

  • xImage
  •  

該文章同步發佈於:我的部落格

也歡迎關注我的 Facebook 以及 Instagram 接收軟體相關的資訊!

在關聯式資料庫中,每一個資料表都會有相對應的 schema,而 Elasticsearch ( 以下簡稱 ES ) 也不例外,但在 ES 的世界中,這叫做 Mapping,而在 Analyzer 的介紹 中,有稍微的提到過 Dynamic Mapping 這個名詞,其實整個 ES 的 Mapping 方式就是兩種:

  1. Dynamic Mapping,交由 ES 替你決定這些欄位的資料類型是什麼。
  2. Explicit Mapping,我們自己定義每一個欄位的資料類型,就像關聯式資料庫的 schema 一樣。

Explicit Mapping

要如何做呢?很簡單,一樣透過 Kibana 的 Dev Tools 來嘗試看看:

PUT /users
{
  "mappings": {
    "properties": {
      "name": {
        "type": "text"
      },
      "age": {
        "type": "integer"
      },
      "address": {
        "type": "object",
        "properties": {
          "city": {
            "type": "text"
          },
          "zipcode": {
            "type": "keyword"
          }
        }
      }
    }
  }
}

在上面的示範中:

使用 PUT 的動詞來建立 users 的 index,這和我們一開始學習到的沒有什麼不同。

重點就在於傳送過去的 payload 之中,我們替每一個欄位都定義了它的資料類型,text integer 就不提了,都是很常見的資料類型。

但要記得 上一篇文章 中提到的 inverted indices,只有 text 的資料類型可以被全文搜尋!

其實手動建立 mapping 就是這麼簡單,接著我們來討論一下資料的類型。

資料類型

這邊有一個比較有趣的 keyword 資料類型,為什麼 zipcode 會選擇使用 keyword 呢?因為郵遞區號通常不需要全文被搜尋,所以在 Analyzer 的介紹 中也有說過,只有 text 會被分析,keyword 自然就不會經過清洗和拆解。

那 keyword 有什麼用?

  1. 不經過分析,所以特別適合用於像是 email, 主機編號, 狀態碼這些不需要被全文搜尋的值等等。
  2. 由於 keyword 的值不會被分解成多個 tokens,所以它特別適合排序和 aggregation 的操作。當這個值需要 aggregation 或 order 時,通常使用 keyword
  3. 大小寫敏感,因為 keyword 不經過分析過程,所以它們是大小寫敏感的。這意味著 APPLE 和 apple 會被視為兩個不同的值。
  4. 正規表示法搜尋,使用 keyword 的資料型態可以支持基於正規表示法的搜尋。

Object 資料類別

可以看到 address 本身的 type 是 object,這個很好懂,因為他是一個物件的型態,而在 ES 中,使用 object 本身就意味著普通的 JSON 物件,所以沒什麼好介紹的。

而 ES 本身還有提供另外兩種物件類型,分別是 flattened 以及 nested

Flattened

關於 flattened 這個在 ES 7.3 之後引入的 object 類型,在介紹它之前,需要重新科普一下 ES 本身是搭建於 Apache Lucene 這套全文搜尋的開源軟體之上的。

為什麼要提到 Apache Lucene?是因為 Apache Lucene 本身並不接受 JSON 物件的格式,所以 ES 其實一直都在幫我們轉換這些資料並儲存到 Apache Lucene 之中,這邊先有個概念就好。

好的,回到 flattened,它長什麼樣子呢?以剛剛建立的 mapping 為例,下面是我們想像中 JSON 物件的結構:

{
  "name": "Robert",
  "age": 50,
  "address": {
    "city": "Taipei",
    "zipcode": "123"
  }
}

但如果把 mapping 中 address 的資料類型換成 flattened 的話,會是這樣:

{
  "name": "Robert",
  "age": 50,
  "address.city": "Taipei",
  "address.zipcode": "123"
}

至於這樣的 mapping 要怎麼建立呢?如下面所示:

{
  "mappings": {
    "properties": {
      "name": {
        "type": "name"
      },
      "age": {
        "type": "integer"
      },
      "address": {
        "type": "flattened"
      }
    }
  }
}

其實在 ES 7.3 提供這個資料類型給使用者使用之前,ES 就一直在 『 扁平 』資料並儲存到 Apache Lucene 之中,就像我說的,Lucene 沒有支援 JSON 物件,所以 ES 必須做這件事。

所以這樣的內容:

{
  "name": "Robert",
  "age": 50,
  "address": {
    "city": "Taipei",
    "zipcode": "123"
  }
}

會被轉成下方的樣子,接著給 Lucene 讀取並儲存。

name: Robert
age: 50
address.city: Taipei
address.zipcode: 123

flattened 這個資料類型有什麼好處或是壞處嗎?

好處的部分:

  1. 動態欄位數量管理:使用 flattened 資料類型可以避免 mapping 中的欄位數量爆炸,當 index 具有大量動態產生的欄位時,使用標準的 object 或是 nested 資料類型可能會迅速地達到 index 的欄位數量限制。使用 flattened 可以有效地管理這些動態欄位,因為它將整個 JSON 物件存儲為單一欄位。

Index 的欄位預設限制是 1000

  1. 節省儲存空間flattened 資料類型把整個物件儲存為一個已知的資料結構,這可以減少儲存的消耗並且提高儲存的效率。

簡單來說,很像是關聯式資料庫的 json 欄位的用法,反正不知道要怎麼用,都先存進去再說。

壞處的部分:

  1. 不支援深層搜尋:由於所有欄位的值都合併到單一值裡面,因此不能執行針對特定嵌套深度的查詢。
  2. 無法區分相同 key 的值:如果物件有多層且有相同的 key,那麼在搜尋時將無法區分這些 key 的特定值。

下面的例子就可以投射到上面的兩個壞處之上:

{
  "a": {
    "b": "value1",
    "c": {
      "b": "value2"
    },
    "e": {
      "b": {
        "f": "value3"
      }
    }
  }
}

假設 a 本身就是以 flattened 的格式儲存,那它就會長成這樣:

a.b = value1
a.c.b = value2
a.e.b.f = value3

沒辦法深度搜尋 e 裡面的值,而且搜尋 b 也不知道是哪一個 key。

Nested

什麼時候會用到 nested 呢?通常用於處理那些包含物件陣列的 JSON,其中每個物件都需要被視為獨立、可以查詢的小組件。

以下面的結構舉例,當我們故意不宣告 skills 是 nested 的資料類型時,會發生什麼事呢?

{
  "name": "Robert",
  "skills": [
    { "name": "Java", "level": "expert" },
    { "name": "Python", "level": "intermediate" }
  ]
}

會被 ES 給 flatten 成:

{
  "name": "Robert",
  "skills.name": ["Java", "Python"],
  "skills.level": ["expert", "intermediate"]
}

這會發生什麼狀況?當我們嘗試搜尋 intermediate 又是使用 Java 的人時,這份資料也會被搜尋進來,但很顯然這是錯誤的結果。

所以才會需要 nested 的資料類型,告訴 ES 說:『 嘿!這個陣列裡面的東西很重要,不要壓扁了。』

至於使用 nested 的好處以及壞處呢?

基本上就是 flattened 的相反,更靈活的搜尋,更好的搜尋結果;反之,更多的儲存空間,效能的降低。

nested 的背後是怎麼運作的呢?其實就是一堆 隱藏的 document,假設這個使用者有 100 個 skills,那就會有 101 份 documents 被 index ( 算上本來的那份 )。

既然是 隱藏的 document 意味著,還是可以被搜尋到,但因為我們還沒有提到搜尋的環節,所以...有機會再說吧!

文章參考

ES 官方完整的資料類型文件

ES 官方解釋 Mapping 文件


上一篇
Day 18 Elasticsearch - 使用 Analyze API 以及 Inverted indices
下一篇
Day 20 Elasticsearch - 資料類型的強制轉換
系列文
30 天學習 Elasticsearch30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言