上一篇:
對於很多前端轉全棧的朋友們來說,數據庫操作選擇MongoDB相對容易上手。後端對關係型數據庫SQL熟悉的朋友們,對於MongoDB數據庫瞭解起來就比較快了,很多操作跟SQL類似。
在準備本篇以前,我準備了兩篇介紹MongoDB的文章如下,建議對MongoDB不瞭解的朋友先看一下,有個概念。兩篇文章屬於操作型,讀起來挺快的。
本篇會介紹node.js連接數據庫,增刪改查,查詢過程的具體操作以及數據庫連接池等等。
![node.js 09 MongoDB增刪改查,聚合,連接池](http://p2.ttnews.xyz/loading.gif)
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>
- 需要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>
- 啟動認證
但是MongoDB默認情況下是不需要用戶名和密碼登錄的,如果需要用戶名/密碼進行登錄驗證,仍然修改mongod.cfg文件,添加如下信息:
<code>security:
authorization: enabled/<code>
- 創建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>
數據庫連接池
實際項目中不會只有一個數據庫連接,也不會只要有請求,就創建連接,請求結束,就關掉連接。項目中一般都會使用數據庫連接池。
- 定義option,在option中定義連接池的連接數,重新連接次數,是否自動連接以及timeout。
<code>var option = {
reconnectTries: 3,
auto_reconnect: true,
poolSize : 40,
connectTimeoutMS: 500
};/<code>
- 在建立數據庫連接時,添加上面的option參數
<code>MongoClient.connect(url, option, function(err, db) {
});/<code>
- 在應用啟動時建立數據庫連接池
- 當有數據庫操作請求時,返回一個連接實例進行操作。
總結
通過node.js進行MongoDB的數據庫操作,進行了增刪改查的實例,以及相對複雜的聚合操作的介紹。實際項目中的數據庫連接池也在內容中,希望對朋友們的工作有所幫助。
這篇文章的初衷是一個node.js對MongoDB的操作示例,在編程過程中可以快速查找到需要的示例。
歡迎朋友們留言討論。
閱讀更多 IT人Vincent 的文章