描述
尝试着实现最简单版本的 mongoose 。
难度
4 星
需求
假定实现的小框架名为 SMDB (Simple MongoDB)。
要求实现如下能力:
const smdb = require('./smdb');
// 注册一个 user Schema
const UserSchema = new smdb.Schema({
account: String,
password: String,
});
smdb.model('User', UserSchema);
// 得到一个 model
const UserModel = smdb.model('User');
// 连接数据库
smdb.connect('mongodb://localhost:27017/smdb-test').then(async () => {
const user = new UserModel({
account: '1',
password: '2',
});
const res = await user.save();
console.log('添加成功', res);
const list = await UserModel.find();
console.log('列表:', list);
}).catch(e => {
console.log(e);
});
只需要实现以上功能,Schema 的属性定义,类型也只需要支持 String,其他非空等都不需要处理。
目的
为了更好的了解 mongoose 处理机制,培养工程思维,提升编码水平,以及脱离 mongoose 应该怎么去操作 MongoDB。
研究路线
首先要了解脱离 mongoose 后,怎么使用 node.js 去链接 MongoDB,并且操作 MongoDB。
node.js 有一个 mongodb 包,翻阅 mongoose 的源码或者观察 mongoose 项目的 package.json 会发现也使用了这个包,所以可以从这个包入手。
了解完这个包的API后,其实就是对他进行一个封装,对应的套用mongoose的模式即可。
实现
// 引入 mongodb
const mongodb = require('mongodb');
// mongodb 客户端操作工具库
const MongoClient = mongodb.MongoClient;
// 获取数据类型
const getType = (sth) => {
const flag = Object.prototype.toString.call(sth);
return flag.replace('[object ', '').replace(']', '');
};
// 放数据库实例 后续操作数据库都通过这个 db
let db = null;
// 连接数据库
const connect = (url) => {
return new Promise((resolve, reject) => {
MongoClient.connect(url, (err, client) => {
if (err) {
reject(err);
return;
}
db = client.db();
resolve(client);
});
});
};
// Schema 类
class Schema {
schema = {};
constructor(schema) {
this.schema = schema;
}
}
// 所有注册过的 model 都放在这里
const modelMap = {};
const model = (key, schemaInst) => {
// 如果model已经存在,就返回已存在的 model
if (modelMap[key]) {
return modelMap[key];
}
// 如果model不存在 则创建一个
modelMap[key] = class {
// 放置 model 对应的 schema
schema = schemaInst;
// 创建新文档时候的数据
data = {};
// 静态方法,用于寻找所有文档
static find() {
return new Promise((resolve, reject) => {
db
.collection(`${key}s`)
.find()
.toArray((err, docs) => {
if (err) {
reject(err);
return;
}
resolve(docs);
});
})
}
// 数据类型校验
check() {
for (let key of Object.keys(this.data)) {
const type = this.schema.schema[key];
if (!type.toString().includes(getType(this.data[key]))) {
throw TypeError(`${key} 应该是 String 类型`);
}
}
}
// 构造函数
constructor(data) {
this.data = data;
this.check();
}
// 保存
save() {
return new Promise((resolve, reject) => {
db
.collection(`${key}s`)
.insertOne(this.data)
.then((res) => {
resolve(res.result);
})
.catch((err) => {
reject(err);
});
});
}
};
return modelMap[key];
};
module.exports = {
connect,
Schema,
model,
};