【问题标题】:How to add many records to mongoDB from directory of JSON files?如何从 JSON 文件目录向 mongoDB 添加许多记录?
【发布时间】:2017-04-21 18:07:48
【问题描述】:

我在目录“D:/njs/nodetest1/imports/source1/”的许多子目录中保存了大约一百万个 JSON 文件,我想将它们导入到我的 mongoDB 数据库中的集合“users”中。

以下代码正确遍历文件系统。如您所见,它读取目录中的每个项目,如果该项目是目录,它会读取其中的每个项目。对于不是目录的每个项目,它会在将保存变量的变量发送到函数之前对其执行一些操作。

function traverseFS (path){
     var files = fs.readdirSync(path);
     for (var i in files){
          var currentFile = path + '/' + files[i];
          var stats = fs.statSync(currentFile);
          if (stats.isFile())
               runOnFile(currentFile);
          else
               traverseFS(currentFile);
     }
}
traverseFS("D:/njs/nodetest1/imports/source1/")

接下来,我对代码运行一些操作(见下文)。这会读取文件,将其解析为 JSON 对象,将该对象的两个属性读取到变量中,在变量“entry”中创建一个对象并将该变量传递给另一个函数。

function runOnFile(currentFile){
    var fileText = fs.readFileSync(currentFile,'utf8');
    var generatedJSON = JSON.parse(fileText);
    var recordID = generatedJSON.recordID;
    var recordText = generatedJSON.recordTexts;
    var entry = {recordID:recordID, recordText:recordText};
    insertRecord(entry);
}

然后应该使用最后一个函数将数据插入到 mongoDB 中。我认为这就是问题所在。

function insertRecord(entry){
    var MongoClient = mongodb.MongoClient;
    var MongoURL = 'mongodb://localhost:27017/my_database_name';
    MongoClient.connect(MongoURL, function (err, db) {
         var collection = db.collection('users');
         collection.insert([entry], function (err, result) {
             db.close();
         });
     });
 }

我希望这会贯穿文件结构,将 JSON 文件读入对象,然后将这些对象插入到我的 mongoDB 中。相反,它将第一个文件读入数据库,然后停止/挂起。

注意事项:

  • 我不想使用 mongoimport,因为我不想将这些文件中的所有数据插入到我的 MongoDB 数据库中。然而,我并不依赖这种方法的任何方面。如果存在其他解决方案,我愿意接受。
  • 这可以很好地连接到数据库。对于目录中的每个项目,这都会成功创建一个“条目”对象并将其传递给 insertRecord 函数。换句话说,问题必须出现在 insertRecord 部分。但这显然可能是由该过程早期的某些事情引起的。
  • 如果我添加错误处理,不会产生错误。我在这篇文章中省略了错误处理,因为它扰乱了代码 sn-ps 的可读性。

【问题讨论】:

  • 不清楚你的 runOnJson() 函数应该做什么 -- 将 json 内容和内容放入 mongo?
  • 不是我问的。我问那个特定的 function runOnJson() 应该做什么?如果它需要做的只是在文件上运行 mongoimport(就像示例中的一样),你尝试过吗?

标签: javascript json node.js mongodb import


【解决方案1】:

根据mongodb2.2 (current latest) documentationinsert 已弃用

已弃用

使用 insertOne、insertMany 或 bulkWrite

所以简短的回答可能是将collection.insert([entry], ...) 更改为collection.insertOne(entry, ...),然后你就完成了。


然后,对于长答案,您说“大约有一百万个 json 文件”,这通常应该以最少的开销使用完整的异步方法。

示例代码中有两个(潜在的)瓶颈:

  • fs.readFileSync,这是一个阻塞操作
  • 连接、插入记录和关闭数据库连接

两者都被执行“大约一百万次”。诚然,导入通常不会一遍又一遍地完成,并且(希望)不会在需要其性能来完成其他重要任务的机器上进行。不过,示例代码可以轻松地变得更加健壮。

考虑使用glob模块获取json文件列表。

glob('imports/**/*.json', function(error, files) {...})

这会以异步方式轻松地为您提供完整的文件列表。

然后考虑只连接一次数据库,插入所有内容并关闭一次。

保持或多或少与示例中相同的步骤,我建议如下:

var glob = require('glob'),
    mongodb = require('mongodb'),
    fs = require('fs'),
    MongoClient = mongodb.MongoClient,
    mongoDSN = 'mongodb://localhost:27017/my_database_name',
    collection; //  moved this to the "global" scope so we can do it only once

function insertRecord(json, done) {
    var recordID = json.recordID || null,
        recordText = json.recordText || null;

    //  the question implies some kind of validation/sanitation/preparation..
    if (recordID && recordText) {
        //  NOTE: insert was changed to insertOne
        return collection.insertOne({recordID: recordID, recordText: recordText}, done);
    }

    done('No recordID and/or recordText');
}

function runOnFile(file, done) {
    //  moved to be async
    fs.readFile(file, function(error, data) {
        if (error) {
            return done(error);
        }

        var json = JSON.parse(data);

        if (!json) {
            return done('Unable to parse JSON: ' + file);
        }

        insertRecord(json, done);
    });
}

function processFiles(files, done) {
    var next = files.length ? files.shift() : null;

    if (next) {
        return runOnFile(next, function(error) {
            if (error) {
                console.error(error);
                // you may or may not want to stop here by throwing an Error
            }

            processFiles(files, done);
        });
    }

    done();
}

MongoClient.connect(mongoDSN, function(error, db) {
    if (error) {
        throw new Error(error);
    }

    collection = db.collection('users');

    glob('imports/**/*.json', function(error, files) {
        if (error) {
            throw new Error(error);
        }

        processFiles(files, function() {
            console.log('all done');
            db.close();
        });
    });
});

注意:您可以使用insertMany 收集多个“条目”记录以利用多个插入的性能增益,尽管我感觉插入的记录比描述的要复杂,如果不处理可能会出现一些内存问题正确。

【讨论】:

  • 这似乎很有帮助,我还没有消化完。但是当我运行它时,我得到了以下错误。我显然很想调试自己——但我想我会发布,以防问题很明显。 ||| “{错误:EISDIR:对目录的非法操作,读取错误(本机)errno:-4068,代码:'EISDIR',系统调用:'read'}全部完成”
  • 会不会有一个目录被读取为文件?如果您使用glob 方法,则可能意味着该模式正在返回一个目录。这就是我使用**/*.json 模式的原因,这意味着“在任何文件夹中,无论嵌套级别如何,所有以.json 结尾的项目”(我的假设是所有文件实际上都有.json 扩展名)
  • 好的,所以我仍然对放入 glob(something, . .) 插槽的目录地址感到困惑。我的 JSON 文件开始的完整扩展名如下:“D:\njs\nodetest1\imports\files” 在“files”目录中,有大约 100 个子目录,每个子目录都包含 .json 文件。假设我只想要 .json 文件,你是正确的。但是如何正确搜索该目录?您一直在帮助我的脚本位于“D:\njs”
  • glob('nodetest1/imports/files/**/*.json', function(error, files) { console.log(files); }); 是否会为您提供您要查找的文件的(长)列表?
  • 不幸的是它什么也没给我。
【解决方案2】:

只需将您的数据构造成一大堆对象,然后运行db.collection.insertMany

【讨论】:

  • 太多数据无法一次保存在 ram 中。
【解决方案3】:

我建议你使用 Promises 来做这件事:

const Bluebird = require('bluebird');
const glob = Bluebird.promisify(require('glob'));
const mongodb = require('mongodb');
const fs = Bluebird.promisifyAll(require('fs'));
const Path = require('path');
const MongoClient = mongodb.MongoClient;

const insertMillionsFromPath = Bluebird.coroutine(function *(path, mongoConnString) {
    const db = yield MongoClient.connect(mongoConnString);
    try {
        const collection = db.collection('users');
        const files = yield glob(Path.join(path, "*.json"));
        yield Bluebird.map(
            files,
            Bluebird.coroutine(function *(filename) {
                console.log("reading", filename);
                const fileContent = yield fs.readFileAsync(filename);
                const obj = JSON.parse(fileContent);

                console.log("inserting", filename);
                yield collection.insertOne(obj);
            }),
            {concurrency: 10} // You can increase concurrency here
        );
    } finally {
        yield db.close();
    }
});

insertMillionsFromPath("./myFiles", "mongodb://localhost:27017/database")
    .then(()=>console.log("OK"))
    .catch((err)=>console.log("ERROR", err));

为了工作,您需要安装以下软件包:

npm install --save mongodb bluebird glob

并且您需要使用 node.js 版本 6 或更高版本,否则您需要转译您的 javascript(由于使用了 function *() 生成器)。

【讨论】:

    猜你喜欢
    • 2017-01-22
    • 1970-01-01
    • 1970-01-01
    • 2011-01-14
    • 1970-01-01
    • 2015-10-24
    • 2021-12-17
    • 1970-01-01
    • 2020-02-15
    相关资源
    最近更新 更多