0
votes

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

3
What does your insertMap() function do? - chrispytoes
I meant to remove that. In this case it just returns the argument. - thorell9
Hang on, I've just Googled around and may have found your answer. Are you making this query to the local database? Apparently only the local database allows this behavior. Any other DB and it will work. - chrispytoes
Huh. Yes, it's local. - thorell9
Yup, you shouldn't use that DB for applications. You should make a new one for each application. I will post this as an answer for you to accept if that fixes it. - chrispytoes

3 Answers

0
votes

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.
0
votes

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.

0
votes

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