假設我在設計一個有 使用者-角色 關系的系統
受長久以來的關系資料庫的經驗影響,一開始我的設計是這樣的.
const UserSchema = new Schema(
{
username: { type: String, require: true, unique: true },
password: { type: String, require: true },
nickName: String,
},
);
UserSchema.virtual('roles', {
ref: 'UserRole',
localField: '_id',
foreignField: 'user',
});
//UserSchema中定義了一個roles的虛拟鍵,
//收集UserRole集合中字段user與之相等的記錄,
//即得到所有的角色
const UserRoleSchema = new Schema(
{
user: { type: Schema.Types.ObjectId, ref: 'User' },
role: { type: Schema.Types.String, index: true, require: true },
roleData: { type: Schema.Types.ObjectId, refPath: 'role' },
createTime: { type: Date, default: new Date() },
status: Number,
}
);
//UserRole中的Role是一個字元串,
//其值為類似Admin,Teacher,Student這樣的具體類型
//這些具體角色,即意味着有單獨的集合存儲
//roleData即指向那些具體的集合中的記錄
//通過refPath指向role的值,一種動态的引用
const AdminSchema = new Schema(
{
user: { type: 'ObjectId', ref: 'User' },
adminProp:String
},
);
//Admin中就是存放與這類型角色有關的字段,
我的思路是盡量通過引用和populate方法來實作自動的資料引用更新. 比如我如果想移除一個使用者的角色,我隻需要删除UserRole集合中對應資料,在userModel.populate('roles')的時候就會得到更新後的結果.
另外,因為不同角色類型,字段不一樣,我又希望使用明确鍵定義的Schema(友善資料校驗).是以在UserRole中通過refPath做了動态引用.
雖然上邊的設計能正常工作,不過呢,确實産生了好幾個不同的集合.User,UserRole,Admin,Teacher,Student...
如果不考慮'強Schema',按照mongodb的設計理念來說,總感覺象我這裡的使用者與角色應該用嵌入的方式直接塞到User下邊
調整如下
const UserSchema = new Schema(
{
username: { type: String, require: true, unique: true },
password: { type: String, require: true },
nickName: String,
roles: { type: [UserRoleSchema], default: [] },
},
}
//直接定義成數組子文檔嵌套的方式
const UserRoleSchema = new Schema(
{
role: { type: Schema.Types.String, index: true, require: true },
roleData: Schema.Types.Mixed,
createTime: { type: Date, default: new Date() },
status: Number,
}
}
//UserRoleSchema這個子文檔定義裡,
//去掉了原本用于關聯User集合的user字段,
//roleData也從原來的引用到具體角色的類型變成了混合類,
//以友善直接插入各種類型角色資料
const AdminSchema = new Schema(
{
adminProp:String
},
{ _id: false },
);
//Admin定義中隻是注意省掉沒意義的_id
是以最後的一個使用者文檔變成
{
"_id" : ObjectId("5de87d8de6399413683b6c13"),
"createTime" : ISODate("2019-12-05T03:45:27.731Z"),
"username" : "user_test",
"password" : "pass_test",
"roles" : [{
"_id" : ObjectId("5de8a3b448dc3530c8ebc64e"),
"role" : "Admin",
"roleData" : {
"adminProp":"test",
},
"createTime" : ISODate("2019-12-05T06:29:07.465Z")
}]
}
對比前後兩種方式.前者用到了引用,後者用的是嵌套.象這種使用者-角色的話,用嵌套更适合.畢竟一個使用者的角色資料通常都不多.
這樣設計更符合MongoDB的MODEL設計思想.而且最後就隻需要一個USER集合,也沒有populate vituals.更簡潔了些
不知道大家怎麼看?
最後參考一下這個連結
https://coderwall.com/p/px3c7g/mongodb-schema-design-embedded-vs-references