如何在for循环中更新MongoDb文档

5

我有一个对象数组

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

我有一个集合,其中包含userkey1字段。我想查找用户并使用dato.newdata更新'key1'。我尝试将其包含在循环中,但它不起作用。这是我的代码:

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

额外信息:我注意到它仅适用于找到的第一个用户。也许updateOne返回了一个Promise,而我没有正确处理它?

我已尝试其他一些人建议的代码。但它不起作用。

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

编译器报错:SyntaxError: await只能在异步函数中使用

如果你想要更新 newdata 字段,我建议使用 { $set: { newdata: dato.newdata } } 而不是 { $set: { key1: dato.newdata } } - prasad_
不,我想用dato.newdata更新key1字段。我已经更新了我的问题以避免混淆。 - Juan
5个回答

2

由于MongoDB操作是异步的,因此在迭代过程中,for循环不会等待插入/更新文档等操作。

因此,您必须使用forEach和await来确保在进行下一次迭代之前更新文档。

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

1
如果您能详细阐述您的答案,而不仅仅展示正确的代码,那将使您的答案变得更好。 - mbuechmann
我创建了一个带参数的异步函数。在该函数中,我使用数据库对象(db)和集合名称调用了更新方法。更新方法的第一个参数是选择条件,第二个参数是要设置的新值,第三个参数是upsert,如果为true,则在选择条件不匹配时创建新文档,否则将更新文档。通过将multi设置为true,它将更新多个文档,如果选择条件匹配。 - Jay Gandhi
如果您可以通过编辑直接将其添加到答案中,那么每个人都可以立即看到它,您的答案也更容易阅读。 - mbuechmann
它不起作用。我声明了async function changeValue。并且在await changeValue中出现了错误。错误信息是:SyntaxError: await is only valid in async function - Juan

0

最终,这段代码可行。

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

0
如果你在updateOne中使用await,它就会起作用。例如,我使用以下循环来迭代Node.js中的HTTP请求体,并更新我的集合中的文档:
// 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);
}

0

有两种方法可以做到这一点:

确实使用for循环。

这里是关于浏览您的循环,并对每个元素执行更新操作。

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


使用聚合管道。
for..循环方法有一个主要缺点,你将执行与数组中的元素数量相同的操作。对于小数组来说没问题,但是考虑更新200个元素,就会变得很糟糕。 另一种选择是使用聚合框架一次性更新所有元素。
首先将df数组保存到一个新的集合中,我们称之为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})));

现在你有一个名为temp_update_data的数据,它将用户与key1关联起来,你可以使用合并操作,通过匹配user字段,将newData添加到你的collectionname对象中。
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({});


这只需要3次对数据库的调用(保存数据、合并、删除临时数据),并利用MongoDB算法的优化,随着要更新的对象数量增加,节省时间。

-1

MongoDB的调用是异步的,但目前循环完成所有迭代并且不等待操作结束。

我认为你可以像这样做,

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

这只是一个大致的想法,你可能需要对其进行一些更改。但我认为它可能有助于理解需要做什么。


1
@chandukomati 怎么回事?请解释一下。 - mbuechmann
@chandukomati,您删除了评论,但忘记取消投票。如果有什么要补充的,请随时发表评论或发布新答案。 - gprathour
它不起作用。我声明了async function changeValue。并且在await changeValue中出现了错误。错误信息是:SyntaxError: await is only valid in async function - Juan
@Juan 你需要将包含for循环的函数变成async函数。 - gprathour
@gprathour,我尝试使用异步,但是失败了。请检查我在问题中添加的新代码。 - Juan
@Juan 我的意思是你需要将 client.connect(function(error) 改为异步,也可以尝试使用 client.connect(async function(error) - gprathour

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接