簡體   English   中英

將一個新字段添加到集合的所有文檔中,將文檔字段中的值添加到 MongoDB (Mongoose) 中,記錄為 300K+

[英]Add a new field to all documents of a collection with the value from the document field into MongoDB (Mongoose) with records of 300K+

我很難將另一個字段添加到user集合的所有記錄中,並且每個文檔都有一個值。 我熟悉將$setdb.model.updateMany一起使用,將$addFields與聚合管道一起使用,我過去曾使用這兩者來解決問題,在這種情況下,我必須在添加值之前執行一些邏輯/計算,這就是我的問題所在。

說,我有這樣的架構:

{
  "users": [
    {
      "wallets": {...},
      "avatar": "",
      "isVerified": false,
      "suspended": false,
      "country": "Nigeria",
      "_id": "123",
      "resetPasswordToken": "",
      "email": "example@gmail.com",
      "phone": "08012398743",
      "name": "Agbakwuru Nnaemeka Kennedy ",
      "role": "user",
    },
    {...}
}

我想添加一個新字段phoneNumber ,它將采用現有字段phone的值,但在添加之前,我想在其上運行一個邏輯,因為某些電話值有空格,其中大多數不正確格式化,我想在將國家代碼添加到新的phoneNumber字段之前將其添加到phone值中。

我能夠使用 Mongoose db.mode.aggregate方法中的游標完成此操作,使用$match過濾器,並使用聚合$addFields管道將字段添加到每個文檔,這證明需要花費大量時間,我有停止操作,因為運行時間太長。

我想相信有更好的方法,拜托,我將不勝感激。

編輯:

這是我正在使用的聚合:

const userCursor = User.aggregate([{$match: {phone: {$exists: true}}}]);
for await (const doc of userCursor) {
  await User.findByIdAndUpdate(doc._id, {$set: {
          phoneNumber: convertPhoneNumber({phoneNumber: doc.phone.replace(/\s+/g, "")})}
  });
}

convertPhoneNumber是我在convertPhoneNumber程序中定義的一個輔助方法,用於在電話號碼前添加國家/地區交易代碼。

您可以使用$function並在數據庫中調用該 javascript 代碼。

這需要 >=MongoDB 4.4

db.Users.update(
  {phone: {$exists: true}},
  [{$set: {phoneNumber:
            {
             "$function": {
             "body": YOUR_convertPhoneNumber_FUNCTION_DEF,
             "args": ["$phoneNumber"],
             "lang": "js"
             }
            }])

此外,如果 convertPhoneNumber 的代碼可以使用聚合運算符在 MongodBD 中編寫,您也可以避免使用 javascript。

以上是管道更新,更新時我們可以使用所有聚合運算符。


編輯

如果 mongoose 的$function有問題,或者 nodejs 驅動程序方法有管道更新的問題,你也可以這樣做。

db.runCommand(
   {
      update: "yourCollectionName",
      updates: [
         {
           q: {phone: {$exists: true}},
           u: 
           [{$set: {phoneNumber:
            {
             "$function": {
             "body": YOUR_convertPhoneNumber_FUNCTION_DEF,
             "args": ["$phoneNumber"],
             "lang": "js"
             }
            }],
           multi: true
         }
      ],
      ordered: false
   }
)

我會嘗試直接在mongo命令行或 Robo3T 中運行這樣的腳本:

db.getCollection("users").find({}).forEach( doc => {

    doc.users.forEach( user => {

        // do your logic here
        let phoneNumber = "12345";
        phoneNumber = "+007" + phoneNumber;

        user.phoneNumber = phoneNumber;
    })

    db.users.save(doc);
})

處理 30 萬多個文檔仍然需要一段時間,但請給它幾分鍾時間。

您可以嘗試Bulk Operation ,這會分批更新 1000 個文檔的集合:

var bulkOperations = [];
db.getCollection("users").find({}).forEach(doc => {
   doc.users.forEach(user => {
      user.phoneNumber = convertPhoneNumber({phoneNumber: user.phone.replace(/\s+/g, "")});
   })
   bulkOperations.push({
      updateOne: {
         filter: { id: doc._id },
         update: { $set: { users: doc.users } }
      }
   });
   if (bulkOperations.length > 1000) {
      db.getCollection("users").bulkWrite(bulkOperations, { ordered: false });
      bulkOperations = [];
   }
})
if (bulkOperations.length > 0) 
   db.getCollection("users").bulkWrite(bulkOperations, { ordered: false });

的幫助下@Jeremy Thille的答案在這里,我能夠用MongoDB的指南針來解決它mongo與下面的代碼片段的命令行。

db.users.find({phone: {$exists: true}}).forEach( user => {
  const phone = user.phone.replace(/\s+/g, "");
  const phoneNumber = `+234${phone.slice((phone.length - 10))}`;
  db.users.updateOne({_id: user._id}, {$set: {phoneNumber}});
})

缺點是更新 300K 文檔需要 10-15 分鍾,這與我最初的實現相比,這是一個顯着的改進,我最初的實現只花了一天時間來更新幾萬個文檔。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM