在 Node.js 中进行同步 MongoDB 查询的正确方法是啥?

Posted

技术标签:

【中文标题】在 Node.js 中进行同步 MongoDB 查询的正确方法是啥?【英文标题】:What is the right way to make a synchronous MongoDB query in Node.js?在 Node.js 中进行同步 MongoDB 查询的正确方法是什么? 【发布时间】:2012-08-15 08:17:53 【问题描述】:

我正在使用 MongoDB 的 Node.JS 驱动程序,我想执行一个同步查询,如下所示:

function getAThing()

    var db = new mongo.Db("mydatabase", server, );

    db.open(function(err, db)
    
        db.authenticate("myuser", "mypassword", function(err, success)
        
            if (success)
            
                db.collection("Things", function(err, collection)
                
                    collection.findOne( name : "bob", function(err, thing)
                                               
                        return thing;
                    );
                );
            
        );
    );

问题是,db.open 是一个异步调用(它不会阻塞),所以 getAThing 返回“未定义”,我希望它返回查询结果。我确信我可以使用某种阻塞机制,但我想知道执行此类操作的正确方法。

【问题讨论】:

【参考方案1】:

ES 6(节点 8+)

您可以使用async/await

await 操作符暂停异步函数的执行,直到 Promise 被解析并返回值。

这样您的代码将以同步方式工作:

const query = MySchema.findOne( name: /tester/gi );
const userData = await query.exec();
console.log(userData)


较旧的解决方案 - 2013 年 6 月 ;)

现在Mongo Sync 可用,这是在 Node.js 中进行同步 MongoDB 查询的正确方法。

我也在用这个。您可以编写如下同步方法:

var Server = require("mongo-sync").Server;
var server = new Server('127.0.0.1');
var result = server.db("testdb").getCollection("testCollection").find().toArray();
console.log(result);

注意:它依赖于node-fiber,并且在 Windows 8 上存在一些问题。

编码愉快:)

【讨论】:

我用 mongo-sync 编写了一个 5 行脚本,但它失败了,尽管它几乎完美地匹配了他们的测试代码。它似乎有错误。 @jcollum :您能描述一下您遇到的确切问题吗?因为它对我有用,没有大问题。如果您确定它是模块中的错误,您可以在 Repo 上提出新问题 我提交了一个错误。显然,您必须从 mongo-sync 库中的 node_modules 中删除光纤模块。看起来像是包装问题。 如果它依赖于节点光纤,那么它不是同步的【参考方案2】:

没有办法在没有某种可怕的黑客攻击的情况下实现这种同步。正确的方法是让getAThing 接受一个回调函数作为参数,然后在thing 可用时调用该函数。

function getAThing(callback)

    var db = new mongo.Db("mydatabase", server, );

    db.open(function(err, db)
    
        db.authenticate("myuser", "mypassword", function(err, success)
        
            if (success)
            
                db.collection("Things", function(err, collection)
                
                    collection.findOne( name : "bob", function(err, thing)
                           
                        db.close();                    
                        callback(err, thing);
                    );
                );
            
        );
    );

Node 7.6+ 更新

async/await 现在提供了一种在使用返回承诺的异步 API 时以同步样式编码的方式(就像原生 MongoDB 驱动程序一样)。

使用这种方式,上面的方法可以写成:

async function getAThing() 
    let db = await mongodb.MongoClient.connect('mongodb://server/mydatabase');
    if (await db.authenticate("myuser", "mypassword")) 
        let thing = await db.collection("Things").findOne( name: "bob" );
        await db.close();
        return thing;
    

然后您可以从另一个async 函数调用它作为let thing = await getAThing();

然而,值得注意的是MongoClient 提供了一个连接池,所以你不应该在这个方法中打开和关闭它。相反,在您的应用启动期间调用MongoClient.connect,然后将您的方法简化为:

async function getAThing() 
    return db.collection("Things").findOne( name: "bob" );

请注意,我们不会在方法中调用await,而是直接返回由findOne 返回的promise。

【讨论】:

感谢 Johnny 提供的解决方法!我希望有一个开箱即用的简单方法...即使编写一个简单的if_exists() 函数也令人沮丧...顺便说一句,如果有人知道更简单的方法或驱动程序的更新,请在此处发布。 您可以随时使用异步库,以避免识别“厄运”github.com/caolan/async 这将使代码更具可读性和美观性。 @Logan 我不会称其为“解决方法”,这就是节点的设计方式。 返回的thing 是一个'[object Promise]'。我们如何读取 Promise 对象中的数据? (没有.then 回调) @user1063287 是的。有关更多示例,请参阅here。【参考方案3】:

虽然它不是严格同步的,但我反复采用并发现非常有用的一种模式是在异步函数上使用 co 和 promisify yield。对于 mongo,你可以重写上面的:

var query = co( function* () 

    var db = new mongo.Db("mydatabase", server, );
    db = promisify.object( db );
    db = yield db.open();

    yield db.authenticate("myuser", "mypassword");

    var collection = yield db.collection("Things");
    return yield collection.findOne(  name : "bob" );

);

query.then( result => 

 ).catch( err => 

 );

这意味着:

    您可以使用任何异步库编写类似“同步”的代码 从回调中抛出错误,这意味着您不需要成功检查 您可以将结果作为承诺传递给任何其他代码

【讨论】:

以上是关于在 Node.js 中进行同步 MongoDB 查询的正确方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章

node.js+express+mongoose实现用户增删查改案例

Mongodb 以及 node.js中使用mongoose操作数据库

Node.js Express+Mongodb 项目实战

node.js 增删改查(原始)

Node对MongoDB实现增删改查,分页功能

Mongoose简要API