'MongoDB: Filter on _id == obj._id without setting obj._id to null on insert

I'd like to configure an upsert. If _id already exists on my object, it recognizes that and updates the match. If _id doesn't exist, it should insert the new document and generate an _id. I'd expect { _id: obj._id } to work, but that overrides the auto-generation of _id. The document appears with _id: null. Is there a filter that would work for this? Do I need to route to insert/update in-code?

Edit: add query.

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

Edit: try delete. Even when I delete the property, no luck.

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

But the new document is this: screenshot of new document



Solution 1:[1]

You have to use $setOnInsert to Insert _id

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

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

As the name suggests it will set the _id on insert

If you are using mongodb then new ObjectID() should work,

If its mongoose then you can use mongoose.Types.ObjectId() to generate new ObjectID

Well I Found your Issue

Changed in version 3.0: When you execute an update() with upsert: true and the query matches no existing document, MongoDB will refuse to insert a new document if the query specifies conditions on the _id field.

So in a nutshell you cannot insert a new doc using upsert:true if your query is using _id, Reference

Solution 2:[2]

First of all, the _id property WILL always exist, whether you set it yourself or let it auto-generate. So there's no need to check if it exists.

The syntax for upsert is as follows:

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

If a document with the given _id exists, it will be updated with the $set object. If it does not exist, a new one will be created with the properties of the $set object. If _id is not specified in the $set object, a new _id value will be auto-generated, or will keep using the existing _id value.

The key here is using $set, rather than putting the object right in there. Using set will merge and replace the properties. Without $set it will replace the entire document, removing any unset properties.

Edit: Now seeing the actual query you made, I would suggest you delete the _id property from the entity before setting it. This will make sure it is left alone.

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

Solution 3:[3]

From our comments you mentioned you were using the local database. The local database allows _id to be null. Using any other DB should fix your issue.

Solution 4:[4]

If the entity._id is null then it will create a doc with _id null, We can solve this issue by adding Types.ObjectId(). If the _id doesn't exist then it will create a new document with the proper _id. otherwise, it will update the existing document.

const { Types } = require("mongoose")

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

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2
Solution 3 chrispytoes
Solution 4 SUJIN S