web-dev-qa-db-fra.com

Comment réparer MongoError: impossible d'utiliser une session qui s'est terminée

J'essaie de lire les données d'une collection MongoDB Atlas en utilisant Node.js. Lorsque j'essaye de lire le contenu de ma collection, j'obtiens l'erreur MongoError: Cannot use a session that has ended. Voici mon code

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

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

Je suis en mesure de rechercher un document spécifique, mais je ne sais pas comment renvoyer tous les documents d'une collection. J'ai recherché cette erreur, je ne trouve pas grand-chose dessus. Merci

7
awebdev

Une option consiste à utiliser une chaîne Promise. collection.find({}).toArray() peut recevoir une fonction de rappel ou renvoyer une promesse, vous pouvez donc enchaîner les appels avec .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
);

Bien entendu, cette connexion en guirlande rend le code plus synchrone. Ceci est utile lorsque le prochain appel de la chaîne est lié au précédent, comme obtenir un identifiant d'utilisateur dans le premier, puis rechercher les détails de l'utilisateur dans le suivant.

Plusieurs requêtes non liées doivent être exécutées en parallèle (asynchrone) et lorsque tous les résultats sont de retour, supprimez la connexion à la base de données. Vous pouvez le faire en suivant chaque appel dans un tableau ou un compteur, par exemple.

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

Vous avez 3 requêtes. Au fur et à mesure que chacun appelle dispose() le compteur s'incrémente. Quand ils ont tous appelé dispose(), le dernier fermera également la connexion.

Async/Await devrait le rendre encore plus facile, car ils déroulent le résultat Promise de la fonction 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();

Vous trouverez ci-dessous un exemple de la façon dont Async/Await peut finir par amener le code async à se comporter de manière séquentielle et à s'exécuter de manière inefficace en attendant qu'une fonction async se termine avant d'appeler la suivante, lorsque le scénario idéal est de les invoquer tous immédiatement et d'attendre seulement qu'ils sont complets.

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

Remarque: En attendant comme dans l'exemple ci-dessus, les appels sont à nouveau séquentiels. Si chacun prend 2 secondes pour s'exécuter, la fonction prendra 6 secondes pour se terminer.

En combinant le meilleur de tous les mondes, vous voudriez utiliser Async/Await tout en exécutant tous les appels immédiatement. Heureusement, Promise a une méthode pour faire cela, donc test() peut être écrit comme ceci: -

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

Voici un exemple de travail pour démontrer la différence de performance: -

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