【问题标题】:how should i store likes/dislikes and rating in mongoose/mongdb using node.js我应该如何使用 node.js 在 mongoose/mongodb 中存储喜欢/不喜欢和评分
【发布时间】:2012-10-11 06:27:17
【问题描述】:

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

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: [???]
});

用户存储一个item ref,item存储一个用户的喜欢/不喜欢的参考。不过,我不确定如何将评分存储为属性,因为我想要用户和他们对项目评分的值。

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 的新手,也许我过于复杂了。

【问题讨论】:

    标签: node.js mongodb mongoose


    【解决方案1】:

    您希望避免双重交叉链接,因为这意味着要维护两倍的数据,而且由于 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 });
    

    在这种情况下,最简单的规则是正面评价是喜欢,负面评价是不喜欢。

    【讨论】:

      【解决方案2】:

      我会照你说的做。我会使用评级模式:

      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);
        });
      

      【讨论】:

      • 我认为你还不能在 mongoose 中做到这一点:.populate('ratings._item') -- 这就是我所说的 deep populate()。
      • 是的,如果评级是嵌入文档而不是参考,您可以这样做。我正在我自己的代码中进行这个确切的调用。 (当然有不同的名字)
      • 如果我使用评级:[ RatingSchema ] -- 如何填充它?我将什么推入评级数组?我想要一个单独的评级数组——我不认为我想将评级嵌入和松散到我无法搜索的用户对象中。我最终会想做 Ratings.find({_item: item._id}, fn);
      • 您可以搜索并填充它。见mongoosejs.com/docs/subdocs.html。即:usr._ratings.push({..rating...}); usr.markModified('_ratings'); usr.save()。如果要搜索:User.find({_ratings._item: item._id});
      • 这将返回所有对该项目有评分的用户。如果性能是一个问题,也许你应该使用第一个解决方案。
      猜你喜欢
      • 2013-05-26
      • 2019-07-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-02-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多