MongoDB架构设计 - 在帖子上投票

rea*_*ers 8 mongodb

假设我有一个像digg.com这样的网站我有一堆文章,人们可以投票他们喜欢的文章.

我想能够查询以获得在投票数量排序的特定时间(最后一小时,最后一天,上周)内投票最多的文章.

与MongoDB一样,有几种不同的方法可以实现这一点,但我不确定哪一种是正确的.

  • 一个帖子文档,其中包含一个投票数组 - 投票本身就是包含用户ID,用户名和投票日期的文档:
    {
    "_id": "ObjectId(xxxx)",
    "title": "Post Title",
    "postdate": "21/02/2012+1345",
    "summary": "Summary of Article",

    "Votes": [
        {
            "userid":ObjectId(xxxx),
            "username": "Joe Smith",
            "votedate": "03/03/2012+1436"
        },
            ]
     }
Run Code Online (Sandbox Code Playgroud)
  • 单独的投票收集,包含个人投票的详细信息和对投票的帖子的引用:
{
    "_id": "ObjectId(xxxx)",
    "postId": ObjectId(xxxx),
    "userId": ObjectId(xxxx),
    "votedate": "03/03/2012+1436"
}
Run Code Online (Sandbox Code Playgroud)

第一个更像是Documentey,但我不知道如何查询投票数组以获得过去24小时内投票最多的文件.

我倾向于第二个,因为我更容易查询按照投票分组的投票计数,但我不确定它的表现如何.这就是你在关系数据库中做到这一点的方法,但它似乎并不是很有文档 - 但我不确定它是否有问题,是吗?

或者我使用两者的组合?我也会实时,每页加载这种类型的聚合查询.或者我只是每分钟运行一次查询,并将结果存储在查询结果集合中?

你会如何实现这个架构?

Asy*_*sky 10

跟踪整体投票计数的常用方法是保留帖子文档中的投票数,并在将新值推送到投票数组时以原子方式更新投票数.

由于它是单个更新,因此可以保证计数与数组中的元素数量相匹配.

如果聚合的数量是固定的并且站点非常繁忙,您可以扩展此范例并增加额外的计数器,例如月,日和小时的计数器,但这可能会很快失控.因此,您可以使用新的聚合框架(2.1.2开发版中提供,将在2.2版本中生产.使用比Map/Reduce更简单,它将允许您非常简单地进行计算,尤其是您需要注意将您的投票日期存储为ISODate()类型.

本月顶级投票获取者聚合查询的典型管道可能如下所示:

today = new Date();
thisMonth = new Date(today.getFullYear(),today.getMonth());
thisMonthEnd = new Date(today.getFullYear(),today.getMonth()+1);

db.posts.aggregate( [
    {$match: { "Votes.votedate": {$gte:thisMonth, $lt:thisMonthEnd} } },
    {$unwind: "$Votes" },
    {$match: { "Votes.votedate": {$gte:thisMonth, $lt:thisMonthEnd} } },
    {$group: { _id: "$title", votes: {$sum:1} } },
    {$sort: {"votes": -1} },
    {$limit: 10}
] );
Run Code Online (Sandbox Code Playgroud)

这通过将投票日期与您计算的月份相匹配来限制对具有投票的帖子的输入,"展开"数组以获得每个投票一个文档,然后执行"group by"等效总结每个标题的所有投票(我假设标题是独一无二的).然后按投票数下降并将输出限制为前十.

您还可以按天(例如)汇总该月份的投票,以查看投票最活跃的日期:

db.posts.aggregate( [
    {$match: { "Votes.votedate": {$gte:thisMonth, $lt:thisMonthEnd} } },
    {$unwind: "$Votes" },
    {$match: { "Votes.votedate": {$gte:thisMonth, $lt:thisMonthEnd} } },
    {$project: { "day" : { "$dayOfMonth" : "$Votes.votedate" }  } },
    {$group: { _id: "$day", votes: {$sum:1} } },
    {$sort: {"votes": -1} },
    {$limit: 10}
] );
Run Code Online (Sandbox Code Playgroud)