11

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

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.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 和一个评级值(整数)并有这些的集合?

我用我的方法看到的另一个问题是猫鼬还不支持深度填充,所以我必须为它使用一个模块(https://github.com/JoshuaGross/mongoose-subpopulate),这在很大程度上是un - 测试或以不超过一层嵌套的不同方式存储它,因此我可以使用 .populate() 获取我的数据

任何反馈都表示赞赏,因为我是 noSQL 的新手,也许我过于复杂了。

4

2 回答 2

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);
  });
于 2012-10-21T05:58:58.127 回答
4

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

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

于 2012-10-21T07:40:46.410 回答