MongoDB原子“findOrCreate”:findOne,如果不存在则插入,但不更新

Posted

技术标签:

【中文标题】MongoDB原子“findOrCreate”:findOne,如果不存在则插入,但不更新【英文标题】:MongoDB atomic "findOrCreate": findOne, insert if nonexistent, but do not update 【发布时间】:2013-04-27 20:29:39 【问题描述】:

正如标题所说,我想通过_id对文档执行查找(一个),如果不存在,则创建它,然后无论是找到它还是创建它,都在回调中返回.

如果它存在,我不想更新它,就像我读过的 findAndModify 一样。我在 *** 上看到了许多其他关于此的问题,但同样,不想更新任何内容。

我不确定是否通过创建(不存在),这实际上是每个人都在谈论的更新,这一切都令人困惑:(

【问题讨论】:

【参考方案1】:

从 MongoDB 2.4 开始,不再需要依赖唯一索引(或任何其他解决方法)来进行原子 findOrCreate 类操作。

这要感谢 the $setOnInsert operator 2.4 的新功能,它允许您指定仅在插入文档时才发生的更新。

这与upsert 选项结合使用,意味着您可以使用findAndModify 来实现类似findOrCreate 的原子操作。

db.collection.findAndModify(
  query:  _id: "some potentially existing id" ,
  update: 
    $setOnInsert:  foo: "bar" 
  ,
  new: true,   // return new doc if one is upserted
  upsert: true // insert the document if it does not exist
)

由于$setOnInsert 只影响正在插入的文档,如果找到现有的文档,则不会进行任何修改。如果不存在文档,它将使用指定的_id 更新插入一个,然后执行仅插入集。在这两种情况下,都会返回文档。

【讨论】:

@Gank 如果你使用 mongodb 本地驱动节点,语法会更像collection.findAndModify(_id:'theId', <your sort opts>, $setOnInsert:foo: 'bar', new:true, upsert:true, callback)。见the docs 有没有办法知道文档是否已更新或插入? 如果你想检查上面的查询(db.collection.findAndModify(query: _id: "some potentially existing id", update: $setOnInsert: foo: "bar", new: true, upsert: true))insert(upsert)ed一个文档,你应该考虑使用@987654332 @。如果插入了文档,则返回"acknowledged": true, "matchedCount": 0, "modifiedCount": 0, "upsertedId": ObjectId("for newly inserted one"),如果文档已存在,则返回"acknowledged": true, "matchedCount": 1, "modifiedCount": 0 似乎在 findOneAndUpdatefindOneAndReplacefindOneAndDelete 的风格中已被弃用 不过,这里需要小心。这仅在 findAndModify/findOneAndUpdate/updateOne 的选择器通过 _id 唯一标识一个文档时才有效。否则,upsert 在服务器上被拆分为查询和更新/插入。更新仍然是原子的。但是查询和更新不会一起原子执行。【参考方案2】:

驱动程序版本 > 2

使用最新的驱动程序(> 版本 2),您将使用 findOneAndUpdate,因为 findAndModify 已弃用。新方法有 3 个参数,filterupdate 对象(其中包含您的默认属性,应该为新对象插入)和options,您必须在其中指定 upsert 操作。

使用 promise 语法,它看起来像这样:

const result = await collection.findOneAndUpdate(
   _id: new ObjectId(id) ,
  
    $setOnInsert:  foo: "bar" ,
  ,
  
    returnOriginal: false,
    upsert: true,
  
);

const newOrUpdatedDocument = result.value;

【讨论】:

【参考方案3】:

它有点脏,但你可以插入它。

确保键上有一个唯一的索引(如果你使用 _id 没关系,它已经是唯一的了)。

这样,如果元素已经存在,它将返回一个您可以捕获的异常。

如果不存在,将插入新文档。

更新:在MongoDB Documentation上详细解释了这种技术

【讨论】:

好的,这是个好主意,但是对于预先存在的值,它会返回一个错误,而不是值本身,对吧? 这实际上是隔离操作序列的推荐解决方案之一(如果找不到,则查找然后创建,大概)docs.mongodb.org/manual/tutorial/isolate-sequence-of-operations @Discipol 如果你想做一组原子操作,你应该先锁定 Document,然后修改它,最后释放它。这将需要更多查询,但您可以针对最佳情况进行优化,并且大多数时候只执行 1-2 次查询。见:docs.mongodb.org/manual/tutorial/perform-two-phase-commits【参考方案4】:

这是我所做的(Ruby MongoDB 驱动程序):

$db[:tags].update_one(:tag => 'flat', '$set' => :tag => 'earth' , :upsert => true )

如果存在则更新,不存在则插入。

【讨论】:

以上是关于MongoDB原子“findOrCreate”:findOne,如果不存在则插入,但不更新的主要内容,如果未能解决你的问题,请参考以下文章

Sails.js Waterline ORM:.findOrCreate() 没有 .populate() 方法

node.js+mongoose UserSchema findOrCreate

用包含关联续集 findOrCreate

MongoDB 原子操作

如何处理异步。护照和猫鼬的 findOrCreate 方法

续集 findOrCreate 插入新行但返回未定义主键的对象