【问题标题】:How to project additional data from an aggregate result with MongoDB?如何使用 MongoDB 从聚合结果中投影附加数据?
【发布时间】:2016-02-03 09:11:12
【问题描述】:

我正在学习 MongoDB 并尝试对集合进行分组。 我正在寻找的是按年份分组,获取最大“平均注释”字段并显示与此平均值相关的文档的字段主名称

例如,如果我有:

Name    | Average   | Year
Name_01 | 7.56      | 1995
Name_02 | 8.96      | 1995
Name_03 | 3.25      | 2005
Name_04 | 4.36      | 2005
Name_05 | 7.52      | 2020

我需要:

Name    | Average   | Year
Name_02 | 8.96      | 1995
Name_05 | 7.52      | 2020
Name_04 | 4.36      | 2005

我已经完成了分组和最大值。这是我的代码:

db.foobar.aggregate([
    {
        $group: { _id: '$year_published', max: { $max: '$statistics.average' }}
    },
    {
        $project: { _id: 1, max: 1 }
    }, 
    {
        $sort: { max: -1 }
    }    
])

这给了我这样的结果:

{
    "result" : [ 
        {
            "_id" : 1999,
            "max" : 8.0343000000000000
        }, 
        {
            "_id" : 1985,
            "max" : 7.8833299999999999
        }
        // An so on...
}

但我还想将与“max”相关的文档的主要名称投影为:

 {
    "result" : [ 
        {
            "_id" : 1999,
            "max" : 8.0343000000000000,
            "name": "Foo Bar"
        }, 
        {
            "_id" : 1985,
            "max" : 7.8833299999999999,
            "name": "Lorem Ipsum"
        }
        // An so on...
}

NB:问题的下一部分增加了名称的复杂性(因为我的文档结构)。这不是我现在主要关心的问题,但我将其添加到问题中以反映我的所有问题。

主要名称有点难以获得。对于每个文档,我都有一个这样的对象数组:

{
    "names" : [ 
        {
            "type" : "primary",
            "value" : "Foo bar"
        }, 
        {
            "type" : "alternate",
            "value" : "Foo foo"
        }, 
        {
            "type" : "alternate",
            "value" : "Bar bar"
        }
    ]
}

我想要得到的是具有“主要”类型的名称(即我的示例中的“Foo bar”)。

这是我的文档的结构:

{
    "_id" : ObjectId("56338f2bdc99b8ec22a43328"),
    "names" : [ 
        {
            "type" : "primary",
            "value" : "Foo bar"
        },
        {
            "type" : "alternate",
            "value" : "Barr foo"
        }
    ],
    "year_published" : 1992
    "statistics" : {
        "average" : 6.6057699999999997
    }
}

我想我还没有到这一步,但我不知道该怎么做...你能帮帮我吗?

【问题讨论】:

  • 如果您需要帮助,您需要展示您最初拥有的数据以及您需要从中获得什么。否则只是猜测。
  • @BlakesSeven 我在乞讨时添加了一个示例以及我的文档结构。
  • @user3100115 我不同意,因为这不是单点课程,问题还有其他部分。指向零件的副本并不能解决其他零件。
  • @BlakesSeven 好点。
  • @user3100115 我的指针(和个人偏好),如果我看到一个不完全“清楚”的问题,我会将其标记为结束。我认为这在很大程度上是对 OP 提问的一种保护,因为无论如何,不​​清楚的问题大多会得到答案(大多是错误的和不正确的)。因此,还为澄清点留下了评论,并且 OP 有责任明确他们的观点。如果他们这样做(在任何时候),那么我撤回/投票重新开放,因为 OP 已经表明了他们的观点。这同样适用于“部分”重复,或者 OP 不理解标记的重复。保护和解决是目标

标签: mongodb mongodb-query aggregation-framework


【解决方案1】:

如果您希望特定文档中的“paried”值具有“max”值,那么$max 不适合您。相反,您需要做的是先$sort 数据,然后使用$first 运算符。

db.foobar.aggregate([
    { "$sort":  { "year_published": 1, "statistics.average": -1 } },
    { "$group": { 
        "_id": "$year_published", 
        "max": { "$first": "$statistics.average" }},
        "name": { 
            "$first": {
                "$setDifference": [
                    { "$map": {
                        "input": "$names",
                        "as": "name",
                        "in": {
                            "$cond": {
                                "if": { "$eq": [ "$$name.type", "primary" ] },
                                "then": "$$name.value",
                                "else": false
                            }
                        }
                    }},
                    [false]
                ]
            }
        }
    }},
    { "$unwind": "$name" }
])

$first$last 运算符作用于“分组边界”数据。这意味着它们从属性返回数据,该数据出现在用于分组 _id 的值的开头或结尾。

这就是为什么你首先“排序”,所以文档是为了选择。

相比之下,$max$min 只需从示例文档中的任何位置选择“最大/最小”值。满足您的所有需求时这很好,但如果您想要“相关”字段,那么您必须首先进行排序。

这就是它的基础。处理过滤数组的另一部分最好使用$map$setDifference 组合完成,如图所示。 $map 允许通过$cond 对每个数组元素“内联”测试条件,并根据真假返回值。结果当然仍然是一个等长的数组。

$setDifference 基本上会过滤掉以false 返回的任何内容,因此唯一剩下的应该是“主要”。仍然是一个数组,这就是为什么仍然使用$unwind,尽管它只是一个单元素数组。

未来的 MongoDB 版本将通过 $filter$arrayElemAt 在这方面做得更好。先来一睹为快:

db.foobar.aggregate([
    { "$sort":  { "year_published": 1, "statistics.average": -1 } },
    { "$group": { 
        "_id": "$year_published", 
        "max": { "$first": "$statistics.average" }},
        "name": { 
            "$first": {
                "$arrayElemAt": [
                    { "$filter": {
                        "input": "$names",
                        "as": "name",
                        "cond": {
                            "$eq": [ "$$name.type", "primary" ]
                        }
                    }},
                    0
                ]
            }
        }
    }}
])

但这并没有改变“先排序”的基本规则,然后只是从分组边界中提取值。

【讨论】:

    【解决方案2】:

    请尝试以下代码: 您需要在$First的帮助下选择组管道操作中的“名称”。

    $First 选择将表达式应用于按键共享同一组的一组文档中的第一个文档所产生的值。

    db.foobar.aggregate([ 
    { "$unwind" : "$names" },
    { $match :
               { "$names.type" : "primary"}
    } ,
    { $sort : 
              { "year_published" : 1, "statistics.average" : -1 } 
    },
    { $group : 
              { _id :  "$year_published" , 
                name : {
                         $first : "$names.value" 
                       }, 
                max: { $max: "$statistics.average" } 
              }
    },
    { $sort: 
            { max: -1 } 
    }  
    ]).pretty();
    

    这将为您提供所需的结果:

    {
        "result" : [ 
            {
                "_id" : 1999,
                "max" : 8.0343000000000000,
                "name": "Foo Bar"
            }, 
            {
                "_id" : 1985,
                "max" : 7.8833299999999999,
                "name": "Lorem Ipsum"
            }
            // An so on...
    }
    

    【讨论】:

    • 嗯,我试过了,但它不是正确的名称。我认为这是今年的名字(即_id),但不是最大'$statistics.average'的名字。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-09
    • 2022-01-18
    • 2020-09-16
    • 2021-07-01
    • 2015-09-08
    • 1970-01-01
    相关资源
    最近更新 更多