Как исправить MongoError: Невозможно использовать сеанс, который закончился - PullRequest
5 голосов
/ 20 января 2020

Я пытаюсь прочитать данные из коллекции MongoDB Atlas, используя Node.js. Когда я пытаюсь прочитать содержимое моей коллекции, я получаю ошибку 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();
});

Я могу запросить конкретный c документ, но я не уверен, как вернуть все документы коллекции. Я искал эту ошибку, я не могу найти много на ней. Спасибо

Ответы [ 3 ]

5 голосов
/ 21 января 2020

В вашем коде он не ждет, пока find() завершит свое выполнение, и переходит к оператору client.close(). Таким образом, к тому времени, когда он пытается прочитать данные из базы данных, соединение уже закончилось. Я столкнулся с этой же проблемой и решил ее так:

// 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 голос
/ 23 января 2020

Возникла та же проблема, когда я обновил MongoClient с 3.3.2 до последней версии (3.5.2 на момент написания этой статьи). Либо установите только версию 3.3.2, изменив пакет. json "mongodb": "3.3.2", или просто используйте asyn c и ждите упаковщик.

Если проблема не устранена, удалите node_modules и установите снова.

0 голосов
/ 26 февраля 2020

Один из вариантов - использовать 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
);

Конечно, такое последовательное соединение делает код более синхронным. Это полезно, когда следующий вызов в цепочке относится к предыдущему, например, получение идентификатора пользователя в первом, а затем поиск сведений о пользователе в следующем.

Несколько несвязанных запросов должны выполняться параллельно (asyn c), а когда все результаты вернутся, избавиться от соединения с базой данных. Это можно сделать, например, отслеживая каждый вызов в массиве или счетчике.

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 может закончиться создание асин c кода, который ведет себя последовательно и работает неэффективно, ожидая завершения одной асин c функции перед вызовом следующей, когда идеальным сценарием является немедленный вызов их всех и ожидание их завершения.

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