上一篇: node.js 08 代码创建 http 静态web服务器
对于很多前端转全栈的朋友们来说,数据库操作选择MongoDB相对容易上手。后端对关系型数据库SQL熟悉的朋友们,对于MongoDB数据库了解起来就比较快了,很多操作跟SQL类似。
在准备本篇以前,我准备了两篇介绍MongoDB的文章如下,建议对MongoDB不了解的朋友先看一下,有个概念。两篇文章属于操作型,读起来挺快的。
- 五分钟了解MongoDB介绍,安装与使用
- MongoDB数据库读写操作
本篇会介绍node.js连接数据库,增删改查,查询过程的具体操作以及数据库连接池等等。
node.js & MongoDB
安装MongoDB驱动
跟Java连接数据库需要驱动jar文件,node.js连接数据库也需要驱动。这里需要使用的驱动是"mongodb",安装通过npm进行,命令如下:
D:ProjectsodejsNodeDemoode09>npm install mongodb --savenpm notice created a lockfile as package-lock.json. You should commit this file.npm WARN node09@1.0.0 No descriptionnpm WARN node09@1.0.0 No repository field.+ mongodb@3.5.5added 20 packages from 11 contributors and audited 21 packages in 4.977sfound 0 vulnerabilities
连接MongoDB 本地数据库
首先需要引入"MongoDB",获取MongoClient。写入数据库URL,通过MongoClient.connect()进行连接。要注意的是,相关操作结束后,需要通过MongoClient.close()关闭数据库连接。
//1. 获取MongoClientconst MongoClient = require('mongodb').MongoClient;//2. Connection URLconst url = 'mongodb://localhost:27017';// 3. Database Nameconst dbName = 'TestDB';const client = new MongoClient(url, {useNewUrlParser: true});//4. 连接数据库client.connect(function(err) { console.log("Connected successfully to server");//5. 获取目标数据库 const db = client.db(dbName); client.close();});
连接MongoDB 远端数据库
之前的文章里面我们关于MongoDB的安装实在本地的,这时候如果把上面代码中的url换成IP地址仍然是不可行的。
const url = 'mongodb://192.168.0.xxx:27017';
- 需要MongoDB能够提供网络服务,需要修改配置文件。该文件在Windows系统下为:/Server/4.2/bin/mongod.cfg。
在该文件中将"bindIp: 127.0.0.1"注释掉,添加"bindIp: 0.0.0.0"。示例如下:
# bindIp: 127.0.0.1 bindIp: 0.0.0.0
- 启动认证
但是MongoDB默认情况下是不需要用户名和密码登录的,如果需要用户名/密码进行登录验证,仍然修改mongod.cfg文件,添加如下信息:
security: authorization: enabled
- 创建admin用户
接下来还需要为自己的数据库创建admin用户,在命令行中输入"mongo"通过MongoDB命令行。
D:ProjectsodejsNodeDemoode09>mongoMongoDB shell version v4.2.3connecting to: mongodb://127.0.0.1:27017/?compressors=disabled&gssapiServiceName=mongodbImplicit session: session { "id" : UUID("0d7f3322-88f9-400c-9b50-068de80f9956") }MongoDB server version: 4.2.3
切换到自己的数据库,这里我的数据库为TestDB
> use TestDBswitched to db TestDB
创建admin用户"vincent"
> db.createUser({ user: 'vincent', // 用户名 pwd: '123456', // 密码 roles:[{ role: 'root', // 角色 db: 'admin' // 数据库 }] })Successfully added user: { "user" : "vincent", "roles" : [ { "role" : "root", "db" : "admin" } ]}
修改结束后,重启MongoDB服务即可。
在代码中,我们可以进行数据库连接了。对比之前的代码,主要的改动就是url,在url中添加用户名/密码,以及数据库名称即可。
const url = 'mongodb://vincent:123456@192.168.0.104:27017/TestDB';
添加文档
MongoDB支持单个文档记录添加,也支持多个文档记录添加。
- 单条添加:db.collection('collection_name').insertOne()
- 多条添加:db.collection('collection_name').insertMany()
示例如下
const MongoClient = require('mongodb').MongoClient;//1. 远端MongoDB连接const url = 'mongodb://vincent:123456@192.168.0.104:27017/TestDB';const client = new MongoClient(url,{ useUnifiedTopology: true});client.connect(function(err, client) { console.log("Connected correctly to server"); const db = client.db('TestDB'); // 2. 添加单条记录 db.collection('items').insertOne({"item":"ruler","qty":30}, function(err, r) { console.log(r.insertedCount) // 3.添加多条记录 db.collection('items').insertMany([{"item":"box","qty":15}, {"item":"glue","qty":60}], function(err, r) { console.log(r.insertedCount) //4. 关闭数据库连接 client.close(); }); }); });
需要注意的是,由于node.js是单线程异步操作,所以数据库连接关闭一定是在最后一个操作的闭包函数里。
更新文档
与添加文档一样,MongoDB同样提供更新一条记录和更新多条记录的方法。分别是updateOne和updateMany。另外还有是upsert,在方法updateOne中通过参数upsert来控制。如果需要更新的记录不存在,那么该方法将记录添加到数据库。
const MongoClient = require('mongodb').MongoClient;const url = 'mongodb://vincent:123456@192.168.0.104:27017/TestDB';const client = new MongoClient(url,{ useUnifiedTopology: true});client.connect(function(err, client) { const db = client.db('TestDB'); const col = db.collection('items') // 更新单条记录 col.updateOne({"item":"box"}, {$set: {"qty": 75}}, function(err, r) { console.log(r.matchedCount) console.log(r.modifiedCount); // 更新多条记录 col.updateMany({"item":"ruler"}, {$set: {"qty": 105}}, function(err, r) { console.log(r.matchedCount) console.log(r.modifiedCount); //upsert一条记录,如果该记录存在,则进行更新;如果不存在,则插入记录到数据库 col.updateOne({"item":"book"}, {$set: {"qty": 18}}, { upsert: true }, function(err, r) { console.log(r.matchedCount) console.log(r.modifiedCount); client.close(); }); }) })})
删除文档
提供deleteOne和deleteMany两个方法进行单条和多条记录删除。
const MongoClient = require('mongodb').MongoClient;const url = 'mongodb://vincent:123456@192.168.0.104:27017/TestDB';const client = new MongoClient(url,{ useUnifiedTopology: true});client.connect(function(err, client) { const db = client.db('TestDB'); const col = db.collection('items') // 删除一条记录 col.deleteOne({"item":"box"}, function(err, r) { console.log(r.deletedCount) // 删除多条记录 col.deleteMany({"item":"ruler"},function(err, r) { console.log(r.deletedCount) client.close(); }) })})
查询文档
提供find方法就行查询,在查询后可以使用toArray将结果转为Array。
示例代码:
const MongoClient = require('mongodb').MongoClient;const url = 'mongodb://vincent:123456@192.168.0.104:27017/TestDB';const client = new MongoClient(url,{ useUnifiedTopology: true});client.connect(function(err, client) { const db = client.db('TestDB'); const col = db.collection('items') // 查找所有记录 col.find().toArray(function(err, r) { console.log(r) client.close(); })})
如果需要添加查询条件,可以在find()中添加参数
col.find({"qty":50}).toArray(function(err, r) {
另外,MongoDB还可以提供多个查询方法,我这里不一一提供示例了,有兴趣的朋友们可以试以下。
collection.find({}).project({a:1}) // Create a projection of field acollection.find({}).skip(1).limit(10) // Skip 1 and limit 10collection.find({}).batchSize(5) // Set batchSize on cursor to 5collection.find({}).filter({a:1}) // Set query on the cursorcollection.find({}).comment('add a comment') // Add a comment to the query, allowing to correlate queriescollection.find({}).addCursorFlag('tailable', true) // Set cursor as tailablecollection.find({}).addCursorFlag('oplogReplay', true) // Set cursor as oplogReplaycollection.find({}).addCursorFlag('noCursorTimeout', true) // Set cursor as noCursorTimeoutcollection.find({}).addCursorFlag('awaitData', true) // Set cursor as awaitDatacollection.find({}).addCursorFlag('exhaust', true) // Set cursor as exhaustcollection.find({}).addCursorFlag('partial', true) // Set cursor as partialcollection.find({}).addQueryModifier('$orderby', {a:1}) // Set $orderby {a:1}collection.find({}).max(10) // Set the cursor maxcollection.find({}).maxTimeMS(1000) // Set the cursor maxTimeMScollection.find({}).min(100) // Set the cursor mincollection.find({}).returnKey(10) // Set the cursor returnKeycollection.find({}).setReadPreference(ReadPreference.PRIMARY) // Set the cursor readPreferencecollection.find({}).showRecordId(true) // Set the cursor showRecordIdcollection.find({}).sort([['a', 1]]) // Sets the sort order of the cursor querycollection.find({}).hint('a_1') // Set the cursor hint
在查询操作中,如果需要对每一条结果记录进行读取再进行操作,可以使用each
col.find({"qty":30}).each(function(err, doc) { });
聚合Aggregation
这部分相对前面的增删改查要复杂一点。MongoDB使用Aggregration这个单词来包括了聚合管道Aggregation Pipeline, 计数count,分组group,去重distinct等。
- 聚合管道Aggregation Pipeline
聚合管道类似于流水线,由多个stages组成。每个stage在对上一个stage的结果进行处理,处理完成后将结果交给下一个stage。
比如一个聚合管道的多个stages可以包括,$match (匹配),$grouop(分组),$project(投射,即抽取列),$lookup(联合查询)。这里举一个简单的代码示例,找出上海附近的餐馆,按照类别进行分组。
function simplePipeline(db, callback) { const collection = db.collection( 'restaurants' ); collection.aggregate( [ { '$match': { "city": "Shanghai" } }, { '$unwind': '$categories'}, { '$group': { '_id': "$categories", 'Bronx restaurants': { '$sum': 1 } } } ], function(err, cursor) { cursor.toArray(function(err, documents) { console.log(documents) callback(documents); }); } );}
可以看到聚合管道的操作比较复杂。
- 计数count
通过collection.countDocuments()进行计算,原有的方法count已经废弃。示例代码如下:
function simpleCount(db, callback) { const collection = db.collection( 'items' ); collection.countDocuments({ 'item': 'canvas' }, function(err, result) { console.log(result) callback(result); } );}
- 分组group
MongoDB3.6以上版本已经不再支持group命令,官方建议使用上述的Aggregrate Pipeline。
- 去重distinct
MongoDB提供方法collection.distinct()来进行去重。示例对字段item进行去重,结果只会显示不重复的item。
const MongoClient = require('mongodb').MongoClient;const url = 'mongodb://vincent:123456@192.168.0.104:27017/TestDB';const client = new MongoClient(url,{ useUnifiedTopology: true});client.connect(function(err, client) { const db = client.db('TestDB'); const collection = db.collection('items'); collection.distinct( 'item', function(err, result) { console.log(result) client.close(); });})
结果如下
[ 'book', 'canvas', 'glue', 'pencil' ]
数据库连接池
实际项目中不会只有一个数据库连接,也不会只要有请求,就创建连接,请求结束,就关掉连接。项目中一般都会使用数据库连接池。
- 定义option,在option中定义连接池的连接数,重新连接次数,是否自动连接以及timeout。
var option = { reconnectTries: 3, auto_reconnect: true, poolSize : 40, connectTimeoutMS: 500};
- 在建立数据库连接时,添加上面的option参数
MongoClient.connect(url, option, function(err, db) { });
- 在应用启动时建立数据库连接池
- 当有数据库操作请求时,返回一个连接实例进行操作。
总结
通过node.js进行MongoDB的数据库操作,进行了增删改查的实例,以及相对复杂的聚合操作的介绍。实际项目中的数据库连接池也在内容中,希望对朋友们的工作有所帮助。
这篇文章的初衷是一个node.js对MongoDB的操作示例,在编程过程中可以快速查找到需要的示例。
欢迎朋友们留言讨论。
下一篇: node.js 10 Web框架Express 介绍,安装,静态页面,路由