首先,“用户类中的字典”不是一个好主意。为什么?添加额外的速率对象需要将新项目推送到数组中,这意味着旧项目将被删除,这种插入称为“移动文档”。移动文档的速度很慢,而且 MongoDB 不太擅长重用空白空间,因此频繁移动文档可能会导致大量空数据文件(《MongoDB 权威指南》一书中的一些文字)。
那么正确的解决方案是什么:假设您有一个名为博客的集合,并且想要为您的博客文章实现评级解决方案,并另外跟踪每个基于用户的评级操作。
博客文档的架构如下:
{
_id : ....,
title: ....,
....
rateCount : 0,
rateValue : 0,
rateAverage: 0
}
您需要具有此文档架构的另一个集合(费率):
{
_id: ....,
userId: ....,
postId:....,
value: ..., //1 to 5
date:....
}
你需要为它定义一个合适的索引:
db.Rates.ensureIndex({userId : 1, postId : 1})// very useful. it will result in a much faster search operation in case you want to check if a user has rated the post previously
当用户想要评分时,首先需要检查用户是否对帖子进行了评分。假设用户是'user1'
,那么查询将是
var ratedBefore = db.Rates.find({userId : 'user1', postId : 'post1'}).count()
并基于ratedBefore
, if !ratedBefore
然后将新的评分文档插入到评分集合中并更新博客状态,否则用户不允许评分
if(!ratedBefore)
{
var postId = 'post1'; // this id sould be passed before by client driver
var userId = 'user1'; // this id sould be passed before by client driver
var rateValue = 1; // to 5
var rate =
{
userId: userId,
postId: postId,
value: rateValue,
date:new Date()
};
db.Rates.insert(rate);
db.Blog.update({"_id" : postId}, {$inc : {'rateCount' : 1, 'rateValue' : rateValue}});
}
那么会发生什么rateAverage
?
我强烈建议根据rateCount
and rateValue
在客户端,很容易更新rateAverage
with mongoquery
,但你不应该这样做。为什么?简单的答案是:对于客户来说,处理此类工作是一件非常容易的工作,并且对每个博客文档进行平均需要进行不必要的更新操作。
平均查询将计算为:
var blog = db.Blog.findOne({"_id" : "post1"});
var avg = blog.rateValue / blog.rateCount;
print(avg);
通过这种方法,您将获得 mongodb 的最大性能,并且您可以根据用户、帖子和日期跟踪每个费率。