One effective strategy is to ensure that the arrays are stored in a sorted manner from the beginning. It is likely that they are already sorted, especially since any $push
operation (or even using .push()
) will simply add items to the end of the array, making the latest item the "last" one by default.
If you are not altering the "date"
properties after creation and the latest date is always the last item, then using $slice
would be sufficient:
Data.find({ "hid": "testhid" }).select({
"sensors": { "$slice": -1 },
"actuators": { "$slice": -1 },
"status": { "$slice": -1 }
}).exec(function(err,data) {
]);
If for some reason the data is not stored in date order, consider using the $sort
modifier with $push
to consistently sort additions to the arrays. You can update the entire collection in one statement:
Date.update(
{},
{
"$push": {
"sensors": { "$each": [], "$sort": { "date": 1 } },
"actuators": { "$each": [], "$sort": { "date": 1 } },
"status": { "$each": [], "$sort": { "date": 1 } }
}
},
{ "multi": true },
function(err,num) {
}
)
This statement ensures that each array in every document is re-sorted so that the latest date is the last entry. This makes using $slice
as mentioned above appropriate.
If none of the previous methods apply and the data must be retrieved without being commonly stored in date order, only then resort to using .aggregate()
:
Data.aggregate([
{ "$match": { "hid": "testhid" } },
{ "$unwind": "$sensors" },
{ "$sort": { "_id": 1, "sensors.date": -1 } },
{ "$group": {
"_id": "$_id",
"sensors": { "$first": "$sensors" },
"actuators": { "$first": "$actuators" },
"status": { "$first": "$status" },
"updated": { "$first": "$updated" },
"created": { "$first": "$created" }
}},
{ "$unwind": "$actuators" },
{ "$sort": { "_id": 1, "actuators.date": -1 } },
{ "$group": {
"_id": "$_id",
"sensors": { "$first": "$sensors" },
"actuators": { "$first": "$actuators" },
"status": { "$first": "$status" },
"updated": { "$first": "$updated" },
"created": { "$first": "$created" }
}},
{ "$unwind": "$status" },
{ "$sort": { "_id": 1, "status.date": -1 } },
{ "$group": {
"_id": "$_id",
"sensors": { "$first": "$sensors" },
"actuators": { "$first": "$actuators" },
"status": { "$first": "$status" },
"updated": { "$first": "$updated" },
"created": { "$first": "$created" }
}}
],
function(err,data) {
}
)
MongoDB does not have a direct way to sort array content inline in query results or aggregation pipelines. The method involves using $unwind
, $sort
, and $group
with $first
.
Each array needs to be processed separately due to $unwind
creating separate documents for each array item. An alternative approach could involve combining all operations into one:
Data.aggregate([
{ "$match": { "hid": "testhid" } },
{ "$unwind": "$sensors" },
{ "$unwind": "$actuators" },
{ "$unwind": "$status" },
{ "$sort": {
"_id": 1,
"sensors.date": -1,
"actuators.date": -1,
"actuators.status": -1
}},
{ "$group": {
"_id": "$_id",
"sensors": { "$first": "$sensors" },
"actuators": { "$first": "$actuators" },
"status": { "$first": "$status" },
"updated": { "$first": "$updated" },
"created": { "$first": "$created" }
}}
],
function(err,data) {
}
)
Although it provides a combined approach, it may not offer significant improvement over the previously mentioned methods.
The key takeaway is to maintain sorted arrays and utilize $slice
for simple retrieval of the latest item.