Is there an "upsert" option in the mongodb insert command? - mongodb

I know this may be a silly question, but I read on an e-book that there is an upsert option in MongoDB insert. I couldn't find proper documentation about this. Can someone educate me about this?

Since upsert is defined as operation that "creates a new document when no document matches the query criteria" there is no place for upsertsin insert command. It is an option for the update command. If you execute command like below it works as an update, if there is a document matching query, or as an insert with document described by update as an argument.
db.collection.update(query, update, {upsert: true})
MongoDB 3.2 adds replaceOne:
db.collection.replaceOne(query, replacement, {upsert: true})
which has similar behavior, but its replacement cannot contain update operators.

As in the links provided by PKD, db.collection.insert() provides no upsert possibility. Instead, mongo insert inserts a new document into a collection. Upsert is only possible using db.collection.update() and db.collection.save().
If you happen to pass a document to db.collection.insert() which is already in the collection and thus has an _id similar to an existing _id, it will throw a duplicate key exception.

For upserting a singe document using the java driver:
FindOneAndReplaceOptions replaceOptions = new FindOneAndReplaceOptions();
replaceOptions.upsert(true);
collection.findOneAndReplace(
Filters.eq("key", "value"),
document,
replaceOptions
);
Although uniqueness should be ensured from Filters.eq("key", "value") otherwise there is a possibility of adding multiple documents. See this for more

Related

MongoDB updateOne with upsert failed: Duplicate Key

I have a collection with 2 compound unique index, uuid and id. I want to update a document if the collection have a document with unique value of uuid and id (composite unique) and I found in the documentation that updateOne with upsert=true can do this. So, I use:
db.collection("messages").updateOne({uuid:this.uuid, id:new_message.id}, {$set: {uuid: this.uuid, ...new_message}}, {upsert:true})
and this always throw an error saying that there's a document with duplicate value of uuid=xxx and id=yyy. I looked up and found a post stating there's data race happening on update and insert on mongodb upsert operation so this will always happen. Is there another way to do this? How do I properly and efficiently upsert a collection with 1 million documents?
EDIT:
I gave the wrong code for this question. The code should be:
db.collection("messages").updateOne({uuid:this.uuid, key:{id:new_message.key.id}}, {$set: {uuid: this.uuid, ...new_message}}, {upsert:true})
Since you have multi-threading, this is a common problem. All the supported operations in mongo will run into this issue as it is based on your architecture.
You can catch the exception and retry the operation. In this case, one of the threads would be succeeded. Other one will pass through exception handling. This is a feasible workaround.
When do you except both threads updating the same document at the same time? This is a serious design problem. This will alter the desired document state.
So, after trying out things I found out that I should use dot notation in the query, I changed it to:
db.collection("messages").updateOne({uuid:this.uuid, "key.id":new_message.key.id}}, {$set: {uuid: this.uuid, ...new_message}}, {upsert:true})
and now it works.

Which is the best way to insert data in mongodb

While writing data to mongodb, we are checking if the data is present get the _id and using save update it else using insert add the data. Read save is the best way if you are providing _id in the query while saving it will update/insert based on if the _id is present in the db. Is the save the best method or is there any other way.
If you have all data available to save, just run update() each time but use the upsert functionality. Only one query required:
db.collection.update(
['_id' => $id],
$data,
['upsert' => true]
);
If your _id is generated by mongo you always know there is a record in the database and update is the one to use, but then again you could also save().
If you generated your id's (and thus don't know if it comes from the collection), this will always work without having to run an extra query.
From the documentation
db.collection.save()
Updates an existing document or inserts a new document, depending on its document parameter.
db.collection.insert()
Inserts a document or documents into a collection.
If you use db.collection.insert() in your case you will get duplication key error since it will try to insert new document which has same _id with an existing document. But instead of using save you should use the update method.

Mongodb - duplicate fields in $set and $setOnInsert

In this post, the accepted answer explains that you cannot have the same fields under $set and $setOnInsert in an upsert operation.
Can someone explain why this is? It seems like the $setOnInsert shouldn't conflict with $set, since the former is used when a document is inserted, and the latter is used when the document is updated.
I faced this problem. If someone is looking for a solution, you need to understand how the $set and $setOnInsert mechanism works
$set refreshes if found (ignoring $setOnInsert)
$setOnInsert inserts a new record (and then executes $set)
I did not know this and thought that only one operator would work. This way I was able to get rid of duplicate fields
$set operator is used on upsert too. So it's nonsense to refer same fields both on $set and $setOnInsert.
Just try this on an empty collection:
db.items.remove();
db.items.update({},{$set:{a:1},$setOnInsert:{b:2}},{upsert:1})
db.items.find({});

What is 'upsert' in the context of MongoDB?

In the context of MongoDB, what is upsert?
Is this an update and insert?
Just curious as I see the usage of this term in many articles and documentation on the MongoDB website.
From the documentation: An operation that will either update the first document matched by a query or insert a new document if none matches. The new document will have the fields implied by the operation.
See http://docs.mongodb.org/manual/reference/glossary/#term-upsert
To put it into SQL terms it is much like a ON DUPLICATE KEY ... UPDATE except that it isn't so verbose in how to query for it.
So essentially it is when you query for an update document, MongoDB doesn't find it and so inserts it.
The condition for the upsert accepts all the same stuff as a normal update except it also has the $setOnInsert ( http://docs.mongodb.org/manual/reference/operator/update/setOnInsert/ ) operator which allows you to define a set of fields that will only be taken into consideration on an insert.

Mongodb update. setOnInsert Mod on _id not allowed

I understand the fact that you can't update _id on an existing mongodb document.
But is there a reason that we can't use it in an upsert in the 'setOnInsert' part ? Because it is 'on insert' so it's not an update.
My expected usage is this:
db.myCol.update({_id:12345},{$setOnInsert:{_id:12345},$set:{myValue:'hi'}});
Is this a bug or am i missing something ?
MongoDB uses the 'query' part for an upsert query as part of the set, meaning that you don't have to specify the _id in the set part of you want to specify your own _id.
note: my query above also had a small bug which was the missing upsert flag.
This is the correct query:
db.myCol.update({_id:12345},{$set:{myValue:'hi'}},{upsert:true});
If the record doesn't exist, this query will insert a record which looks like this:
{_id:12345,myValue:'hi'}
Really, this is a bug in mongo fixed in development version 2.5.4 or release 2.6.0:
https://jira.mongodb.org/browse/SERVER-9958
Once that's fixed, I believe this should work as desired. In the general case, the query for update won't necessarily specify an "_id" field.