Mongodb update. setOnInsert Mod on _id not allowed - mongodb

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.

Related

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.

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

Doing an upsert in mongo, can I specify a custom query for the "insert" case? [duplicate]

I am trying to use upsert in MongoDB to update a single field in a document if found OR insert a whole new document with lots of fields. The problem is that it appears to me that MongoDB either replaces every field or inserts a subset of fields in its upsert operation, i.e. it can not insert more fields than it actually wants to update.
What I want to do is the following:
I query for a single unique value
If a document already exists, only a timestamp value (lets call it 'lastseen') is updated to a new value
If a document does not exists, I will add it with a long list of different key/value pairs that should remain static for the remainder of its lifespan.
Lets illustrate:
This example would from my understanding update the 'lastseen' date if 'name' is found, but if 'name' is not found it would only insert 'name' + 'lastseen'.
db.somecollection.update({name: "some name"},{ $set: {"lastseen": "2012-12-28"}}, {upsert:true})
If I added more fields (key/value pairs) to the second argument and drop the $set, then every field would be replaced on update, but would have the desired effect on insert. Is there anything like $insert or similar to perform operations only when inserting?
So it seems to me that I can only get one of the following:
The correct update behavior, but would insert a document with only a subset of the desired fields if document does not exist
The correct insert behavior, but would then overwrite all existing fields if document already exists
Are my understanding correct? If so, is this possible to solve with a single operation?
MongoDB 2.4 has $setOnInsert
db.somecollection.update(
{name: "some name"},
{
$set: {
"lastseen": "2012-12-28"
},
$setOnInsert: {
"firstseen": <TIMESTAMP> # set on insert, not on update
}
},
{upsert:true}
)
There is a feature request for this ( https://jira.mongodb.org/browse/SERVER-340 ) which is resolved in 2.3. Odd releases are actually dev releases so this will be in the 2.4 stable.
So there is no real way in the current stable versions to do this yet. I am afraid the only method is to actually do 3 conditional queries atm: 1 to check the row, then a if to either insert or update.
I suppose if you had real problems with lock here you could do this function with sole JS but that's evil however it would lock this update to a single thread.

last update in mongoengine

Is there any way to find last update Document in Collection? in other way sort collection by update
somethings like this
people = Person.objects.order_by_update()
or i must add update time for each doc?
I use mongodb, mongoengine, flask
You must add a field such as last_updated_time if you want to be able to sort in this way. Also, since you're sorting on it, you should probably index it.
The only thing that mongodb stores by default is _id, which can be used roughly as a created_time timestamp.

Does Key order matter in a MongoDB BSON doc?

I know certain commends need the hashmap / dictionary to be ordered, but does the actual BSON document in MongoDB matter and would the index still work?
E.g.
db.people.ensureIndex({LName:1, FName:1});
Would it work on both:
{LName:"abc", FName:"def"},
{FName:"ghi", LName:"jkl"}
?
Thanks
The order of a document's properties does not affect indexing.
You can see this for yourself by running this query:
db.people.find({LName: "abc"}).explain()
and then this query:
db.people.find({LName: "jkl"}).explain()
you should see that MongoDB will use the index in both cases (the cursor property should be something like "BtreeCursor LName_1_FName_1").