2

我正在尝试使用 Node.js (0.8.18) 和 Mongoose (3.5.4) 将商店目录加载到 MongoDb (2.2.2) 中——所有这些都在 Windows 7 64 位上。该数据集包含大约 12,500 条记录。每个数据记录都是一个 JSON 字符串。

我最近的尝试是这样的:

var fs = require('fs');
var odir = process.cwd() + '/file_data/output_data/';
var mongoose = require('mongoose');
var Catalog = require('./models').Catalog;

var conn = mongoose.connect('mongodb://127.0.0.1:27017/sc_store');

exports.main = function(callback){
    var catalogArray = fs.readFileSync(odir + 'pc-out.json','utf8').split('\n');
    var i = 0;

    Catalog.remove({}, function(err){
        while(i < catalogArray.length){
            new Catalog(JSON.parse(catalogArray[i])).save(function(err, doc){
                if(err){
                    console.log(err);
                } else {
                    i++;                    
                }
            });
            if(i === catalogArray.length -1) return callback('database populated');
        }
    });
};

我在尝试填充数据库时遇到了很多问题。在以前的场景(和这个场景)下,节点与处理器挂钩并最终耗尽内存。请注意,在这种情况下,我试图让 Mongoose 保存一条记录,然后在记录保存后迭代到下一条记录。

但是 Mongoose 保存函数内部的迭代器永远不会增加。此外,它从不抛出任何错误。但是如果我将迭代器 (i) 放在对 Mongoose 的异步调用之外,它会起作用,前提是我尝试加载的记录数不是太大(我已经成功加载了 2,000 条这种方式)。

所以我的问题是:为什么 Mongoose 保存调用中的迭代器没有增加?而且,更重要的是,使用 Mongoose 将大型数据集加载到 MongoDb 的最佳方法是什么?

4

2 回答 2

4

i是您从 in 中提取输入数据的位置的索引catalogArray,但您也在尝试使用它来跟踪已保存的数据数量,这是不可能的。尝试像这样单独跟踪它们:

var i = 0;
var saved = 0;
Catalog.remove({}, function(err){
    while(i < catalogArray.length){
        new Catalog(JSON.parse(catalogArray[i])).save(function(err, doc){
            saved++;
            if(err){
                console.log(err);
            } else {
                if(saved === catalogArray.length) {
                    return callback('database populated');
                }
            }
        });
        i++;
    }
});

更新

如果您想为流程添加更严格的流程控制,您可以使用async模块的forEachLimit功能将未完成save操作的数量限制为您指定的任何数量。例如,要将其限制为一次未完成save的:

Catalog.remove({}, function(err){
    async.forEachLimit(catalogArray, 1, function (catalog, cb) {
        new Catalog(JSON.parse(catalog)).save(function (err, doc) {
            if (err) {
                console.log(err);
            }
            cb(err);
        });
    }, function (err) {
        callback('database populated');
    });
}
于 2013-01-19T19:23:59.483 回答
2

抢,

简短的回答:

您创建了一个无限循环。您正在同步思考和阻塞,Javascript 函数异步且没有阻塞。你想要做的就像试图直接把饥饿感变成三明治一样。你不能。最接近的是你用饥饿的感觉来激励你去厨房做饭。不要试图让 Javascript 阻塞。它行不通。现在,学习 async.forEachLimit。它适用于您想要在这里做的事情。

您可能应该查看异步设计模式并更深入地理解它的含义。回调不仅仅是返回值的替代方案。它们在执行方式和执行时间上有根本的不同。这是一个很好的入门:http ://cs.brown.edu/courses/csci1680/f12/handouts/async.pdf

长答案:

这里有一个潜在的问题,那就是您对非阻塞 IO 和异步的含义缺乏了解。我不确定您是否正在进入节点开发,或者这只是一个一次性项目,但如果您确实打算继续使用节点(或任何异步语言),那么值得花时间了解同步和异步之间的区别设计模式,以及它们的动机。所以,这就是为什么你有一个逻辑错误,将循环不变增量放在异步回调中,这会创建一个无限循环。

在非计算机科学中,这意味着你对 i 的增量永远不会发生。原因是 Javascript 在调用任何异步回调之前执行单个代码块来完成。因此,在您的代码中,您的循环将一遍又一遍地运行,而我永远不会增加。而且,在后台,您一遍又一遍地将相同的文档存储在 mongo 中。循环的每次迭代都开始向 mongo 发送索引为 0 的文档,直到循环结束,回调才能触发,并且循环外的所有其他代码都运行完成。因此,回调排队。但是,您的循环再次运行,因为 i++ 从未执行(请记住,回调排队直到您的代码完成),再次插入记录 0,在您的循环完成后排队另一个回调以执行。

一般来说,如果不做一些非常糟糕的事情,就没有办法让 Javascript 阻塞。例如,最重要的事情是让你的厨房着火,为我在“简短回答”中谈到的那个三明治煎鸡蛋。

我的建议是利用像 async 这样的库。https://github.com/caolan/async JohnnyHK 在这里提到了它,他这样做是正确的。

于 2013-01-20T19:06:52.610 回答