2014-08-31 112 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