$ push和$在一个数组中设置相同的子文档

时间:2017-07-26 23:06:31

标签: node.js mongodb mongoose

我试图用mongoosejs 4.9.5和mongo 3.2.7保存子文档数组中的状态历史

文档结构示例:

  • 公司(架构)
    • 员工(架构):[]
    • currentState:String
    • (架构):[]
      • state:String
      • 开始:日期
      • 结束:日期

当我更改员工状态时,我想要更改currentState,将新状态添加到states数组中,并更新最后一个状态以定义'结束'时间戳

// I get the last state position from a previous find request
var lastStateIndex = employee.stateHistory.length - 1; 
var changeStateDate = new Date();

// Prepare the update
var query = { _id: companyId, "employees._id": employeeId };
var update = { 
    $set: { 
        "employees.$.state": newState, 
        `employees.$.stateHistory.${lastStateIndex}.ends`: changeStateDate
    },
    $push: {
        "employees.$.stateHistory": {
            state: newState,
            starts: changeStateDate 
        }
    }
}
Company.findOneAndUpdate(query, update, { multi:false, new:true}, ... )

Mongo正在返回以下错误

{"name":"MongoError","message":"Cannot update 'employees.0.stateHistory.0.ends' and 'employees.0.stateHistory' at the same time","ok":0,"errmsg":"Cannot update 'employees.0.stateHistory.0.ends' and 'employees.0.stateHistory' at the same time","code":16837}
  • 有关如何避免为此目的运行两个更新的任何建议吗?
  • 任何可以避免存储'结束的工作。日期,但能够在基于'开始'之后计算它。数组中的下一个项目?

谢谢,

1 个答案:

答案 0 :(得分:1)

我预计这已在其他地方得到解答,但似乎没有其他合理的回应。如评论所述,您无法在单个更新操作中实际执行此操作,因为操作"冲突"在同一条路上。但.bulkWrite()允许"多次更新"要应用于单个请求和响应。

Company.bulkWrite([
  { "updateOne": {
    "filter": { "_id": companyId, "employees._id": employeeId },
    "update": {
      "$set": { 
        "employees.$.state": newState, 
        [`employees.$.stateHistory.${lastStateIndex}.ends`]: changeStateDate
      } 
  }},
  { "updateOne": {
    "filter": { "_id": companyId, "employees._id": employeeId },
    "update": { 
      "$push": {
        "employees.$.stateHistory": {
          "state": newState,
          "starts": changeStateDate 
        }
      }
    }
  }}
])

现在当然.bulkWrite()不会返回"修改后的文档"像.findOneAndUpdate()那样。因此,如果您需要实际返回文档,则需要添加到Promise链中:

Company.bulkWrite([
  { "updateOne": {
    "filter": { "_id": companyId, "employees._id": employeeId },
    "update": {
      "$set": { 
        "employees.$.state": newState, 
        [`employees.$.stateHistory.${lastStateIndex}.ends`]: changeStateDate
      } 
  }},
  { "updateOne": {
    "filter": { "_id": companyId, "employees._id": employeeId },
    "update": { 
      "$push": {
        "employees.$.stateHistory": {
          "state": newState,
          "starts": changeStateDate 
        }
      }
    }
  }}
]).then( result => {
  // maybe inspect the result
  return Company.findById(companyId);
})

当然注意到它是可能的"在应用.bulkWrite()并执行.findById()时,可以对文档进行另一次修改。但这就是你正在进行的操作的成本。

通常最好考虑您是否需要返回的文档。在大多数情况下,您只需拥有信息和任何更新"你应该知道,因为你是"发布它们",如果你想要"真正反应"那么你应该通过套接字监听数据上的其他变化事件。

  

注意你可以简单地"链" "倍数" .findOneAndUpdate()来电,但这确实是多次"来自服务器的呼叫和响应,而不是使用.bulkWrite()一个。否则,除此之外无法获得任何好处。