关闭一个不会永远运行的脚本中的Mongoose连接

Closing a Mongoose connection in a script which does not run forever

本文关键字:运行 永远 脚本 连接 Mongoose 一个      更新时间:2023-09-26

我有一个JavaScript程序,应该运行一段时间,插入行到MongoDB数据库,然后退出。以下是该应用程序的精简版本:

var mongoose = require('mongoose');
var models = require('./models');
mongoose.connect('mongodb://localhost/test')
var db = mongoose.connection;
db.on('error', console.error.bind(console, 'connection error:'));
db.once('open', function callback() {
var row = models('testschema')({
    name : 'test'
});
row.save(function (err, obj) {
    if (err) {
        console.log(err);
    } else {
        console.log('Saved item.');
    }
});
console.log('Closing DB');
db.close();
});

现在上面的代码不能正常工作了,因为项目永远不会进入数据库。我的感觉是,因为save()是异步的,db.close()首先发生,项目永远不会被保存。如果我将db.close()调用移动到保存的回调中,如下所示:

row.save(function (err, obj) {
    if (err) {
        console.log(err);
    } else {
        console.log('Saved meeting details');
    }
    console.log('Closing DB');
    db.close();
});

那么它工作得很好。然而,这并没有多少实际帮助,因为这意味着我只能在需要关闭数据库之前写入一行。我的问题是,当我在这种情况下如何正确关闭Mongoose连接:

var mongoose = require('mongoose');
var models = require('./models');
mongoose.connect('mongodb://localhost/test')
var db = mongoose.connection;
db.on('error', console.error.bind(console, 'connection error:'));
db.once('open', function callback() {
    itemsToSave.forEach(function(item) {
        var row = models('testschema')({
            name : item.name
        });
        row.save(function (err, obj) {
            if (err) {
                console.log(err);
            } else {
                console.log('Saved meeting details');
            }
            // Can't do this here.
            //console.log('Closing DB');
            //db.close();
        });
    });
    // Nor here.
    //console.log('Closing DB');
    //db.close();
});

编辑:下面是使用C Blanchard答案的最终版本。我应该注意到,虽然它确实达到了预期的结果,但我觉得它在这一点上已经失去了猫鼬的便利性。如果您要像这样批量调用save(),您不妨利用MongoDB的底层批量插入功能,并使用它来执行插入。我可能会在另一种语言中完成这项任务,因为node.js的异步性质似乎使得几乎不可能编写优雅的代码来做诸如"打开文本文件,将每一行插入数据库,关闭连接并退出"之类的事情。总之,不多说了,最后一个程序:

var mongoose = require('mongoose');
var models = require('./models');
var async = require("async");

var rowsToSave = [];

var saveRow = function(item) {
return function (callback) {
    console.log('Saving meeting details');
    item.save(callback);
};
}
mongoose.connect('mongodb://localhost/test')
var db = mongoose.connection;
db.on('error', console.error.bind(console, 'connection error:'));
db.once('open', function callback() {
rowsToSave.push(saveRow(models('testschema')({ name: 'name1' })));
rowsToSave.push(saveRow(models('testschema')({ name: 'name2' })));
rowsToSave.push(saveRow(models('testschema')({ name: 'name3' })));
rowsToSave.push(saveRow(models('testschema')({ name: 'name4' })));
console.log(JSON.stringify(rowsToSave));
async.series(rowsToSave, function (err, res) {
    if (err) {
        console.log(err);
    } else {
        console.log('Saved meeting details');
    }
    console.log('Closing DB');
    db.close();
});
});

另一种方法,在查看代码时在某些方面更好,但也是一种非常糟糕的绕过此缺陷的方法,是简单地猜测脚本所需的时间,然后在此时间过去后关闭db:

setTimeout(function() { db.close(); }, 5000);

我不会责怪任何人这样做,MongoDB & &;猫鼬把你逼到了一个可怕的境地。

可以使用async库来管理控制流。通过使用其中一种控制流方法,您可以在完成所有保存后在Mongoose上调用disconnect。

你可以做的是将所有保存操作存储在一个数组中,然后将其传递给async.seriesasync.series有两个参数。1)一系列要调用的函数。2)当参数1中的所有函数被调用时调用的函数。

下面是遵循上述方法的解决方案的草图:

// Bring in async library
var async = require("async");
// Create an array to store your save operations
var rowsToSave = [];
// Returns a function that will save a row when invoked
var saveRowMethod = function (item) {
  var row = models('testschema')({
    name : item.name
  });
  return function (callback) {
    row.save(callback);
  };
}
db.once('open', function callback() {
  itemsToSave.forEach(function(item) {
    // Store the row to save
    rowsToSave.push(saveRowMethod(item))
  });
  // This will invoke each save operation in rowsToSave (in series)
  async.series(rowsToSave, function (error, result) {
    if (error) {
      // Handle an error if one of the rows fails to save
    }
    console.log('Closing DB');
    db.close();
  })
});

注意,传递给rowsToSave的函数必须在保存完成时接受并调用回调。这就是async如何能够跟踪操作何时完成。

更新:看了你的评论,我又想了一下。找到了一个更简洁的解决方案,它只依赖于Model#create -但是Mongoose现在不支持批量插入(Issue #723)

Model#create接受一个对象数组,它将做一些类似于我上面概述的解决方案,除了它不需要async。

var rowsToSave = [];
rowsToSave.push({ name: 'name1' });
rowsToSave.push({ name: 'name2' });
rowsToSave.push({ name: 'name3' });
rowsToSave.push({ name: 'name4' });
TestModel.create(rowsToSave, function (err, name1, name2, name3, name4) {
  if (err) {
    console.log(err);
  } else {
    console.log('Saved meeting details');
  }
  console.log('Closing DB');
  db.close();
});