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