复杂的MongoDB聚合

时间:2014-08-27 21:30:28

标签: mongodb mongodb-query aggregation-framework

我有一种情况,我需要根据一个数值来执行一个组,该数组值总结了一个字段值的出现次数。然后过滤计数并准备结果,以便可以根据条件显示它们。基本上,如果您只是使用find函数,文档将转换回它们的呈现方式。由于matchedDocuments数组中收集的项目数量,我遇到临时文档太大的问题。关于如何改进这一点的任何建议都会有所帮助。

db.collection1.aggregate([
{
    '$unwind': '$arrayOfValues'
}, {
    '$group': {
        '_id': '$arrayOfValues',
        'x_count': {
            $sum: {
                $cond: [{
                        $eq: ['$field.value', 'x']
                    },
                    1, 0
                ]
            }
        },
        'y_count': {
            $sum: {
                $cond: [{
                        $eq: ['$field.value', 'y']
                    },
                    1, 0
                ]
            }
        },
        'matchedDocuments': {
            '$push': '$$CURRENT'
        }
    }
},
{'$match': {'$or': [{'x_count': {'$gte': 2}}, {'y_count': { '$gte': 1}}]}},
{'$unwind': '$matchedDocuments'},
{
    '$group': {
        '_id': '$matchedDocuments.key',
        'document': {
            '$last': '$$CURRENT.matchedDocuments'
        }
    }
}
], {
    allowDiskUse: true
})

以下是一些示例文档和基于上述标准的预期结果:

// Sample documents

{ "_id" : ObjectId("5407c76b7b1c276c74f90524"), "field" : "x", "arrayOfValues" : [ "a", "b", "c" ] }
{ "_id" : ObjectId("5407c76b7b1c276c74f90525"), "field" : "x", "arrayOfValues" : [ "b", "c" ] }
{ "_id" : ObjectId("5407c76b7b1c276c74f90526"), "field" : "z", "arrayOfValues" : [ "a" ] }
{ "_id" : ObjectId("5407c76b7b1c276c74f90527"), "field" : "x", "arrayOfValues" : [ "a", "c" ] }
{ "_id" : ObjectId("5407c76b7b1c276c74f90528"), "field" : "z", "arrayOfValues" : [ "b" ] }
{ "_id" : ObjectId("5407c76b7b1c276c74f90529"), "field" : "y", "arrayOfValues" : [ "k" ] }


// Expected Result

[
    { "_id" : ObjectId("5407c76b7b1c276c74f90524"), "field" : "x", "arrayOfValues" : [ "a", "b", "c" ] }
    { "_id" : ObjectId("5407c76b7b1c276c74f90525"), "field" : "x", "arrayOfValues" : [ "b", "c" ] }
    { "_id" : ObjectId("5407c76b7b1c276c74f90527"), "field" : "x", "arrayOfValues" : [ "a", "c" ] }
    { "_id" : ObjectId("5407c76b7b1c276c74f90529"), "field" : "y", "arrayOfValues" : [ "k" ] }
]

1 个答案:

答案 0 :(得分:2)

我认为最终你要从一个查询中得到一点点过多,因为显然这里最大的问题是尝试存储数组元素来源的所有原始文档,同时尝试聚合总数。

对我来说,我只会尝试确定文档中的哪些条件会导致匹配,然后发出单独的查询以获取实际文档。您可以调整下面的聚合以尝试返回文档,但我认为这样做很可能会失败,因为它与使用数组的方式相反。

这个过程通常会更有效地进行匹配,使你首先选择#34;选择你感兴趣的元素并匹配条件"其次,"使用自然分组条件而不是依赖条件总和"。

var cursor = db.collection.aggregate([
    { "$match": { "field": { "$in": ["x", "y"] } } },
    { "$unwind": "$arrayOfValues" },
    { "$group": {
        "_id": {
           "elem": "$arrayOfValues",
           "field": "$field"
        },
        "count": { "$sum": 1 }
    }},
    { "$match": {
        "$or": [
            { "_id.field": "x", "count": { "$gte": 2 } },
            { "_id.field": "y", "count": { "$gte": 1 } }
         ]
    }},
    { "$group": {
        "_id": "$_id.field",
        "values": { "$push": "$_id.elem" }
    }}
])

var query = { "$or": [] };

cursor.forEach(function(doc) {
    query["$or"].push({
        "field": doc._id,
        "arrayOfValues": { "$in": doc.values }
    });
});

db.collection.find(query)

对于记录,在给定提供的数据的情况下,查询应如下所示:

{
    "$or" : [
        {
            "field" : "x",
            "arrayOfValues" : {
                "$in" : [
                    "c",
                    "b",
                    "a"
                ]
            }
        },
        {
            "field" : "y",
            "arrayOfValues" : {
                "$in" : [
                    "k"
                ]
            }
        }
    ]
}

只需查找" field"的值即可满足基本逻辑。你感兴趣的,所以至少从可能的结果中消除所有其他的。那么你基本上想要计算每个"字段"下每个数组元素的计数。值和测试满足所需事件的位置。

这可能会或者可能不会以相反的方式发挥作用,但此处的示例显示了" arrayOfValues"因此,作为第二级分组是有意义的。

如前所述,我认为基本上要求的东西太多了#34;东西"将所有父文档信息分成一个数组,用于每个" arrayOfValues"因为这超出了合理模式的基本原则,其中这种关系自然地存储为单独的文档。所以这里的最终原则就是找到"条件"匹配那些最终结果的文件。

然后针对集合发布转换后的查询,其中将返回满足从先前分析确定的条件的所有文档。在一天结束时,转移"提取"匹配文档到另一个查询,而不是尝试存储匹配在数组中的文档。

这似乎是最合乎逻辑且可扩展的方法,但如果您倾向于在此类结果中使用您的数据,那么您应该考虑重新设计您的架构以更好地适应这种情况。但是这里确实没有足够的具体信息来进一步评论。