2014-08-31 222 views
1

我正在連接並使用Node/MongoDB進行插入,但由於範圍問題,我無法從函數訪問連接。任何想法如何使'db'變量全局範圍?MongoDB/Javascript範圍問題

mongodb.connect("mongodb://localhost:27017/userDB", function(err, db) { 

    if(!err) { 
     console.log("We are connected"); 
    } else { 
     console.log(err); 
    } 

}); 

function RegisterUser(user, pass) { 
    var collection = db.collection('users'); 
    var docs = [{username:user}, {password: pass}]; 
    collection.insert(docs, {w:1}, function(err, result) { 
     collection.find().toArray(function(err, items) {}); 
     socket.emit('message', items); 
    }); 
} 

/var/www/baseball/app.js:80 
    var collection = db.collection('users'); <--db is not defined 
        ^
ReferenceError: db is not defined 
at RegisterUser (/var/www/baseball/app.js:80:20) 
at ParseData (/var/www/baseball/app.js:63:6) 

回答

1

在一般情況下,只能使一次連接,可能是因爲你的應用程序啓動。至少,MongoLabs的專家告訴我,mongo驅動程序將處理池等。請注意,這與您可能在Java中執行的操作有很大區別,等等......將connect()返回的某處,可能是全局或應用程序中或常用模塊中的值保存起來。然後根據需要在註冊用戶,刪除用戶等中使用它。

+0

我不好,我只是在回調之外聲明瞭一個全局變量global_db。我以爲我已經嘗試過,並沒有工作,但我再次嘗試,它這樣做,謝謝。 – 2014-08-31 22:45:06

+0

從長遠來看,你可能需要做一些更復雜的事情,如尼爾的答案,你可能不想要一個全球化的,但至少你是開始。 – user949300 2014-09-01 00:44:54

0

首先,你只打算一旦你有一個連接到被註冊用戶,所以做什麼都工作,你需要在那裏做......所以從連接範圍內調用RegisterUser。如果你想使用這個函數中的數據庫對象,你將需要在參數中傳遞爲DB

RegisterUsers(db, user, pass)

你便可以在函數中使用DB

1

更多的節點問題真的,但可能值得標籤,因爲它被問了一下。所以你說有一個範圍問題,你是正確的,因爲變量是.connect()方法的回調函數的本地,並且在其他任何地方都不可見。一種方法是將所有邏輯轉儲到回調中,因此沒有範圍問題,但您可能不希望這樣做。

問「我該如何設定一個全球」,也不是真正的正確方法。那麼不是直接的,因爲有關於分解節點的「異步」模式的一般有趣的事情。因此,更好的方法是使用某種「單例」實例,在該實例中只設置一次連接,但因爲這是全局的,或者可能「需要」用於應用程序的其他區域。

這裏是一個「微不足道」的方式來證明,但也有很多方法可以做同樣的事情:

var async = require('async'), 
    mongodb = require('mongodb'), 
    MongoClient = mongodb.MongoClient; 


var Model = (function() { 

    var _db; 
    var conlock; 
    return { 
    getDb: function(callback) { 
     var err = null; 
     if (_db == null && conlock == null) { 
     conlock = 1; 
     MongoClient.connect('mongodb://localhost/test',function(err,db) { 
      _db = db; 
      conlock == null; 
      if (!err) { 
      console.log("Connected") 
      } 
      callback(err,_db); 
     }); 
     } else if (conlock != null) { 
     var count = 0; 
     async.whilst(
      function() { return (_db == null) && (count < 5) }, 
      function(callback) { 
      count++ 
      setTimeout(callback,500); 
      }, 
      function(err) { 
      if (count == 5) 
       err = new Error("connect wait exceeded"); 
      callback(err,_db); 
      } 
     ); 
     } else { 
     callback(err,_db); 
     } 
    } 
    }; 

})(); 


async.parallel(
    [ 
    function(callback) { 
     console.log("call model"); 
     Model.getDb(function(err,db) { 
     if (err) throw err; 
     if (db != undefined) 
      console.log("db is defined"); 
     callback(); 
     }); 
    }, 
    function(callback) { 
     console.log("call model again"); 
     Model.getDb(function(err,db) { 
     if (err) throw err; 
     if (db != undefined) 
      console.log("db is defined here as well"); 
     callback(); 
     }) 
    } 
    ], 
    function(err) { 
    Model.getDb(function(err,db) { 
     db.close(); 
    }); 
    } 
); 

這裏因此,伸出小「模型」對象在.getDb()一個方法,也維護一個私有變量,一旦建立連接,就會保留_db連接。該方法的基本邏輯是看看是否定義了_db,以及它沒有與驅動程序建立連接。在連接回調中,然後設置_db變量。

這裏的另一件事是該方法本身接受一個「回調」,所以這就是你以後如何使用它的地方,在這裏將返回一個錯誤或當前連接。

最後一部分只是代碼中實現的兩個函數的演示。在第一次調用中,連接到數據庫的調用在進入提供的回調函數之前進行。

但是,下一次我們調用時,連接已經設置在私有變量中,因此數據僅僅被返回並且不會再次建立連接。

有很多種方法來實現這種事情,但這是遵循的基本邏輯模式。還有很多其他的「幫手」實現,它們包裝MongoDB驅動程序以使這些事情變得簡單,並且管理連接池並確保連接也適合您,因此即使您仍然堅持自己從底層司機基地完成所有工作。

+0

請參閱答案#1中的註釋。我非常欣賞細節,但它非常簡單,只需在回調之外聲明一個全局(窗口)變量,並將本地數據庫對象設置爲等於該全局變量即可。謝謝。 – 2014-08-31 22:46:57

+0

@TheHawk I當您的生產應用程序在啓動後直接接收到URL上的一個命中時,數據庫尚未連接時,可能會發生這種情況。延遲幾秒鐘,全球將被宣佈,但否則它不會。這是你錯過的一點。 – 2014-09-01 00:12:34

+0

@ Neil有效的點,但如果他們在我的_think_早期得到2次命中,你的代碼將會打開2個連接,對嗎?似乎應該有一些標準的「配方」來做到這一點。 – user949300 2014-09-01 00:48:04