MongoDb:如何在日期字段上进行聚合,分组和排序?

时间:2015-12-29 19:30:15

标签: mongodb mongodb-query aggregation-framework

在我的MongoDB人员集合中,我需要使用相同的“别名”属性值过滤人,保留第一个属性值,并保留所有人的空“别名”。

数据是这样的:

{ "_id" : "1", "flag" : true,  "name" : "Alice",    "alias" : null, "dateOfBirth": new ISODate('1995-12-27T00:00:00.000Z') },
{ "_id" : "2", "flag" : true,  "name" : "Bob",      "alias" : "4c", "dateOfBirth": new ISODate('1996-12-27T00:00:00.000Z') },
{ "_id" : "3", "flag" : true,  "name" : "Bobby",    "alias" : "4c", "dateOfBirth": new ISODate('1997-12-27T00:00:00.000Z') },
{ "_id" : "4", "flag" : true,  "name" : "Cristina", "alias" : null, "dateOfBirth": new ISODate('1998-12-27T00:00:00.000Z') },
{ "_id" : "5", "flag" : false, "name" : "Diego",    "alias" : null, "dateOfBirth": new ISODate('1999-12-27T00:00:00.000Z') },
{ "_id" : "6", "flag" : true,  "name" : "Zoe",      "alias" : "22", "dateOfBirth": new ISODate('2000-12-27T00:00:00.000Z') }

这是我的疑问:

db.people.aggregate([ 
    { '$match': { 'flag': true } }, 
    { '$project': {
        'name': 1,          
        'alias': { 
            '$cond': [
                { '$eq': [ '$alias', null ] }, 
                '$_id', 
                '$alias' 
            ]
        }
    }},
    { '$group': {
        '_id': '$alias',         
        'name':  { '$first': '$name' },          
        'id': { '$first': '$_id' }       
    }}, 
    { '$project': {
        'alias': {
            '$cond': [ 
                { '$eq': [ '$id', '$_id' ] }, 
                null, 
               '$_id' 
            ]
        }, 
        'name': 1,
        '_id': '$id'
    }}
])

返回:

{ "_id" : "6", "name" : "Zoe",      "alias" : "22" }
{ "_id" : "4", "name" : "Cristina", "alias" : null }
{ "_id" : "2", "name" : "Bob",      "alias" : "4c" }
{ "_id" : "1", "name" : "Alice",    "alias" : null }

到目前为止一切顺利。

更新:到目前为止,qestion与suggested duplicate question类似。现在出现了不同的部分:

我需要根据“dateOfBirth”字段对其进行排序 非常有信心,我将查询更改为:

db.people.aggregate([ 
    { '$match': { 'flag': true } }, 
    { '$project': {
        'name': 1,          
        'dateOfBirth': 1,
        'alias': { 
            '$cond': [
                { '$eq': [ '$alias', null ] }, 
                '$_id', 
                '$alias' 
            ]
        }
    }},
    { '$group': {
        '_id': '$alias',         
        'name':  { '$first': '$name' },          
        'dateOfBirth': { '$first': '$dateOfBirth' },
        'id': { '$first': '$_id' }       
    }}, 
    { '$project': {
        'alias': {
            '$cond': [ 
                { '$eq': [ '$id', '$_id' ] }, 
                null, 
               '$_id' 
            ]
        }, 
        'name': 1,
        '_id': '$id',
        'dateOfBirth': 1,
    }},
    { '$sort': { 'dateOfBirth': 1 }}
])

但是这给了:

{ "_id" : "1", "name" : "Alice",    "dateOfBirth" : ISODate("1995-12-27T00:00:00Z"), "alias" : null }
{ "_id" : "6", "name" : "Zoe",      "dateOfBirth" : null, "alias" : "22" }
{ "_id" : "4", "name" : "Cristina", "dateOfBirth" : null, "alias" : null }
{ "_id" : "2", "name" : "Bob",      "dateOfBirth" : null, "alias" : "4c" }

哪个错误,当然:dateOfBirth字段未通过$group阶段......

有关如何让它通过的任何线索?

1 个答案:

答案 0 :(得分:4)

实际上对我来说效果很好,我怀疑你正在运行的真实代码中有一个拼写错误(不是那个列表),但是错过了某个地方(或可能是错误的)包含“dateOfBirth”。

但是如果要在这里学到一个教训,那就不应该分开$project$group阶段,因为引入另一个管道阶段不仅效率低(这意味着需要额外传递数据) )但是当指定要包含在管道中的数据时,这是造成混淆的常见原因。

所以宁愿做:

db.people.aggregate([
    { "$match": { "flag": true } },
    { "$group": {
        "_id": {
            "$ifNull": [ "$alias", "$_id" ]
        },
        "name": { "$first": "$name" },
        "dateOfBirth": { "$first": "$dateOfBirth" },
        "id": { "$first": "$_id" }
    }},
    { "$project": {
        "_id": "$id",
        "name": 1,
        "dateOfBirth": 1,
        "alias": {
            "$cond": [
                { "$eq": [ "$_id", "$id" ] },
                null,
                "$_id"
            ]
        }
    }},
    { "$sort": { "dateOfBirth": 1 } }
]) 

其中也使用$ifNull作为自然测试,而不是使用$cond而不需要。{/ p>

当然会返回所需的结果:

{ "_id" : "1", "name" : "Alice", "dateOfBirth" : ISODate("1995-12-27T00:00:00Z"), "alias" : null }
{ "_id" : "2", "name" : "Bob", "dateOfBirth" : ISODate("1996-12-27T00:00:00Z"), "alias" : "4c" }
{ "_id" : "4", "name" : "Cristina", "dateOfBirth" : ISODate("1998-12-27T00:00:00Z"), "alias" : null }
{ "_id" : "6", "name" : "Zoe", "dateOfBirth" : ISODate("2000-12-27T00:00:00Z"), "alias" : "22" }

如果您想“首先按出生日期”,请在$group阶段之前移动排序,$first操作员将在那里完成所有工作。