For the most optimal operation in modern MongoDB versions (3.2 and higher), utilizing $redact
is recommended as it allows for a "logical filter" based on specified conditions, paired with $arrayElemAt
to extract single values from an array.
Consider the following example:
{ "_id": 1, "data": [1,2,3] },
{ "_id": 2, "data": [3,2,1] }
The corresponding query would be:
db.collection.aggregate([
{ "$redact": {
"$cond": {
"if": {
"$lt": [
{ "$arrayElemAt": [ "$data", -1 ] },
{ "$arrayElemAt": [ "$data", -2 ] }
]
},
"then": "$$KEEP",
"else": "$$PRUNE"
}
}}
])
In cases where the document includes sub-document properties within the array elements, $map
can be applied to extract desired property values for comparison. Additionally, $let
aids in avoiding repetition of expressions.
Another illustration:
{
"_id": 1,
"data": [
{ "name": "c", "value": 1 },
{ "name": "b", "value": 2 },
{ "name": "a", "value": 3 }
]
},
{
"_id": 2,
"data": [
{ "name": "a", "value": 3 },
{ "name": "b", "value": 2 },
{ "name": "c", "value": 1 }
]
}
And the respective query:
db.collection.aggregate([
{ "$redact": {
"$cond": {
"if": {
"$let": {
"vars": {
"data": {
"$map": {
"input": "$data",
"as": "el",
"in": "$$el.value"
}
}
},
"in": {
"$lt": [
{ "$arrayElemAt": [ "$$data", -1 ] },
{ "$arrayElemAt": [ "$$data", -2 ] }
]
}
}
},
"then": "$$KEEP",
"else": "$$PRUNE"
}
}}
])
Ensuring extraction of the "property" value plays a crucial role here due to potential discrepancies when comparing Object
values directly against other array elements.
While $where
remains available for older MongoDB versions or alternate approaches, it involves JavaScript evaluation which impacts performance compared to using native aggregation framework operators.
Prior versions may still support achieving this through the aggregation framework, but such methods are discouraged due to lower efficiency. It typically entails manipulating array elements to derive comparisons between subsequent items, resulting in increased processing time:
db.collection.aggregate([
// Unwind array
{ "$unwind": "$data" },
// Group back and get $last
{ "$group": {
"_id": "$_id",
"data": { "$push": "$data" },
"lastData": { "$last" "$data" }
}},
// Unwind again
{ "$unwind": "$data" },
// Compare to mark the last element
{ "$project": {
"data": 1,
"lastData": 1,
"seen": { "$eq": [ "$lastData", "$data" ] }
}},
// Filter the previous $last from the list
{ "$match": { "seen": false } },
// Group back and compare values
{ "$group": {
"_id": "$_id",
"data": { "$push": "$data" },
"lastData": { "$last": "$lastData" },
"secondLastData": { "$last": "$data" },
"greater": {
"$last": { "$lt": [ "$data.value", "$lastData.value" ] }
}
}},
// Filter to return only true
{ "$match": { "greater": true } }
])
Conversely, employing $where
proves cleaner and more efficient for scenarios involving earlier MongoDB versions that necessitate further aggregation operations post-condition matching.
Hence, transitioning to recent MongoDB releases and leveraging $redact
along with logical comparisons in a singular pipeline stage yields enhanced performance by minimizing processing stages.