上一篇: node.js 08 代码创建 http 静态web服务器

对于很多前端转全栈的朋友们来说,数据库操作选择MongoDB相对容易上手。后端对关系型数据库SQL熟悉的朋友们,对于MongoDB数据库了解起来就比较快了,很多操作跟SQL类似。

在准备本篇以前,我准备了两篇介绍MongoDB的文章如下,建议对MongoDB不了解的朋友先看一下,有个概念。两篇文章属于操作型,读起来挺快的。

  • 五分钟了解MongoDB介绍,安装与使用
  • MongoDB数据库读写操作

本篇会介绍node.js连接数据库,增删改查,查询过程的具体操作以及数据库连接池等等。




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';
  1. 需要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
  1. 启动认证

但是MongoDB默认情况下是不需要用户名和密码登录的,如果需要用户名/密码进行登录验证,仍然修改mongod.cfg文件,添加如下信息:

security:    authorization: enabled
  1. 创建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' ]

数据库连接池

实际项目中不会只有一个数据库连接,也不会只要有请求,就创建连接,请求结束,就关掉连接。项目中一般都会使用数据库连接池。

  1. 定义option,在option中定义连接池的连接数,重新连接次数,是否自动连接以及timeout。
var option = {    reconnectTries: 3,    auto_reconnect: true,    poolSize : 40,    connectTimeoutMS: 500};
  1. 在建立数据库连接时,添加上面的option参数
MongoClient.connect(url, option, function(err, db) {  });
  1. 在应用启动时建立数据库连接池
  2. 当有数据库操作请求时,返回一个连接实例进行操作。

总结

通过node.js进行MongoDB的数据库操作,进行了增删改查的实例,以及相对复杂的聚合操作的介绍。实际项目中的数据库连接池也在内容中,希望对朋友们的工作有所帮助。

这篇文章的初衷是一个node.js对MongoDB的操作示例,在编程过程中可以快速查找到需要的示例。

欢迎朋友们留言讨论。

下一篇: node.js 10 Web框架Express 介绍,安装,静态页面,路由