2016-12-06 142 views
6

我有大約一百萬個JSON文件保存在目錄「D:/ njs/nodetest1/imports/source1 /」的許多子目錄中,我想將它們導入到我的mongoDB數據庫的集合「users」中。如何從JSON文件目錄向mongoDB添加多條記錄?

以下代碼正確遍歷文件系統。正如你所看到的,它讀取目錄中的每個項目,如果該項目是一個目錄,它將讀取其中的每個項目。對於不是目錄的每個項目,在發送包含函數的變量之前,它會對其執行一些操作。

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部分。但顯然這可能是由過程中的某些事件引起的。
  • 如果我添加錯誤處理,沒有錯誤產生。我已經將錯誤處理留在了這篇文章之外,因爲它混淆了代碼片段的可讀性。
+0

不清楚您的runOnJson()函數是應該做的 - 啜食JSON內容和東西到蒙戈? –

+0

不完全是我問的。我問什麼應該具體*函數* runOnJson()做什麼?如果它需要做的就是在文件上運行mongoimport(例如從示例中),那麼你是否嘗試過? –

回答

8

mongodb2.2 (current latest) documentationinsert已被棄用

DEPRECATED

Use insertOne, insertMany or bulkWrite

那麼簡單的答案是可能改變collection.insert([entry], ...)collection.insertOne(entry, ...),就大功告成了。


那麼對於長的答案,你說「一百萬的JSON文件」,它通常與值得的開銷最少的一個完整的異步方法。

有在示例代碼2(潛在的)瓶頸:

  • 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(); 
     }); 
    }); 
}); 

注意:您可以收集多個「入口」 -records利用多個插入的性能增益使用insertMany,雖然我感覺插入的記錄比描述更復雜,如果處理不正確,它可能會給一些內存問題。

+0

這似乎很有幫助,我還沒有完成消化。但是當我跑它時,我得到了以下錯誤。我顯然很想調試自己 - 但我想我會發布以防萬一這個問題很明顯。 ||| 「{錯誤:EISDIR:在目錄上違規操作,在錯誤(本機)的errno閱讀:-4068,代碼:‘EISDIR’,系統調用:‘讀’} 全部完成」 – COMisHARD

+0

難道是因爲有一個目錄當中被讀爲一個文件?如果你使用'glob'方法,它可能意味着該模式正在返回一個目錄。這就是爲什麼我使用'**/*。json'模式,這意味着「在任何文件夾中,不管嵌套層次,所有以'.json'結尾的項目」(我的假設是所有文件實際上都有'.json' extension) –

+0

好吧,我仍然難以將目錄地址放入glob(something,...)插槽。我的JSON文件開始位置的完整擴展如下所示:「D:\ njs \ nodetest1 \ imports \ files」在「files」目錄中有大約100個子目錄,每個子目錄都有.json文件。假設我只想要.json文件,你是正確的。但是,如何正確搜索該目錄?您一直在幫助我的腳本位於「D:\ njs」 – COMisHARD

2

我建議你這樣做使用承諾:

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

,你將需要使用節點.js版本6或更高版本,否則您將需要傳輸您的JavaScript(由於function *()生成器的使用)。