【问题标题】:Insert document loop - RangeError: Maximum call stack size exceeded插入文档循环 - RangeError:超出最大调用堆栈大小
【发布时间】:2017-06-27 17:22:12
【问题描述】:

我实际上是在使用 node 和 mongodb 迈出第一步,我最近遇到了这个 RangeError 墙。

这就是我要做的,我有一个文件,其中包含我想添加到我的 mongo db 的国家列表。这将是我让应用运行的“种子”机制的一部分。

我加载 json,然后遍历对象集合并将它们一一添加到“Countries”集合中。 但是,每次我运行代码时,都会收到“RangeError: Maximum call stack size exceeded”。

我用谷歌搜索过,但建议的解决方案似乎都不适用于我。 我的猜测是我的 insertCountry 函数有问题...

无论如何,这是我的代码:

var mongoose = require('mongoose');
var countries = require('./seed/countries.json');


// mongodb
var Country = mongoose.Schema({
    name: String,
    code: String,
    extra: [Extra]
});

var Extra = mongoose.Schema({
    exampleField: Boolean,
    anotherField: Boolean
});


var mCountry = mongoose.model('Countries', Country);
var mExtra = mongoose.model('Extras', Extra);

// do connection
mongoose.connect('...');

var db = mongoose.connection;
db.on('error', console.error.bind(console, 'connection error'));
db.once('open', function callback() {

});


// async function
var insertCountry = function(document, callback) {

    db.model('Countries').count({code: document.code}, function (err, count) {
        if (count < 1) {
            db.collection('Countries').insert(document, function (err, result) {
                if (!err) {
                    console.log('country ' + document.name + ' added');
                }
                else {
                    console.log('- [' + document.name + '] ' + err);
                }
            });
        }
        callback(null,document);
    });
};


// doing countries
var Country = mongoose.model('Countries');
var Extras = mongoose.model('Extras');


for(i = 0; i < countries.length; i++) 
{

    nCountry = new Country();
    nCountry.name = countries[i].name;
    nCountry.code = countries[i].code;
    nCountry.benefits = new Extras();
    nCountry.benefits.exampleField = false;
    nCountry.benefits.anotherField = false;

    insertCountry(nCountry, function (err, value) {
        console.log(value.name + ' added to collection (callback)');
    });
}

我一直在使用我发现的一些指南来构建它,因此这可能不是最佳代码。欢迎您分享任何最佳实践、标准、指南或教程!

【问题讨论】:

    标签: javascript node.js mongodb mongoose


    【解决方案1】:

    您的回调位于错误的位置。在您从它自己的回调返回之前,它不会等待插入操作完成。修改代码:

    var insertCountry = function(document, callback) {
    
        db.model('Countries').count({code: document.code}, function (err, count) {
            if (count < 1) {
                db.collection('Countries').insert(document, function (err, result) {
                    if (!err) {
                        console.log('country ' + document.name + ' added');
                    }
                    else {
                        console.log('- [' + document.name + '] ' + err);
                    }
                    callback(null,document);
                });
            }
        });
    };
    

    这是你的问题的一部分,但它并没有完全解决它。另一部分是循环,它也不会等待包装函数在继续之前完成。您需要asyc.eachSeries 之类的东西,以便在执行下一次迭代之前等待插入完成。这主要是您超出调用堆栈的原因:

    async.eachSeries(
        countries,
        function(current,callback) {
           // make your nCountry object
           insertCountry(nCountry,function(err,value) {
              // do something, then
              callback(err);
           }) 
        },
        function(err) {
           // called where done, err contains err where set
           console.log( "done" );
        }
    );
    

    数组确实存在问题,如果超出调用堆栈限制,数组必须相当大。您可能应该考虑使用事件流来处理它,而不是将内存中的所有内容加载到数组中。

    就个人而言,如果您只是想不为字段插入重复项并且有可用的 MongoDB 2.6,我只会将 Bulk Operations API 与“无序操作”一起使用,并允许重复键出现非致命故障。再加上批量操作是“批量”发送的,而不是一次发送一个,这比检查每个请求是否存在要高效得多:

    var Country = mongoose.Schema({
        name: String,
        code: { type: String, unique: true },  // define a unique index
        extra: [Extra]
    });
    
    var insertCountries = function(countries,callback) {
        var bulk = Country.collection.initializeUnorderedBulkOp();
        var counter = 0;
    
        async.eachSeries(
            countries,
            function(current,callback) {
                // same object construction
                bulk.insert(nCountry);
                counter++;
    
                // only send once every 1000
                if ( counter % 1000 == 0 ) {
                    bulk.execute(function(err,result) {
                       // err should generally not be set
                       // but result would contain any duplicate errors
                       // along with other insert responses
    
                       // clear to result and callback
                       bulk = Country.collection.initializeUnorderedBulkOp();
                       callback();
                    });
                } else {
                    callback();
                }
            },
            function(err) {
                // send anything still queued
                if ( counter % 1000 != 0 )
                    bulk.execute(function(err,result) {
                        // same as before but no need to reset
                        callback(err);
                    });
            }
        );
    };
    
    mongoose.on("open",function(err,conn) {
        insertCountries(countries,function(err) {
            console.log("done");
        });
    });
    

    请记住,与直接在 mongoose 模型上实现的方法不同,本机驱动程序方法需要在调用之前实际建立连接。 Mongoose 为您“排队”这些,但除此之外,您需要一些东西来确保连接实际上是打开的。这里以“open”事件为例。

    也请查看event streams。如果您正在构建一个足够大的数组以通过缺少回调执行而导致问题,那么您可能不应该将它全部从您的源加载到内存中。该源的流处理与如上所示的方法相结合应该可以提供有效的加载。

    【讨论】:

    • 我将代码更改为使用异步库,但没有运气。没有使用批量插入 api,因为这需要我直接使用 mongo 而不是 mongoose。我无法解决调用堆栈问题。所以解决方法是调用 Country.create(countries) ,它就像一个魅力。感谢尼尔的帮助!
    猜你喜欢
    • 2018-02-06
    • 1970-01-01
    • 2021-05-30
    • 2021-07-11
    • 2019-07-05
    • 2015-04-26
    • 2014-06-11
    相关资源
    最近更新 更多