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

Node.js $push和$set在一个数组中设置相同的子文档,node.js,mongodb,mongoose,Node.js,Mongodb,Mongoose,我试图用MongooseJS4.9.5和Mongo3.2.7在子文档数组中保存状态的历史记录 文档结构示例: 公司(架构) 员工(架构):[] 当前状态:字符串 状态(模式):[] 状态:字符串 开始日期:日期 完:日期 当我更改employee状态时,我想更改currentState,将新状态添加到states数组中,并更新定义“ends”时间戳的最后一个状态 // I get the last state position from a previous find request

我试图用MongooseJS4.9.5和Mongo3.2.7在子文档数组中保存状态的历史记录

文档结构示例:

  • 公司(架构)
    • 员工(架构):[]
    • 当前状态:字符串
    • 状态(模式):[]
      • 状态:字符串
      • 开始日期:日期
      • 完:日期
当我更改employee状态时,我想更改currentState,将新状态添加到states数组中,并更新定义“ends”时间戳的最后一个状态

// 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}
  • 有没有建议如何避免为此运行两个更新
  • 避免存储“结束”日期,但能够根据数组中下一项的“开始”计算结束日期的任何解决方法

谢谢,

我希望其他地方已经回答了这个问题,但似乎没有其他合理的回答。如前所述,您实际上无法在单个更新操作中执行此操作,因为操作在同一路径上“冲突”。但允许在单个请求和响应中应用“多个更新”

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 
        }
      }
    }
  }}
])
现在当然不会像以前那样返回“修改过的文档”。因此,如果您需要实际返回文档,则需要添加到承诺链:

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);
})
当然要注意的是,在应用和执行时,“可能”会对文档进行另一次修改。但这就是你正在做的手术的成本

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

注意您可以简单地将“多个”调用“链接”,但这实际上是来自服务器的“多个”调用和响应,而不是使用一个。所以不这样做真的没有什么好处


这是《国家历史》的一部分。update语句中的操作应用程序没有“顺序”。因此,您不能修改相同的路径。您应该改为使用
.bulkWrite()
并在两个单独的操作中在“同一路径”上发出
$set
$push
。它们需要分开,但“批量”所做的是作为一个请求和一个响应发送,而不是每个请求“多个”。谢谢Neil,非常感谢!