MongoDB正确聚合嵌套数组

时间:2016-10-12 13:42:01

标签: mongodb mongodb-query aggregation-framework

好的,我对Mongo很新,而且我已经卡住了。

Db具有以下结构(当然非常简化):

{
    {
        "_id" : ObjectId("57fdfbc12dc30a46507044ec"),

        "keyterms" : [ 
            {
                "score" : "2",
                "value" : "AA",
            }, 
            {
                "score" : "2",
                "value" : "AA",
            }, 
            {
                "score" : "4",
                "value" : "BB",
            },
            {
                "score" : "3",
                "value" : "CC",
            }
        ]
    },

    {
        "_id" : ObjectId("57fdfbc12dc30a46507044ef"),

        "keyterms" : [ 
        ...

有一些物体。每个对象都有一个数组" keywords"。每个阵列条目,都有分数和价值。虽然有一些重复(不是真的,因为在真正的数据库中,关键字条目有更多的字段,但是关于价值和分数,它们是重复的。)

现在我需要一个查询,

  • 按ID
  • 选择一个对象
  • 按值分组其关键字
  • 并统计了dublicates
  • 按分数对其进行排序

所以我希望得到类似的结果

// for Object 57fdfbc12dc30a46507044ec
"keyterms"; [
    {
        "score" : "4",
        "value" : "BB",
        "count" : 1
    },


    {
        "score" : "3",
        "value" : "CC",
        "count" : 1
    }

    {
        "score" : "2",
        "value" : "AA",
        "count" : 2
    }

]

在SQL中我会写这样的东西

select 
    score, value, count(*) as count
from
    all_keywords_table_or_some_join
group by
    value
order by
    score

但遗憾的是,它不是SQL。

在Mongo,我设法写了这个:

db.getCollection('tests').aggregate([
    {$match: {'_id': ObjectId('57fdfbc12dc30a46507044ec')}},
    {$unwind: "$keyterms"}, 
    {$sort: {"keyterms.score": -1}}, 
    {$group: {
        '_id': "$_id", 
        'keyterms': {$push: "$keyterms"}
    }},
    {$project: {
        'keyterms.score': 1,
        'keyterms.value': 1
    }}
])

但缺少一些东西:按关键字对关键字进行分组。我无法摆脱这种感觉,这根本就是错误的做法。如何选择关键字数组并继续使用,并在此处使用聚合函数 - 这很容易。

BTW我读过这篇文章 (Mongo aggregate nested array) 但不幸的是,我不能为我的例子弄清楚......

2 个答案:

答案 0 :(得分:3)

您需要一个聚合管道,在您 $unwind 数组之后,您可以按数组的valuescore键对展平的文档进行分组,聚合使用 $sum 累加器运算符进行计数,并使用 $first 运算符保留主文档的_id

然后,前面的管道应该通过_id键对上一个管道中的文档进行分组,以便保留原始架构并使用 $push <重新创建keyterms数组/ strong>运营商。

以下演示尝试解释上述聚合操作:

db.tests.aggregate([
    { "$match": { "_id": ObjectId("57fdfbc12dc30a46507044ec") } },
    { "$unwind": "$keyterms" },
    {
        "$group": {
            "_id": {
                "value": "$keyterms.value",
                "score": "$keyterms.score"
            },
            "doc_id": { "$first": "$_id" },
            "count": { "$sum": 1 }
        }
    },
    { "$sort": {"_id.score": -1 } },
    {
        "$group": {
            "_id": "$doc_id",
            "keyterms": {
                "$push": {
                    "value": "$_id.value",
                    "score": "$_id.score",
                    "count": "$count"
                }
            }
        }
    }
])

示例输出

{
    "_id" : ObjectId("57fdfbc12dc30a46507044ec"),
    "keyterms" : [ 
        {
            "value" : "BB",
            "score" : "4",
            "count" : 1
        }, 
        {
            "value" : "CC",
            "score" : "3",
            "count" : 1
        }, 
        {
            "value" : "AA",
            "score" : "2",
            "count" : 2
        }
    ]
}

<强>演示

enter image description here

答案 1 :(得分:1)

与此同时,我自己解决了这个问题:

aggregate([
        {$match: {'_id': ObjectId('57fdfbc12dc30a46507044ec')}},
        {$unwind: "$keyterms"},
        {$sort: {"keyterms.score": -1}}, 
        {$group: {
            '_id': "$keyterms.value", 
            'keyterms': {$push: "$keyterms"},
            'escore': {$first: "$keyterms.score"},
            'evalue': {$first: "$keyterms.value"}
        }},
        {$limit: 15},
        {$project: {
          "score": "$escore", 
          "value": "$evalue",
          "count": {$size: "$keyterms"}
        }}      
])