通过Node MongoDB本机驱动程序使用Nodejs和MongoDB。需要检索一些文档并进行修改,然后将它们保存回来。这是一个例子:
db.open(function (err, db) {
db.collection('foo', function (err, collection) {
var cursor = collection.find({});
cursor.each(function (err, doc) {
if (doc != null) {
doc.newkey = 'foo'; // Make some changes
db.save(doc); // Update the document
} else {
db.close(); // Closing the connection
}
});
});
});
具有异步性质,如果更新文档的过程需要更长时间,那么当光标到达文档末尾时,数据库连接将关闭。并非所有更新都保存到数据库中。
如果省略db.close()
,则所有文档都会正确更新,但应用程序会挂起,永不退出。
我看到一篇帖子建议使用计数器跟踪更新次数,当回落到零时,然后关闭数据库。但我在这里做错了吗?处理这种情况的最佳方法是什么?是否必须使用db.close()
来释放资源?或者是否需要打开新的数据库连接?
答案 0 :(得分:24)
这是一个基于计数方法的潜在解决方案(我没有对它进行测试,也没有错误捕获,但它应该传达这个想法)。
基本策略是:获取需要更新的记录数,异步保存每条记录以及成功回调,如果计数达到0(上次更新完成时),将减少计数并关闭数据库)。通过使用{safe:true}
,我们可以确保每次更新都成功。
mongo服务器将为每个连接使用一个线程,因此最好是a)关闭未使用的连接,或者b)池/重用它们。
db.open(function (err, db) {
db.collection('foo', function (err, collection) {
var cursor = collection.find({});
cursor.count(function(err,count)){
var savesPending = count;
if(count == 0){
db.close();
return;
}
var saveFinished = function(){
savesPending--;
if(savesPending == 0){
db.close();
}
}
cursor.each(function (err, doc) {
if (doc != null) {
doc.newkey = 'foo'; // Make some changes
db.save(doc, {safe:true}, saveFinished);
}
});
})
});
});
答案 1 :(得分:13)
最好使用池连接,然后在应用程序生命周期结束时在清理函数中调用db.close():
process.on('SIGINT', cleanup);
process.on('SIGTERM', cleanup);
请参阅http://mongodb.github.io/node-mongodb-native/driver-articles/mongoclient.html
有点旧线程,但无论如何。
答案 2 :(得分:4)
我发现使用计数器可能适用于简单的场景,但在复杂情况下可能很难。这是我在数据库连接空闲时关闭数据库连接的解决方案:
var dbQueryCounter = 0;
var maxDbIdleTime = 5000; //maximum db idle time
var closeIdleDb = function(connection){
var previousCounter = 0;
var checker = setInterval(function(){
if (previousCounter == dbQueryCounter && dbQueryCounter != 0) {
connection.close();
clearInterval(closeIdleDb);
} else {
previousCounter = dbQueryCounter;
}
}, maxDbIdleTime);
};
MongoClient.connect("mongodb://127.0.0.1:27017/testdb", function(err, connection)(
if (err) throw err;
connection.collection("mycollection").find({'a':{'$gt':1}}).toArray(function(err, docs) {
dbQueryCounter ++;
});
//do any db query, and increase the dbQueryCounter
closeIdleDb(connection);
));
这可以是任何数据库连接的通用解决方案。 maxDbIdleTime可以设置为与db query timeout相同或更长的值。
这不是很优雅,但我想不出更好的方法来做到这一点。我使用NodeJ来运行查询MongoDb和Mysql的脚本,如果数据库连接没有正确关闭,脚本将永远挂起。
答案 3 :(得分:1)
根据上面@mpobrien的建议,我发现async模块在这方面非常有帮助。以下是我采用的示例模式:
const assert = require('assert');
const async = require('async');
const MongoClient = require('mongodb').MongoClient;
var mongodb;
async.series(
[
// Establish Covalent Analytics MongoDB connection
(callback) => {
MongoClient.connect('mongodb://localhost:27017/test', (err, db) => {
assert.equal(err, null);
mongodb = db;
callback(null);
});
},
// Insert some documents
(callback) => {
mongodb.collection('sandbox').insertMany(
[{a : 1}, {a : 2}, {a : 3}],
(err) => {
assert.equal(err, null);
callback(null);
}
)
},
// Find some documents
(callback) => {
mongodb.collection('sandbox').find({}).toArray(function(err, docs) {
assert.equal(err, null);
console.dir(docs);
callback(null);
});
}
],
() => {
mongodb.close();
}
);
答案 4 :(得分:1)
这是我提出的解决方案。它避免使用toArray并且它非常简短和甜蜜:
var MongoClient = require('mongodb').MongoClient;
MongoClient.connect("mongodb://localhost:27017/mydb", function(err, db) {
let myCollection = db.collection('myCollection');
let query = {}; // fill in your query here
let i = 0;
myCollection.count(query, (err, count) => {
myCollection.find(query).forEach((doc) => {
// do stuff here
if (++i == count) db.close();
});
});
});
答案 5 :(得分:0)
我提出了一个涉及这样的计数器的解决方案。它不依赖于count()调用,也不等待超时。在每个()中的所有文档都用尽之后,它将关闭数据库。
var mydb = {}; // initialize the helper object.
mydb.cnt = {}; // init counter to permit multiple db objects.
mydb.open = function(db) // call open to inc the counter.
{
if( !mydb.cnt[db.tag] ) mydb.cnt[db.tag] = 1;
else mydb.cnt[db.tag]++;
};
mydb.close = function(db) // close the db when the cnt reaches 0.
{
mydb.cnt[db.tag]--;
if ( mydb.cnt[db.tag] <= 0 ) {
delete mydb.cnt[db.tag];
return db.close();
}
return null;
};
因此,每次要进行db.each()或db.save()等调用时,都可以使用这些方法确保数据库在工作时就绪,并在完成时关闭。
OP的例子:
foo = db.collection('foo');
mydb.open(db); // *** Add here to init the counter.**
foo.find({},function(err,cursor)
{
if( err ) throw err;
cursor.each(function (err, doc)
{
if( err ) throw err;
if (doc != null) {
doc.newkey = 'foo';
mydb.open(db); // *** Add here to prevent from closing prematurely **
foo.save(doc, function(err,count) {
if( err ) throw err;
mydb.close(db); // *** Add here to close when done. **
});
} else {
mydb.close(db); // *** Close like this instead. **
}
});
});
现在,假设每个回调的第二个到最后一个回调使得它通过mydb.open(),然后从每个回调到mydb.close()....所以,当然,让我知道是否这是一个问题。
所以:在db调用之前放一个mydb.open(db)并在回调的返回点或db调用之后放置一个mydb.close(db)(取决于调用类型)。
我觉得这种计数器应该在db对象中维护,但这是我目前的解决方法。也许我们可以创建一个新的对象,在构造函数中使用db并包装mongodb函数以更好地处理close。
答案 6 :(得分:0)
无需计数器,库或任何自定义代码的现代方式:
let MongoClient = require('mongodb').MongoClient;
let url = 'mongodb://yourMongoDBUrl';
let database = 'dbName';
let collection = 'collectionName';
MongoClient.connect(url, { useNewUrlParser: true }, (mongoError, mongoClient) => {
if (mongoError) throw mongoError;
// query as an async stream
let stream = mongoClient.db(database).collection(collection)
.find({}) // your query goes here
.stream({
transform: (readElement) => {
// here you can transform each element before processing it
return readElement;
}
});
// process each element of stream (async)
stream.on('data', (streamElement) => {
// here you process the data
console.log('single element processed', streamElement);
});
// called only when stream has no pending elements to process
stream.once('end', () => {
mongoClient.close().then(r => console.log('db successfully closed'));
});
});
在mongodb驱动程序3.2.7版上进行了测试,但根据链接,自version 2.0起可能有效
答案 7 :(得分:0)
这里是the answer given by pkopac的扩展示例,因为我不得不弄清楚其余的细节:
const client = new MongoClient(uri);
(async () => await client.connect())();
// use client to work with db
const find = async (dbName, collectionName) => {
try {
const collection = client.db(dbName).collection(collectionName);
const result = await collection.find().toArray()
return result;
} catch (err) {
console.error(err);
}
}
const cleanup = (event) => { // SIGINT is sent for example when you Ctrl+C a running process from the command line.
client.close(); // Close MongodDB Connection when Process ends
process.exit(); // Exit with default success-code '0'.
}
process.on('SIGINT', cleanup);
process.on('SIGTERM', cleanup);
这里是link to the difference between SIGINT
和SIGTERM
。
我必须添加process.exit()
,否则在命令行的运行进程中执行Ctrl + C
时,我的节点Web服务器不会干净退出。