本文将介绍MongoClient,Mongodb官方提供的Nodejs驱动,虽然已经不是Node.js操作 MongoDB的首选库了,但还是很值得学习的。目前Node.js操作 MongoDB的首选库是Mongoose,👉Node.js集成MongoDB之Mongoose入门
MongoClient
MongoClient是Mongodb官方提供的Node.js驱动,继承于mongo类(现在mongo模块被淘汰了),对MongoDB的操作和在mongo shell中几乎一致,有兴趣可以看下我这篇入门MongoDb教程。
1️⃣ 基础
初始化项目
mkdir mongo-client //新建一个文件夹
cd mongo-client //进入这个文件夹
npm init -y //生成package.json
npm i mongodb //安装mongodb模块
至此,项目目录结构应如此:
mongo-client
├── node_modules/
├── package-lock.json
└── package.json
连接数据库
//simple_connect.js
const MongoClient = require("mongodb").MongoClient;
const url="mongodb://localhost/test";
MongoClient.connect(url,(err,db)=>{
if (err) throw err;
console.log('connected!');
db.close();//虽说不是非要主动断开数据库连接资源,但这不失为一个好习惯
})
运行node simple_connect.js
,控制台打印了connected!
,预示着已经连接上了本地test数据库!但是也产生一串告警,虽然目前不影响使用,但是有代码洁癖要求的人一般无法容忍,简单看下,大约是xx要被版本抛弃,传递xxx参数以便使用新特性(这种写法其实是为了向下兼容,慢慢过渡到新版本)。之后很多运行都会产生类似告警,其实普通学习者简单了解足矣,不用太深入考虑为什么要加这个option项,就因为运行它这么提示要加上去的即可。
ok!改一下,就没有产生那一堆告警了。
MongoClient.connect(url,{ useUnifiedTopology: true },(err,db)=>{
if (err) throw err;
console.log('connected!');
db.close();//虽说不是非要主动断开数据库连接资源,但这不失为一个好习惯
})
CRUD
主要简单介绍集合和文档的增删改查,更多(如:分页等)请查看我这篇入门MongoDb教程或者👉Node.js 连接 MongoDB,其实和Mongo Shell操作几乎一致,只是加了回调函数作为I/O反馈。以下I/O操作都是同步执行的,如果其中包含多个I/O操作,要保证其能够顺序执行,往往就会引发地狱回调。
集合
1.新建集合
//新建
MongoClient.connect(url, { useUnifiedTopology: true }, (err, db) => {
if (err) throw err;
const dbo = db.db("test"); //切换数据库
dbo.createCollection("student", (err, res) => {//创建集合
if (err) throw err;
console.log("built");
db.close();
});
});
2.删除集合
//删除
dbo.collection("student").drop((err, res) => {
if (err) throw err;
console.log("removed!");
db.close();
});
文档
1.插入文档
const MongoClient = require("mongodb").MongoClient;
const url = "mongodb://localhost";
MongoClient.connect(url, { useUnifiedTopology: true }, (err, db) => {
const dbo = db.db("test");
let doc = {
name: "Joe",
age: 18,
favor: { animals: "cat", city: "guangzhou" },
};
const stuCol = dbo.collection("student"); //选取集合
stuCol.insertOne(doc, (err, res) => {//插入文档
console.log(res.result); //{ n: 1, ok: 1 }
db.close();
});
});
2.查询文档
let query = { age: { $gte: 18 } }; //查询条件
stuCol.find(query).toArray((err, res) => {//将查询结果转化为数组
console.log(res);
db.close();
});
3.修改文档
stuCol.updateOne({name:"Joe"},{$set:{"favor.city":"ShenZhen"}},(err,res)=>{//修改
console.log(res.result);//{ n: 1, nModified: 1, ok: 1 }
db.close();
})
4.删除文档
stuCol.deleteOne({name:'Joe'},(err,res)=>{
console.log(res.result);//{ n: 1, ok: 1 }
db.close();
})
异步风格(Promise Async)
对数据库的I/O操作都是异步执行的,上文的方法要保证同时对数据库的多个操作能顺序执行,那只能不停的嵌套回调函数。比如:
回调函数
//伪代码 保证先执行完文档插入操作再进行读取
const stuCol = dbo.collection("student"); //选取集合
stuCol.insertOne(docObj, (err, res) => {
stuCol.find(query).toArray((err, res) => {
console.log(res);
db.close();
});
});
ES6推出了Promise和async函数来更好进行异步编程,如果不熟悉这两个新特性的小伙伴,可以点击链接过去看看我的拙文。
Promise
1.创建集合
const MongoClient = require("mongodb").MongoClient;
const url="mongodb://localhost";
MongoClient.connect(url,{ useUnifiedTopology: true }).then((conn)=>{
const dbo=conn.db('test');
dbo.createCollection('teacher').then(res=>{
console.log('collection built!');
}).catch((e)=>{
console.log(e);
}).finally(()=>{
conn.close();
})
})
2.文档CRUD
collection.find()
返回的是Curcor对象,需要调用其toArray()
方法将集合中的文档装载成数组。
MongoClient.connect(url, { useUnifiedTopology: true }).then((conn) => {
const tchCol = conn.db("test").collection("teacher");
let tchs = [
{ name: "GG", age: 30, subject: "Math" },
{ name: "MM", age: 26, subject: "English" },
];
tchCol
.insertMany(tchs).then(() => { //插入数据
return tchCol.find().toArray().then((arr) => {//尾调用 将返回值传递给下一个then方法
console.log(arr);
});
}).then((res) => { //此时res是一个数组 想要继续数据库操作 那就还是使用collection对象
return tchCol.updateOne({ name: "GG" }, { $set: { age: 28 } });//修改数据
}).then(()=>{
return tchCol.find().toArray().then((res) => {
console.log(res);
});
})
.catch((e) => console.log(e))
.finally(() => {
conn.close();
});
}).catch(e=>console.log(e));
必须清晰每一步的操作和产生的结果!虽然没了地狱回调,讲道理,也就那样吧!
Async
大佬总是最后出场的!异步函数是目前书写异步编程中最优雅的,也是最适合在多次使用异步操作的时候使用。
const MongoClient = require("mongodb").MongoClient;
const url = "mongodb://localhost";
const ADD = 1;
const UPDATE = 2;
const QUERY = 3;
const DELETE = 4;
async function dbOperateOnTeacher(operator = 0) {
let conn=null;
let docs=null;
try {
conn = await MongoClient.connect(url, { useUnifiedTopology: true });
const tchCol = await conn.db("test").collection("teacher");
switch (operator) {
case ADD:
await tchCol.insertOne({ name: "KK", age: 42, subject: "Chinese" });
docs=await tchCol.find().toArray();
console.log(docs);
break;
case UPDATE:
await tchCol.updateOne({ name: "KK" }, { $set: { age: 36 } });
docs=await tchCol.find().toArray();
console.log(docs);
break;
case QUERY:
docs=await tchCol.find().toArray();
console.log(docs);
break;
case DELETE:
await tchCol.deleteOne({ name: "KK" });
docs=await tchCol.find().toArray();
console.log(docs);
break;
default:
console.log("不知道你想干嘛");
break;
}
} catch (error) {
console.log(error);
} finally{
conn.close();
}
}
(async function autoCRUD(){
console.log("ADD");
await dbOperateOnTeacher(ADD);
console.log("UPDATE");
await dbOperateOnTeacher(UPDATE);
console.log("DELETE");
await dbOperateOnTeacher(DELETE);
})()
2️⃣进阶案例 模块化
现在都是模块化编程,以下举个简单例子,操作用户的增删改查。
数据库连接类
//db_utils.js
const MongoClient = require("mongodb").MongoClient;
const url = "mongodb://localhost";
module.exports = {
conn: null,
connect(){
return new Promise((resolve,reject)=>{
MongoClient.connect(url, { useUnifiedTopology: true },(err,client)=>{
if(err) reject(err);
this.conn=client;
resolve(this.conn);
})
})
},
close() {
this.conn?.close();
},
};
集合操作类
使用静态方法可以不用新建实例对象,直接通过类就可以调用方法,绑定在类对象身上,在声明类对象的时候分配内存。为了代码呈现整洁,暂未做异常捕获,使用时把每个异步函数方法体try…catch包起来即可。
//user_modal.js
const dbHelper = require("./db_utils");
class User {
constructor(username, pwd) {
this.username = username;
this.pwd = pwd;
}
static async insertOne({ username, pwd }) {
const conn = await dbHelper.connect();
const dbo = conn.db("test");
const userCol = dbo.collection("user");
await userCol.insertOne({ username: username, pwd: pwd });
const users = await userCol.find().toArray();
console.log(users);
dbHelper.close();
}
static async find(username) {
const conn = await dbHelper.connect();
const dbo = conn.db("test");
const userCol = dbo.collection("user");
const user = await userCol.find({ username: username }).toArray();
console.log(user);
dbHelper.close();
}
static async findAll() {
const conn = await dbHelper.connect();
const dbo = conn.db("test");
const userCol = dbo.collection("user");
const users = await userCol.find().toArray();
console.log(users);
dbHelper.close();
}
static async updateUserName(oldName, newName) {
const conn = await dbHelper.connect();
const dbo = conn.db("test");
const userCol = dbo.collection("user");
await userCol.updateOne( { username: oldName },{ $set: { username: newName}});
const users = await userCol.find().toArray();
console.log(users);
dbHelper.close();
}
static async deleteOneByUserName(username) {
const conn = await dbHelper.connect();
const dbo = conn.db("test");
const userCol = dbo.collection("user");
await userCol.deleteOne( { username: username });
const users = await userCol.find().toArray();
console.log(users);
dbHelper.close();
}
}
module.exports = User;
业务执行
//index.js
const { insertOne, updateUserName, deleteOneByUserName } = require('./user_modal');
const User=require('./user_modal');
const Joe=new User('Joe','123');
(async ()=>{
await User.insertOne(Joe);
await User.updateUserName('Joe','KK');
await User.deleteOneByUserName('KK');
})()