MongoDB
 sql >> Baza danych >  >> NoSQL >> MongoDB

MongoDB :Struktura agregacji :Pobierz ostatni datowany dokument według identyfikatora grupowania

Aby bezpośrednio odpowiedzieć na twoje pytanie, tak, jest to najskuteczniejszy sposób. Ale myślę, że musimy wyjaśnić, dlaczego tak jest.

Jak sugerowano w alternatywach, jedyną rzeczą, na którą ludzie patrzą, jest „sortowanie” wyników przed przejściem do $group stage i to, na co patrzą, to wartość „timestamp”, więc chciałbyś się upewnić, że wszystko jest w kolejności „timestamp”, stąd forma:

db.temperature.aggregate([
    { "$sort": { "station": 1, "dt": -1 } },
    { "$group": {
        "_id": "$station", 
        "result": { "$first":"$dt"}, "t": {"$first":"$t"} 
    }}
])

Jak już wspomniano, będziesz oczywiście chciał, aby indeks to odzwierciedlał, aby sortowanie było efektywne:

Jednak i to jest prawdziwy punkt. To, co wydaje się być pomijane przez innych (jeśli nie dla Ciebie), to fakt, że wszystkie te dane są prawdopodobnie wstawiane już w kolejności czasowej, w której każdy odczyt jest rejestrowany jako dodany.

Piękno tego polega na _id pole ( z domyślnym ObjectId ) jest już w kolejności "timestamp", ponieważ sama w sobie zawiera wartość czasu, co umożliwia stwierdzenie:

db.temperature.aggregate([
    { "$group": {
        "_id": "$station", 
        "result": { "$last":"$dt"}, "t": {"$last":"$t"} 
    }}
])

I to jest szybciej. Czemu? Cóż, nie musisz wybierać indeksu (dodatkowy kod do wywołania), nie musisz również „ładować” indeksu oprócz dokumentu.

Wiemy już, że dokumenty są w porządku ( przez _id ), więc $last granice są całkowicie aktualne. Mimo wszystko skanujesz wszystko, a także możesz zapytać o „zakres” w _id wartości są jednakowo ważne dla dwóch dat.

Jedyną prawdziwą rzeczą do powiedzenia jest to, że w użyciu „w prawdziwym świecie” może być po prostu bardziej praktyczne, aby $match między zakresami dat podczas tego rodzaju akumulacji, w przeciwieństwie do pobierania „pierwszego” i „ostatniego” _id wartości, aby zdefiniować „zakres” lub coś podobnego w twoim rzeczywistym użyciu.

Więc gdzie jest na to dowód? Cóż, jest to dość łatwe do odtworzenia, więc zrobiłem to, generując przykładowe dane:

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
    });

}

Na moim sprzęcie (laptop 8 GB z dyskiem obrotowym, co nie jest wybitne, ale z pewnością wystarczające) uruchomienie każdej formy instrukcji wyraźnie pokazuje wyraźną przerwę z wersją za pomocą indeksu i sortowania (te same klawisze w indeksie, co instrukcja sort). To tylko drobna pauza, ale różnica jest na tyle znacząca, by ją zauważyć.

Nawet patrząc na dane wyjściowe wyjaśniania (wersja 2.6 i nowsze, lub faktycznie jest tam w wersji 2.4.9, chociaż nie jest to udokumentowane), można zauważyć w tym różnicę, chociaż $sort jest zoptymalizowany ze względu na obecność indeksu, wydaje się, że czas poświęcony na wybór indeksu, a następnie ładowanie zindeksowanych wpisów. Uwzględnienie wszystkich pól dla "pokrytego" zapytanie indeksowe nie ma znaczenia.

Również w przypadku rekordu samo indeksowanie daty i sortowanie tylko według wartości dat daje ten sam wynik. Prawdopodobnie nieco szybszy, ale wciąż wolniejszy niż naturalny indeks bez sortowania.

Tak długo, jak możesz szczęśliwie „zasięgnąć” pierwszego i ostatni _id wartości, to prawdą jest, że użycie naturalnego indeksu w zamówieniu reklamowym jest w rzeczywistości najskuteczniejszym sposobem, aby to zrobić. Twój przebieg w świecie rzeczywistym może się różnić w zależności od tego, czy jest to dla Ciebie praktyczne, czy nie, i może po prostu wygodniej zaimplementować indeks i sortować według daty.

Ale jeśli byłeś zadowolony z używania _id zakresy lub większe niż „ostatni” _id w zapytaniu, a następnie być może jedno ulepszenie, aby uzyskać wartości wraz z wynikami, dzięki czemu można faktycznie przechowywać i wykorzystywać te informacje w kolejnych zapytaniach:

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" } 
    }}
])

A jeśli faktycznie „śledziłeś” takie wyniki, możesz określić maksymalną wartość ObjectId z wyników i użyj go w następnym zapytaniu.

W każdym razie baw się dobrze, ale znowu tak, w tym przypadku to zapytanie jest najszybszym sposobem.



  1. Redis
  2.   
  3. MongoDB
  4.   
  5. Memcached
  6.   
  7. HBase
  8.   
  9. CouchDB
  1. Błąd krytyczny — nie znaleziono klasy „Mongo”

  2. Zapytania C# mongo z ciągami json

  3. Sortowanie według wirtualnego pola w mongoDB (mongoose)

  4. Jak skonfigurować AppArmor dla zestawów replik MongoDB?

  5. Obsługa wielu typów użytkowników przez Passport-local mongoose node.js