MongoDB更新操作符的实践

和查询操作符类似,更新操作符处理最复杂的对象也是数组。我们按照上一篇文章的方法,先总结我所用到的操作符,再结合项目中遇到的实例组合使用操作符。


1. 字段更新操作符

上图描述十分清晰地阐明了使用场景。$inc不仅可以增加还可以减少字段的值。$setOnInsert则需要和{ upsert: true }一起使用,当需要插入新数据时,$setOnInsert就会实效;反之则不会触发插入新字段。

2. 数组更新操作符

$的作为为占位符,匹配查询条件的数组元素以提供更新数据的数组位置,由于其使用条件的限制,就不能和upsert参数一起使用,因为upsert插入新数据时会将$解释为字段名的一个元素。比如下面的代码就是将grades数组中的grade为80的元素std更新为6。

db.students.update(
 { _id: 4, "grades.grade": 85 },
 { $set: { "grades.$.std" : 6 } }
)

$addToSet更新不存在的数组元素,如果参数是传递一个数组,则将整个数组以一个更新上去,只有与$each一起使用才可以同时更新多个元素。比如下面的代码就是将"camera", "electronics", "accessories"更新到tags数组中。

db.inventory.update(
   { _id: 2 },
   {$addToSet: { tags: { $each: [ "camera", "electronics", "accessories" ] } } }
)

接下来的5个就说数组更新操作了。

3. 修饰符

$slice将指定数量的数组元素保留到文档中,$position则是将数组元素插入数组的指定位置。其他不表。


1. 添加数组的元素案例

operations字段的格式为对象数组,而$push操作能将新添加的operation数据插入到数组最末端。Mongoose的update方法的第二个参数就是更新数据。

Mails.update(updateCondition, {
  'commonStatus': updateData.commonStatus,
  $push: {
      'operations': updateData.operation
  }
}, function(err, numAffected){});

2. 添加对象数组的字段案例

与上篇文章的privateInfo字段格式一样(如下),我们需要依据应用的业务逻辑,添加数组中的_creator和userStatus字段。

privateInfo: [{
  _creator: {
    type: Schema.Types.ObjectId, ref: 'User'
  },
  score: {
    type: Number
  },
  scoredTime: {
    type: Date
  },
  userStatus: {
    type: String
  },
  tag: [{
    type: Schema.Types.ObjectId, ref: 'User.customTags'
  }]
}],

我们就可以这样编写更新语句。

Mails.update(updateCondition, {
  $set: {
    'privateInfo.$._creator': updateData.userId,
    'privateInfo.$.userStatus': updateData.userStatus
  }
}, function(err, numAffected){});

3. 移除数组元素案例

通过在数据库中记录特征字段来指定文档的临时状态,当需要取消这个状态或者条件时,就需要清楚记录。比如一个标记实效、收藏操作的一个取消操作。这里需要使用$pull操作符。 以取消精华操作举例,每个用户都可以将数据设置为自己的收藏或者取消。

//还需依据用户id定位privateInfo
updateCondition.privateInfo = {
  $elemMatch: { '_creator': userId }
};
Mails.update(updateCondition, {
  $pull: { 
    'operations': { '_operator': userId, 'keyword': updateData.operation.keyword },
    'privateInfo.$.tag': customTags[0]._id }
}, function(err, numAffected){});

4. 更新不存在的数据案例

当我们按照数据定期获取数据时,不可避免的出现重复数据的更新,而我们只需要没有提交到mongodb的数据,当$addToSet与$each一起使用时就能提供这个功能,在$each的参数设置为一个数组,里面有数据库已经存入的数据。比如当我们通过一个字段'contactAddress'与mails的mail.to.address字段匹配时就插入ContactLists数组表示对同一个联系人的归类的功能实现。

所以Mongoose的对象是ContactList,而使用findOneAndUpdate方法的意义在于当数据库没有查询的数据中时能插入当前数据。

ContactList.findOneAndUpdate({ 'contactAddress': fromAddr },
{
  'subject': mailSubject,
  'date': Date.now(),
  $addToSet: {
    'mails': {
      $each: mails
    },
    'contactors': new Object(userIdStr)                
  }
}, { upsert: true }, function(err, numAff){});

总结

上述内容就是MongoDB更新操作符使用,update的操作符在使用上可能更常见,数组操作也是最复杂的。通过这篇文章和上一篇文章总结了MongoDB的一些比较常用的操作符,希望在以后的项目中更新提供姿势水平。

参考
  1. 更新数组元素

  2. 删除数组元素

  3. upsert数组元素