mongodb - 对查询中的数组进行排序并投影所有字段

标签 mongodb mongodb-query aggregation-framework

我想在查询时对嵌套数组进行排序,同时投影文档中的所有字段。

示例文档:

{ "_id" : 0, "unknown_field" : "foo", "array_to_sort" : [ { "a" : 3, "b" : 4 }, { "a" : 3, "b" : 3 }, { "a" : 1, "b" : 0 } ] }

我可以通过聚合执行排序,但无法保留我需要的所有字段。应用程序在查询时不知道每个文档中可能出现哪些其他字段,因此我无法显式投影它们。如果我有一个通配符来投影所有字段,那么这将起作用:

db.c.aggregate([
    {$unwind: "$array_to_sort"},
    {$sort: {"array_to_sort.b":1, "array_to_sort:a": 1}},
    {$group: {_id:"$_id", array_to_sort: {$push:"$array_to_sort"}}}
]);

...但不幸的是,它产生的结果不包含“unknown_field”:

    {
        "_id" : 0,
        "array_to_sort" : [
            {
                "a" : 1,
                "b" : 0
            },
            {
                "a" : 3,
                "b" : 3
            },
            {
                "a" : 3,
                "b" : 4
            }
        ]
    }

如果您想尝试一下,这是插入命令:

db.c.insert({"unknown_field": "foo", "array_to_sort": [{"a": 3, "b": 4}, {"a": 3, "b":3}, {"a": 1, "b":0}]})

我无法对数组进行预排序,因为排序标准是动态的。我可以在查询时按 a 和/或 b 升序/降序的任意组合进行排序。我意识到我可能需要在客户端应用程序中执行此操作,但如果我可以在 mongo 中执行此操作,那就太好了,因为这样我还可以 $slice/skip/limit 分页结果,而不是每次都检索整个数组。

最佳答案

由于您要对文档 _id 进行分组,因此您只需将想要保留的字段放入分组 _id 中即可。然后你可以使用$project重新形成

db.c.aggregate([
    { "$unwind": "$array_to_sort"},
    { "$sort": {"array_to_sort.b":1, "array_to_sort:a": 1}},
    { "$group": { 
        "_id": {
            "_id": "$_id",
            "unknown_field": "$unknown_field"
        },
        "Oarray_to_sort": { "$push":"$array_to_sort"}
    }},
    { "$project": {
        "_id": "$_id._id",
        "unknown_field": "$_id.unknown_field",
        "array_to_sort": "$Oarray_to_sort"
    }}
]);

其中的另一个“技巧”是在分组阶段为数组使用临时名称。当您$project时就是这样。并更改名称,您将按照投影语句中指定的顺序获取字段。如果您没有这样做,那么“array_to_sort”字段将不会是顺序中的最后一个字段,因为它是从前一阶段复制的。

这是 $project 中的预期优化,但如果您想要顺序,那么您可以按照上面的方式进行。


对于完全未知的结构,可以使用 mapReduce 的方法:

db.c.mapReduce(
    function () {
        this["array_to_sort"].sort(function(a,b) {
            return a.a - b.a || a.b - b.b;
        });

        emit( this._id, this );
    },
    function(){},
    { "out": { "inline": 1 } }
)

当然,它具有特定于 mapReduce 的输出格式,因此不完全是您拥有的文档,但所有字段都包含在“值”下:

{
    "results" : [
            {
                    "_id" : 0,
                    "value" : {
                            "_id" : 0,
                            "some_field" : "a",
                            "array_to_sort" : [
                                    {
                                            "a" : 1,
                                            "b" : 0
                                    },
                                    {
                                            "a" : 3,
                                            "b" : 3
                                    },
                                    {
                                            "a" : 3,
                                            "b" : 4
                                    }
                            ]
                    }
            }
    ],
}

future 版本(截至撰写本文时)允许您聚合使用 $$ROOT 变量来表示文档:

db.c.aggregate([
    { "$project": {
        "_id": "$$ROOT",
        "array_to_sort": "$array_to_sort"
    }},
    { "$unwind": "$array_to_sort"},
    { "$sort": {"array_to_sort.b":1, "array_to_sort:a": 1}},
    { "$group": { 
        "_id": "$_id",
        "array_to_sort": { "$push":"$array_to_sort"}
    }}
]);

因此,使用最后的“项目”阶段是没有意义的,因为您实际上并不了解文档中的其他字段。但它们都将包含在结果文档的 _id 字段中(包括原始数组和 order )。

关于mongodb - 对查询中的数组进行排序并投影所有字段,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22888885/

相关文章:

mongodb - 如何在 Mongo 中独立地按日期和时间进行过滤?

node.js - 我如何编写一个从 mongodb 查询的辅助函数,然后可以为任何 ejs 模板调用此函数

Mongodb 查询用于计算所有文档中的不同值

node.js - 如何解决 - 首次连接时出现 MongoError : failed to connect to server [zzzzz. mlab.com:xxxx]

mongodb - meteor 蒙戈 : Untrusted code may only update documents by ID. [403]

mongodb - Spark Map RDD 与连接

MongoDB $查找嵌套文档

javascript - 我想从 MongoDB 文档返回特定字段值,但我不断将 [object Promise] 作为返回值

mongodb - 当排序值相同时,Mongo 在 $skip 之后不会维持 $sort 顺序

node.js - 将 MongoDB View 合并到 Node 中