描述
尝试着实现最简单版本的 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,
};