在MongoDB中按日期分组

时间:2012-10-10 07:50:25

标签: node.js mongodb group-by mapreduce mongoose

我正在AppFog上运行博客风格的Web应用程序(来自Nodester)。 它是用NodeJS + Express编写的,并使用Mongoose框架持久化到MongoDB。

MongoDB是1.8版本,我不知道AppFog是否会将其升级到2.2。

为什么这个介绍?好吧,现在我的“帖子”显示在一个基本的“分页”可视化中,我的意思是它们只是从mongo中获取,按日期降序排序,一次一页。这是一个片段:

Post
                .find({pubblicato:true})
                .populate("commenti")
                .sort("-dataInserimento")
                .skip(offset)
                .limit(archivePageSize)
                .exec(function(err,docs) {
                    var result = {};
                    result.postsArray = (!err) ? docs : [];
                    result.currentPage = currentPage;
                    result.pages = howManyPages;
                    cb(null, result);
            });

现在,我的目标是GROUP BY'dataInserimento'并显示像“日记”这样的帖子,我的意思是:

第1页=> 2012/10/08:我发布了3个帖子

第2页=> 2012/10/10:我展示了2个帖子(2012/10/09没有帖子,所以我不允许白页)

第3页=> 2012/10/11:35个帖子等......

我的想法是首先获得所有日期的分组列表(并且可能计算每天的帖子),然后构建页面链接,当访问页面(日期)时,查询如上,添加日期作为参数。

  • 聚合框架对此非常完美,但我无法掌握那个版本的Mongo,现在

  • 以某种方式使用.group(),但它在分片环境中不起作用的想法并不让我兴奋! : - (

  • 写一个MAP-REDUCE!我认为这是正确的方法,但我无法想象应该如何编写map()和reduce()。

请帮我一个小例子吗?

由于

编辑

peshkira的答案是正确的,然而,我不知道我是否真的需要。

我的意思是,我会有/ archive / 2012/10/01,/ archive / 2012/09/20等网址。

在每个页面中,足以获得查询帖子的日期。但是我必须显示“NEXT”或“PREV”链接,所以我需要知道包含帖子的下一天或前一天是什么,如果有的话。也许我可以只查询日期大于或小于当前日期的帖子,并获得第一个日期?

1 个答案:

答案 0 :(得分:1)

假设你有类似的东西:

{
"author" : "john doe",
"title" : "Post 1",
"article" : "test",
"created" : ISODate("2012-02-17T00:00:00Z")
}
{
"author" : "john doe",
"title" : "Post 2",
"article" : "foo",
"created" : ISODate("2012-02-17T00:00:00Z")
}
{
"author" : "john doe",
"title" : "Post 3",
"article" : "bar",
"created" : ISODate("2012-02-18T00:00:00Z")
}
{
"author" : "john doe",
"title" : "Post 4",
"article" : "foo bar",
"created" : ISODate("2012-02-20T00:00:00Z")
}
{
"author" : "john doe",
"title" : "Post 5",
"article" : "lol cat",
"created" : ISODate("2012-02-20T00:00:00Z")
}

然后您可以使用map reduce,如下所示:

<强>地图

它只是将日期作为键和帖子标题发出。您可以将标题更改为_id,这可能对您更有用。如果您存储日期的时间,您只想使用日期(没有时间)作为键,否则mongo将按日期时间而不仅是日期进行分组。在我的测试用例中,所有帖子的时间都是00:00:00,所以没关系。

function map() {
  emit(this.created, this.title);
}

<强>减少

它只执行任何操作,然后将键的所有值都推送到数组,然后将数组包装在结果对象中,因为mongo不允许数组是reduce函数的结果。

function reduce(key, values) {
  var array = [];
  var res = {posts:array};
  values.forEach(function (v) {res.posts.push(v);});
  return res;
}

<强>执行

使用db.runCommand({mapreduce: "posts", map: map, reduce: reduce, out: {inline: 1}})将输出以下结果:

{
"results" : [
    {
        "_id" : ISODate("2012-02-17T00:00:00Z"),
        "value" : {
            "posts" : [
                "Post 2",
                "Post 1"
            ]
        }
    },
    {
        "_id" : ISODate("2012-02-18T00:00:00Z"),
        "value" : "Post 3"
    },
    {
        "_id" : ISODate("2012-02-20T00:00:00Z"),
        "value" : {
            "posts" : [
                "Post 5",
                "Post 4"
            ]
        }
    }
],
...
}

我希望这会有所帮助