我应该如何使用node.js在mongoose / mongodb中存储喜欢/不喜欢和评级

时间:2012-10-21 03:12:16

标签: node.js mongodb mongoose

我有一个项目和用户的架构。我想允许用户喜欢或不喜欢某个项目,我还希望在项目和用户上存储评级关联。

var UserSchema = new Schema({
    username    : { type: String, required: true, index: { unique: true }
   , likes : [{ type: Schema.ObjectId, ref: 'Item'}]
   , dislikes : [{ type: Schema.ObjectId, ref: 'Item'}]
   , ratings: [???]
});

var ItemSchema = new Schema({
    name: { type: String},
   , likes : [{ type: Schema.ObjectId, ref: 'User'}]
   , dislikes : [{ type: Schema.ObjectId, ref: 'User'}]
   , ratings: [???]
});

用户存储项目ref,并且该项目存储用户对于喜欢/不喜欢的参考。我不知道如何将评级作为属性存储,因为我想要用户和他们评价项目的价值。

item.ratings.forEach(function(rating){
  <%= rating.user.username %> gave <%= item.name %> a <%= rating.value %>.
});

我还想获得用户评级的项目列表以及评级值:

user.ratings.forEach(function(rating){
  <%= user.username %> has rated <%= rating.item.name %> and gave it a <%= rating.value %>
});

我的“评分”架构应该是什么样的?是否可以存储两个值?用户对象ID和评级值(整数)并具有这些的集合?

我用我的方法看到的另一个问题是mongoose还不支持深度填充,所以我必须使用一个模块(https://github.com/JoshuaGross/mongoose-subpopulate),很大程度上是未测试或以不同的方式存储它,不会有多个嵌套级别,所以我可以用.populate()

获取我的数据

感谢任何反馈,因为我是noSQL的新手,也许我对此过于复杂。

2 个答案:

答案 0 :(得分:6)

我会像你说的那样做。我会使用评级架构:

var RatingSchema = new Schema({
   , _user : { type: ObjectId, ref: 'User' }
   , _item : { type: ObjectId, ref: 'Item' }
   , value : Integer
});

如果您希望能够从User架构访问评级,则必须添加一个钩子,以便将任何已保存的RatingSchema添加到user.ratings。

var UserSchema = new Schema({
  /* ... */
  ratings: [{ type: Schema.ObjectId, ref: 'Rating'}]
});

RatingSchema.post('save', function () {
  // push this.id to this._user.ratings
  // save this._user
});

关于“我用我的方法看到的另一个问题是mongoose还不支持深度填充”,如果你不想使用mongoose-subpopulate hack,我建议你重构你的模型加载到静态方法。例如:

UserSchema.statics.findByIdAndDeepPopulate = function (i, cb) {
  UserSchema.findOne(id)
    .exec(function(err, user) {
      if (err || !user) return cb(new Error('User not found'));
      if (user._ratings.length == 0) return cb(null, user);

      // Load and populate every ratings in user._ratings
      for(var i = 0; i < user._ratings.length; i++) {
        function(i) {
          RatingSchema
            .populate('_item')
            .exec(err, function(rating) {
              user._ratings[i] = rating;
              if (i == user._ratings.length) return cb(null, user);
            });
        }(i);
      }
    });
}

编辑:现在我再考虑一下,为什么不简单地将评级存储为UserSchema中的嵌入文档?

var UserSchema = new Schema({
  /* ... */
  ratings: [ RatingSchema ]
});

然后你可以这样填充:

UserSchema.findOne(id)
  .populate('ratings._item')
  .exec(function(err, user) {
    if (err || !user) return next(new Error('User not found'));

    console.log(user.ratings);
  });

答案 1 :(得分:3)

您希望避免双重交叉链接,因为这意味着维护的次数是原来的两倍,并且由于Mongo不支持事务,因此当您预期双重连接时,它会创建半连接数据的可能性。话虽这么说,我重新考虑你的模式:

var like = {
    itemid: { type: Schema.ObjectId, ref: 'Item'}
   , rating: Number
};

var UserSchema = new Schema({
    username    : { type: String, required: true, index: { unique: true }
   , likes : [like]
   , dislikes : [like]
});
UserSchema.index({ 'likes.itemid': 1 });
UserSchema.index({ 'dislikes.itemid': 1 });
var User = db.model('User', UserSchema);

var ItemSchema = new Schema({
    name: String
});
var Item = db.model('Item', ItemSchema);

您可以通过以下方式添加喜欢或不喜欢的内容:

var piano = new Item({name: 'Mason & Hamlin Baby Grand Piano' });
var Joe = new User({ username: 'Joe_Smith' });
Joe.likes.push({ itemid: piano._id, rating: 10 });

当你想查找项目的喜欢时:

User.find({ 'likes.itemid': piano._id }, function(err, userLikes) {
    ...
});

如果对你没有任何感觉,那么你可以创建第三个集合......

var UserSchema = new Schema({
    username    : { type: String, required: true, index: { unique: true }
});
var User = db.model('User', UserSchema);

var ItemSchema = new Schema({
    name: String
});
var Item = db.model('Item', ItemSchema);

var LikesSchema = new Schema({
   , userid: { type: Schema.ObjectId, ref: 'User'}
   , itemid: { type: Schema.ObjectId, ref: 'Item'}
   , rating: Number
});
LikesSchema.index({ userid: 1, itemid: 1 }, { unique: true });

在这种情况下,最简单的做法是正面评级是一个类似而负面评级是不喜欢的。