admin管理员组

文章数量:1134247

I have a node.js application using mongodb native driver. In the process of migrating my application code to async/await using node v8.9.1, I am struggling to find an elegant way for the mongodb queries. The major problem with mongodb driver is, that all queries are using callbacks where promises functions are mandatory for the async methods.

Alternatives:

  • mongoose- promises queries deprecated and it forces using Schema model which is a little overhead for my app.
  • mongoist- allegedly great, since it built with async/await in mind and fully promise, but errors with SSL connection to mongodb and poor documentations- drew me away from this solution.

The only workaround I succeeded to implement in an elegant way is using callback-promise npm package to convert mongodb driver API to fully promise.

Any fresh ideas for an elegant high performance way?

I have a node.js application using mongodb native driver. In the process of migrating my application code to async/await using node v8.9.1, I am struggling to find an elegant way for the mongodb queries. The major problem with mongodb driver is, that all queries are using callbacks where promises functions are mandatory for the async methods.

Alternatives:

  • mongoose- promises queries deprecated and it forces using Schema model which is a little overhead for my app.
  • mongoist- allegedly great, since it built with async/await in mind and fully promise, but errors with SSL connection to mongodb and poor documentations- drew me away from this solution.

The only workaround I succeeded to implement in an elegant way is using callback-promise npm package to convert mongodb driver API to fully promise.

Any fresh ideas for an elegant high performance way?

Share Improve this question edited Jan 13, 2019 at 6:37 Hendy Irawan 21.3k12 gold badges108 silver badges116 bronze badges asked Nov 18, 2017 at 20:03 Ido LevIdo Lev 1,0711 gold badge8 silver badges10 bronze badges 1
  • @MikaS Is seems to require a ‘co’ package. I am basically looking for a full promise native library – Ido Lev Commented Nov 18, 2017 at 21:45
Add a comment  | 

11 Answers 11

Reset to default 70

Since all answers are missing some bits (catch blocks, checking that client is not null) I came with my own solution. Tested with Mongo server v4.0.7 and Node JS driver 3.2.2.

Note that the example is a console program, where we close the connection to the server in the finally block. In a web application, the connections are reused. See Node Mongo docs. Also, the errors are logged with libraries such as Winston or Morgan and not console logged.

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

Edit: 'mongodb' v3.x

according to mongoDB ES6 future you can use this way;

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));

Thanks. Working great with 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);
}))

This is the smallest piece of code I found that is compatible with Mongo3 and async/await. Enjoy!

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

If u don't pass a callback, mongodb client returns a promise.

The official MongoDB Node.js driver provides both callback based as well as Promised based interaction with MongoDB allowing applications to take full advantage of the new features in ES6

From the official docs

If you want to work with cursor without unloading to Array, you can't use await with find() or aggregate() functions, then you have to use the code:

UPD by Usas: For the general case, the answers using toArray() are sufficient.

But when huge document collections are involved, using toArray() would exceed available RAM. Thus a "high performance" solution in those situations must not use toArray().

For those cases, you can use MongoDB streams, which works well, but even simpler than using streams is to:

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

I'm posting this as an answer because I can't comment on Ido Lev's answer. I will move this as soon as I have reached 50 reputation.

Don't forget to close the db connection. Otherwise it's possible that your application can't connect to the db because of too many open connections (happened to me a week ago).

Your query may succeed or fail, so it makes sense to close the connection in a 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();
}

Update: It seems that this also didn't fix my problem. Some people say that you don't even need to close the connection manually. It seems like it's best to reuse your connection across you application if possible.

(Based on Pax Beach's answer. It had been downvoted, and I wanted to add a comment explaining why in some situations, Pat's answer is the best. I don't have enough rep to add comments.)

For the general case, the answers using toArray() are sufficient.

But when huge document collections are involved, using toArray() would exceed available RAM. Thus a "high performance" solution in those situations must not use toArray().

For those cases, you can use MongoDB streams, which works well, but even simpler than using streams is to:

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

mongoose find Query Using async/await

dont Use mongoose.connect in case of async/await

    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)
    }
    })

I came here for the async/await solution, but couldn't really like any of the answers, so I came up with the below snippet.

I'm using the native driver and this works pretty well. I find it more readable then the for loop:

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

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

The toArray() method is also OK, but –as pointed out before– you have to be sure you never get a large result set that eats up your RAM.

I am trying to get the data from mongodb using mongojs and display in graphiql server. I have used promises and got it finally.

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);
                                }else{reject("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]
                }
            }

本文标签: javascriptNodejs mongodb driver asyncawait queriesStack Overflow