За да отговорите директно на вашия въпрос, да, това е най-ефективният начин. Но мисля, че трябва да изясним защо това е така.
Както беше предложено в алтернативите, единственото нещо, което хората гледат, е "сортиране" на резултатите ви, преди да преминат към $group
етап и това, което те гледат, е стойността на "timestamp", така че бихте искали да сте сигурни, че всичко е в ред "timestamp", така че оттук и формата:
db.temperature.aggregate([
{ "$sort": { "station": 1, "dt": -1 } },
{ "$group": {
"_id": "$station",
"result": { "$first":"$dt"}, "t": {"$first":"$t"}
}}
])
И както беше посочено, разбира се, ще искате индекс, който да отразява това, за да направите сортирането ефективно:
Въпреки това, и това е истинската точка. Това, което изглежда е било пренебрегнато от другите (ако не е така за себе си), е, че всички тези данни вероятно са вмъкнати вече във времеви ред, така че всяко отчитане се записва като добавено.
Така че красотата на това е _id
поле ( с ObjectId
по подразбиране). ) вече е в ред "timestamp", тъй като самият той всъщност съдържа времева стойност и това прави изявлението възможно:
db.temperature.aggregate([
{ "$group": {
"_id": "$station",
"result": { "$last":"$dt"}, "t": {"$last":"$t"}
}}
])
И енат по-бързо. Защо? Е, не е нужно да избирате индекс (допълнителен код за извикване), също така не е нужно да „зареждате“ индекса в допълнение към документа.
Вече знаем, че документите са в ред (от _id
) така че $last
границите са напълно валидни. Вие така или иначе сканирате всичко и бихте могли също да „разпространите“ заявка в _id
стойности като еднакво валидни за между две дати.
Единственото истинско нещо, което трябва да се каже тук, е, че при използване на "реалния свят" може да е по-практично за вас да $match
между диапазони от дати, когато правите този вид натрупване, за разлика от получаването на "първия" и "последния" _id
стойности, за да дефинирате "диапазон" или нещо подобно във вашата действителна употреба.
И така, къде е доказателството за това? Е, сравнително лесно е да се възпроизвежда, така че току-що го направих, като генерирах някои примерни данни:
var stations = [
"AL", "AK", "AZ", "AR", "CA", "CO", "CT", "DE", "FL",
"GA", "HI", "ID", "IL", "IN", "IA", "KS", "KY", "LA",
"ME", "MD", "MA", "MI", "MN", "MS", "MO", "MT", "NE",
"NV", "NH", "NJ", "NM", "NY", "NC", "ND", "OH", "OK",
"OR", "PA", "RI", "SC", "SD", "TN", "TX", "UT", "VT",
"VA", "WA", "WV", "WI", "WY"
];
for ( i=0; i<200000; i++ ) {
var station = stations[Math.floor(Math.random()*stations.length)];
var t = Math.floor(Math.random() * ( 96 - 50 + 1 )) +50;
dt = new Date();
db.temperatures.insert({
station: station,
t: t,
dt: dt
});
}
На моя хардуер (8GB лаптоп с въртящ се диск, който не е звезден, но със сигурност е достатъчен) стартирането на всяка форма на изявлението ясно показва забележима пауза с версията, използваща индекс и сортиране (същите клавиши в индекса като оператора за сортиране). Това е само малка пауза, но разликата е достатъчно значителна, за да се забележи.
Дори да погледнете изхода за обяснение (версия 2.6 и по-нова, или всъщност има в 2.4.9, макар и не документирано), можете да видите разликата в това, въпреки че $sort
се оптимизира поради наличието на индекс, времето изглежда е с избор на индекс и след това зареждане на индексираните записи. Включително всички полета за „покрити“ индексната заявка не прави разлика.
Също така за запис, чистото индексиране на датата и само сортирането на стойностите на датата дава същия резултат. Вероятно малко по-бързо, но все пак по-бавно от естествената индексна форма без сортирането.
Така че стига да можете с удоволствие да „обхватите“ на първия и последно _id
стойности, тогава е вярно, че използването на естествения индекс в реда за вмъкване всъщност е най-ефективният начин да направите това. Вашият реален пробег може да варира в зависимост от това дали това е практично за вас или не и може просто да се окаже по-удобно да приложите индекса и сортирането на датата.
Но ако сте доволни от използването на _id
диапазони или по-големи от "последния" _id
във вашата заявка, тогава може би една настройка, за да получите стойностите заедно с вашите резултати, така че всъщност да можете да съхранявате и използвате тази информация в последователни заявки:
db.temperature.aggregate([
// Get documents "greater than" the "highest" _id value found last time
{ "$match": {
"_id": { "$gt": ObjectId("536076603e70a99790b7845d") }
}},
// Do the grouping with addition of the returned field
{ "$group": {
"_id": "$station",
"result": { "$last":"$dt"},
"t": {"$last":"$t"},
"lastDoc": { "$last": "$_id" }
}}
])
И ако всъщност сте "следвали" резултатите по този начин, тогава можете да определите максималната стойност на ObjectId
от вашите резултати и го използвайте в следващата заявка.
Както и да е, забавлявайте се да играете с това, но отново Да, в този случай тази заявка е най-бързият начин.