子文档中的Mongodb 1to1关系

时间:2014-06-05 11:45:15

标签: mongodb mongodb-query aggregation-framework

我有一个庞大的集合,其中每个文档都有子文档,它们之间有关系。我的架构如下所示:

{
    userName: "user44",
    userID: "44",
    posts : [
        ...
        {
            title : "post1",
            id : "123"
            ...
        },
        {
            title : "post2",
            id : "124"
            ...
        },
        ...
    ],
    comments: [
        ...
        {
            id: 1910,
            postId : "123",
            title : "comment1",
            comment : "some comment",
            user: "user13"
        },
        {
            id: 1911,
            postId : "124",
            title : "comment2",
            comment : "some comment",
            user: "user22"
        },
        ...
    ], 
    commentUpvotes: [
        ...
        {
            id : 12,
            commentId : "1910",
            upvotedBy: "user91"
        },
        {
            id: 13,
            commentId : "1910",
            upvotedBy: "user92"
        },
        ...
    ]
}

虽然这与我的数据库无关,但原始模式与上面完全相同。因此,上面的示例是一个用户集合,我存储了用户的posts; comments由其他用户commentUpvotes发布的帖子,用于存储有关谁投票的信息。不要考虑其设计的逻辑,不要建议任何其他模式。

问题:db.users.find({"commentUpvotes.id" : 12})应该返回此集合,但仅返回此upvote所做的评论(1910)和帖子(123)。我用$ unwinding解决了它,这导致了性能问题。因此请建议在不放松的情况下解决。有什么想法?

1 个答案:

答案 0 :(得分:2)

考虑"缩进"我在列表中使用,这实际上可能看起来比你正在做的更长,但实际上它不是。

这是使用$map作为MongoDB 2.6及更高版本可用的另一个非常好的示例。仍然有一些$unwind的使用,但数组是"解开"实际上只有一个元素。所以请原谅我无法抗拒的"Highlander"个引用:)

db.users.aggregate([

    // Match your document or documents
    { "$match": {
        "commentUpvotes.id": 12
    }},

    // Get the one "up-votes" entry that matches
    { "$project": {
        "posts": 1,
        "comments": 1,
        "commentUpVotes": {
            "$setDifference": [
                { 
                    "$map": {
                        "input": "$commentUpvotes",
                        "as": "el",
                        "in": {
                            "$cond": [
                                { "$eq": [ "$$el.id", 12 ] },
                                "$$el",
                                false
                            ]
                        }  
                    }
                },
                [false]
            ]
        }
    }},

    // There is only one!
    { "$unwind": "$commentUpVotes" },

    // Get the one comments entry that matches
    { "$project": {
        "posts": 1,
        "comments": {
            "$setDifference": [
                { 
                    "$map": {
                        "input": "$comments",
                        "as": "el",
                        "in": {
                            "$cond": [
                                { 
                                    "$eq": [ 
                                        { "$substr": [ "$$el.id", 0, 4 ] }, 
                                        "$commentUpVotes.commentId"
                                    ] 
                                },
                                "$$el",
                                false
                            ]
                        }  
                    }
                },
                [false]
            ]
        },
        "commentUpVotes": 1
    }},

    // And there is only one!
    { "$unwind": "$comments" },

    // Get the one post that matches
    { "$project": { 
        "posts": {
            "$setDifference": [
                { 
                    "$map": {
                        "input": "$posts",
                        "as": "el",
                        "in": {
                            "$cond": [
                                { 
                                    "$eq": [ 
                                        "$$el.id", 
                                        "$comments.postId"
                                    ] 
                                },
                                "$$el",
                                false
                            ]
                        }  
                    }
                },
                [false]
            ]
        },
        "comments": 1,
        "commentUpVotes": 1
    }},

    // Optionally group back to arrays. There can be only one!
    { "$group": {
        "_id": "$_id",
        "posts": { "$first": "$posts" },
        "comments": { "$push": "$comments" },
        "commentUpVotes": { "$push": "$commentUpVotes" }
    }}

])

所以最终的结果是:

{
    "_id" : ObjectId("539065d3cd0f2aac5f55778e"),
    "posts" : [
            {
                    "title" : "post1",
                    "id" : "123"
            }
    ],
    "comments" : [
            {
                    "id" : 1910,
                    "postId" : "123",
                    "title" : "comment1",
                    "comment" : "some comment",
                    "user" : "user13"
            }
    ],
    "commentUpVotes" : [
            {
                    "id" : 12,
                    "commentId" : "1910",
                    "upvotedBy" : "user91"
            }
    ]
}

我知道您要求"没有架构更改",但实际上不是架构更改,而是说将id值保持在一致类型是个好主意。目前你在这个过程中混合了整数和字符串(我希望它只是一个例子),这不是一个好主意。

因此,有一些"有限的铸造"实际上可以使用$substr在这里使用,但是你的实际解决方案可能会有所不同。如果确实需要修复数据,我强烈建议修复数据。

无论如何,$map

的用法非常酷