MongoDB 如果条件匹配则更新数组

MongoDB update array if condition matches

我正在寻找 MongoDB 聚合管道,它使用条件语句更新数组。 我的数据如下所示:

{
    "_id": 1,
    "locations": [
      {
        "controllerID": 1,
        "timestamp": 1234
      },
      {
        "controllerID": 2,
        "timestamp": 2342
      }
    ]
  },...

可能的新条目:

{
 "controllerID": 2,
 "timestamp": //will be set automatically
}

起初我想匹配 _id(不是问题),然后如果具有 newest/latest 时间戳的元素具有不同的 controllerID,则将新条目推送到 locations 数组。 推送新位置对象时,将自动设置时间戳。

示例 1

输入:

{
 "controllerID": 2,
}

预期结果:

{
    "_id": 1,
    "locations": [
      {
        "controllerID": 1,
        "timestamp": 1234
      },
      {
        "controllerID": 2,
        "timestamp": 2342
      }//noting is added because the newset entry in the array has the same controllerID
    ]
  },

示例 2

输入:

{
 "controllerID": 1,
}

预期结果:

 {
        "_id": 1,
        "locations": [
          {
            "controllerID": 1,
            "timestamp": 1234
          },
          {
            "controllerID": 2,
            "timestamp": 2342
          },
          {//added because the controllerID is different to te last element
            "controllerID": 1, 
            "timestamp": 4356
          }
        ]
      },

提前致谢!

这是一个解决方案。

var candidate = 2;

rc=db.foo.update({}, // add matching criteria here; for now, match ALL                                
         [ 
         [
             // We cannot say "if condition then set fld = X else do nothing".    
             // We must say "set fld to something based on condition."            
             // The common pattern becomes:                                       
             //   "Set fld to (if condition then X else fld)"                     
             // in other words, set the fld to *itself* 
             //
             // Note the use of the dot operator on the $locations field.
             // Also, not sure about what sort of new timestamp is desired so let's
             // just throw in an ISODate() for now.                          
             {$set: {'locations': {$cond: [
                 {$ne:[candidate, {$last:'$locations.controllerID'}]}, // IF not same as candidate...                                                                             
                 {$concatArrays: ['$locations',
// $concatArrays wants arrays, not objects, so we must wrap our new
// object with [] to make an array of 1:
                                  [ {controllerId:candidate,timestamp:new ISODate() } ]
                                 ]}, // THEN concat a new entry to end of existing locations                                                                       
                 '$locations' // ELSE just set back to existing locations         
                 ]}
             }}
         ],
         {multi:true}
);

引擎“足够聪明”,意识到将字段设置为自身不会不会触发修改,因此该方法是高效的,不会重写整个匹配对象集;这可以在 update() 调用的输出中看到,例如:

printjson(rc);
{ "nMatched" : 1002, "nUpserted" : 0, "nModified" : 1 }