6

I have an array of objects

df =[{user: "name1", newdata: "data1"},
     {user: "name2", newdata: "data3"},
     ....
]

I have a collection with user and key1 fields. I want to find the users and update 'key1' with dato.newdata. I tried to include in a for loop, but it does not work. This is my code:

const MongoClient = require('mongodb').MongoClient;
const url = 'mongodb://localhost:27017';

client.connect(function(error){
    for (dato of df){
        client.db(dbasename).collection(collectionname).updateOne(
            {user: dato.user},
            {$set: {key1: dato.newdata}},
            function(error,result){
                if (error) console.log(error);
                if (result) {
                    console.log(JSON.stringify(result));
                }
            }
        );  
    }
})

Additional information: I noticed that it works for the first user found. Maybe updateOne returns a promise and I don't manage it correctly?

I have tried this other code as suggested by some of you. But it does not work.:

const MongoClient = require('mongodb').MongoClient;
const url = 'mongodb://localhost:27017';

async function changeValue (dbasename,collectionname, user, newData) {
  client.db(dbasename).collection(collectionname).updateOne(
      {user: user},
      {$set: {key1: newdata}},
      {upsert:false},
      {multi:false},
      function(error,result){
        if (error) console.log(error);
        if (result) {
            console.log(JSON.stringify(result));
        }
    }
  );
}

client.connect(function(error){
  for (dato of df){
     await changeValue(dbasename,collectionname, dato.user, dato.newdata);
  }
})

The compiler says: SyntaxError: await is only valid in async function

2
  • I am thinking { $set: { newdata: dato.newdata } } instead of { $set: { key1: dato.newdata } }, if you want to update newdata field. Commented Nov 22, 2019 at 1:38
  • No, I want to update key1 field with dato.newdata. I have updated my question to avoid confussions. Commented Nov 25, 2019 at 7:44

5 Answers 5

3

Since MongoDB operations are async, for loop won't wait the operations like insert/update documents during iterations.

So, You will have to use forEach with await to ensure the document is updated before doing next iteration.

async changeValue(user, newData) {
        db.collectionName.updateOne(
            {user: user},
            {$set: {key1: newdata}},
            {upsert:false},
            {multi:false}
        );
    }
      df.forEach(function(item){
       await changeValue(item.user,item.newData);
    });
Sign up to request clarification or add additional context in comments.

4 Comments

If you could elaborate on your answer and not just show the correct code, that would make your answer even better.
I have created a asynchronous function with parameter. In that function, i have called the update method using the database object (db) and collection name.Update method have first argument is selection criteria, second argument is setting the new value which you want to set, third argument is upsert which means if it true than it will create new document when selection criteria is not matched other wise it will update the document.By setting the multi true it will update multiple document is selection criteria is matched.
If you could add that directly to your answer by editing it, then everyone can see it at once and you answer is easier to read.
It does not work. I declare the async function changeValue. And there is an error in await changeValue. The error message is: SyntaxError: await is only valid in async function
0

Finally, this code works.

    async function changeValue(dbasename, collectionname, dato){
        let client = await MongoClient.connect(url).catch(error => {console.log(error)});
        if (!client) {return;};
        let db = client.db(dbasename);
        let collection = db.collection(collectionname);
        collection.updateOne(
            {user: dato.user},
            {$set: {key1: dato.newdata}},
            function(error,result){
                    if (error) console.log(error);
                    if (result.result.n === 0) {
                            console.log(dato.user); //print not found users
                    }
            }
        );
        await client.close();
    };  

    for (dato of df){
        changeValue(dbasename, collectionname, dato);
    } 

Comments

0

If you use await with updateOne then it will work, for example I use following loop to iterate over a http request's body in Node.js and update documents in my collection :

// req.body looks like this { firstname: 'Jack', age: '34', ... }
// db collection looks like this:
// { name: "firstname", value" "Tom" }
// { name: "age", value" "23" }

async (req, res) => {
    await client.connect();
    const database = client.db('test');
    const profile = database.collection('profile');

    for (const property in req.body) {
      const query = { name: property };
      const updateDoc = {
        $set: { value: req.body[property] }
      };
      await profile.updateOne(query, updateDoc);
    }

    const data = await profile.find().toArray();
    res.json(data);
}

Comments

0

2 ways to do this:

Using a for loop indeed.

Here it's about browsing throught your loop and for each element, performing and update operation.

const collection = client.db(dbasename).collection(collectionname);
const promises = df.map(x => {
    return new Promise(function(resolve, reject){
        collection.updateOne({
            user : x.user
        },
        {$set: {key1: x.newdata}},
        function(err, result){
            if(err) reject(err)
            else resolve(result)
        })
    })
});
await Promise.all(promises);

Using an aggregation pipeline.

The for.. loop methods has a major drawback, you will perform as much operation as you have elements in your array. It's fine for small array, but consider updating even 200 elements and it's starts to stinks. The alternative is to use the aggregation framework to update 1 one go all your elements.

First save your df array into a new colection, let's call it temp_update_data.

// using the promise API, we save df into a new collection, we also rename the newdata field into key1
await client.db(dbasename).collection("temp_update_data").insertMany(df.map(x => ({user : x.user, key1: x.newData})));

now you have you data in temp_update_data that associate a user to a key1, you can use a merge operation to add the newData in your collectionname's object by matching the user field

const pl = [
    {
         $merge: {
             into: "collectionname",
             on: "user",
             whenMatched: "merge",
             whenNotMatched: "discard",
        }
    }
];
await client.db(dbasename).collection("temp_update_data").aggregate(pl);

// remove temp data
await client.db(dbasename).collection("temp_update_data").deleteMany({});

This only requieres 3 calls to the DB (saving the data, merging, removing the temp data) and leverage the optimizations of MongoDB algorythms saving times as the number of object to update grows.

Comments

-1

The MongoDB calls are async, but as of now the loop completes its all iterations and doesn't wait for the operation to end.

I think you can do something like this,

async changeValue(user, newData) {
    client.db(dbasename).collection(collectionname).updateOne(
        {user: user},
        {$set: {key1: newdata}},
        function(error,result){
            if (error) console.log(error);
            if (result) {
                console.log(JSON.stringify(result));
            }
        }
    );
}

for (dato of df){
    await changeValue(dato.user, dato.newdata);
}

It's just a rough idea, you might have to do some changes for this. But I think it might help to understand what needs to be done.

6 Comments

@chandukomati How so? Please explain.
@chandukomati you deleted your comment but forgot to remove down vote. If there is anything to add feel free to comment or post a new answer.
It does not work. I declare the async function changeValue. And there is an error in await changeValue. The error message is: SyntaxError: await is only valid in async function
@Juan You need to make the function (in which you have your for loop) async
@gprathour, I tried to make async, but it fails. Please check the new code that I added in my question.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.