Home > Software engineering >  How to properly async the MongoDB .save() with mongoose?
How to properly async the MongoDB .save() with mongoose?

Time:01-20

I'm trying to insert multiple documents with mongoose on a MongoDB database, but I have some problems when trying to synchronize two middlewares. Let me explain the flow:

  1. An array of documents [A] is passed, and I need to insert them all inside collection [A]
  2. Each document from [A], when inserted, has to create multiple documents [B] (from one to three) inside another collection [B]
  3. Each [B] document has to update informations inside a document [C] in another collection [C].
  4. When all these tasks are finished, I can go on with the second [A] document, ad so on...

In my code, I structured this with one .pre "save" middleware on the [A] documents, and a .pre "save" middleware on the [B] documents.

I tried with ".insertMany()", but I need to run these middlewares on a single document .save() too.

Here is the code for looping through all the documents of the array [A], called "data", and save the document inside its collection.:

data.forEach(async (transaction) => {
    const document = new Transaction({
        date: transaction.date,
        amount: transaction.amount,
        type: transaction.type,
    });
    await document.save();
});

I use await because I want to wait the save of this document before going on the second one. So, with a .pre "save" middleware, I can create the [B] documents before going on the second [A] document.

TransactionSchema.pre("save", async function (next) {
    const allTransactions = this.model("AllTransactions");
    var amount = Math.abs(this.amount);

    switch (this.type) {
        case "A":
            const A_Transaction = new allTransactions({
                transaction_id: this._id,
                amount: amount,
                date: this.date,
                type: "A",
            });
            await A_Transaction.save();
            break;
            
            case "B":
                // ...
                break;

            case "C":
                // ...
                break;
    }
    next();
});

With the same logic, I create the [B] document (in this case only one document) with await .save(), so I can us a second middleware on a .pre save() to update the documents in the third collection.

AllTransactionsSchema.pre("save", function (next) {
    const Data = this.model("Data");
    const DataToUpdate = Data.findById(//data id);

    DataToUpdate.then(async (instance) => {
        instance.values.forEach(async (value) => {
            //operations
            await instance.save();
        });
    });
    next();
});

The problem is, the second document of the [A] array is inserted before the end of the execution of all middlewares. I used async await on all .save(), but it's like it isn't working.

I'm trying to figure out how to synchronize all these operations, one by one; I'm still a student on MongoDB and noSQL.

Thanks!

CodePudding user response:

Replace forEach with a normal loop. JavaScripts' higher order array functions do not actually wait when await is placed inside, instead they just fire off multiple async calls.

EDIT:

Added an example

const fireTransactions = async () => {
    for (const transaction of data) {
        const document = new Transaction({
            date: transaction.date,
            amount: transaction.amount,
            type: transaction.type,
        });
        await document.save();
    }
}
  • Related