_parentId汇总组

时间:2014-08-14 22:15:33

标签: mongodb mongodb-query aggregation-framework

我有一个元素列表。一些_parentId属性指向同一集合中元素的id。因此,所有具有_parentId的元素都是那些没有_parentId的元素的子元素。

我需要一个结果,它包含所有父元素(没有_parentId),子元素的数量以及每个父元素的最新添加(它有一个dateCreated属性)子元素的“content”属性。例如:

[{
   _id: "some id",
   title: "some title",
   email: "some email",
   dateCreated: "date",
   children:{
     count: 7, 
     lastAddedChildContent: "some content", 
     dateCreated: "date:
   }],
  parentCount:23 // total for parent elements
}

最好的方法是什么? 谢谢!

1 个答案:

答案 0 :(得分:3)

公平的问题。仅仅为了示例目的简化文档,比如说有这样的文档:

{
    "_id" : ObjectId("53ed7efca75ca1a5248a281a"),
    "dateCreated" : ISODate("2014-08-15T03:46:57.784Z"),
    "title" : "Master",
    "content" : "Orig content"
}
{
    "_id" : ObjectId("53ed80bba75ca1a5248a281b"),
    "title" : "Other title",
    "content" : "More content",
    "dateCreated" : ISODate("2014-08-15T03:38:35.694Z"),
    "_parent" : ObjectId("53ed7efca75ca1a5248a281a")
}
{
    "_id" : ObjectId("53ed80d1a75ca1a5248a281c"),
    "title" : "Other title",
    "content" : "Last content",
    "dateCreated" : ISODate("2014-08-15T03:38:57.750Z"),
    "_parent" : ObjectId("53ed7efca75ca1a5248a281a")
}

您的聚合管道基本上会将_id字段替换为父项,然后对结果进行排序:

db.collection.aggregate([
    { "$project": {
        "_id": { "$ifNull": [ "$_parent", "$_id" ] },
        "title": 1,
        "dateCreated": 1,
        "content": 1,
        "_parent": { "$ifNull": [ "$_parent", false ] }
    }},
    { "$sort": { "_id": 1, "_parent": -1, "dateCreated": 1 } },
    { "$group": {
        "_id": "$_id",
        "title": { "$min": { "$cond": [ "$_parent", false, "$title" ] } },
        "dateCreated": { 
            "$max": { 
                "$cond": [
                    "$_parent",
                    false,
                    "$dateCreated"
                ]
            }
        },
        "childCount": {
            "$sum": {
                "$cond": [
                    "$_parent",
                    1,
                    0
                ]
            }
        },
        "lastContent": { "$last": "$content" },
        "lastDate": { "$last": "$dateCreated" }
    }},
    { "$project": {
        "title": 1,
        "dateCreated": 1,
        "children": {
            "count": "$childCount",
            "lastContent": "$lastContent",
            "dateCreated": "$lastDate"
        }
    }}
])

由此产生的反应是:

{
    "_id" : ObjectId("53ed7efca75ca1a5248a281a"),
    "title" : "Master",
    "dateCreated" : ISODate("2014-08-15T03:46:57.784Z"),
    "children" : {
        "count" : 2,
        "lastContent" : "Last content",
        "dateCreated" : ISODate("2014-08-15T03:38:57.750Z")
    }
}

要查看的主要运算符是$ifNull,它们测试字段存在或实际上是null值,并返回字段内容或备用参数。另一个是$cond,它是三元运算符。它评估逻辑条件或值作为它的第一个参数,然后在该参数为true的情况下返回第二个参数,或者在false的情况下返回最后一个参数。在现代版本中,这甚至可以写成" if .. then .. else"如果这看起来更清楚。

最初的$project用于提供$sort的信息,这基本上是完成的,因为您需要$last运算符构成您的响应的一部分。

$group声明中,属于" parent"的所有文件现在与父进程具有相同的_id,这是正确的分组键。任何"父字段"有条件地评估,以便只返回该内容,这是**$min$sum上的创造性用法,作为"字符串"仅为"父母"文档,其值将小于false,否则返回。

"计数"儿童是简单的评估文件是否是父母"在选择要提供给{{3}}的值时是否。其他字段以及count将使用排序顺序中的$last进行评估,并且全部单独返回,因为您无法返回"子文档"在聚合组中形成。这些$project稍后会进入所需的输出。

我故意搞砸了那里的日期以展示"排序"原则,但这主要是关于父母"鉴定。另一个故意的行动不是将所有父母和#34;成阵列。我不认为这是一个很好的做法,只是为了得到一个计数,并且它确实有可能打破16MB的BSON限制。如果您希望响应看起来像发送到其他地方那样,最好进行后期处理并添加计数,因为您不会有相同的限制并且可以使用尽可能多的内存。

所以,如果你必须,那么另外一组添加到" push"和"计数"最后,但是如果出现问题,不要说你没有得到更好的建议。