数组中的 MongoDB 更新失败:更新路径“companies.$.updatedAt”会在“companies.$”处产生冲突

Rüd*_*uße 7 mongoose mongodb

我们(从 MongoDB 3.4)升级到:

MongoDB:4.2.8

猫鼬:5.9.10

现在我们收到了这些错误。对于最小的例子,模型是:

[公司.js]

'use strict';

const Schema = require('mongoose').Schema;

module.exports = new Schema({
  name: {type: String, required: true},
}, {timestamps: true});

Run Code Online (Sandbox Code Playgroud)

[target_group.js]

'use strict';

const Schema = require('mongoose').Schema;

module.exports = new Schema({
  title: {
    type: String,
    required: true,
    index: true,
  },
  minAge: Number,
  maxAge: Number,
  companies: [Company],
}, {timestamps: true});

Run Code Online (Sandbox Code Playgroud)

当我尝试更新目标组内的公司时

  _updateTargetGroup(companyId, company) {
    return this.targetGroup.update(
      {'companies._id': companyId},
      {$set: {'companies.$': company}},
      {multi: true});
  }
Run Code Online (Sandbox Code Playgroud)

我收到

MongoError:更新路径“companies.$.updatedAt”会在“companies.$”处产生冲突

即使我前置

    delete company.updatedAt;
    delete company.createdAt;
Run Code Online (Sandbox Code Playgroud)

我收到这个错误。

如果我尝试类似的数据库工具(Robo3T),一切正常:

db.getCollection('targetgroups').update(
  {'companies.name': "Test 1"},
  {$set: {'companies.$': {name: "Test 2"}}},
  {multi: true});
Run Code Online (Sandbox Code Playgroud)

当然我可以使用解决方法

  _updateTargetGroup(companyId, company) {
    return this.targetGroup.update(
      {'companies._id': companyId},
      {$set: {'companies.$.name': company.name}},
      {multi: true});
  }
Run Code Online (Sandbox Code Playgroud)

(这确实有效),但我想了解这个问题,并且我们的项目中还有更大的模型也存在同样的问题。

这是{timestamps: true}的问题吗?我寻找解释但找不到任何东西......:-(

Tom*_*ert 5

该问题源于使用timestamps您提到的但我不会将其称为“错误”,因为在这种情况下我可以说它正在按预期工作。

首先让我们了解 usingtimestamps在代码中的作用,下面是 mongoose 对带有时间戳的数组 ( companyarray ) 执行的代码示例:(源代码)

  for (let i = 0; i < len; ++i) {
    if (updatedAt != null) {
      arr[i][updatedAt] = now;
    }
    if (createdAt != null) {
      arr[i][createdAt] = now;
    }
  }
Run Code Online (Sandbox Code Playgroud)

这在每次更新/插入时运行。正如您所看到的,它设置了数组中每个对象的updatedAt和,这意味着更新对象从以下位置更改:createdAt

{$set: {'companies.$.name': company.name}}
Run Code Online (Sandbox Code Playgroud)

到:

{
  "$set": {
    "companies.$": company.name,
    "updatedAt": "2020-09-22T06:02:11.228Z", //now
    "companies.$.updatedAt": "2020-09-22T06:02:11.228Z" //now
  },
  "$setOnInsert": {
    "createdAt": "2020-09-22T06:02:11.228Z" //now
  }
}
Run Code Online (Sandbox Code Playgroud)

现在,当您尝试使用两个不同的值/操作更新同一字段时,就会发生错误,例如,如果您要更新同一字段$set,并且$unset同一更新中的同一字段 Mongo 不执行任何操作,因此会引发错误。

在你的情况下,它是由于companies.$.updatedAt场而发生的。因为您正在更新整个对象companies.$,这意味着您基本上将其设置为{name: "Test 2"}这也意味着您正在“删除”该updatedAt字段(以及其他字段),而猫鼬试图将其设置为它自己的值,从而导致错误。这也是为什么您的更改companies.$.name有效,因为您只设置name字段而不是整个对象,因此不会产生冲突。