【问题标题】:Complex MongoDB Aggregation复杂的 MongoDB 聚合
【发布时间】:2014-10-21 15:13:59
【问题描述】:

我有一种情况,我需要根据一个汇总字段值出现次数的数组值执行分组操作。然后过滤计数并准备结果,以便可以根据条件显示它们。从本质上讲,如果您仅使用 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" ] }
]

【问题讨论】:

  • 问题出在你的第一个分组键上。但是,由于您对刚刚展开的数组中的值进行了分组,因此很难看到您在此处实际尝试执行的操作。文档样本和预期结果通常最能说明您的意图。
  • 我添加了示例文档和预期结果以帮助可视化问题。
  • 为什么不统计arrayOfValues 数组大小的文档呢?这将使聚合成为field 的每个值的简单查找。
  • 我认为这不会帮助解决这个问题。您可以将 arrayOfValues 视为每个文档的分组。我希望按每个组中出现值 x (>= 2) 或 y (>= 1) 的字段的出现次数过滤结果。

标签: mongodb mongodb-query aggregation-framework


【解决方案1】:

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

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

该过程通常在进行匹配的方式上也更加有效,让您首先“使用匹配条件选择您感兴趣的元素”,其次,“使用自然分组条件而不是依赖条件总和”。

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"
                ]
            }
        }
    ]
}

只需查找您感兴趣的“字段”值即可满足基本逻辑,因此至少从可能的结果中消除所有其他值。然后,您基本上想要计算每个“字段”值下每个数组元素的计数,并测试满足所需出现的位置。

反之亦然,这可能效果最好,也可能效果不佳,但此处的示例显示了“arrayOfValues”的最大变化,因此作为第二级分组是有意义的。

如前所述,我认为要求将所有父文档信息基本上“填充”到每个“arrayOfValues”元素的数组中,因为这超出了合理模式的基本原则,其中排序的关系自然会存储为单独的文档。所以这里的最终原则就是找到与那些文档相匹配的“条件”,这就是最终结果。

然后针对集合发出转换后的查询,其中将返回满足先前分析确定的条件的所有文档。归根结底,将“获取”匹配文档的责任转移到另一个查询中,而不是尝试将匹配的文档存储在数组中。

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

【讨论】:

  • 感谢您为回答所做的努力。这与我想出的类似。但是,我不确定是否有更好的方法可用。有用户定义的前提条件可以进一步过滤结果,从而减少大文档的问题。此外,每个集合总是少于 550 万个文档。我试图尽可能地抽象出领域特定的东西以避免复杂性。
  • @user1595702 如果您正在谈论发出单独的查询,那么没有更好的方法,我想我已经解释过了。聚合框架功能强大,但要小心“摇尾巴”。明智地做事。
猜你喜欢
  • 2019-11-04
  • 2018-10-31
  • 1970-01-01
  • 2021-03-09
  • 1970-01-01
  • 1970-01-01
  • 2014-03-08
  • 1970-01-01
  • 2012-06-09
相关资源
最近更新 更多