I want to create a document such as
{
increment: 12
}
Where the value of increment is the max of all values stored in that collection + 1.
You could separate this into a query and an update, but then you run the risk of a race condition, if two separate calls are are made to the method within an inopportune short period of time.
Is there a way of doing this in a single, atomic call?
You can use findAndModify with the option {"new" :true}
updates the document
returns the updated document (before anyone else can change it)
without the new option it would still update, but return the old document before the update.
*if this is what you are asking for
Related
Is there a simple OR elegant method (or query that I can write) to retrieve the last updated timestamp (of the last updated document) in a collection. I can write a query like this to find the last inserted document
db.collection.find().limit(1).sort({$natural:-1})
but I need information about the last updated document (it could be an insert or an update).
I know that one way is to query the oplog collection for the last record from a collection. But it seems like an expensive operation given the fact that oplog could be of very large size (also not trustworthy as it is a capped collection). Is there a better way to do this?
Thanks!
You could get the last insert time same way you mentioned in the question:
db.collection.find().sort({'_id': -1}).limit(1)
But, There isn't any good way to see the last update/delete time. But, If you are using replica sets you could get that from the oplog.
Or, you could add new field in document as 'lastModified'.
You can also checkout collection-hooks. I hope this will help
One way to go about it is to have a field that holds the time of last update. You can name it updatedAt. Every time you make an update to the document, you'll just update the value to the current time. If you use the ISO format to store the time, you'll be able to sort without issues (that's what I use).
The other way is the _id field.
Method 1
db.collection.find().limit(1).sort({updatedAt: -1})
Method 2
db.collection.find().limit(1).sort({_id: -1})
You can try with ,
db.collection.findOne().sort({$natural:-1}).limit(1);
I would like to simulate a transaction on MongoDB and you wonder if there is a better solution. I have a document that has inside an array. I would like to increase the values of this. But if at the same time another user selects the document with the same values, result wrong data. Must wait until the update is resolved, and only after another user may request the document and he also increase the arry.
Any sugestion?
Thankyou
You should let mongo handle these issues of concurrency. It knows how to handle situations like this and each update will be atomic.
Instead of submitting the actual value to be updated you can specify only the amount you want to increment. I would suggest using the appropriate operators to allow mongo to do its work. If all you are doing is an increment on a value, you can use the $inc operator.
Taken from the linked documentation:
The $inc operator increments a field by a specified value and has the following form:
{ $inc: { <field1>: <amount1>, <field2>: <amount2>, ... } }
To specify a in an embedded document or in an array, use dot notation.
Note that this method will also work for decrementing the value; Just pass a negative value as the amount.
Starting from version 4.0 MongoDB will add support for multi-document transactions.
Transactions will be multi-statement, with syntax like start_transaction and commit_transaction.
For details visit this link: https://www.mongodb.com/blog/post/multi-document-transactions-in-mongodb?jmp=community
I have mongodb collection users.
Each user have field called rating which is between 1 and 5. It means that when user votes on another user he 'gives' him his vote which is a number between 1 and 5. I have a problem with storing this data in mongo document beacause I have to query user collection by rating field and I have to update it atomicly...
If I store both rating and number of votes when I can update votes_number with $inc operator but I cant atomicly set rating = ((rating*votes) + vote_val)/(votes+1)
I could just keep sum of votes and votes number in document and update both using $inc but then I cant query like WHERE votes_sum/votes_num > 3...
Is there any solution to this problem?
What you can do is use option two from above and then combine it with a cached result field. You can set up the data flow so the result field remains consistent with the rest of the document by using the filter predicate on your update.
Step one is to add a new field to your schema which will be your cached rating field. This will allow you to perform your range query without having to do the dynamic division. The problem you'll run into there is that you can't atomically increment the votes_sum & votes_num fields AND in the same atomic operation set the cached rating fields. So here's what you do.
1) Atomically increment the votes_sum and votes_num fields
2) Grab the _id, votes_sum & votes_num for the updated document
3) Update the rating but, as part of the filter predicate, include the _id, expected votes_sum and expected votes_num fields.
db.collection.update({_id: $id, votes_sum: $votes_sum, votes_num: $votes_num}, {$set: {rating: $votes_sum / $votes_num}});
This will ensure that nothing has changed since you updated the doc. If someone else comes along and updates those fields in between you updating them and generating the rating then the doc will not be returned in the find part of the update statement and thus it will not be updated with stale data
This pattern takes advantage of the fact that writes are atomic at the document level in MongoDB so you don't have to worry about the consistency of data within a document. The nice thing is that the rating will be set correctly because every operation to update the votes_sum and votes_num fields is followed by an update to rating.
See here for some sample code: http://docs.mongodb.org/manual/tutorial/isolate-sequence-of-operations/
I have a MongoDB collection as follows:
comment_id (number)
comment_title (text)
score (number)
time_score (number)
final_score (number)
created_time (timestamp)
Score is and integer that's usually updated using $inc 1 or -1 whenever someone votes up or down for that record.
but time_score is updated using a function relative to timestamp and current time and other factors like how many (whole days passed) and how many (whole weeks passed) .....etc
So I do $inc and $dec on db directly but for the time_score, I retrieve data from db calculate the new score and write it back. What I'm worried about is that in case many users incremented the "score" field during my calculation of time_score then when I wrote time_score to db it'll corrupt the last value of score.
To be more clear does updating specific fields in a record in Mongo rewrites the whole record or only the updated fields ? (Assume that all these fields are indexed).
By default, whole documents are rewritten. To specify the fields that are changed without modifying anything else, use the $set operator.
Edit: The comments on this answer are correct - any of the update modifiers will cause only relevant fields to be rewritten rather than the whole document. By "default", I meant a case where no special modifiers are used (a vanilla document is provided).
The algorithm you are describing is definitely not thread-safe.
When you read the entire document, change one field and then write back the entire document, you are creating a race condition - any field in the document that is modified after your read but before your write will be overwritten by your update.
That's one of many reasons to use $set or $inc operators to atomically set individual fields rather than updating the entire document based on possibly stale values in it.
Another reason is that setting/updating a single field "in-place" is much more efficient than writing the entire document. In addition you have less load on your network when you are passing smaller update document ({$set:{field:value}}, rather than entire new version of the document).
I have 10000 documents in one MongoDB collection. I'd like to update all the documents with datetime values that are 1 second apart for each document (so all the date time values are unique and are spaced 1 second apart). Is there any way to do this with a single update instead of updating each document in turn which results in 10000 distinct update operations?
Thanks.
No, there is no way to do this with a single update statement. There are no expressions which run at the server to allow this type of update. There is a feature request for this but it is not done so it cannot be used.