Node.js mongodb 驱动程序异步/等待查询

Posted

技术标签:

【中文标题】Node.js mongodb 驱动程序异步/等待查询【英文标题】:Node.js mongodb driver async/await queries 【发布时间】:2018-05-02 09:38:12 【问题描述】:

我有一个使用 mongodb 本机驱动程序的 node.js 应用程序。 在使用节点 v8.9.1 将我的应用程序代码迁移到 async/await 的过程中,我正在努力为 mongodb 查询找到一种优雅的方式。 mongodb 驱动程序的主要问题是,所有查询都使用回调,其中承诺函数对于异步方法是必需的。

替代方案:

mongoose- 承诺不推荐使用查询,它强制使用 Schema 模型,这对我的应用程序来说有点开销。 mongoist - 据说很棒,因为它在构建时考虑到了 async/await 并完全承诺,但是与 mongodb 的 SSL 连接错误和糟糕的文档 - 让我远离了这个解决方案。

我以优雅的方式成功实现的唯一解决方法是使用 callback-promise npm 包将 mongodb 驱动程序 API 转换为完全承诺。

对于优雅的高性能方式有什么新鲜想法吗?

【问题讨论】:

@MikaS Is 似乎需要一个“co”包。我基本上是在寻找一个完整的原生库 【参考方案1】:

由于所有答案都缺少一些位(捕获块,检查客户端不是null),因此我提供了自己的解决方案。使用 Mongo server v4.0.7 和 Node JS driver 3.2.2 测试。

请注意,该示例是一个控制台程序,我们在finally 块中关闭与服务器的连接。在 Web 应用程序中,连接被重用。 见Node Mongo docs。此外,错误会记录在 Winston 或 Morgan 等库中,而不是控制台记录。

const MongoClient = require('mongodb').MongoClient;

const url = 'mongodb://localhost:27017';

async function findOne() 

    const client = await MongoClient.connect(url,  useNewUrlParser: true )
        .catch(err =>  console.log(err); );

    if (!client) 
        return;
    

    try 

        const db = client.db("testdb");

        let collection = db.collection('cars');

        let query =  name: 'Volkswagen' 

        let res = await collection.findOne(query);

        console.log(res);

     catch (err) 

        console.log(err);
     finally 

        client.close();
    


await findOne();

【讨论】:

【参考方案2】:

编辑:'mongodb' v3.x

根据mongoDB ES6 future 你可以用这种方式;

let MongoClient = require('mongodb').MongoClient;
const connectionString = 'mongodb://localhost:27017';

    (async () => 
        let client = await MongoClient.connect(connectionString,
             useNewUrlParser: true );

        let db = client.db('dbName');
        try 
           const res = await db.collection("collectionName").updateOne( 
               "someKey": someValue
           ,  $set: someObj ,  upsert: true );

           console.log(`res => $JSON.stringify(res)`);
        
        finally 
            client.close();
        
    )()
        .catch(err => console.error(err));

【讨论】:

这很漂亮,正是我想要的。谢谢! 从版本 3 开始,MongoClient 返回一个客户端,而不是一个 db 对象! 这不再起作用了。请问我们可以获取最新版本的 mongodb 的更新吗? 假设如果我们在使用 await 调用的函数中再调用 3 个函数,我们是否需要使用 await 调用内部 3 个函数。【参考方案3】:

谢谢。与 ES6 配合得很好:

const middleWare = require('middleWare');
const MONGO = require('mongodb').MongoClient;

router.get('/', middleWare(async (req, res, next) => 
    const db = await MONGO.connect(url);
    const MyCollection = db.collection('MyCollection');
    const result = await MyCollection.find(query).toArray();
    res.send(result);
))

【讨论】:

这是一个很好的答案。对于其他试图弄清楚require('middleware') 步骤的人,这里有一个很棒的指南:medium.com/@Abazhenov/… 这也不再起作用了。有更新的机会吗? 我现在忽略了 mongodb 并使用 mongoose。强烈推荐,因为有很多更新和承诺支持。【参考方案4】:

这是我发现的与 Mongo3 和 async/await 兼容的最小代码段。 享受吧!

module.exports = 
  myFunction: async (query) => 
    let db, client;
    try 
      client = await MongoClient.connect(process.env.MONGODB_CONNECTION_STRING,  useNewUrlParser: true );
      db = client.db(dbName);
      return await db.collection(collectionName).find(query).toArray();
     finally 
      client.close();
    
  

【讨论】:

[重复答案] 这个答案没有给其他答案添加任何唯一值,并且可执行代码sn-p不起作用,它的用法是错误的。 可能是的,我只是发现其他答案不够简洁【参考方案5】:

如果你不传递回调,mongodb 客户端会返回一个 promise。

官方的 MongoDB Node.js 驱动程序提供基于回调和基于 Promised 的与 MongoDB 交互,允许应用程序充分利用 ES6 中的新功能

来自官方 docs

【讨论】:

【参考方案6】:

如果你想在不卸载到 Array 的情况下使用 cursor,你不能将 await 与 find() 或 aggregate() 函数一起使用,那么你必须使用代码:

Usa 的 UPD: 对于一般情况,使用 toArray() 的答案就足够了。

但是当涉及大量文档集合时,使用 toArray() 会超出可用 RAM。因此,在这些情况下,“高性能”解决方案不得使用 toArray()。

对于这些情况,您可以使用 MongoDB 流,它运行良好,但比使用流更简单的是:

const cursor = db.collection('name').aggregate(
    [
        
            "$match": code: 10
        ,
        
          "$count": "count"
        
    ],
    
        "allowDiskUse": false
    
)

for (let doc = await cursor.next(); doc != null; doc = await cursor.next()) 
    console.log('aggregate:', doc.count);

【讨论】:

@Pax_Beach - 请编辑您的答案,以便我将其视为有用的内容。请参阅上面的美国评论。由于美国对与您的答案相关的极少数情况进行了解释,因此需要进行编辑(不做任何更改)。【参考方案7】:

我将其发布为答案,因为我无法对 Ido Lev's answer 发表评论。我会在声望达到 50 后立即移动它。

不要忘记关闭数据库连接。否则,由于打开的连接太多(一周前发生在我身上),您的应用程序可能无法连接到数据库。

您的查询可能成功或失败,因此在finally-block 中关闭连接是有意义的。

const db = await MongoClient.connect(url);
try 
    const stuff = await db.collection("Stuff").find();
    // Do something with the result of the query
 finally 
    db.close();

更新:看来这也没有解决我的问题。有人说你甚至不需要手动关闭连接。如果可能的话,似乎最好在您的应用程序中重复使用您的连接。

【讨论】:

是的!我正在尝试打开一个连接并将其重用于处理当前请求所需的所有数据库操作。 AFAIK 打开连接是一项昂贵的操作,而读取和写入要轻得多,并且可以通过单个连接完成。大多数关于连接、更新和关闭的示例都在同一个代码块中。我想将我的打开连接存储在共享上下文中,以使其可用于所有功能。【参考方案8】:

(基于 Pax Beach 的回答。它被否决了,我想添加一条评论,解释为什么在某些情况下,Pat 的回答是最好的。我没有足够的代表来添加 cmets。)

对于一般情况,使用 toArray() 的答案就足够了。

但是当涉及到 巨大 文档集合时,使用 toArray() 会超出可用 RAM。因此,在这些情况下的“高性能”解决方案不得使用 toArray()。

对于这些情况,您可以使用 MongoDB 流,它运行良好,但比使用流更简单的是:

const cursor = db.collection('someCollection').find()
for (let doc = await cursor.next(); doc; doc = await cursor.next()) 
    // Process the document.

【讨论】:

【参考方案9】:

mongoose find Query Using async/await

在异步/等待的情况下不要使用mongoose.connect

    var router = require("express").Router()
    var mongoose = require("mongoose")
var await = require("await")
var async = require("async")

    var mongoUrl = "mongodb://localhost:27017/ekaushalnsdc"

    router.get("/async/await/find",async(req, res,  next) => 
      try 
        var db =  await mongoose.createConnection(mongoUrl)
          var colName = db.collection('collectionName')
        var result  = await colName.find().toArray()
        res.json(result)
    catch(ex) 
       res.json(ex.message)
    
    )

【讨论】:

【参考方案10】:

我来这里是为了async/await 的解决方案,但真的不喜欢任何答案,所以我想出了下面的 sn-p。

我正在使用本机驱动程序,而且效果很好。我发现它比for 循环更具可读性:

const cursor = await db.collection('myCollection').find();

while (await cursor.hasNext()) 
  const doc = await cursor.next();
  // do whatever, ex.
  console.log(doc);
;

toArray() 方法也可以,但是——正如前面所指出的——你必须确保你永远不会得到一个占用你 RAM 的大结果集。

【讨论】:

这里没有必要使用await,因为hasNext() 和next() 是同步的并且不会返回Promise。因此,您正在等待is converted to resolved Promise 的原始/对象。【参考方案11】:

我正在尝试使用 mongojs 从 mongodb 获取数据并显示在 graphiql 服务器中。我用过promise,终于搞定了。

async resolve(parent,args)
                function getdata()
                return new Promise(function(resolve,reject)
                    var o_id = ObjectId(args.id);
                    var obj = _id:o_id
                    db.book.find(obj,(err,data) => 
                        if(err)
                            console.log(err);
                            reject("error in sending the book data");
                            
                        else
                                if(data.length>0)
                                   resolve(data);
                                elsereject("data length is ! > 0")
                        
                    );
                )
                
                async function calldata()
                    var dataNew = await getdata().then(function(returnedData)
                        return returnedData;
                    ).catch(function(errorinpassing)
                        console.log(errorinpassing);
                    )
                    return dataNew
                
                var hello = await calldata()
                if(hello.length>0)
                    return hello[0]
                
            

【讨论】:

以上是关于Node.js mongodb 驱动程序异步/等待查询的主要内容,如果未能解决你的问题,请参考以下文章

等待异步函数完成而不添加回调

Node.js 与 .net 中的异步/等待

在 Node.js 中等待异步函数返回

Node.js 异步等待快递

异步/等待 Discord.js Node.js Javascript JS

如何编写一个在“返回”之前等待事件触发的 node.js 函数?