在MongoDB中处理"用户-角色"关系

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

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

上一篇:.NET平台开源项目速览(3)小巧轻量级NoSQL文件数据库LiteDB


下一篇:exportfs命令、NFS客户端问题、FTP介绍、使用vsftpd搭建ftp