Is there a way to traverse query results in MongoDB / Mongoose from the back of the array?
I have a Story schema with messages an array element in the schema.
I can do
Story.findById(story_id, { messages: { $slice: -limit } } )
OR
.where('messages').slice(-limit)
to get limit elements from the end of the array. However, what I want to do is to skip N elements (from the end) and get limit elements. i.e. traverse the entire array limit at a time.
I tried using skip() as well passing an array to slice() (i.e. slice([skip, limit]), however it seems to be looking for a positive value for skip (i.e. position from beginning of the array, rather than from the end).
Is there a straightforward way to do this?
What I need is limit elements from skip away from the end of the array.
So as said, the real solution here it to store the array in reverse to how you are doing it presently, which is basically appending items to the end.
As of MongoDB 2.6 and later you can use the $position modifier to basically "pre-pend" items to the array:
Story.update(
{ "_id": story_id },
{ "$push": {
"messages": {
"$each": elementsAsArray,
"$position": 0
}
}},
function(err,numAffected) {
}
);
This needs to be called with the $each modifier even when adding a single element.
Otherwise you can apply the $sort modifier, but it does make sense to have something to "sort" on so the elements are in fact ordered. So possibly a "modified" date field:
Story.update(
{ "_id": story_id },
{ "$push": {
"messages": {
"$each": elementsAsArray,
"$sort": { "modified": -1 },
"$slice": 100
}
}},
function(err,numAffected) {
}
);
For versions prior to MongoDB 2.6 the $slice modifier is required as well, but you can set this to a number larger than the number of elements you expect to be in the array. Or in fact use it in order to "limit" how large the array can possibly get.
You can even note the "tip" on the $sort documentation:
"TIP You can pass an empty array [] to the $each modifier such that only the $sort modifier has an effect."
So that could be used to update existing items in bulk where there is indeed a "sub-field" that is available to sort on.
Once you have this in place, then the $slice operator for projection will function as required. Where skip will progress forwards through the array and limit will restrict the number of items to return.