How can I get first and last document based on time
field.
I can use $group
and get $first
and $last
document, but I don't need grouping here, just get first and last full document. Maybe I could use slice
? This query doesn't work:
{
"aggregate": "353469045637980_data",
"pipeline": [
{
"$match": {
"$and": [
{
"time": {
"$gte": 1461369600
}
},
{
"time": {
"$lt": 1461456000
}
}
]
}
},
{
"$project": {
"first": {
"$slice": 1
},
"last": {
"$slice": -1
}
}
}
]
}
This means $last returns the last order type for the documents between the current document and the end of the partition.
This means $first returns the first order type for the documents between the beginning of the partition and the current document.
The Aggregation command is slower than the find command. If you access to the data like ToList() the aggregation command is faster than the find.
To skip records in MongoDB, use skip(). With that, to display only a specific number of records, use limit().
Well you need $group
but you can simply use a constant (e.g. null
, see the docs) for its id
so that it results in a single group. $$ROOT then refers to the document itself which you can use with $first
and $last
like so
$group: {
_id: null,
first: { $first: "$$ROOT" },
last: { $last: "$$ROOT" }
}
Of course you can introduce further $project
stages to shape that data into an array (as you mentioned you want a list) etc.
As a side note you may want to introduce a $sort
stage to make sure $first
and $last
have a proper meaning.
You can make use of a $facet
stage, combined with $sort
/$limit
stages:
// { time: ISODate("2021-12-04"), b: "hello" }
// { time: ISODate("2021-12-07"), b: "world" }
// { time: ISODate("2021-12-05"), b: "oups" }
db.collection.aggregate([
{ $facet: {
first: [{ $sort: { time: 1 } }, { $limit: 1 }],
last: [{ $sort: { time: -1 } }, { $limit: 1 }]
}},
{ $set: { first: { $first: "$first" }, last: { $last: "$last" } } }
])
// { first: { time: ISODate("2021-12-04"), b: "hello" }, last: { time: ISODate("2021-12-07"), b: "world" } }
The $facet
stage allows us to run multiple aggregation pipelines within a single stage on the same set of input documents. Each sub-pipeline has its own field in the output document where its result is stored as an array of documents.
Each field is thus produced by its own aggregation pipeline whose first stage $sort
s documents by the time
field (in opposite orders), followed by a $limit
stage that'll only keep the first item (the smallest as defined by the chosen ordering).
The second part of the pipeline (the $set
stage) is just there to clean-up the $facet
output format.
Note that a $sort
followed by a $limit
stage is optimised by Mongo (see here). This allows the sort operation to only maintain the top n results (in our case only 1 element) as it progresses.
Also note that our $sort
stages will benefit from having an index on time
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With