如何根据布尔字段对查询进行分组,并在MongoDB中通过布尔值分别检索所有记录?

时间:2014-04-01 13:38:05

标签: mongodb mapreduce aggregation-framework nosql

以下是一些插入语句

db.users.insert({ courseId: 1, stDt: new Date(2014, 01, 01), endDt: new Date(2014, 01, 20), active: false }); 
db.users.insert({ courseId: 1, stDt: new Date(2014, 01, 25), endDt: new Date(2014, 02, 10), active: false }); 
db.users.insert({ courseId: 1, stDt: new Date(2014, 02, 25), endDt: new Date(2014, 03, 10), active: true }); 
db.users.insert({ courseId: 1, stDt: new Date(2014, 02, 28), endDt: new Date(2014, 06, 10), active: true }); 
db.users.insert({ courseId: 1, stDt: new Date(2014, 02, 25), endDt: new Date(2014, 02, 30), active: false }); 
db.users.insert({ courseId: 1, stDt: new Date(2014, 05, 25), endDt: new Date(2014, 10, 30), active: false }); 
db.users.insert({ courseId: 1, stDt: new Date(2013, 10, 01), endDt: new Date(2014, 08, 10), active: true }); 
db.users.insert({ courseId: 1, stDt: new Date(2014, 09, 01), endDt: new Date(2014, 11, 30), active: false }); 

案例1: 我们可以从上面插入的值创建以下结果集吗? 这里所有的结果都是"活跃的"字段为true和false按" endDt"排序单个查询单独检索字段。

{ "result" : { 
    true : [ 
      { endDt: new Date(2014, 03, 10), active: true }, 
      { endDt: new Date(2014, 06, 10), active: true },  
      { endDt: new Date(2014, 08, 10), active: true }, 
   ],
   false: [ 
      { endDt: new Date(2014, 01, 20), active: false }, 
      { endDt: new Date(2014, 02, 10), active: false }, 
      { endDt: new Date(2014, 02, 30), active: false }, 
      { endDt: new Date(2014, 10, 30), active: false }, 
      { endDt: new Date(2014, 11, 30), active: false } 
   ]
  }
} 

是否可以通过单一查询来完成?

案例2: 我们可以做以下结果集吗?

{ "result" : [ 
    { endDt: new Date(2014, 01, 20), active: false }, 
    { endDt: new Date(2014, 02, 10), active: false }, 
    { endDt: new Date(2014, 02, 30), active: false }, 
    [   
        { endDt: new Date(2014, 03, 10), active: true }, 
        { endDt: new Date(2014, 06, 10), active: true },    
        { endDt: new Date(2014, 08, 10), active: true } 
    ], 
    { endDt: new Date(2014, 10, 30), active: false }, 
    { endDt: new Date(2014, 11, 30), active: false } 
  ]
} 

此处按endDt字段排序,但endDt字段为true的最后active条记录将包含以前所有"有效"字段中的真实记录。

我认为,第二种情况是不可能的。

3 个答案:

答案 0 :(得分:0)

第一个很简单,你可以这样做:

db.users.aggregate({$sort:{endDt:1}}, {$group:{_id:"$active", dates:{$push:"$endDt"}}})
{
    "_id" : true,
    "dates" : [
        ISODate("2014-04-10T07:00:00Z"),
        ISODate("2014-07-10T07:00:00Z"),
        ISODate("2014-09-10T07:00:00Z")
    ]
},
{
    "_id" : false,
    "dates" : [
        ISODate("2014-02-20T08:00:00Z"),
        ISODate("2014-03-10T07:00:00Z"),
        ISODate("2014-03-30T07:00:00Z"),
        ISODate("2014-11-30T08:00:00Z"),
        ISODate("2014-12-30T08:00:00Z")
    ]
}

第二个可能是可行的,但您需要更准确地定义您想要返回的内容。

答案 1 :(得分:0)

您可以使用group命令......

db.users.aggregate({'$group' : {'_id' : '$active','endDt' : {'$addToSet' : '$endDt'}}})
{
        "result" : [
                {
                        "_id" : true,
                        "endDt" : [
                                ISODate("2014-09-09T21:00:00Z"),
                                ISODate("2014-07-09T21:00:00Z"),
                                ISODate("2014-04-09T21:00:00Z")
                        ]
                },
                {
                        "_id" : false,
                        "endDt" : [
                                ISODate("2014-12-29T21:00:00Z"),
                                ISODate("2014-11-29T21:00:00Z"),
                                ISODate("2014-03-29T21:00:00Z"),
                                ISODate("2014-03-09T21:00:00Z"),
                                ISODate("2014-02-19T21:00:00Z")
                        ]
                }
        ],
        "ok" : 1
}

您可以在mongo docs

了解更多信息

答案 2 :(得分:0)

案例1 可以返回确切的结果集。但案例2 不是有效的文件。

实际上,我的朋友道格在blog给了我答案。我只是把它复制到SO。

以下是案例1 的查询,并通过评论解释。

db.users.aggregate([    
    // Project only what we need.
    {
        "$project": {
            "endDt": 1,
            "active": 1,
            "_id": 0
        }
    },

    // Group to true and false buckets. This will give us arrays with null.
    // We'll remove them in a bit.
    {
        "$group": {
            "_id": "$active",
            "true": {
                "$addToSet": {
                    "$cond": [
                        {"$eq": ["$active", true]},
                        "$endDt",
                        null
                    ]
                }
            },
            "false": {
                "$addToSet": {
                    "$cond": [
                        {"$eq": ["$active", false]},
                        "$endDt",
                        null
                    ]
                }
            }
        }
    },

    // We need to unwind our arrays so we can build them back up without the
    // "null" array.
    {"$unwind": "$true"},
    {"$unwind": "$false"},

    // Project out the values. This will give both a true and false key for
    // each item. This builds our arrays up with the proper endDt and
    // active values.
    {
        "$project": {
            "true": {"endDt": "$true", "active": "$_id"}, 
            "false": {"endDt": "$false", "active": "$_id"}
        }
    },

    // Project out a single value to clean up the "issue" a couple steps above.
    {
        "$project": {
            "value": {
                "$cond": [
                    {"$eq": ["$_id", true]},
                    "$true",
                    "$false"
                ]
            }
        }
    },

    // Group things up again to rebuild our arrays.
    // This adds a single "null" entry that will need to be cleaned up.
    {
        "$group": {
            "_id": null,
            "true": {
                "$addToSet": {
                     "$cond": [
                         {"$eq": ["$_id", true]},
                         "$value",
                         null
                     ]
                }
            },
            "false": {
                "$addToSet": {
                    "$cond": [
                        {"$eq": ["$_id", false]},
                        "$value",
                        null
                    ]
                }
            }
        }
    },

    // Unwind our arrays again so we can clean up one more time.
    {"$unwind": "$true"},
    {"$unwind": "$false"},

    // Match only documents where true and false are not null.
    {
        "$match": {
            "true": {"$ne": null},
            "false": {"$ne": null}
        }
    },

    // Sort our items so we can add the to the array in the correct order.
    // I'm not sure why it has to be descending order, but it works.
    {
        "$sort": {
            "true.endDt": -1,
            "false.endDt": -1
        }
    },

    // Group again to build our array.
    {
        "$group": {
            "_id": null,
            "true": {"$addToSet": "$true"},
            "false": {"$addToSet": "$false"}
        }
    },

    // Once again project out just the fields we need
    {
        "$project": {
            "true": 1,
            "false": 1,
            "_id": 0
        }
    }
])

以下是MongoDB 2.2.x - 2.4.x中返回的结果:

{
    "result" : [
        {
            "true" : [
                {
                    "endDt" : ISODate("2014-04-10T06:00:00Z"),
                    "active" : true
                },
                {
                    "endDt" : ISODate("2014-07-10T06:00:00Z"),
                    "active" : true
                },
                {
                    "endDt" : ISODate("2014-09-10T06:00:00Z"),
                    "active" : true
                }
            ],
            "false" : [
                {
                    "endDt" : ISODate("2014-02-20T07:00:00Z"),
                    "active" : false
                },
                {
                    "endDt" : ISODate("2014-03-10T06:00:00Z"),
                    "active" : false
                },
                {
                    "endDt" : ISODate("2014-03-30T06:00:00Z"),
                    "active" : false
                },
                {
                    "endDt" : ISODate("2014-11-30T07:00:00Z"),
                    "active" : false
                },
                {
                    "endDt" : ISODate("2014-12-30T07:00:00Z"),
                    "active" : false
                }
            ]
        }
    ],
    "ok" : 1
}

在即将发布的版本2.6.0中,结果如下所示:

{
    "true" : [
        {
            "endDt" : ISODate("2014-04-10T06:00:00Z"),
            "active" : true
        },
        {
            "endDt" : ISODate("2014-07-10T06:00:00Z"),
            "active" : true
        },
        {
            "endDt" : ISODate("2014-09-10T06:00:00Z"),
            "active" : true
        }
    ],
    "false" : [
        {
            "endDt" : ISODate("2014-02-20T07:00:00Z"),
            "active" : false
        },
        {
            "endDt" : ISODate("2014-03-10T06:00:00Z"),
            "active" : false
        },
        {
            "endDt" : ISODate("2014-03-30T06:00:00Z"),
            "active" : false
        },
        {
            "endDt" : ISODate("2014-11-30T07:00:00Z"),
            "active" : false
        },
        {
            "endDt" : ISODate("2014-12-30T07:00:00Z"),
            "active" : false
        }
    ]
}