MongoDB 架构:将 id 存储为 FK 或整个文档

MongoDB schema: store id as FK or whole document

我正在设计 MongoDB 结构(实际上是 NodeJS 应用程序中的模型结构)。我会有 玩家 比赛 collections。

是只存储玩家参加的比赛的 ID,在每个玩家的 object 中(就像 RDBM 中的 FK)还是在玩家 object 中存储整个比赛 object =27=]?

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

有什么建议吗?

在我看来,这里的matches集合是一个独立存在的文档集合,然后与参加比赛的玩家联系起来。话虽如此,我会做一个匹配键数组

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

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

您有 2 个主要选择:

1-) 使用子引用。 (引用比赛中的球员)。

所以如果我们想使用 mongoose 模型来实现它:

球员模型:

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-) 将玩家嵌入比赛中,并仍然保持独立的玩家集合。 但这将比第一个选项需要更多 space。

因此您的匹配项在匹配项集合中将如下所示:

    {
        "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);
});

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

(1) 嵌入:

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

结果是两个 collections.The 主要的是比赛。次要的是玩家;这将包含玩家的所有源数据(id、姓名、dob、国家和其他详细信息)。

一场比赛只存储了少数球员数据,比赛集合中只存储了球员数据的一个子集。 导致播放器数据重复。这很好,它主要是将被复制的静态信息;名字和国家之类的东西。但是,随着时间的推移,其中一些可能需要更新,应用程序需要处理这个问题。

玩家数据存储为 matches 集合中的嵌入文档数组。 此设计是可能的解决方案。

匹配项:

_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, ... ]

players 集合将具有与之前情况相同的数据。