Reputation: 422
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
Upvotes: 6
Views: 2671
Reputation: 3456
2 ways to do this:
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);
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.
Upvotes: 0
Reputation: 882
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);
}
Upvotes: 0
Reputation: 422
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);
}
Upvotes: 0
Reputation: 51
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);
});
Upvotes: 3
Reputation: 15333
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.
Upvotes: -1