MongoDB模式:将id存储为FK或整个文档



我正在设计MongoDB结构(实际上是NodeJS应用程序中的模型结构(。我将收集玩家比赛

最好只将玩家加入的比赛的id存储在每个玩家的对象中(就像RDBM中的FK(,还是将整个比赛对象存储在玩家对象中?

在应用程序中,其中一个操作是显示比赛的详细信息,在这个视图中,用户将看到参加这场特定比赛的球员(他们的姓名、国家/地区等(。这让我认为将整个Match文档存储在Player文档中会更好。

有什么建议吗?

我认为将整个Match文档存储在Player文档中不是一个好的选择。每次球员参加比赛时,您的球员文档都需要更新。

你有两个主要的选择:

1-(使用子引用。(参考比赛中的选手(。

因此,如果我们想使用猫鼬模型来实现这一点:

播放器型号:

const mongoose = require("mongoose");
const playerSchema = mongoose.Schema({
name: String,
country: String
});
const Player = mongoose.model("Player", playerSchema);
module.exports = Player;

匹配型号:

const mongoose = require("mongoose");
const matchSchema = mongoose.Schema({
date: {
type: Date,
default: Date.now()
},
players: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "Player"
}
]
});
const Match = mongoose.model("Match", matchSchema);
module.exports = Match;

有了这些模型,我们的比赛文档将是这样的(引用playerId(:

{
"_id" : ObjectId("5dc419eff6ba790f4404fd07"),
"date" : ISODate("2019-11-07T16:19:39.691+03:00"),
"players" : [
ObjectId("5dc41836985aaa22c0c4d423"),
ObjectId("5dc41847985aaa22c0c4d424"),
ObjectId("5dc4184e985aaa22c0c4d425")
],
"__v" : 0
}

我们可以使用此路线获取所有玩家的比赛信息:

const Match = require("../models/match");

router.get("/match/:id", async (req, res) => {
const match = await Match.findById(req.params.id).populate("players");
res.send(match);
});

结果是这样的:

[
{
"date": "2019-11-07T13:19:39.691Z",
"players": [
{
"_id": "5dc41836985aaa22c0c4d423",
"name": "player 1",
"country": "country 1",
"__v": 0
},
{
"_id": "5dc41847985aaa22c0c4d424",
"name": "player 2",
"country": "country 1",
"__v": 0
},
{
"_id": "5dc4184e985aaa22c0c4d425",
"name": "player 3",
"country": "country 2",
"__v": 0
}
],
"_id": "5dc419eff6ba790f4404fd07",
"__v": 0
}
]

2-(在比赛中嵌入球员,并保持独立的球员集合。但这将需要比第一种选择更多的空间。

所以你的匹配在匹配集合中会是这样的:

{
"date": "2019-11-07T13:19:39.691Z",
"players": [
{
"_id": "5dc41836985aaa22c0c4d423",
"name": "player 1",
"country": "country 1",
"__v": 0
},
{
"_id": "5dc41847985aaa22c0c4d424",
"name": "player 2",
"country": "country 1",
"__v": 0
},
{
"_id": "5dc4184e985aaa22c0c4d425",
"name": "player 3",
"country": "country 2",
"__v": 0
}
],
"_id": "5dc419eff6ba790f4404fd07",
"__v": 0
}

但当获得比赛信息时,这可能会快一点,因为不需要填充球员信息。

const Match = require("../models/match");
router.get("/match/:id", async (req, res) => {
const match = await Match.findById(req.params.id);
res.send(match);
});

在我看来,这里的matches集合是一个独立存在的文档集合,然后与参与比赛的玩家连接。话虽如此,我将执行一组匹配键

如果嵌套的文档可以被视为父文档"拥有",我建议使用嵌套文档结构。例如,todo文档中的todo嵌套文档。

这是一个多对多关系的例子。我猜最初会有大约100名球员和100场比赛的数据。设计选项为嵌入引用

(1(嵌入:

查询最多的一侧将嵌入查询较少的一侧。根据您的要求(显示比赛的详细信息,在该视图中,用户将看到参加这场特定比赛的球员及其详细信息(,比赛方将嵌入球员数据。

结果是两个集合。主要的是火柴。次要的是球员;这将拥有一个玩家的所有源数据(id、name、dob、country和其他详细信息(。

一场比赛只存储少数球员数据,而比赛集合中只存储球员数据的子集。导致玩家数据重复。这很好,大部分是静态信息会被复制;比如名字和国家。但是,其中一些可能需要随着时间的推移进行更新,应用程序需要处理这一问题。

球员数据以嵌入文档数组的形式存储在比赛集合中这种设计是可能的解决方案

匹配:

_id
matchId
date
place
players [ { playerId 1, name1, country1 }, { playerId 2, ... }, ... ]
outcome

玩家:

_id
name
dob
country
ranking


(2(引用:

这也将有两个集合:球员和比赛。任何一方都可以进行参考,比赛可以参考球员,反之亦然。根据需求,查询最多的一方将具有查询较少的一方的引用;比赛将有球员id参考。这将是一组玩家id。

匹配:

_id
matchId
date
place
players [ playerId 1, playerId 2, ... ]

玩家集合将具有与先前情况相同的数据。