【问题标题】:Sort by subdocuments fields in mongo按 mongo 中的子文档字段排序
【发布时间】:2016-07-26 01:35:32
【问题描述】:

给定以下集合:

[
    {
        name: User1
        metadata: [
            {k:"score", v: 5},
            {k:"other", v: 10}
        ]
    },
    {
        name: User2
        metadata: [
            {k:"score", v: 1},
            {k:"other", v: 1}
        ]
    },
    {
        name: User3
        metadata: [
            {k:"score", v: 2},
            {k:"other", v: 0}
        ]
    }
]

如何按“分数”对这些项目进行排序?我可以按 metadata.v 字段排序,但我不确定如何只考虑匹配“k”的子文档的“v”值:“score”

预期的输出是:

[
    {
        name: User2
        metadata: [
            {k:"score", v: 1},
            {k:"other", v: 1}
        ]
    },
    {
        name: User3
        metadata: [
            {k:"score", v: 2},
            {k:"other", v: 0}
        ]
    },
    {
        name: User1
        metadata: [
            {k:"score", v: 5},
            {k:"other", v: 10}
        ]
    }
]

【问题讨论】:

  • 不清楚你在这里问什么。请考虑将预期的输出添加到您的问题中。
  • 您的结果中是否需要其他 metadata 元素(例如 k:"other" 元素)?
  • @JohnnyHK 是的,我想要文档中的所有数据。
  • @JohnnyHK 是否仍然可以使用展开匹配来扩充文档,但也可以返回完整文档?

标签: mongodb


【解决方案1】:

另一种解决方案可能如下所示,如果您可以使用额外的scoredata 属性,则最终投影阶段是可选的。

db.yourCollection.aggregate([
    {
        $project: {
            name: 1,
            metadata: 1,
            scoredata: {
                $filter: {
                    input: '$metadata',
                    as: 'metadoc',
                    cond: {
                        $eq: [ '$$metadoc.k', 'score' ]
                    }
                }
            }
        }
    },
    {
        $sort: {
            scoredata: 1
        }
    },
    {
        $project: {
            name: 1,
            metadata: 1            
        }
    }
])  

输出看起来像这样

/* 1 */
{
    "_id" : ObjectId("5796387b3360e0a2e9dd9fc3"),
    "name" : "User2",
    "metadata" : [ 
        {
            "k" : "score",
            "v" : 1
        }, 
        {
            "k" : "other",
            "v" : 1
        }
    ]
}

/* 2 */
{
    "_id" : ObjectId("5796387b3360e0a2e9dd9fc4"),
    "name" : "User3",
    "metadata" : [ 
        {
            "k" : "score",
            "v" : 2
        }, 
        {
            "k" : "other",
            "v" : 0
        }
    ]
}

/* 3 */
{
    "_id" : ObjectId("5796387b3360e0a2e9dd9fc2"),
    "name" : "User1",
    "metadata" : [ 
        {
            "k" : "score",
            "v" : 5
        }, 
        {
            "k" : "other",
            "v" : 10
        }
    ]
}

【讨论】:

  • 好一个。这应该比我的回答更有效率。
  • 感谢您的诚实评论!
  • @DAXaholic 这很好用!您知道是否有一种方法可以获取原始文档中的所有字段而无需显式指定它们?
  • 不是直接的,但有一个变量 $$ROOT (docs.mongodb.com/manual/reference/aggregation-variables/…) 可用于将整个文档投影为子文档,即所有字段将向下移动一级
【解决方案2】:

您可以使用aggregate 执行此操作,方法是在展开元数据以进行过滤和排序之前投影每个文档的副本及其metadata

db.test.aggregate([
    // Project a copy of each document along with its metadata
    {$project: {doc: '$$ROOT', metadata: '$metadata'}},
    // Duplicate the docs, one per metadata element
    {$unwind: '$metadata'},
    // Filter to just the score metadata for sorting
    {$match: {'metadata.k': 'score'}},
    // Sort on the score values
    {$sort: {'metadata.v': 1}},
    // Project just the original docs in their sorted order
    {$project: {_id: 0, doc: '$doc'}}
])

输出:

{ 
    "doc" : {
        "_id" : ObjectId("57962f891be8975795eee18a"), 
        "name" : "User2", 
        "metadata" : [
            {
                "k" : "score", 
                "v" : 1.0
            }, 
            {
                "k" : "other", 
                "v" : 1.0
            }
        ]
    }
}
{ 
    "doc" : {
        "_id" : ObjectId("57962f891be8975795eee18b"), 
        "name" : "User3", 
        "metadata" : [
            {
                "k" : "score", 
                "v" : 2.0
            }, 
            {
                "k" : "other", 
                "v" : 0.0
            }
        ]
    }
}
{ 
    "doc" : {
        "_id" : ObjectId("57962f891be8975795eee189"), 
        "name" : "User1", 
        "metadata" : [
            {
                "k" : "score", 
                "v" : 5.0
            }, 
            {
                "k" : "other", 
                "v" : 10.0
            }
        ]
    }
}

如果需要,您可以更改最终的 $project 以重塑文档以将 doc 字段提升回顶层。

【讨论】:

    【解决方案3】:

    使用aggregate 中的$elemMatch 匹配您的条件并按metadata.v 排序,检查以下查询

    db.collection.aggregate({"$match":{"metadata":{"$elemMatch":{"k":"score"}}}},
                            {"$sort":{"metadata.v":1}}).pretty()
    

    【讨论】:

    • 这使用所有 metadata.v 值进行排序,而不仅仅是匹配 {"k": "score"}
    猜你喜欢
    • 2019-11-03
    • 1970-01-01
    • 1970-01-01
    • 2018-01-07
    • 2018-06-01
    • 2012-05-18
    • 1970-01-01
    • 1970-01-01
    • 2019-10-09
    相关资源
    最近更新 更多