在遍历光标和更新文档时,连接因应用程序错误而关闭

Connection closed by application error when iterating through cursor and updating documents

本文关键字:应用程序 错误 连接 光标 遍历 更新 文档      更新时间:2023-09-26

我有以下简单的node.js应用程序,它从查询中创建光标,该查询按城市和温度对collectoin进行排序。之后,我遍历游标,并通过添加最高温度:true来更新每个城市温度最高的文档。

var MongoClient = require('mongodb').MongoClient;
MongoClient.connect('mongodb://localhost:27017/temp', function(err, db) {
    if(err) throw err;
    var cursor = db.collection('data').find().sort( { City : 1,  Temperature : -1 } );
    var previous = '';
    cursor.each(function(err, doc) { 
        if(err) throw err;
        if(doc == null) {
            console.dir("Closing database connection"); 
            return db.close();
        }
        if (previous != doc.City) {
            previous = doc.City;
            var query = { _id : doc._id };
            var operator = { '$set' : { highest : true } };
            console.dir(doc.City + " is " + doc.Temperature + "; ");

            db.collection('data').update(query, operator, function(err, updated) {
                if(err) {
                    console.error('Error:', err);
                    throw err;
                }

                console.dir("Successfully updated: " + JSON.stringify(updated));    
            });    
        }
    });
});

这里的问题是,只有第一个城市得到了正确的更新,这是输出:

"柏林81岁了;"已成功更新:1''巴黎-佛罗里达州为83;'"华沙-新墨西哥州57岁;"巴塞罗那佛蒙特州57岁交割数据库连接错误:{[MongoError:连接关闭者Application]名称:'MongoError'}错误:{[MongoError:ConnectionClosed By Application]name:'MongoError'}错误:{[MongoError:连接已由应用程序关闭]名称:"MongoError"}

我对发生的事情的猜测是:光标遍历所有文档,调用温度最高的文档的更新:

db.collection('data').update(query, operator, function(err, updated)

但在回调返回之前,游标完成迭代,并调用此代码片段,从而关闭连接:

if(doc == null) {
            console.dir("Closing database connection"); 
            return db.close();
        }

之后,由于没有可用的数据库连接,所有未完成处理的更新都将出错。

正确的处理方法是什么,以便只有在所有文档都成功更新后才能关闭连接?

正如Neil所提到的,我们可以使用.stream(),但我能够通过计算已经处理的更新并在我们希望更新的所有文档更新后关闭数据库连接,使程序按预期执行。在我的案例中,它非常简单,因为我在数据库中只有4个城市,所以我希望只有4个文档可以更新。我们也可以通过查询和计数结果获得这个数字,但这对我来说已经足够了

这是工作代码:

var MongoClient = require('mongodb').MongoClient;
MongoClient.connect('mongodb://localhost:27017/temp', function(err, db) {
    if(err) throw err;
    var cursor = db.collection('data').find().sort( { City : 1,  Temperature : -1 } );
    var previous = '';
    var updatedCount = 0;
    var expectedToUpdate = 4; // Hardcoded, but we might want to obtain it pragmatically
    cursor.each(function(err, doc) { 
        if(err) throw err;
        if(doc == null) { 
            return
        }
        if (previous != doc.City) {
            previous = doc.City;
            var query = { _id : doc._id };
            var operator = { '$set' : { highest : true } };
            console.dir(doc.City + " is " + doc.Temperature + "; ");

            db.collection('data').update(query, operator, function(err, updated) {
                if(err) {
                    console.error('Error:', err);
                    throw err;
                }

                console.dir("Successfully updated: " + JSON.stringify(updated));    
                updatedCount++;
                if (updatedCount == expectedToUpdate) {
                    console.dir("updated expected number of documents. Closing db.");
                    db.close();
                }
            });    
        }
    });
});

我通常认为节点流接口是迭代器的更好选择。光标对象上有一个.stream()方法:

var stream = db.collection('data').find().sort( 
    { City : 1,  Temperature : -1 } 
).stream();
stream.on('data',function(data) {
  // do things with current document
  // but pause on things with a callback then resume;
  stream.pause();
  db.collection('data').update(query, operator, function(err, updated) {
      // resume the stream when this callback is done
      stream.resume();
  })
});
stream.on('end',function() {
    // Called when everything is complete
    // db.close is safe here as long as you are no longer using the connection
    db.close();
});

事实上,从本机驱动程序的2.0版本开始,流接口就是默认游标对象的一部分。

但一般情况下,对于一次性处理脚本,只调用db.close()。通常,在服务器类型的实现中根本不应该调用它,而应该在整个生命周期中保持连接打开。