web-dev-qa-db-ja.com

MongoErrorの修正方法:終了したセッションは使用できません

Node.jsを使用してMongoDB Atlasコレクションからデータを読み取ろうとしています。コレクションの内容を読み込もうとすると、エラーMongoError: Cannot use a session that has ended。これが私のコードです

client.connect(err => {
    const collection = client
        .db("sample_airbnb")
        .collection("listingsAndReviews");

    const test = collection.find({}).toArray((err, result) => {
        if (err) throw err;
    });
    client.close();
});

特定のドキュメントをクエリすることはできますが、コレクションのすべてのドキュメントを返す方法がわかりません。このエラーを検索しましたが、あまり見つかりません。ありがとう

7
awebdev

1つのオプションは、a Promiseチェーンを使用することです。 collection.find({}).toArray()は、コールバック関数を受け取るか、promiseを返すことができるため、.then()で呼び出しをチェーンできます。

_collection.find({}).toArray() // returns the 1st promise
.then( items => {
    console.log('All items', items);
    return collection.find({ name: /^S/ }).toArray(); //return another promise
})
.then( items => {
    console.log("All items with field 'name' beginning with 'S'", items);
    client.close(); // Last promise in the chain closes the database
);
_

もちろん、このデイジーチェーン接続により、コードの同期性が向上します。これは、最初の呼び出しでユーザーIDを取得し、次の呼び出しでユーザーの詳細を検索するなど、チェーンの次の呼び出しが前の呼び出しに関連している場合に役立ちます。

いくつかの無関係なクエリを並列(非同期)で実行する必要があり、すべての結果が戻ったら、データベース接続を破棄します。たとえば、配列またはカウンターの各呼び出しを追跡することでこれを行うことができます。

_const totalQueries = 3;
let completedQueries = 0;

collection.find({}).toArray()
.then( items => {
    console.log('All items', items);
    dispose(); // Increments the counter and closes the connection if total reached
})

collection.find({ name: /^S/ }).toArray()
.then( items => {
    console.log("All items with field 'name' beginning with 'S'", items);
    dispose(); // Increments the counter and closes the connection if total reached
);

collection.find({ age: 55 }).toArray()
.then( items => {
    console.log("All items with field 'age' with value '55'", items);
    dispose(); // Increments the counter and closes the connection if total reached
);

function dispose(){
    if (++completedQueries >= totalQueries){
        client.close();
    }
}
_

3つのクエリがあります。それぞれがdispose()を呼び出すと、カウンターが増加します。それらがすべてdispose()を呼び出すと、最後のものも接続を閉じます。

Async/AwaitはPromise関数からのthen結果をアンラップするため、さらに簡単になります。

_async function test(){
    const allItems = await collection.find({}).toArray();
    const namesBeginningWithS = await collection.find({ name: /^S/ }).toArray();
    const fiftyFiveYearOlds = await collection.find({ age: 55 }).toArray();
    client.close();
}

test();
_

以下は、Async/Awaitが非同期コードを順番に動作させ、次の関数を呼び出す前に1つの非同期関数が完了するのを待機することによって非効率的に実行する方法の例です。完成しました。

_let counter = 0;

function doSomethingAsync(id, start) {
  return new Promise(resolve => {
    setTimeout(() => {
      counter++;
      const stop = new Date();    
      const runningTime = getSeconds(start, stop);
      resolve(`result${id} completed in ${runningTime} seconds`);
    }, 2000);
  });
}

function getSeconds(start, stop) {
  return (stop - start) / 1000;
}

async function test() {
  console.log('Awaiting 3 Async calls');
  console.log(`Counter before execution: ${counter}`);
  
  const start = new Date();
  
  let callStart = new Date();
  const result1 = await doSomethingAsync(1, callStart);
  
  callStart = new Date();
  const result2 = await doSomethingAsync(2, callStart);
  
  callStart = new Date();
  const result3 = await doSomethingAsync(3, callStart);
  
  const stop = new Date();

  console.log(result1, result2, result3);
  console.log(`Counter after all ran: ${counter}`);
  console.log(`Total time to run: ${getSeconds(start, stop)}`);
 }

test();_

注:上記の例のように待機すると、呼び出しが再び順次行われます。それぞれの実行に2秒かかる場合、関数の完了には6秒かかります。

すべての世界のベストを組み合わせて、すべての呼び出しをすぐに実行しながら、非同期/待機を使用する必要があります。幸い、Promiseにはこれを行うメソッドがあるため、test()は次のように記述できます。

_async function test(){
    let [allItems, namesBeginningWithS, fiftyFiveYearOlds] = await Promise.all([
        collection.find({}).toArray(),
        collection.find({ name: /^S/ }).toArray(),
        collection.find({ age: 55 }).toArray()
    ]);

    client.close();
}
_

パフォーマンスの違いを示すための実用的な例を次に示します。-

_let counter = 0;

function doSomethingAsync(id, start) {
  return new Promise(resolve => {
    setTimeout(() => {
      counter++;
      const stop = new Date();    
      const runningTime = getSeconds(start, stop);
      resolve(`result${id} completed in ${runningTime} seconds`);
    }, 2000);
  });
}

function getSeconds(start, stop) {
  return (stop - start) / 1000;
}

async function test() {
  console.log('Awaiting 3 Async calls');
  console.log(`Counter before execution: ${counter}`);
  const start = new Date();
  
  const [result1, result2, result3] = await Promise.all([
        doSomethingAsync(1, new Date()),
        doSomethingAsync(2, new Date()),
        doSomethingAsync(3, new Date())
  ]);
  
  const stop = new Date();

  console.log(result1, result2, result3);
  console.log(`Counter after all ran: ${counter}`);
  console.log(`Total time to run: ${getSeconds(start, stop)}`);
 }

test();_
0
Antony Booth