按mongo中的子文档字段排序

时间:2016-07-25 15:03:17

标签: mongodb

鉴于以下收集:

[
    {
        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”值:“得分”

预期输出为:

[
    {
        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}
        ]
    }
]

3 个答案:

答案 0 :(得分:2)

替代解决方案可能如下所示,如果您可以使用额外的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
        }
    ]
}

答案 1 :(得分:1)

您可以使用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字段提升回顶级。

答案 2 :(得分:0)

使用$elemMatch中的aggregate符合您的条件,并按metadata.v排序查看以下查询

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