0

我正在用 express 和 mongoose/mongodb 构建一个 nodejs 应用程序。

为了管理用户角色(一个用户可能有 0-n 个角色),我决定实现一个用户模式和一个单独的角色模式,并像 DBRefs 一样将它们联系在一起,并使用猫鼬填充功能轻松地从一个到另一个。我选择这种结构是因为我认为这是回答以下问题的最佳方式:“给我一个具有 X 角色的用户列表”或“给我一个 Y 角色即将到期的用户列表”。

这些是我的角色和用户模式的精简版本:

RoleSchema = new Schema {
  _user: { type: Schema.ObjectId, ref: 'User' }
  type: String
  expiration: { type: Date, default : '0' }
}

UserSchema = new Schema {
  email: { type: String, index: { unique: true }}      
  roles : [{ type: Schema.ObjectId, ref: 'Role' }]
}

有了这个,我可以毫无问题地从我的快速脚本中创建/获取/填充用户和角色。当我想向用户添加角色时,我创建角色,保存它,然后将其推送到用户的角色数组,然后保存用户。所以,在 mongoDB 中发生的事情是每个模式都有自己的集合,它们通过 id 字段相互指向。

现在,我接下来要做的是在我的模式上实现布尔类型的方法来检查与角色相关的问题,这样我就可以做类似的事情

if(user.isActiveSubscriber())

我认为我可以通过向我的用户模式添加一个方法来完成此操作,如下所示:

UserSchema.method "isActiveSubscriber", ->
  result = false
  now = new Date()
  @roles.forEach (role) ->
    result = true  if role.type is "SUBSCRIBER" and role.expiration > now
 result

我的问题是角色的属性为空。我想这是有道理的,因为我的用户集合只有角色的 id,但实际属性存储在另一个集合中。

所以,这里有问题:

a) 有没有办法从我的用户模式方法中加载角色属性?我尝试在方法内调用填充,但收到用户对象不知道任何填充方法的错误。我还尝试从方法内部执行 Role.findById(),但也出现错误(尝试使用 Role 和 RoleSchema)

b) 如果没有办法……我应该简单地添加代码来签入我的脚本吗?我讨厌不得不将这种逻辑与应用程序逻辑/流混合。有更好的选择吗?

c) 将这些集合分成两部分是不是一个坏主意?我完全错过了 NoSQL 的意义吗?如果角色只是作为用户集合的一部分存储的嵌入文档数组会更好吗?

4

2 回答 2

1

让我回答你的问题:

a)您可以做的是在您的通话中加载角色。假设你做了

Role = mongoose.model('Role', RoleSchema)

你只需要运行

Role.where('_id').in(user.roles).run( (err, res) ->
    /* you have roles in res now */
)

然而,这是一个异步操作,需要将回调传递给您的方法,即

UserSchema.method "isActiveSubscriber", (callback) ->
    now = new Date()
    if @roles.length is 0
        return callback(false)
    if @roles[0].type
        /* roles are loaded,
           maybe there is some other fancy way to do the check */
        result = false
        @roles.forEach (role) ->
            if role.type is "SUBSCRIBER" and role.expiration > now
                result = true
        callback(result)
    else
        /* we have to load roles */
        Role.where('_id').in(@roles).run( (err, res) =>
            /* you have roles in res now */
            /* you may want to populate the field,
               note => sign in this function definition */
            @roles = res 
            result = false
            res.forEach (role) ->
                if role.type is "SUBSCRIBER" and role.expiration > now
                    result = true
            callback(result)
        )

现在对于用户user,您可以这样称呼它

user.isActiveSubscriber( (result) ->
    /* do something with result */
)

问题是该操作是异步的,它会强制在您的代码中嵌套额外的回调(如果您想检查 100 个用户的角色,这将很痛苦,您将需要一些异步调用处理库,如async)。因此,我建议您在加载用户并使用您向我们展示的代码时填充此字段。您可以为此添加静态方法(也许您甚至可以覆盖默认find方法?不过我不确定)。

b+c) 另一种选择是将角色作为字符串存储在您的集合中,并在应用程序中对可能的角色进行硬编码。这将更容易实现,但是添加/删除新角色将是 $%^&* 中的一个痛苦。您这样做的方式(即通过引用)很好,我认为您唯一需要做的就是在搜索用户时填充该字段,一切都会好起来的。

于 2012-05-11T14:35:08.103 回答
0

采用的方法在 mongoose 如何使用 populate 实现类似 DBRef 的行为的范围内。然而,有些事情似乎被遗漏了。不确定这些细节是否已经被注意并且为简洁起见未显示。将运行完整的步骤集:

RoleSchema = new Schema {
   type: String
   expiration: { type: Date, default : '0' }
}

UserSchema = new Schema {
   email: { type: String, index: { unique: true }}      
   _roles : [{ type: Schema.ObjectId, ref: 'Role' }] // array of pointers for roles
}

Role = mongo.model('Role', 'RoleSchema');
User = mongo.model('User', 'UserSchema');

var life_sub = new Role({ type: 'LIFE_SUBSCRIBER', .... });
var daily_sub = new Role({ type: 'DAILY_SUBSCRIBER', .... });
var monthly_sub = new Role({ type: 'MONTHLY_SUBSCRIBER', .... });

var jane = new User({email: 'jane@ab.com', _roles: [daily_sub, monthly_sub]});

根据需要添加用户。接下来要查找指定的用户和相应的角色集,请使用:

User
 .find({email:'jane@ab.com'})
 .populate('_roles[]')
 .run(function(err, user)

现在 user 是一个指针数组,对应于指定用户的角色。遍历数组并找到用户的每个角色和相应的到期日期。与 Date.now() 进行比较以得出特定用户的哪些角色已过期。

希望这可以帮助。

于 2012-05-11T15:29:23.043 回答