如何修复MongoError:无法使用已结束的会话

时间:2020-01-20 02:29:15

标签: node.js mongodb mongodb-atlas

我正在尝试使用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();
});

我可以查询特定的文档,但是我不确定如何返回集合的所有文档。我已经搜索了此错误,但找不到很多错误。谢谢

5 个答案:

答案 0 :(得分:20)

在您的代码中,它不会等待find()完成执行,而是继续执行client.close()语句。因此,当它尝试从db读取数据时,连接已经结束。我遇到了同样的问题,并像这样解决了它:

// connect to your cluster
const client = await MongoClient.connect('yourMongoURL', { 
    useNewUrlParser: true, 
    useUnifiedTopology: true,
});
// specify the DB's name
const db = client.db('nameOfYourDB');
// execute find query
const items = await db.collection('items').find({}).toArray();
console.log(items);
// close connection
client.close();

编辑:这整个过程应该在async函数中。

答案 1 :(得分:2)

当我将MongoClient从3.3.2更新到最新版本(在撰写本文时为3.5.2)时,遇到相同的问题。要么通过更改package.json "mongodb": "3.3.2",来仅安装3.3.2版本,要么只需使用异步并等待包装器即可。

如果问题仍然存在,请删除node_modules并重新安装。

答案 2 :(得分:0)

一种选择是使用Promise链。 collection.find({}).toArray()可以接收回调函数或返回承诺,因此您可以使用.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()后,最后一个也会关闭连接。

异步/等待应该使它变得更加容易,因为它们会解开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如何最终使异步代码按顺序运行并在调用下一个异步函数之前等待一个异步函数完成而效率低下运行,而理想情况是立即调用所有异步函数并仅等待直到全部完成。

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秒钟才能完成。

结合所有优点,您可能希望在立即运行所有呼叫时使用Async / Await。幸运的是,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();

答案 3 :(得分:0)

其他人已经谈到了这一点,但是我只想强调.toArray()是异步执行的,因此您需要确保它在结束会话之前已经完成

这不起作用

Bearer <access_token>

这将

const randomUser = await db.collection('user').aggregate([ { $sample: { size: 1 } } ]);
console.log(randomUser.toArray()); 
await client.close();

答案 4 :(得分:0)

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

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

        client.close();

    });        
});
相关问题