【问题标题】:Conditionally updating items in mongoose query有条件地更新猫鼬查询中的项目
【发布时间】:2021-07-27 16:25:55
【问题描述】:

我有以下代码,我正在尝试做两件事。首先,我想让我的查询有一个条件,即它在文档中找到“originator”值,但如果还发现“owner_id”与 originator 相同,则第二个部分是不更新。

我要做的第二部分只是设置/更新一个字段是否正在传入。我可以使用三元语句,如下所示???

  Contacts.update(
    {
      'originator': profile.owner_id,
      'owner_id': !profile.owner_id
    }, 
    {
      $set: {
        (phoneNumber) ? ('shared.phones.$.phone_number': phoneNumber):null,
        (emailAddress) ? ('shared.emails.$.email_address': emailAddress):null
      }
    }, 
    {
      'multi': true
    },
    function(err) {
      err === null ? console.log('No errors phone updated for contacts.shared') : console.log('Error: ', err);
    }
  ) 

【问题讨论】:

    标签: node.js mongodb express mongoose


    【解决方案1】:

    你的意思是这样的:

    var updateBlock = {};
    if (phoneNumber)
      updateBlock['shared.phones.$.phone_number'] = phoneNumber;
    if (emailAddress)
      updateBlock['shared.email.$.email_address'] = emailAddress;
    
    Contacts.updateMany(
      { 
        "originator": profile.owner_id
        "owner_id": { "$ne": profile.owner_id }
      },
      { "$set": updateBlock },
      function(err, numAffected) {
         // work with callback
      }
    )
    

    这解决了您在此处的两个“主要”误解,即查询条件中的“不等式”需要 $ne 运算符和 not ! JavaScript 表达式。 MongoDB 在此处不使用 JavaScript 表达式作为查询条件。

    第二个“主要”误解是使用条件键构建“更新块”。相比之下,这是一个您单独构造的“JavaScript 对象”,以便仅指定您希望生效的键。

    但是仍然存在问题,您想使用positional $ operator。假设您实际上在文档中有这样的“数组”:

    {
       "originator": "Bill",
       "owner_id": "Ted",
       "shared": {
         "phones": [ "5555 5555", "4444 4444" ],
         "email": [ "bill@stalyns.org", "bill@example.com" ]
       }
    }
    

    那么您的“双重”新问题是:

    1. 必须指定一个与“查询块中”的数组元素匹配的查询条件,以便获得要更新的“匹配位置”。

      李>
    2. 您可以通过使用 positional $ operatorNOT TWO 返回 ONE 匹配的数组索引,这将是更新这样的文档。

    出于这些原因(以及其他原因),强烈建议不要在单个文档中包含 “多个数组”。更好的方法是使用“单一”数组,并使用属性来表示列表项实际包含的条目“类型”:

    {
       "originator": "Bill",
       "owner_id": "Ted",
       "shared": [
         { "type": "phone", "value": "5555 5555" },
         { "type": "phone", "value": "4444 4444" },
         { "type": "email", "value": "bill@stalyns.org" },
         { "type": "email", "value": "bill@example.com" }
       ]
    }
    

    通过这种方式,您实际上可以处理要更新的“匹配”元素:

    // phoneNumberMatch = "4444 4444";
    // phoneNumber = "7777 7777";
    // emailAddress = null;            // don't want this one
    // emailAddressMatch = null;       // or this one
    // profile = { owner_id: "Bill" };
    
    var query = {
      "originator": profile.owner_id,
      "owner_id": { "$ne": profile.owner_id },
      "shared": {
        "$elemMatch": {
          "type": (phoneNumber) ? "phone" : "email",
          "value": (phoneNumber) ? phoneNumberMatch : emailAddressMatch
        }
      }
    };
    
    var updateBlock = {
      "$set": {
        "shared.$.value": (phoneNumber) ? phoneNumber : emailAddress
      }
    };
    
    Contacts.updateMany(query, updateBlock, function(err, numAffected) {
      // work with callback
    })
    

    在这种情况下,如果选择“二元”选项,那么您“可以”在构造中使用三元条件,因为您不依赖于 “命名键”建设。

    如果您想要组合提供的“任何一个或两个”提供的值,那么您需要更高级的语句:

    // phoneNumberMatch = "5555 5555";
    // phoneNumber = "7777 7777";
    // emailAddress = "bill@nomail.com";
    // emailAddressMatch = "bill@example.com";
    // profile = { owner_id: "Bill" };
    
    var query = {
      "originator": profile.owner_id,
      "owner_id": { "$ne": profile.owner_id },
      "$or": []
    };
    
    var updateBlock = { "$set": {} };
    var arrayFilters = [];
    
    if (phoneNumber) {
      // Add $or condition for document match
      query.$or.push(
        {
          "shared.type": "phone",
          "shared.value": phoneNumberMatch
        }
      );
    
      // Add update statement with named identifier
      updateBlock.$set['shared.$[phone].value'] = phoneNumber;
    
      // Add filter condition for named identifier
      arrayFilters.push({
        "phone.type": "phone",
        "phone.value": phoneNumberMatch
      })
    }
    
    if (emailAddress) {
      // Add $or condition for document match
      query.$or.push(
        {
          "shared.type": "email",
          "shared.value": emailAddressMatch
        }
      );
    
      // Add update statement with named identifier
      updateBlock.$set['shared.$[email].value'] = emailAddress;
    
      // Add filter condition for named identifier
      arrayFilters.push({
        "email.type": "email",
        "email.value": emailAddressMatch
      })
    }
    
    Contacts.updateMany(query, updateBlock, arrayFilters, function(err, numAffected) {
      // work with callback
    })
    

    这里当然要注意,MongoDB 3.6 及更高版本的positional filtered $[<identifier>] 语法是必需,以便在单个更新语句中影响多个数组元素。

    这同样适用于我首先描述的“原始”结构,在文档中使用“多个”数组,而不是上面示例处理的“单一”数组上的命名属性:

    var query = {
      "originator": "Bill",
      "owner_id": { "$ne": "Bill" },
      "$or": []
    };
    
    var updateBlock = { "$set": {} };
    var arrayFilters = [];
    
    if (phoneNumber) {
      query.$or.push({
        "shared.phones": phoneNumberMatch
      });
    
      updateBlock.$set['shared.phones.$[phone]'] = phoneNumber;
    
      arrayFilters.push({
        "phone": phoneNumberMatch
      });
    }
    
    if (emailAddress) {
      query.$or.push({
        "shared.email": emailAddressMatch
      });
    
      updateBlock.$set['shared.email.$[email]'] = emailAddress;
    
      arrayFilters.push({
        "email": emailAddressMatch
      });
    }
    
    Contacts.updateMany(query, updateBlock, arrayFilters, function(err, numAffected) {
      // work with callback
    })
    

    当然,如果您甚至根本没有数组(发布的问题缺少任何示例文档),那么甚至不需要任何形式的位置匹配,但是您仍然可以通过“有条件地”构造 JavaScript 对象“键”构造代码块。您不能“有条件地”以类似 JSON 的表示法指定“键”。

    【讨论】:

    • 不敢相信...欢迎尼尔回来。所有人都在等待。
    • 很棒的反应。如此清晰,非常有教育意义。谢谢尼尔!!
    【解决方案2】:

    下面是一个简单的例子,开关条件的变化如下:

      const transfоrmFunc = function(val) {
        if(val){
          // do whatever you want with the value here
          return val; 
        }
        return null;
      };
      AnyModel.updateMany({ fieldId: { $in: ["MATCH1", "MATCH2"] } }, [
        {
          $set: {
            field2: {
              $switch: {
                branches: [
                  {
                    case: { $eq: ["$fieldId", "MATCH1"] },
                    then: transfоrmFunc("$field3")
                  },
                  {
                    case: { $eq: ["$fieldId", "MATCH2"] },
                    then: transfоrmFunc("$field4.subfield")
                  }
                ]
              }
            }
          }
        }
      ]);
    

    这样您就可以同时使用记录数据和外部数据并有条件地进行更新。您可以随意修改查询条件。而且速度真的很快。

    【讨论】:

      猜你喜欢
      • 2022-01-25
      • 1970-01-01
      • 2018-10-10
      • 2015-10-21
      • 2017-07-18
      • 2021-10-07
      • 2017-05-10
      • 1970-01-01
      • 2021-08-14
      相关资源
      最近更新 更多