【问题标题】:MongoDB: Filter on _id == obj._id without setting obj._id to null on insertMongoDB:过滤 _id == obj._id 而不在插入时将 obj._id 设置为 null
【发布时间】:2022-05-11 14:42:26
【问题描述】:

我想配置一个 upsert。如果 _id 已经存在于我的对象上,它会识别并更新匹配。如果_id 不存在,它应该插入新文档并生成一个_id。我希望{ _id: obj._id } 能够工作,但这会覆盖_id 的自动生成。该文档显示为_id: null。有没有适合这个的过滤器?我需要路由以插入/更新代码吗?

编辑:添加查询。

collection.updateOne(
  { _id: entity._id },
  { $set: entity },
  { upsert: true }
)

编辑:尝试删除。 即使我删除了该属性,也没有运气。

const upsertTest = (collection) => {
  const entity = { date: new Date() };
  console.log(entity);
  // { date: 2019-11-19T22:16:00.914Z }

  const _id = entity._id;
  delete entity._id;
  console.log(entity);
  // { date: 2019-11-19T22:16:00.914Z }

  collection.updateOne(
    { _id: _id },
    { $set: entity },
    { upsert: true }
  );

  console.log(entity);
  // { date: 2019-11-19T22:16:00.914Z }
}

但新文件是这样的: screenshot of new document

【问题讨论】:

  • 你的insertMap()函数是做什么的?
  • 我打算删除它。在这种情况下,它只返回参数。
  • 等一下,我刚刚在 Google 上搜索了一下,可能已经找到了您的答案。您是否对local 数据库进行此查询?显然只有local 数据库允许这种行为。任何其他数据库,它都会工作。
  • 嗯。是的,它是本地的。
  • 是的,您不应该将该数据库用于应用程序。您应该为每个应用程序制作一个新的。如果可以解决问题,我会将其发布为您接受的答案。

标签: mongodb


【解决方案1】:

您必须使用$setOnInsert 插入_id

let ObjectID = require('mongodb').ObjectID;

collection.updateOne(
 { _id: entity._id },
 { $set: entity,$setOnInsert:{_id:new ObjectID()}},
 { upsert: true }
)

顾名思义,它将在insert 上设置_id

如果您使用的是mongodb,那么new ObjectID() 应该可以工作,

如果是mongoose,那么您可以使用mongoose.Types.ObjectId() 生成新的ObjectID

我找到了你的问题

在 3.0 版中更改:当您使用 upsert: true 执行 update() 并且查询不匹配现有文档时,如果查询在 _id 字段上指定条件,MongoDB 将拒绝插入新文档。

因此,简而言之,如果您的查询使用 _idReference,则无法使用 upsert:true 插入新文档

【讨论】:

  • 对我来说它应该工作是有道理的,但我得到了MongoError: Performing an update on the path '_id' would modify the immutable field '_id'
  • 您是否删除了您传递给$setentity 中的_id
  • 两种方法我都试过了。即使集合为空,我也会收到错误消息。
  • 哦不!谢谢你的回答,看来我得另找办法了。
  • 这很简单,而不是标记Upsert:true检查UpdateOne的返回值,如果没有找到你可以用相同的实体运行Insert
【解决方案2】:

首先,_id 属性将始终存在,无论您是自己设置还是让它自动生成。所以不需要检查它是否存在。

upsert的语法如下:

db.collection.update({
  _id: 'the id you want to check for' // can put any valid 'find' query here
}, {
  $set: {
    foo: 'bar',
    biz: 'baz'
  }
}, {
  upsert: true
});

如果存在具有给定_id 的文档,它将使用$set 对象进行更新。如果它不存在,将使用$set 对象的属性创建一个新对象。如果_id 未在$set 对象中指定,则会自动生成一个新的_id 值,或者将继续使用现有的_id 值。

这里的关键是使用$set,而不是把对象放在那里。使用 set 将合并和替换属性。如果没有$set,它将替换整个文档,删除所有未设置的属性。

编辑:现在看到您所做的实际查询,我建议您在设置之前从实体中删除 _id 属性。这将确保它不会受到影响。

const _id = entity._id;
delete entity._id;
// now do your query using the new `_id` value for the ID.

【讨论】:

  • 感谢您的回复!不过,这就是我正在使用的语法。如果我过滤{ _id: obj._id },它会插入obj_id: null,即使_id 不存在于obj 上。如果我过滤另一个属性,_id 会按预期生成。
  • @thorell9 请使用您所做的确切查询编辑您的原始帖子。我知道你是新来的,但你应该总是在提问时发布你的代码示例。
  • @thorell9 更新了我的答案
  • @thorell9 那肯定很奇怪。事实上,在创建文档后,其_id 永远不会更改,如果您尝试,MongoDB 将返回错误。因此,在您的查询之后必须运行一些其他代码来删除_id。没有其他可能的方法。
【解决方案3】:

根据您提到的我们的 cmets,您使用的是 local 数据库。 local 数据库允许 _id 为空。使用任何其他数据库应该可以解决您的问题。

【讨论】:

    【解决方案4】:

    如果entity._idnull,那么它会创建一个_id为null的文档,我们可以通过添加Types.ObjectId()来解决这个问题。如果_id 不存在,那么它将使用正确的_id 创建一个新文档。否则,它将更新现有文档。

    const { Types } = require("mongoose")
    
    collection.updateOne({ _id: Types.ObjectId(entity._id) },{ $set: entity },{ upsert: true})
    

    【讨论】:

      猜你喜欢
      • 2015-01-02
      • 2014-08-20
      • 1970-01-01
      • 2013-02-07
      • 1970-01-01
      • 2011-10-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多