How come this MongoDB update doesn't work? - mongodb

db.posts.update({}, {"pop_score":999});
db.posts.find({},{"pop_score":1});
{ "_id" : ObjectId("4d8eadd6df83500f3b000004"), "pop_score" : 0 }
{ "_id" : ObjectId("4d8eb1e3df83500f3b000035"), "pop_score" : 1 }
{ "_id" : ObjectId("4d8eb238df83500f3b000039"), "pop_score" : 1 }
{ "_id" : ObjectId("4d91377bdf8350063d000000"), "pop_score" : 1 }
{ "_id" : ObjectId("4d913c19df8350063d000001"), "pop_score" : 2 }
{ "_id" : ObjectId("4d8eacabdf83500f3b000000"), "pop_score" : 1 }
I update the pop_score to 999, for all posts. But when I query for them, it didn't update.

It did work, but only updates the FIRST matching document by default. I suspect you have SOME document in there that is now 999.
What you need to do is to tell MongoDB to update every matching document, by setting the optional multi flag to true:
db.posts.update({}, {"pop_score":999}, false, true)
This will update every document rather than just the first it finds.
You may wish to review the docs on updating as well which have more info on these flags.

Beware that update() replaces the found element with the one passed as argument, you should use the $set atomic operator to update a field's value (and of course, Brendan is right about the first vs. multiple match):
db.posts.update({}, { $set: { pop_score: 999 } }, false, true)

Related

mongodb update multi not working

I'm tying to update a field named company, here is my query
user_collection.update({"campaing" => "mediacom"}, {"campaing" => "flatiron"}, {"multi" => true})
but the problem is the update affects only one document not all documents from collection and "multi" flag is set to true
Building on what paul referenced in the docs, you want to use a $set to change a field value:
user_collection.update({ "campaing" : "mediacom" }, { "$set" : { "campaing" : "flatiron" } }, { "multi" : true })
Updating with a document means "replace the matched document with this document":
> db.test.drop()
> db.test.insert({ "_id" : 0, "a" : 1, "b" : 0 })
> db.test.update({ "_id" : 0 }, { "b" : 1 })
> db.test.findOne()
{ "_id" : 0, "b" : 1 }
From the mongoDB documentation:
"If the document contains only field:value expressions, then update() cannot update multiple documents."
http://docs.mongodb.org/manual/reference/method/db.collection.update/
You can try updateMulti function

Return latest record from subdocument in Mongodb

Let's say i want to return the latest inserted document from the subdocument. I want to be able to return the second record within the tags array w/ the _id of 54a1845def7572cd0e3fe288
So I far I have this query but it returns all values in the tags array.
db.modules.findOne({_id:"ui","svn_branches.branch":"Rocky"},{"svn_branches.$":1})
Mongodb array:
{
"_id" : "ui",
"svn_branches" : [
{
"updated_at" : ISODate("2013-06-12T20:48:17.297Z"),
"branch" : "Rocky",
"revision" : 0,
"tags" : [
{
"_id" : ObjectId("54a178b8ef7572d30e3fe288"),
"commit_message" : "r277 | ssmith | 2015-02-11 17:43:23 -0400 (Wed, 11 Feb 2015)",
"latest_tag" : "20150218r1_6.32_abc",
"revision" : 1,
"tag_revision_number" : "280",
"updated_at" : ISODate("2015-02-18T19:54:54.062Z")
},
{
"_id" : ObjectId("54a1845def7572cd0e3fe288"),
"commit_message" : "r271 | sam | 2dskjh\n",
"latest_tag" : "20150218r2_6.32_abc",
"revision" : 2,
"tag_revision_number" : "281",
"updated_at" : ISODate("2015-02-19T19:54:54.062Z")
}
]
}
]
}
Simple Solution
Let say we have a category as a document and items as a subdocument.
// find document from collection
const category = await Category.findOne({ _id:'$hec453d235xhHe4Y' });
// fetch last index of sub-document
const lastItemIndex = category.items.length - 1;
// here is the last item of sub-document
console.log(category.items[lastItemIndex]);
as mongodb inserted the latest sub-document at last index, so we need to find the last index for the latest sub-doc.
Queries in MongoDB do not return subdocuments (or, as in your case, subdocuments of subdocuments). They match and return the the documents in the collection. The documents' shape can be changed a bit by projection, but it's limited. If you want to find the latest tag commonly, you probably want to make your documents represent tags. Having an array in an array is generally a bad idea in MongoDB, too.
If this is an uncommon operation, and one that doesn't need to be particularly fast, you can use an aggregation:
db.modules.aggregate([
{ "$unwind" : "$svn_branches" },
{ "$unwind" : "$svn_branches.tags" },
{ "$sort" : { "svn_branches.tags.updated_at" : -1 } },
{ "$group" : { "_id" : "$_id", "latest_tag" : { "$first" : "$svn_branches.tags" } } }
])
I needed to find the last entry of subdocuments and I managed to make it to work with the $slice projection operator: mondodb.com > $slice (projection)
db.modules.find({_id:'ui', 'svn_branches.branch':'Rocky'},
{ 'svn_branches.tags': {$slice:-1} } )
I had only one level, if this doesn't work, please let me know.

Return range of documents around ID in MongoDB

I have an ID of a document and need to return the document plus the 10 documents that come before and the 10 documents after it. 21 docs total.
I do not have a start or end value from any key. Only the limit in either direction.
Best way to do this? Thank you in advance.
Did you know that ObjectID's contain a timestamp? And that therefore they always represent the natural insertion order. So if you are looking for documents before an after a known document _id you can do this:
Our documents:
{ "_id" : ObjectId("5307f2d80f936e03d1a1d1c8"), "a" : 1 }
{ "_id" : ObjectId("5307f2db0f936e03d1a1d1c9"), "b" : 1 }
{ "_id" : ObjectId("5307f2de0f936e03d1a1d1ca"), "c" : 1 }
{ "_id" : ObjectId("5307f2e20f936e03d1a1d1cb"), "d" : 1 }
{ "_id" : ObjectId("5307f2e50f936e03d1a1d1cc"), "e" : 1 }
{ "_id" : ObjectId("5307f2e90f936e03d1a1d1cd"), "f" : 1 }
{ "_id" : ObjectId("5307f2ec0f936e03d1a1d1ce"), "g" : 1 }
{ "_id" : ObjectId("5307f2ee0f936e03d1a1d1cf"), "h" : 1 }
{ "_id" : ObjectId("5307f2f10f936e03d1a1d1d0"), "i" : 1 }
{ "_id" : ObjectId("5307f2f50f936e03d1a1d1d1"), "j" : 1 }
{ "_id" : ObjectId("5307f3020f936e03d1a1d1d2"), "j" : 1 }
So we know the _id of "f", get it and the next 2 documents:
> db.items.find({ _id: {$gte: ObjectId("5307f2e90f936e03d1a1d1cd") } }).limit(3)
{ "_id" : ObjectId("5307f2e90f936e03d1a1d1cd"), "f" : 1 }
{ "_id" : ObjectId("5307f2ec0f936e03d1a1d1ce"), "g" : 1 }
{ "_id" : ObjectId("5307f2ee0f936e03d1a1d1cf"), "h" : 1 }
And do the same in reverse:
> db.items.find({ _id: {$lte: ObjectId("5307f2e90f936e03d1a1d1cd") } })
.sort({ _id: -1 }).limit(3)
{ "_id" : ObjectId("5307f2e90f936e03d1a1d1cd"), "f" : 1 }
{ "_id" : ObjectId("5307f2e50f936e03d1a1d1cc"), "e" : 1 }
{ "_id" : ObjectId("5307f2e20f936e03d1a1d1cb"), "d" : 1 }
And that's a much better approach than scanning a collection.
Neil's answer is a good answer to the question as stated (assuming that you are using automatically generated ObjectIds), but keep in mind that there's some subtlety around the concept of the 10 documents before and after a given document.
The complete format for an ObjectId is documented here. Note that it consists of the following fields:
timestamp to 1-second resolution,
machine identifier
process id
counter
Generally if you don't specify your own _ids they are automatically generated by the driver on the client machine. So as long as the ObjectIds are generated on a single process on a client single machine, their order does indeed reflect the order in which they were generated, which in a typical application will also be the insertion order (but need not be). However if you have multiple processes or multiple client machines, the order of the ObjectIds for objects generated within a given second by those multiple sources has an unpredictable relationship to the insertion order.

mongodb micro-optimization of batch inserts ? or is this an important optimization?

premise : update statements are harmless since the driver by default works in one way messaging (as long as getLastError isn't used).
question Is the following fragment the best way to do this in mongodb for high volume inserts ? Is it possible to fold step 2 and 3 ?
edit : old buggy form , see below
// step 1 : making sure the top-level document is present (an upsert in the real
example)
db.test.insert( { x :1} )
// step 2 : making sure the sub-document entry is present
db.test.update( { x:1 }, { "$addToSet" : { "u" : { i : 1, p : 2 } } }, false)
// step 3 : increment a integer within the subdocument document
db.test.update( { x : 1, "u.i" : 1}, { "$inc" : { "u.$.c" : 1 } },false)
I have a feeling there is no way out of operation 3, since the$ operator requires priming in the query field of the query part of an update. amirite ? iamrite ?
If this is the best way to do things, can I get creative in my code and go nuts with update operations ?
edit : new form
There was a bug in my logic, thanks Gates. Still want to fold the updates if possible :D
// make sure the top-level entry exists and increase the incidence counter
db.test.update( { x : 1 }, { $inc : { i : 1 } }, true ) --1
// implicetly creates the array
db.test.update( { x : 1 , u : { $not : { $elemMatch : { i : 1 } } } } ,
{ $push : { u : { i : 1 , p :2 , c:0} } }) -- 2
db.test.update( { x :1 , "u.i" : 1}, { $inc : { "u.$.c" : 1 } },false) --3
notes : $addToSet is not usefull in this case, since it does a element-wise match, there is no way to express what elements in an array may be mutable as in C++ OO bitwise comparison parlance
question is pointless Data model is wrong. Please vote to close (OP).
So, the first thing to note is that the $ positional operator is a little sketchy. It has a lot of "gotchas": it doesn't play well with upserts, it only affects the first true match, etc.
To understand "folding" of #2 and #3, you need to look at the output of your commands:
db.test.insert( { x :1} )
{ x:1 } // DB value
db.test.update( { x:1 }, { "$addToSet" : { "u" : { i : 1, p : 2 } } }, false)
{ x:1, u: [ {i:1,p;2} ] } // DB value
db.test.update( { x : 1, "u.i" : 1}, { "$inc" : { "u.$.c" : 1 } },false)
{ x:1, u: [ {i:1,p:2,c:1} ] } // DB value
Based on the sequence you provided, the whole thing can be rolled into a single update.
If you're only looking to roll together #2 & #3, then you're worried about matching 'u.i':1 with u.$.c. But there are some edge cases here you have to clarify.
Let your starting document be the following:
{
x:1,
u: [
{i:1, p:2},
{i:1, p:3}
]
}
What do you expect from running update #3?
As written you get:
{
x:1,
u: [
{i:1, p:2, c:1},
{i:1, p:3}
]
}
Is this correct? Is that first document legal? (semantically correct)? Depending on the answers, this may actually be an issue of document structure.

Mongo does not have a max() function, how do I work around this?

I have a MongoDB collection and need to find the max() value of a certain field across all docs. This value is the timestamp and I need to find the latest doc by finding the largest timestamp. Sorting it and getting the first one gets inefficient really fast. Shall I just maintain a 'maxval' separately and update it whenever a doc arrives with a larger value for that field? Any better suggestions?
Thanks much.
if you have an index on the timestsamp field, finding the highest value is efficientl something like
db.things.find().sort({ts:-1}).limit(1)
but if having an index is too much overhead storing the max in a separate collection might be good.
For sure if it will be big collection and if you need always display max timestamp you may need create separate collection and store statistic data there instead of order big collection each time.
statistic
{
_id = 1,
id_from_time_stamp_collection = 'xxx',
max_timestamp: value
}
And whenever new doc come just update statistic collection with id = 1(with $gt condition in query, so if new timestamp will be greater than max_timestamp then max_timestamp will be updated, otherwise - no).
Also probably you can store and update other statistic data within statistic collection.
Try with db.collection.group
For example, with this collection:
> db.foo.find()
{ "_id" : ObjectId("..."), "a" : 1 }
{ "_id" : ObjectId("..."), "a" : 200 }
{ "_id" : ObjectId("..."), "a" : 230 }
{ "_id" : ObjectId("..."), "a" : -2230 }
{ "_id" : ObjectId("..."), "a" : 5230 }
{ "_id" : ObjectId("..."), "a" : 530 }
{ "_id" : ObjectId("..."), "a" : 1530 }
You can use group using
> db.foo.group({
initial: { },
reduce: function(doc, acc) {
if(acc.hasOwnProperty('max')) {
if(acc.max < doc.a)
acc.max = doc.a;
} else {
acc.max = doc.a
}
}
})
[ { "max" : 5230 } ]
Since there is no key value in group all the objects are grouped in a single result