8

考虑一下我有一个网站,上面有很多文章,人们可以对他们喜欢的文章进行投票。

我希望能够查询到按票数排序的特定时间(最后一小时、最后一天、上周)内得票最多的文章。

与 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"
        },
            ]
     }
  • 一个单独的投票集合,包含个人投票的详细信息和对投票帖子的引用:
{
    "_id": "ObjectId(xxxx)",
    "postId": ObjectId(xxxx),
    "userId": ObjectId(xxxx),
    "votedate": "03/03/2012+1436"
}

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

我倾向于第二个,因为我认为查询按投票分组的投票计数会更容易,但我不确定它的表现如何。这就是你在关系数据库中的做法,但它似乎不是很有文档记录——但我不确定它是否有问题,是吗?

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

您将如何实现此架构?

4

2 回答 2

10

跟踪总体投票计数的常用方法是将投票数保留在 post 文档中,并在将新值推送到投票数组时自动更新它。

由于它是一次更新,因此可以保证计数将与数组中的元素数匹配。

如果聚合的数量是固定的并且站点非常繁忙,您可以扩展此范例并增加额外的计数器,例如一个月、一天和小时的计数器,但这可能很快就会失控。因此,您可以使用新的聚合框架(在 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}
] );

这会将管道的输入限制为通过将投票日期与您计算的月份相匹配来获得投票的帖子,“展开”数组以获得每次投票的一个文档,然后执行等效的“分组依据”总结每个标题的所有投票(我假设标题是唯一的)。然后它按投票数降序排序并将输出限制为前十个。

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

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}
] );
于 2012-07-03T16:13:21.073 回答
0

您选择的架构很大程度上取决于您的用例。如果您期待大量投票/评论并希望独立于它们所属的帖子来处理它们,您可以将它们保存在一个单独的集合中,将 postID 作为 'foriegn key'..但是,如果您想在加载特定帖子时加载所有选票,并且如果没有包含它们的帖子,选票本身没有任何意义,那么请进行嵌入(在您的情况下,第一个) 方法。

于 2012-07-03T14:27:34.590 回答