sql >> Databáze >  >> NoSQL >> MongoDB

MongoDB:Agregační rámec:Získejte poslední datovaný dokument podle ID seskupení

Chcete-li přímo odpovědět na vaši otázku, ano, je to nejúčinnější způsob. Ale myslím, že musíme objasnit, proč tomu tak je.

Jak bylo navrženo v alternativách, jedna věc, na kterou se lidé dívají, je „třídění“ vašich výsledků před přechodem do $group fázi a to, na co se dívají, je hodnota „timestamp“, takže byste se chtěli ujistit, že je vše v pořadí „timestamp“, takže tedy tvar:

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

A jak bylo uvedeno, budete samozřejmě chtít, aby to odrážel index, aby bylo řazení efektivní:

Nicméně, a to je skutečný bod. Zdá se, že ostatní přehlédli (pokud ne vy sami), že všechna tato data jsou pravděpodobně již vložena v časovém pořadí, takže každé čtení je zaznamenáno jako přidané.

Takže krása tohoto je _id pole ( s výchozím ObjectId ) je již v pořadí „časové razítko“, protože sám o sobě ve skutečnosti obsahuje časovou hodnotu, což umožňuje prohlášení:

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

A to je rychlejší. Proč? Nemusíte vybírat index (dodatečný kód k vyvolání), nemusíte také "načítat" index navíc k dokumentu.

Již víme, že dokumenty jsou v pořádku (podle _id ), tedy $last hranice jsou naprosto platné. Stejně skenujete všechno a můžete se také dotazovat na _id hodnoty stejně platné pro období mezi dvěma daty.

Jediná skutečná věc, kterou je zde třeba říci, je, že při použití v „reálném světě“ pro vás může být praktičtější $match mezi rozsahy dat při provádění tohoto druhu akumulace na rozdíl od získání „prvního“ a „posledního“ _id hodnoty k definování "rozsahu" nebo něčeho podobného ve vašem skutečném použití.

Kde je tedy důkaz? Reprodukovat je poměrně snadné, takže jsem to udělal tak, že jsem vygeneroval nějaká ukázková data:

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 mém hardwaru (8GB notebook s rotujícím diskem, který není hvězdný, ale rozhodně adekvátní) běh každé formy příkazu jasně ukazuje výraznou pauzu s verzí pomocí indexu a řazení (stejné klíče na indexu jako příkaz sort). Je to jen malá pauza, ale rozdíl je dostatečně významný, abyste si jej všimli.

I když se podíváte na výstup vysvětlení (verze 2.6 a vyšší, nebo ve skutečnosti existuje ve 2.4.9, i když to není zdokumentováno), můžete v tom vidět rozdíl, ačkoli $sort je optimalizována díky přítomnosti indexu, zdá se, že čas zabere výběr indexu a následné načtení indexovaných položek. Včetně všech polí pro "pokryté" indexový dotaz nemá žádný rozdíl.

Také pro záznam, čistě indexování data a pouze řazení podle hodnot data dává stejný výsledek. Možná o něco rychlejší, ale stále pomalejší než forma přirozeného indexu bez řazení.

Tedy pokud můžete vesele „dostřelit“ na první a poslední _id hodnoty, pak je pravda, že použití přirozeného indexu na objednávce vložení je ve skutečnosti nejúčinnějším způsobem, jak toho dosáhnout. Váš skutečný počet ujetých kilometrů se může lišit podle toho, zda je to pro vás praktické nebo ne, a může být nakonec pohodlnější implementovat index a řazení podle data.

Ale pokud jste byli spokojeni s použitím _id rozsahy nebo větší než "poslední" _id ve vašem dotazu, pak možná jedno vylepšení, abyste získali hodnoty spolu s vašimi výsledky, abyste mohli tyto informace ve skutečnosti ukládat a používat v následných dotazech:

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 pokud jste skutečně „sledovali“ takové výsledky, můžete určit maximální hodnotu ObjectId z vašich výsledků a použijte jej v dalším dotazu.

Každopádně si s tím užijte zábavu, ale opět ano, v tomto případě je tento dotaz nejrychlejší.



  1. Existuje způsob, jak zabránit MongoDB v přidávání množného čísla k názvům kolekcí?

  2. Vytvořte sbírku v MongoDB

  3. Jak spravovat dokumenty v MongoDB

  4. Optimalizovaný způsob dotazování v MongoDB pomocí $in vs $or