Mongoose: atomic FindOne-Or-Insert(), do not updat

2019-07-21 19:38发布

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.

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.

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)
}

Suggestions? Thank you

1条回答
相关推荐>>
2楼-- · 2019-07-21 20:28

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 }
)

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.

查看更多
登录 后发表回答