在MongoDB中按查询聚合分组,按日期显示前5个$ count结果

时间:2015-07-20 21:37:31

标签: mongodb aggregation-framework

我是应用程序堆栈上MongoDb的新手,从LAMP(mysql后端)切换到MEAP(mongo后端),所以请原谅我对非关系查询的无知。

我正在尝试对MongoDb中的数据进行一些简单的分析,以便为特定关键字的用户呈现一段时间内的趋势。

我的(简化)集合对象结构如下所示:(在BSON中)

{
  "_id" : ObjectId("55aae6b21e76a5d02945ccbc"),
  "article" : {
    "category" : [{
        "title" : "Foods"
      }
    ]},
  "published" : new Date("7/17/2015 19:00:00")
}

想象一下,每天都会发表很多文章,每篇都有很多可能的“类别”,所以上面的对象是为了简洁而被截断的。

我想向用户展示每天发布的“前5名”类别,这样他们就可以了解趋势等等......

以下是我在mongo中进行聚合查询的尝试,该查询目前只是对所有类别进行求和,而不管发布日期如何:

{ "$unwind": "$article.category" },
{"$group":
    {
        "_id":  "$article.category.title" ,
        "count": { "$sum" : 1 }
    }
},
{ "$sort" : { "count" : -1, "_id": 1} },
{ $limit : 5 }

产生如下结果:

{ 0: {"_id": "Foods", "count": 50},
  1: {"_id": "Recipes", "count": 45},
   ...
}

问题:如何根据我的收藏中提供的“已发布”日期对结果进行分组?伪对象的结构类似于:

Date: 7/17/2015
    category: Foods, count: 25
    category: Recipes, count: 20
    ... continue top 5 results for that date
Date: 7/18/2015
    category: Foods, count: 25
    category: Recipes, count: 25
    ... continue top 5 results for that date
etc...

非常感谢您对这个mongo newb的任何帮助。我一直在考虑将两个小组的声明放在一起,并试图找出$ push或$ addtoset可以帮助我,但我无法绕过文档以使其适用于我的示例

2 个答案:

答案 0 :(得分:0)

您可能正在寻找的是Mongo Date Aggregation Operators。您需要做的是使用$year$dayOfYear(或$month$dayOfMonth)运算符按类别标题和日期对文档进行分组,以便数组中的每个元素都是按日期和标题排名。

与运营商一起,您需要在结果上运行map以将类别限制在前5名,因为我不知道在聚合调用中直接执行此操作。

我的策略是:

  1. 展开类别。
  2. 按类别和日期(日期和年份)组合在一起计算。
  3. 按类别(及其计数)按日期分组推送到数组。
  4. 在结果上使用map来区分前5个以外的类别。
  5. 这可能是您正在寻找的查询:

    db.articles.aggregate([{
      "$unwind": "$article.category"
    }, {
      "$group": {
        "_id":  {
          title: "$article.category.title",
          year: { $year: "$published" },
          day: { $dayOfYear: "$published" }
        },
        "count": { "$sum" : 1 }
      }
    }, {
      $group: {
        _id: {
          year: "$_id.year",
          day: "$_id.day"
        },
        categories: {
          $push: { title: "$_id.title", count: "$count" }
        }
      }
    }]).map( function (data) {
      // Using map here is the best way I could think to limit
      // the array size. Perhaps someone can do it all together
      // But this should do the trick.
      data.categories.sort( function (a, b) {
        return b.count - a.count;
      });
      data.categories = data.categories.slice(0, 5);
      return data;
    });
    

    我希望结果看起来像这样:

    [{
      _id: {
        year: 2015,
        day: 123
      },
      categories: [{
        title: "Food",
        count: 3
      }, {
        title: "Recipes",
        count: 2
      }]
    }, ...]
    

答案 1 :(得分:0)

虽然目前目前无法使用聚合框架拼接数组(此功能很快将在mongoDB version 3.1.4中提供) 你仍然可以通过followng管道非常接近你想要的东西:

[
{
    "$project": {
        "article": "$article",
        "yymmdd": {
            "$dateToString": {
                "date": "$published",
                "format": "%Y-%m-%d"
            }
        }
    }
},
{
    "$unwind": "$article.category"
},
{
    "$group": {
        "count": {
            "$sum": 1
        },
        "_id": {
            "yymmdd": "$yymmdd",
            "title": "$article.category.title"
        }
    }
},
{
    "$sort": {
        "_id.yymmdd": 1,
        "count": -1
    }
},
{
    "$group": {
        "item": {
            "$push": {
                "count": "$count",
                "item": "$_id.title"
            }
        },
        "_id": "$_id.yymmdd"
    }
}

使用某些数据会以以下形式为您提供结果集:

{u'item': [{u'count': 100, u'item': u'food'}, {u'count': 99, u'item': u'cinema'}, {u'count': 96, u'item': u'tennis'}, {u'count': 92, u'item': u'news'}, {u'count': 91, u'item': u'gossip'}, {u'count': 90, u'item': u'football'}, {u'count': 88, u'item': u'recipes'}, {u'count': 84, u'item': u'tv'}], u'_id': u'2015-05-31'}
{u'item': [{u'count': 96, u'item': u'gossip'}, {u'count': 93, u'item': u'news'}, {u'count': 92, u'item': u'food'}, {u'count': 91, u'item': u'football'}, {u'count': 87, u'item': u'tennis'}, {u'count': 84, u'item': u'recipes'}, {u'count': 84, u'item': u'cinema'}, {u'count': 82, u'item': u'tv'}], u'_id': u'2015-05-29'}
{u'item': [{u'count': 106, u'item': u'cinema'}, {u'count': 104, u'item': u'gossip'}, {u'count': 99, u'item': u'tv'}, {u'count': 98, u'item': u'news'}, {u'count': 96, u'item': u'football'}, {u'count': 94, u'item': u'food'}, {u'count': 93, u'item': u'tennis'}, {u'count': 90, u'item': u'recipes'}], u'_id': u'2015-05-25'}
{u'item': [{u'count': 85, u'item': u'football'}, {u'count': 85, u'item': u'gossip'}, {u'count': 81, u'item': u'cinema'}, {u'count': 80, u'item': u'tennis'}, {u'count': 78, u'item': u'news'}, {u'count': 74, u'item': u'recipes'}, {u'count': 70, u'item': u'food'}, {u'count': 67, u'item': u'tv'}], u'_id': u'2015-05-22'}

每天有一个项目数组,按标题出现次数排序。 然后在应用程序中,您可以将此数组拼接为n以获得前N个计数。 您可以查看重现它的步骤in this example (in python)

相关问题