免责声明:我是新手。
我想模拟我的mongodb嵌入式文档的连接。如果我有一个嵌入式列表:
{
_id: ObjectId("5320f6c34b6576d373000000"),
user_id: "52f581096b657612fe020000",
list: "52f4fd9f52e39bc0c15674ea"
{
player_1: "52f4fd9f52e39bc0c15674ex",
player_2: "52f4fd9f52e39bc0c15674ey",
player_3: "52f4fd9f52e39bc0c15674ez"
}
}
每个玩家的玩家收藏类似于:
{
_id: ObjectId("52f4fd9f52e39bc0c15674ex"),
college: "Louisville",
headshot: "player.png",
height: "6'2",
name: "Wayne Brady",
position: "QB",
weight: 205
}
我想最终:
{
_id: ObjectId("5320f6c34b6576d373000000"),
user_id: "52f581096b657612fe020000",
list: "52f4fd9f52e39bc0c15674ea"
{
player_1:
{
_id: ObjectId("52f4fd9f52e39bc0c15674ex"),
college: "Louisville",
headshot: "player.png",
height: "6'2",
name: "Wayne Brady",
position: "QB",
weight: 205
},
etc...
}
}
所以我可以致电User.lists.first.player_1.name
。
这是我脑子里有意义的事情,因为我是铁杆新手......而且我不想在每个用户的名单中嵌入玩家,因为我有这样的许多裁员......
么?这有可能,如果是这样的话怎么样?这是一个好主意,还是有更好的方法?
答案 0 :(得分:1)
所以有一个典型的关系模型,让我们称之为“一对多”,你有用户或“用户团队”和一整套玩家。在典型的建模形式中,您希望“去规范化”以避免重复。
但是就是这样,MongoDB 没有加入。在当前用语中,联接不是“webscale”。所以它会让你思考该怎么做。或者 MongoDB是否会加入?
db.eval(function() {
var user = db.user.findOne({ "user_id": "52f581096b657612fe020000" });
for ( k in user.list ) {
var player = db.player.findOne({ "_id": user.list[k] });
user.list[k] = player;
}
return user;
});
哪个“可以说” “是一种加入”。这一切都是在服务器上完成的,对吗?
但不要那样做。虽然db.eval()
有用,但您要定期查询的内容并不是实际用途之一。阅读文档,其中显示警告。特别是,所有JavaScript都在一个线程中运行,因此可以非常快速地锁定。
现在客户端方面,您或多或少都在做同样的事情。你选择的ODM可能会再次“做同样的事情”,虽然它通常以某种方式隐藏它,所以你看不到那一部分。同样可以说你的SQL ORM也是如此,当你刚刚访问代码中的对象时,它也会“隐藏在你背后”并查询数据库。
mapReduce 。那么你提出的数据的问题是没有什么可以“减少”。有一种技术称为"incremental mapReduce",但它不适合这种类型的数据。一个话题本身,但你基本上需要与“玩家”相关联的所有“用户”,存储在“玩家数据”中,以实现任何类型的可行性。而这最终只是另一种“欺骗”联合的方式。
这是MongoDB存在的空间。
因此,它不是去做所有这些获取或加入,而是允许“预先加入”您的数据的概念。这样做的目的是允许更快,更原子的读写。这被称为嵌入。
查看您的数据,根本不应该存在嵌入问题。考虑一下要点:
据推测,您正在为给定用户建模“幻想团队”。如果“团队”不包含无数玩家,那将是公平的。
除了其他内容,您的“A1”用法可能会“显示”与该“用户团队”相关联的玩家。在很多情况下,您希望“显示”尽可能多的信息,和将其保留为单个读取操作。您还希望轻松地将“玩家”添加到“用户团队”。
虽然“播放器”可能包含“扩展信息”,甚至可能包含某些全球统计信息或分数,但该信息可能不是您要访问的信息,而与通常是“用户团队”。它可能是独立编写的,只有在查看“播放器详细信息”时才会读取。
这是支持嵌入的三个好例子。当然,您将复制存储在每个用户团队中的信息,而不是仅仅是一个小的“关键”参考。确保信息可能存在于完整的“播放器细节”的其他地方,并且也会重复。
但这里“重复”的意思是优化。所以在这里嵌入“一些数据”似乎是有效的,而不是全部,但是你经常在主要操作中使用它。考虑到“玩家”的名字,位置,身高和体重不太可能定期改变,甚至根本不会改变,那么这似乎是一个合理的权衡。
{
"_id": ObjectId("5320f6c34b6576d373000000"),
"user_id": ObjectId("52f581096b657612fe020000"),
"list": [
{
"_id": ObjectId("52f4fd9f52e39bc0c15674ex"),
"label": "Player1",
"college": "Louisville",
"headshot": "player.png",
"height": "6'2",
"name": "Wayne Brady",
"position": "QB",
"weight": 205
},
{
"label": "Player2",
(...)
}
]
},
那不是那么糟糕。打破16MB limit需要很多时间。考虑到这似乎是一个“用户团队”,那么它也可以用来自“用户”的一些信息。
当数据保存在一起时,你也可以从中获得很多力量,找到每个用户挑选的顶级“玩家”:
db.userteams.aggregate([
// Unwind the array
{ "$unwind": "$list" },
// Group and use the player name
{ "$group": {
"_id": {
"user_id": "$user_id",
"player": "$list.name",
},
"count": { "$sum": 1 }
}},
// Sort the results descending by popularity
{ "$sort": { "_id.user_id": 1, "count": -1 } },
// Group to limit the first one
{ "$group": {
"_id": "$_id.user_id",
"player": { "$first": "$_id.player" },
"picks": { "$first:" "$count" }
}}
])
在这种情况下,无可否认地使用了一个名称,但它是使用某些嵌入可以获得的信息的一个例子。
当然,你真的相信你需要对所有事情进行适当的规范化,然后以这种方式进行,并使用你需要访问它的模式。但这提供了另一种方式的视角。
所以不要过度关注嵌入所有内容,并且在嵌入某些内容时会有点担心。没有“退出监狱免费卡”以使用不适合标准关系方式的关系建模的东西。选择适合您需求的产品。