DatabaseMongoDBData Modeling

MongoDB Schema 設計系列 (四):Attribute Pattern 實戰與優缺點剖析

作者:Simon Cho
MongoDB Schema 設計系列 (四):Attribute Pattern 實戰與優缺點剖析

Attribute Pattern 是我最常用,也是我最喜歡使用的 pattern,它讓 data access layer 的 code 變得簡潔,並使建立 index 變得容易。


用例子示範 Attribute Pattern

1. 貼文的 Ownership (所有權歸屬)

在社交平台上,使用者能夠發貼文,也能夠在所屬的群組發貼文。因此,一篇貼文可能同時屬於某一位使用者,也屬於某一個群組。

如果我們像關係型資料庫一樣設計:

{
  "_id": ObjectId("xxxxxxxxxxxxxxxxxxxxxxxx"),
  "title": "MongoDB Data Modeling — Attribute Pattern",
  "content": "Attribute Pattern 是我最常用,也是我最喜歡使用的 pattern ......",
  "user_id": ObjectId("yyyyyyyyyyyyyyyyyyyyyyyy"),
  "group_id": ObjectId("zzzzzzzzzzzzzzzzzzzzzzzz")
}

如果我們想要加速查詢屬於某位使用者或某個群組的貼文,那就要分別對 user_idgroup_id 兩個欄位各別建立 index。

但如果我們套用 Attribute Pattern:

{
  "_id": ObjectId("xxxxxxxxxxxxxxxxxxxxxxxx"),
  "title": "MongoDB Data Modeling — Attribute Pattern",
  "content": "Attribute Pattern 是我最常用,也是我最喜歡使用的 pattern ......",
  "ownership": [
    { "type": "user", "_id": "yyyyyyyyyyyyyyyyyyyyyyyy" },
    { "type": "group", "_id": "zzzzzzzzzzzzzzzzzzzzzzzz" }
  ]
}

Post Ownership Attribute Pattern

那資料庫就只要針對 ownership 這個單一欄位建立多鍵索引(Multikey Index)。此外,未來如果有更多種的所有權類型(例如:專案專屬貼文、活動專屬貼文),我們可以直接擴充 ownership.type,而完全不必針對新欄位額外建立新的 index

在 Data Access Layer 的程式碼也不必針對特定欄位去寫不同的 query,查詢 function 可以直接高度複用:

function getPost(category: string, _id: ObjectId): Post {
  return this.model.findOne({ 'ownership.type': category, 'ownership._id': _id })
}

我最喜歡的部分是這個 pattern 在資料類型擴充時,程式碼可以改動得非常少。


2. 機器量測訊號

在工廠的產線上,有大量不同類型的機台產生不同的量測訊號。例如:A 機台專門量溫度與濕度,B 機台專門量電阻與透光度。如果我們這樣存:

{
  "machine": "x",
  "temperature": "62.3",
  "humidity": "31.1",
  "datetime": "......"
},
{
  "machine": "y",
  "resistance": "100",
  "transmittance": "60",
  "datetime": "......"
}

那就會遇到下列麻煩:

  • 必須針對眾多不同的感測器欄位建立 index 來加速查詢。
  • 程式碼中會出現大量的實體結構,且 Data Access Layer 的查詢實作會變得很雜亂。

套用 Attribute Pattern 後就能解決這些問題:

{
  "machine": "x",
  "measurement": [
    { "type": "temperature", "value": "62.3" },
    { "type": "humidity", "value": "31.1" }
  ],
  "datetime": "......"
},
{
  "machine": "y",
  "measurement": [
    { "type": "resistance", "value": "100" },
    { "type": "transmittance", "value": "60" }
  ],
  "datetime": "......"
}

Machine Measurements Attribute Pattern

這樣我們只需要在 measurement.typemeasurement.value 上建立複合索引(Compound Index),就能支撐所有不同機台訊號的快速檢索!


缺點與權衡

這個其實是我自己的實務開發經驗,當我們在寫程式碼時:

  1. 屬性存取較不直觀:如果欄位直接存在物件的 property,在程式碼中會非常容易存取(例如:post.user_id)。
  2. 需要額外的過濾操作:如果將屬性放在 array 內,在程式中讀取時,會需要額外寫 filter 邏輯才能撈出你想要的值。
← 返回所有故事