node.js 09 MongoDB增刪改查,聚合,連接池

上一篇:

對於很多前端轉全棧的朋友們來說,數據庫操作選擇MongoDB相對容易上手。後端對關係型數據庫SQL熟悉的朋友們,對於MongoDB數據庫瞭解起來就比較快了,很多操作跟SQL類似。

在準備本篇以前,我準備了兩篇介紹MongoDB的文章如下,建議對MongoDB不瞭解的朋友先看一下,有個概念。兩篇文章屬於操作型,讀起來挺快的。

本篇會介紹node.js連接數據庫,增刪改查,查詢過程的具體操作以及數據庫連接池等等。


node.js 09 MongoDB增刪改查,聚合,連接池

node.js & MongoDB

安裝MongoDB驅動

跟Java連接數據庫需要驅動jar文件,node.js連接數據庫也需要驅動。這裡需要使用的驅動是"mongodb",安裝通過npm進行,命令如下:

<code>D:\\Projects\\nodejs\\NodeDemo\\node09>npm install mongodb --save
npm notice created a lockfile as package-lock.json. You should commit this file.
npm WARN [email protected] No description
npm WARN [email protected] No repository field.

+ [email protected]
added 20 packages from 11 contributors and audited 21 packages in 4.977s
found 0 vulnerabilities
/<code>

連接MongoDB 本地數據庫

首先需要引入"MongoDB",獲取MongoClient。寫入數據庫URL,通過MongoClient.connect()進行連接。要注意的是,相關操作結束後,需要通過MongoClient.close()關閉數據庫連接。

<code>//1. 獲取MongoClient
const MongoClient = require('mongodb').MongoClient;

//2. Connection URL
const url = 'mongodb://localhost:27017';

// 3. Database Name
const 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();
});/<code>

連接MongoDB 遠端數據庫

之前的文章裡面我們關於MongoDB的安裝實在本地的,這時候如果把上面代碼中的url換成IP地址仍然是不可行的。

<code>const url = 'mongodb://192.168.0.xxx:27017';/<code>
  1. 需要MongoDB能夠提供網絡服務,需要修改配置文件。該文件在Windows系統下為:<mongodb>/Server/4.2/bin/mongod.cfg。/<mongodb>

在該文件中將"bindIp: 127.0.0.1"註釋掉,添加"bindIp: 0.0.0.0"。示例如下:

<code>#  bindIp: 127.0.0.1
bindIp: 0.0.0.0/<code>
  1. 啟動認證

但是MongoDB默認情況下是不需要用戶名和密碼登錄的,如果需要用戶名/密碼進行登錄驗證,仍然修改mongod.cfg文件,添加如下信息:

<code>security:
authorization: enabled/<code>
  1. 創建admin用戶

接下來還需要為自己的數據庫創建admin用戶,在命令行中輸入"mongo"通過MongoDB命令行。

<code>D:\\Projects\\nodejs\\NodeDemo\\node09>mongo
MongoDB shell version v4.2.3
connecting to: mongodb://127.0.0.1:27017/?compressors=disabled&gssapiServiceName=mongodb
Implicit session: session { "id" : UUID("0d7f3322-88f9-400c-9b50-068de80f9956") }
MongoDB server version: 4.2.3/<code>

切換到自己的數據庫,這裡我的數據庫為TestDB

<code>> use TestDB
switched to db TestDB/<code>


創建admin用戶"vincent"

<code>> db.createUser({
user: 'vincent', // 用戶名
pwd: '123456', // 密碼
roles:[{
role: 'root', // 角色
db: 'admin' // 數據庫
}]
})
Successfully added user: {
"user" : "vincent",
"roles" : [
{
"role" : "root",
"db" : "admin"
}
]
}/<code>

修改結束後,重啟MongoDB服務即可。

在代碼中,我們可以進行數據庫連接了。對比之前的代碼,主要的改動就是url,在url中添加用戶名/密碼,以及數據庫名稱即可。

<code>const url = 'mongodb://vincent:[email protected]:27017/TestDB';/<code>

添加文檔

MongoDB支持單個文檔記錄添加,也支持多個文檔記錄添加。

  • 單條添加:db.collection('collection_name').insertOne()
  • 多條添加:db.collection('collection_name').insertMany()

示例如下

<code>const MongoClient = require('mongodb').MongoClient;
//1. 遠端MongoDB連接
const url = 'mongodb://vincent:[email protected]: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();
});
});
});/<code>

需要注意的是,由於node.js是單線程異步操作,所以數據庫連接關閉一定是在最後一個操作的閉包函數里。

更新文檔

與添加文檔一樣,MongoDB同樣提供更新一條記錄和更新多條記錄的方法。分別是updateOne和updateMany。另外還有是upsert,在方法updateOne中通過參數upsert來控制。如果需要更新的記錄不存在,那麼該方法將記錄添加到數據庫。

<code>const MongoClient = require('mongodb').MongoClient;
const url = 'mongodb://vincent:[email protected]: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();
});
})
})
})/<code>

刪除文檔

提供deleteOne和deleteMany兩個方法進行單條和多條記錄刪除。

<code>const MongoClient = require('mongodb').MongoClient;
const url = 'mongodb://vincent:[email protected]: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();
})
})
})/<code>

查詢文檔

提供find方法就行查詢,在查詢後可以使用toArray將結果轉為Array。

示例代碼:

<code>const MongoClient = require('mongodb').MongoClient;
const url = 'mongodb://vincent:[email protected]: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();
})
})/<code>

如果需要添加查詢條件,可以在find()中添加參數

<code> col.find({"qty":50}).toArray(function(err, r) {/<code>

另外,MongoDB還可以提供多個查詢方法,我這裡不一一提供示例了,有興趣的朋友們可以試以下。

<code>collection.find({}).project({a:1})                             // Create a projection of field a
collection.find({}).skip(1).limit(10) // Skip 1 and limit 10
collection.find({}).batchSize(5) // Set batchSize on cursor to 5
collection.find({}).filter({a:1}) // Set query on the cursor
collection.find({}).comment('add a comment') // Add a comment to the query, allowing to correlate queries
collection.find({}).addCursorFlag('tailable', true) // Set cursor as tailable
collection.find({}).addCursorFlag('oplogReplay', true) // Set cursor as oplogReplay
collection.find({}).addCursorFlag('noCursorTimeout', true) // Set cursor as noCursorTimeout
collection.find({}).addCursorFlag('awaitData', true) // Set cursor as awaitData
collection.find({}).addCursorFlag('exhaust', true) // Set cursor as exhaust
collection.find({}).addCursorFlag('partial', true) // Set cursor as partial
collection.find({}).addQueryModifier('$orderby', {a:1}) // Set $orderby {a:1}
collection.find({}).max(10) // Set the cursor max
collection.find({}).maxTimeMS(1000) // Set the cursor maxTimeMS
collection.find({}).min(100) // Set the cursor min
collection.find({}).returnKey(10) // Set the cursor returnKey
collection.find({}).setReadPreference(ReadPreference.PRIMARY) // Set the cursor readPreference
collection.find({}).showRecordId(true) // Set the cursor showRecordId
collection.find({}).sort([['a', 1]]) // Sets the sort order of the cursor query
collection.find({}).hint('a_1') // Set the cursor hint/<code>

在查詢操作中,如果需要對每一條結果記錄進行讀取再進行操作,可以使用each

<code>    col.find({"qty":30}).each(function(err, doc) {
});/<code>

聚合Aggregation

這部分相對前面的增刪改查要複雜一點。MongoDB使用Aggregration這個單詞來包括了聚合管道Aggregation Pipeline, 計數count,分組group,去重distinct等。

  • 聚合管道Aggregation Pipeline

聚合管道類似於流水線,由多個stages組成。每個stage在對上一個stage的結果進行處理,處理完成後將結果交給下一個stage。

比如一個聚合管道的多個stages可以包括,$match (匹配),$grouop(分組),$project(投射,即抽取列),$lookup(聯合查詢)。這裡舉一個簡單的代碼示例,找出上海附近的餐館,按照類別進行分組。

<code>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);
});
}
);
}/<code>

可以看到聚合管道的操作比較複雜。

  • 計數count

通過collection.countDocuments()進行計算,原有的方法count已經廢棄。示例代碼如下:

<code>function simpleCount(db, callback) {
const collection = db.collection( 'items' );
collection.countDocuments({ 'item': 'canvas' },\t
\t function(err, result) {
console.log(result)
callback(result);
}
);
}/<code>
  • 分組group

MongoDB3.6以上版本已經不再支持group命令,官方建議使用上述的Aggregrate Pipeline。

  • 去重distinct

MongoDB提供方法collection.distinct()來進行去重。示例對字段item進行去重,結果只會顯示不重複的item。

<code>const MongoClient = require('mongodb').MongoClient;
const url = 'mongodb://vincent:[email protected]: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();
});
})/<code>

結果如下

<code>[ 'book', 'canvas', 'glue', 'pencil' ]/<code>

數據庫連接池

實際項目中不會只有一個數據庫連接,也不會只要有請求,就創建連接,請求結束,就關掉連接。項目中一般都會使用數據庫連接池。

  1. 定義option,在option中定義連接池的連接數,重新連接次數,是否自動連接以及timeout。
<code>var option = {
reconnectTries: 3,
auto_reconnect: true,
poolSize : 40,
connectTimeoutMS: 500
};/<code>
  1. 在建立數據庫連接時,添加上面的option參數
<code>MongoClient.connect(url, option, function(err, db) {
});/<code>
  1. 在應用啟動時建立數據庫連接池
  2. 當有數據庫操作請求時,返回一個連接實例進行操作。

總結

通過node.js進行MongoDB的數據庫操作,進行了增刪改查的實例,以及相對複雜的聚合操作的介紹。實際項目中的數據庫連接池也在內容中,希望對朋友們的工作有所幫助。

這篇文章的初衷是一個node.js對MongoDB的操作示例,在編程過程中可以快速查找到需要的示例。

歡迎朋友們留言討論。


分享到:


相關文章: