To jest coś, czego po prostu nie można zrobić za pomocą struktury agregacji, a jedyną obecną metodą MongoDB dostępną dla tego typu operacji jest mapReduce.
Powodem jest to, że w ramach agregacji nie ma możliwości odwoływania się do żadnego innego przygotowywanego dokumentu niż obecny. W rzeczywistości dotyczy to również „grupowania” etapów potoku, ponieważ nawet jeśli rzeczy są pogrupowane według „klucza”, nie możesz tak naprawdę radzić sobie z poszczególnymi dokumentami w sposób, w jaki chcesz.
Z drugiej strony MapReduce ma jedną dostępną funkcję, która pozwala robić to, co chcesz tutaj, i nie jest nawet „bezpośrednio” związana z agregacją. W rzeczywistości jest to możliwość posiadania „zmiennych o zasięgu globalnym” na wszystkich etapach. A posiadanie „zmiennej” do „przechowywania ostatniego dokumentu” to wszystko, czego potrzebujesz, aby osiągnąć swój wynik.
Jest to więc dość prosty kod i w rzeczywistości nie jest wymagana żadna „redukcja”:
db.collection.mapReduce(
function () {
if (lastVal != null)
emit( this._id, this.val - lastVal );
lastVal = this.val;
},
function() {}, // mapper is not called
{
"scope": { "lastVal": null },
"out": { "inline": 1 }
}
)
Co daje wynik podobny do tego:
{
"results" : [
{
"_id" : ObjectId("54a425a99b8bcd6f73e2d662"),
"value" : 2
},
{
"_id" : ObjectId("54a425a99b8bcd6f73e2d663"),
"value" : 3
},
{
"_id" : ObjectId("54a425a99b8bcd6f73e2d664"),
"value" : 4
}
],
"timeMillis" : 3,
"counts" : {
"input" : 4,
"emit" : 3,
"reduce" : 0,
"output" : 3
},
"ok" : 1
}
To tak naprawdę po prostu wybranie „czegoś wyjątkowego” jako emitowanego _id
wartość, a nie coś konkretnego, ponieważ wszystko, co naprawdę robi, to różnica między wartościami w różnych dokumentach.
Zmienne globalne są zwykle rozwiązaniem tego typu agregacji „parujących” lub tworzenia „suma bieżących”. W tej chwili framework agregacji nie ma dostępu do zmiennych globalnych, nawet jeśli byłoby to dobrze mieć. Framework mapReduce je posiada, więc można śmiało powiedzieć, że powinny być również dostępne dla struktury agregacji.
W tej chwili tak nie jest, więc trzymaj się mapReduce.