猫鼬:原子FindOne-Or-Insert(),如果发现则不更新现有实例 [英] Mongoose: atomic FindOne-Or-Insert(), do not update existing instance if found

查看:76
本文介绍了猫鼬:原子FindOne-Or-Insert(),如果发现则不更新现有实例的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在Mongoose中,我正在寻求一种原子化方式来实现Model.FindOne-Or-Insert(),与当前可用的类似功能和签名 Model.FindOneAndUpdate() ,除非存在实例(即匹配filter),则不使用提供的object 更新,而是返回原样查找的实例,如果不存在(即与filter不匹配),然后插入object并返回新实例.

In Mongoose, I am seeking to perform atomically a way to Model.FindOne-Or-Insert(), similar functionality and signature as currently available Model.FindOneAndUpdate() except if an instance exists (i.e. matches filter) then do not update using provided object but return instance found as is, and if not exists (i.e. no match with filter) then insert object and return new instance.

我找不到使用 Model.FindOneAndUpdate() 的方法通过尝试修改options的差异并不向object提供不希望在实例存在时不进行更新的字段来对现有实例进行更新.

I could not find a way using Model.FindOneAndUpdate() not to perform an update to an existing instance by trying out variances to its options and not providing fields to object that preferred not to update if instance exists.

所以,我当前的非原子解决方法是 Model.FindOne() ,如果找不到,请执行 Document.save() .

So, my current non-atomic workaround is Model.FindOne() and if not found then perform Document.save().

const Foo = DB.model('foo', FooSchema)

async function findOneAndUpdateFoo(jsonFoo, next) {

  const filter = {
    deletedAt: null
  }
  if (jsonFoo.dsAccountId) {
    filter.dsAccountId = jsonFoo.dsAccountId
  }
  if (jsonIntegration.dsUserId) {
    filter.dsUserId = jsonIntegration.dsUserId
  }
  if (jsonFoo.providerId) {
    filter.providerId = jsonFoo.providerId
  }

  const fooDoc = {
    name: jsonFoo.name,
    dsAccountId: jsonFoo.dsAccountId,
    dsUserId: jsonFoo.dsUserId,
    providerId: jsonFoo.providerId,
    providerName: jsonFoo.providerName,

    // Most of these fields could be empty
    accessToken: jsonFoo.accessToken,
    refreshToken: jsonFoo.refreshToken,
    scope: jsonFoo.scope,
    tokenType: jsonFoo.tokenType,
    expiresAt: jsonFoo.expiresAt
  }

  return await Foo.findOneAndUpdate(
    filter, // find a document with that filter
    fooDoc, // document to insert when nothing was found
    { upsert: true, new: true, runValidators: true } // options
  )
    .catch(next)
}

建议?谢谢

推荐答案

您可以使用 $setOnInsert 放在您的update参数中,以便它仅适用于插入大小写;在文档已存在的情况下,更新将变为无操作:

You can use $setOnInsert in your update parameter so that it will only apply in the insert case; with the update becoming a no-op in the case where the document already exists:

return await Foo.findOneAndUpdate(
  filter,                 // find a document with that filter
  {$setOnInsert: fooDoc}, // document to insert when nothing was found
  { upsert: true, new: true, runValidators: true }
)

请注意,您还应该在filter中包含的字段上创建唯一索引,然后处理重复错误的可能性.有关原因的详细信息,请参见这篇文章.

Note that you should also create a unique index over the fields included in your filter and then handle the possibility of a duplicate error. See this post for the details why.

这篇关于猫鼬:原子FindOne-Or-Insert(),如果发现则不更新现有实例的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆