The basic process here is that you need to get the maximum sorted date from the array and obtain the value from there. Of course you need a loop, and you cannot access a value of a document directly in an update statement. So you need to read it first, but Bulk operations help here:
var bulk = db.documents.initializeOrderedBulkOp(),
count = 0;
db.documents.find().forEach(function(doc) {
var last_modified = doc.services.sort(function(a,b) {
return a.last_modified < b.last_modified;
}).slice(-1)[0].last_modified;
bulk.find({ "_id": doc._id }).updateOne({
"$set": { "last_modified": last_modified }
});
count++;
if ( count % 1000 == 0 ) {
bulk.execute();
bulk = db.documents.initializeOrderedBulkOp();
}
});
if ( count % 1000 != 0 )
bulk.execute();
Better yet, consider sorting the array itself on addition of new items. This is basically done with the the $sort
modifier to $push
db.documents.update(
{ "_id": id },
{ "$push": {
"services": {
"$each": [{ "last_modified": date }],
"$sort": { "last_modified": 1 }
}}
)
Or even forget the $sort
since all array values are appended to the end anyway, unless you tell the operation to to otherwise.
Then you can basically shorten the procedure using $slice
.
var bulk = db.documents.initializeOrderedBulkOp(),
count = 0;
db.documents.find(
{},
{
"last_modified": { "$slice": -1}
}
).forEach(function(doc) {
bulk.find({ "_id": doc._id }).updateOne({
"$set": { "last_modified": doc.last_modified[0] }
});
count++;
if ( count % 1000 == 0 ) {
bulk.execute();
bulk = db.documents.initializeOrderedBulkOp();
}
});
if ( count % 1000 != 0 )
bulk.execute();
The aggregation framework could be used here, but is really not necessary considering how simple it is to just get the maximum date value from the object per document anyway.
var bulk = db.documents.initializeOrderedBulkOp(),
count = 0;
db.documents.aggregate([
{ "$unwind": "$services" },
{ "$group": {
"_id": "$_id",
"last_modified": { "$max": "$services.last_modified" }
}}
]).forEach(function(doc) {
bulk.find({ "_id": doc._id }).updateOne({
"$set": { "last_modified": doc.last_modified }
});
count++;
if ( count % 1000 == 0 ) {
bulk.execute();
bulk = db.documents.initializeOrderedBulkOp();
}
});
if ( count % 1000 != 0 )
bulk.execute();
And because of the usage of $unwind
this actually comes at a much greater cost than is necessary.