在MongoDB中查找相关子文档数组的顶级条目

时间:2014-03-09 02:10:02

标签: node.js mongodb database-design nosql

我决定尝试将MongoDB用于我正在开发的游戏的后端服务器,但我没有数据库设计或设置的背景知识。也就是说,数据库似乎相当基础,大多数交易都是简单更新或查找查询。

我的架构的简化版本是这样的(实际上我的_id是数字并且保证唯一,对于索引来说是完美的):

users_collection
{
    _id: 11111,
    nameFirst: "Bilbo",
    nameLast: "Baggins"
    friends: [222222,3333333,444444],
    userScores:[
        { level: "OtherGameType", scores: 
            [{score: 150},{score: 250},{score: 350}] },
        { level: "Level1", scores: [{score: 200}] },
        { level: "Level2", scores: [{score: 200}] }
    ]
}

我的问题是我无法从所有该用户的朋友那里找到给定级别的最高X分数列表的好方法。最终,我希望输出是这样的:

[   {nameFirst:"Martin", nameLast:"Freeman", score:2012},
    {nameFirst:"Ian", nameLast:"Holm", score:2001},
    {nameFirst:"Norman", nameLast:"Bird", score:1978}   ]

最好的方法是什么?

一个问题是某人可能拥有非常多的朋友(500?1000?),每个朋友都有一个20或50分的特定级别的列表(而其他级别只会跟踪1到3个分数)。我意识到数据库通常以数百万的方式工作,但我很紧张,如果这是客户的共同呼叫,这可能会影响性能。

顺便说一下,我正在连接到Node.js中的服务器(在同一台服务器上),连接到它的目标设备将是通过REST请求的浏览器和移动设备。也就是说,如果需要或建议,我非常愿意重新构建模式或将某些计算推送到不同的层。所有建设性的批评都欢迎!

tl; dr:在重新设计数据库作为选项的情况下,如何从给定用户的朋友中检索最高X分数列表?

谢谢你的时间!

1 个答案:

答案 0 :(得分:1)

这个模型似乎并没有考虑到。您将所有分数附加到用户和附加的朋友列表。如果这不会太大,这应该没问题,并且它为您提供了大量单个读/写操作所需的功能。

但是看一下重要的部分,让我们得到你想要的信息。所以你在某个时候需要朋友的列表,你已经在文档中有一个:

{

    _id: 11111,
    friends: [222222,3333333,444444],
    userScores:[
        { level: "OtherGameType", scores: 
        [{score: 150},{score: 250},{score: 350}] },
        { level: "Level1", scores: [{score: 200}] },
        { level: "Level2", scores: [{score: 200}] }
    ]

} 

因此,给定某个用户,并且已将文档检索到名为user的变量中,您可以使用aggregate使用朋友列表来回询该集合:

db.users.aggregate([
    // Match all the friends and try to filter out results that have no
    // score for that level. Keeping the set size down
    { "$match": { 
       "_id": { "$in": user.friends }
       "userScores.level": "Level1",
    }},

    // Unwind the userScores
    { "$unwind": "$userScores" },

    // Really filter out the level that does not match
    { "$match": { "userScores.level": "Level1" } },    

    // Unwind the scores per level for the remaining results
    { "$unwind": "$scores"},

    // Group by user ("friend") to find the top scores
    { "$group": { 
        "_id": "$_id",
        "topScore": { "$max": "$userScores.scores.score"}
    }},

    // Sort the results by topScore descending
    { "$sort": { "topScore": -1 } },

    // Optionally limit to "n" results
    { "$limit": 10 }

])

你基本上拥有它。所有用户朋友在关卡中排名前10位(如果有那么多)。

至于所有您的其他问题,这些问题可能最好在他们自己的背景下作为他们自己的问题呈现。我知道有一种诱惑可以一次性询问所有问题,特别是当你找到某人的答案时,你通过分离它们来获得更有意义的答案。

关于你的架构的最后一点说明。虽然它应该适用于大多数事情,但我只想指出您已经通过在userScores数组中包含内部分数列表来引入嵌套数组。您需要注意的是使用positional operator更新这些项目的限制。

这是可以解决的问题,但你应该知道这些问题。

其他有价值的阅读,更多是关于aggregation operators和更多的管道流程。如果您经常需要统计类型的结果,那么您可能会使用它。