假设我在设计一个有 用户-角色 关系的系统
受长久以来的关系数据库的经验影响,一开始我的设计是这样的.
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