首页 > 解决方案 > 构建 mogodb 模式

问题描述

我在玩 MongoDB,想知道 SQL 模式如何与 MongoDB 相对应的最佳实践是什么。这是我到目前为止的表格/数据:

结构是这样的:

注意:所有用户都至少有一个答案或一个 repo,但不一定必须同时拥有。

我怎么能把它放到一个 mongo 模式中?这会是一个“收藏”吗?或者这将是两个集合:一个用于用户,一个用于 repo;或者更多?

我的查询将是这样的:“用 tay [Python] 获得超过 2 个赞成票的答案或带有超过 2 个星的 [Python] 标签的 repo 的所有用户。

标签: mongodbschemadatabase-schema

解决方案


让我把它分为几个步骤:

第 1 步 - MONGODB 和 MONGOOSE

MongoDB 是一个基于文档的数据库。集合中的每条记录都是一个文档,并且每个文档都应该是自包含的(它应该包含您在其中需要的所有信息)。

由于 MongoDB 是一个无关系数据库,您不能在集合之间创建关系,但您可以将一个集合文档的引用存储为另一个集合文档的属性。为了帮助您管理所有这些,有一个很棒的包叫做Mongoose,它允许您为每个集合创建一个模型。定义模型后,Mongoose将允许您轻松地对数据库进行查询。

第 2 步 - 定义模型

正如我们所说,文档应该是独立的,因此它们应该包含您需要的所有信息。根据您的示例,我们可以有两种方法:

方法一:

为关系数据库中的每个表创建一个集合。当您拥有包含大量数据的文档时,这是最佳实践,因为它是可扩展的。

方法 2:

创建 3 个集合 - 用户、答案和回购。因为repo_contrib没有很多数据,所以可以将所有用户的贡献存储在一个 USERS 文档中。这样,当您获取用户文档时,您将在一个地方拥有所需的一切。这同样适用repo_tag——我们可以将所有 repo 的标签存储在一个 REPOS 文档中。

方法 3:

创建 2 个集合 - USERS 和 REPOS。与方法 2 相同,但您也可以将所有用户添加answers到 USERS 文档中。

推荐:

在这种情况下,我会使用 APPROACH 2,因为它不存储大数据,并且可以轻松地存储在 USERS 和 REPOS 文档中,没有问题repo_contribrepo_tag此外,如果我们采用这种方法,它将使查询数据库变得更加容易。我没有选择选项 3 的原因是因为理论上用户可以有数千或数万个答案,并且它不会很好地扩展。

第 3 步 - 实施

注意:MongoDB 会自动分配_id给每个文档,因此您在实现模型时不必定义id属性。

关系数据库示例中的表可以映射到这样的集合(此实现适用于 APPROACH 2):

用户收藏:

const mongoose = require('mongoose');
const Schema = mongoose.Schema;

var schema = new Schema({
    email: { type: String, required: true, unique: true },
    name: { type: String, required: true, unique: false },
    contributions: [{
      repo_id: { type: mongoose.Schema.Types.ObjectId, ref: 'REPOS' },
      lines_of_code: { type: Numeric, ref: 'REPOS' }
    }]
});
const Users = mongoose.model('USERS', schema);
module.exports = Users;

答案集合:

const mongoose = require('mongoose');
const Schema = mongoose.Schema;

var schema = new Schema({
    user_id: { type: mongoose.Schema.Types.ObjectId, ref: 'USERS', required: true },
    tag: { type: String, required: true, unique: false },
    upvotes:{ type: Number, default: 0, unique: false }
});
const Answers = mongoose.model('ANSWERS', schema);
module.exports = Answers;

回购集合:

const mongoose = require('mongoose');
const Schema = mongoose.Schema;

var schema = new Schema({
    owner: { type: mongoose.Schema.Types.ObjectId, ref: 'USERS', required: true },
    name: { type: String, required: true, unique: false },
    description: { type: String, required: false, unique: false },
    stars:{ type: Number, default: 0, unique: false },
    tags: [{
      name:  { type: String, required: true, unique: false },
      is_language: {type: Boolean, required: true, unique: false},
      percentage:{ type: Number, default: 0, unique: false }
    }]
});
const Repos = mongoose.model('REPOS', schema);
module.exports = Repos ;

第 4 步 - 人口和数据库查询

的最佳功能之一Mongoose称为population. 如果您将一个集合文档的引用存储为另一个集合文档的属性,则在执行数据库查询时,Mongoose会将引用替换为实际文档。

示例 1:

让我们首先以您建议的第一个查询为例:Find all users with an Answer with tag [Python] with more than 2 upvotes. 由于我们将user_idANSWERS 集合作为对来自 USERS 集合的文档的引用存储在 ANSWERS 集合中,这意味着我们可以只查询 ANSWERS 集合,并且在返回最终结果Mongoose时将转到 USERS 集合并将引用替换为实际的用户文档。将执行此操作的数据库查询如下所示:

const ANSWERS = require('../models/answers');

ANSWERS.find({
  "tag": "Python",
  "upvotes": {
    "$gt": 2
  }
}).populate('user_id');

示例 2:

您建议的第二个查询是:Find all repos with the [Python] tag with more than two stars。由于我们将所有 repo 的标签存储在一个数组中,我们只需要检查该数组是否包含name字段等于的项目Python,并且 repo 的stars字段大于 2。将执行此操作的数据库查询如下所示:

const REPOS = require('../models/repos');

REPOS.find({
  "tags.name": "Python",
  "stars": {
    "$gt": 2
  }
})

这也是工作示例:https ://mongoplayground.net/p/rgBtVVDgPzG


推荐阅读