Mongodb - duplicate fields in $set and $setOnInsert - mongodb

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

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.

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.

meteor, find whether user exists in mongodb document array or not?

This is my meteor code to search whether user exist in the array or not
var u_exist=Polls_Coll.findOne( {option1:{$elemMatch:{ids:"xxx"}}} );
My question is, How to know whether the statement returning something or not(user exist or not)
$elemMatch will return only where one of the conditions supplied actually finds a match in the array. So if you don't get a document back then there was no match.
Also findOne is a single document. Modifiers such as .count() will not work on that. If you have more documents to be expected use find intstead. Also findOne not not make much sense without applying a unique identifier such as _id in the query. Without that you are almost certainly not getting what you want.
While useful for your purpose, findOne is not a good match with the $elemMatch operator. The reasoning is you can possibly get multiple results of the same document having the same set of array elements that matched the condition that you gave.
Buyer beware.

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

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

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.