在数组Mongodb

时间:2015-05-29 06:46:32

标签: mongodb mongodb-query aggregation-framework

我有很多像这样的文件:

    { 
    "_id" : ObjectId("54a94200aa76d3db6cd51977"), 
    "URL" : "http://...", 
    "Statistics" : [
        {
            "Date" : ISODate("2010-05-18T18:07:29.000+0000"), 
            "Clicks" : NumberInt(250), 
        }, 
        {
            "Date" : ISODate("2010-05-21T12:06:41.000+0000"), 
            "Clicks" : NumberInt(165), 
        }, 
        {
            "Date" : ISODate("2010-05-30T08:37:50.000+0000"), 
            "Clicks" : NumberInt(263), 
        } 
    ]
}

我的查询如下:

db.clicks.aggregate([
    { $match : 'Statistics.Date' : { $gte: new Date("2010-05-18T00:00:00.000Z"), $lte: new Date("2010-05-18T23:59:59.999Z") } },
    { $unwind' => '$Statistics' },
    { $group : { _id : { year : { $year : '$Statistics.Date' }, month : { $month : '$Statistics.Date' }, day : { $dayOfMonth : '$Statistics.Date' } }, Clicks : { $sum : '$Statistics.Clicks' } },
    { $sort : { _id : 1 } }
])

当我尝试总结特定日期的点击时,它会为我提供所有日期,而不是只有一个。我究竟做错了什么?提前谢谢。

编辑1: 由于该集合中有> 80.000个文档,因此我无法在$unwind之前执行$match。此外,这不是一个好主意,因为这会使查询慢于必要。 其中包含大量文档和数据是我必须使用$sum的原因。我上面提到的文件只是一个例子,只有结构与我的项目相同。

上面的查询让我回想起这样的事情:

{
    "_id" : [
        { 
        "year" : 2010,
        "month" : 5,
        "day" : 18
        }
    ],
    "Clicks" : 250
},
{
    "_id" : [
        { 
        "year" : 2010,
        "month" : 4,
        "day" : 21
        }
    ],
    "Clicks" : 165
},
{
    "_id" : [
        { 
        "year" : 2010,
        "month" : 5,
        "day" : 30
        }
    ],
    "Clicks" : 263
}

如果我不使用$group我也必须使用$limit,因为查询将超过16MB,否则:

db.clicks.aggregate([
        { $match : 'Statistics.Date' : { $gte: new Date("2010-05-18T00:00:00.000Z"), $lte: new Date("2010-05-18T23:59:59.999Z") } },
        { $unwind' : '$Statistics' },
        { $limit : 1 }
    ])

结果:

{ 
    "_id" : ObjectId("54a94200aa76d3db6cd51977"), 
    "URL" : "http://...", 
    "Statistics" : {
        "Date" : {
            "sec" : 1274166878,
            "usec" : 0
        },
        "Clicks" : 250
    }
}

由于性能原因,我必须使用$group而不使用它不是一种选择。

正如我在PHP中所做的那样,我提到的文档,查询和结果可能存在一些错误。希望这不会成为一个问题。我仍然无法弄清楚导致我的问题的原因。任何人都可以帮助我吗?

编辑2: 由于这似乎是一个无法解决的性能问题,因此我将从“统计数据”中迁移所有数据。数组到自己的集合中。感谢任何人的帮助。

2 个答案:

答案 0 :(得分:0)

您需要在$match之后之前运行$unwind两次:

db.clicks.aggregate([
    { $match : { 'Statistics.Date' : { 
        $gte: new ISODate("2010-05-18T00:00:00.000Z"), 
        $lte: new ISODate("2010-05-18T23:59:59.999Z") } } },
    { $unwind: '$Statistics' },
    { $match : { 'Statistics.Date' : {
        $gte: new ISODate("2010-05-18T00:00:00.000Z"), 
        $lte: new ISODate("2010-05-18T23:59:59.999Z") } } },
    { $group : { 
        _id : { year : { $year : '$Statistics.Date' }, 
                month : { $month : '$Statistics.Date' }, 
                day : { $dayOfMonth : '$Statistics.Date' } },
        Clicks : { $sum : '$Statistics.Clicks' } } },
    { $sort : { _id : 1 } }
])

第一个$match用于选择在正确的日期范围内至少有一个Statistics元素的文档。第二个用于过滤掉那些不在正确日期范围内的其他Statistics元素。

答案 1 :(得分:0)

事情可能已经解决,但是为那些从这个问题寻求帮助的人发布答案

{ $match : 'Statistics.Date' : { $gte: new Date("2010-05-18T00:00:00.000Z"), 
enter code here$lte: new Date("2010-05-18T23:59:59.999Z") } }

此匹配将过滤主文档。你想要的是过滤统计数组中的文档 现在,由$match过滤的文档将包含完整的Statistic数组。过滤后展开可能包含Statistic的子文档,其兄弟文档(同一数组中的文档)已通过$match条件。

  

注意:简单查找投影:   db.col_name.find({},{"Statistics.$":1})也会过滤数组   聚合中的$project无助于过滤文档数组。