DatabaseMongoDBData Modeling
MongoDB Schema 設計系列 (四):Attribute Pattern 實戰與優缺點剖析
作者:Simon Cho•
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_id 和 group_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" }
]
}

那資料庫就只要針對 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": "......"
}

這樣我們只需要在 measurement.type 與 measurement.value 上建立複合索引(Compound Index),就能支撐所有不同機台訊號的快速檢索!
缺點與權衡
這個其實是我自己的實務開發經驗,當我們在寫程式碼時:
- 屬性存取較不直觀:如果欄位直接存在物件的 property,在程式碼中會非常容易存取(例如:
post.user_id)。 - 需要額外的過濾操作:如果將屬性放在 array 內,在程式中讀取時,會需要額外寫 filter 邏輯才能撈出你想要的值。
