web-dev-qa-db-ja.com

Node.js mongodbドライバーの非同期/待機クエリ

Mongodbネイティブドライバーを使用するnode.jsアプリケーションがあります。ノードv8.9.1を使用してアプリケーションコードをasync/awaitに移行するプロセスで、mongodbクエリのエレガントな方法を見つけるのに苦労しています。 mongodbドライバーの主な問題は、すべてのクエリがコールバックを使用しており、promises関数が非同期メソッドに必須であることです。

代替案:

  • mongoose-クエリは非推奨になり、スキーマモデルの使用が強制されますが、これは私のアプリにとって少しオーバーヘッドです。
  • mongoist-async/awaitを念頭に置いて構築され、完全に約束されているため、素晴らしいと言われていますが、mongodbへのSSL接続のエラーと貧弱なドキュメントがこのソリューションから私を引き離しました。

エレガントな方法で実装に成功した唯一の回避策は、callback-promise npmパッケージを使用してmongodbドライバーAPIを完全に約束するように変換することです。

エレガントで高性能な方法のための新鮮なアイデアはありますか?

20
Ido Lev

すべての回答にいくつかのビットが欠けているため(ブロックをキャッチし、クライアントがnullではないことを確認します)、私は自分の解決策を見つけました。 Mongoサーバーv4.0.7およびNode JSドライバーでテスト済み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();
    }
}

findOne();
9
Jan Bodnar

編集: '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));
26
Serhat Ates

ありがとう。 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);
}))
24
Ido Lev

これは、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();
    }
  }
}
11
Sovattha Sok

Ido Lev's answer にコメントできないため、これを回答として投稿しています。評判が50に達したらすぐにこれを移動します。

Db接続を閉じることを忘れないでください。そうしないと、開いている接続が多すぎるためにアプリケーションがデータベースに接続できない可能性があります(1週間前に私に起こりました)。

クエリは成功または失敗する可能性があるため、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:これも私の問題を解決しなかったようです。手動で接続を閉じる必要さえないと言う人もいます。可能であれば、アプリケーション間で接続を再利用するのが最善のようです。

4
0xC0DEBA5E

Arrayにアンロードせずにカーソルを操作したい場合、find()またはaggregate()関数でawaitを使用できない場合、コードを使用する必要があります。

sasによる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);
}
3
Pax Beach

コールバックを渡さない場合、mongodbクライアントはpromiseを返します。

公式のMongoDB Node.jsドライバーは、コールバックベースと約束ベースのMongoDBとのやり取りの両方を提供し、アプリケーションがES6の新機能を最大限に活用できるようにします

公式から docs

3
Surya

async/awaitを使用したmongoose findクエリ

非同期/待機の場合は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)
    }
    })
3
muthukumar

(Pax Beachの回答に基づきます。それはダウン投票されていたので、状況によってはPatの回答が最高である理由を説明するコメントを追加したかったです。コメントを追加するのに十分な担当者がいません。)

一般的な場合、toArray()を使用した答えで十分です。

しかしhugeドキュメントコレクションが含まれる場合、toArray()を使用すると使用可能なRAMを超えることになります。したがって、これらの状況での「高性能」ソリューションでは、toArray()を使用しないでください。

そのような場合には、MongoDBストリームを使用できますが、これはうまく機能しますが、ストリームを使用するよりも簡単です:

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