让我把这分成几个步骤:
第 1 步 - MONGODB 和 MONGOOSE
MongoDB 是一个基于文档的数据库。集合中的每条记录都是一个文档,并且每个文档都应该是自包含的(它应该包含您在其中需要的所有信息)。
由于 MongoDB 是一个无关系数据库,您不能在集合之间创建关系,但您可以将一个集合文档的引用存储为另一个集合文档的属性。为了帮助您管理所有这些,有一个名为Mongoose 的很棒的包,它允许您为每个集合创建一个模型。定义后
模型,Mongoose 将允许您轻松地对数据库进行查询。
第 2 步 - 定义模型
正如我们所说,文档应该是独立的,因此它们应该包含您需要的所有信息。根据您的示例,我们可以有两种方法:
方法 1:
为关系数据库中的每个表创建一个集合。当您拥有包含大量数据的文档时,这是最佳做法,因为它是可扩展的。
方法 2:
创建 3 个集合 - USERS、ANSWERS 和 REPOS。因为repo_contrib 没有很多数据,所以可以将所有用户的贡献存储在一个USERS 文档中。这样,当您获取用户文档时,您将在一个地方拥有所需的一切。 repo_tag 也是如此——我们可以将所有 repo 的标签存储在一个 REPOS 文档中。
方法 3:
创建 2 个集合 - USERS 和 REPOS。同 APPROACH 2,但您也可以将所有用户的answers 添加到 USERS 文档中。
建议:
在这种情况下,我会使用 APPROACH 2,因为 repo_contrib 和 repo_tag 不存储大数据,并且可以轻松存储在 USERS 和 REPOS 文档中,没有问题。此外,如果我们采用这种方法,它将使查询数据库变得更加容易。我没有选择选项 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;
REPOS 集合:
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_id 存储在 ANSWERS 集合中作为对来自 USERS 集合的文档的引用,这意味着我们可以只查询 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 的标签存储在一个数组中,我们只需要检查该数组是否包含具有等于 Python 的 name 字段的项目,并且 repo 的 stars 字段大于 2。将执行此操作的数据库查询如下所示:
const REPOS = require('../models/repos');
REPOS.find({
"tags.name": "Python",
"stars": {
"$gt": 2
}
})
这也是工作示例:https://mongoplayground.net/p/rgBtVVDgPzG