MongoDB
 sql >> Teknologi Basis Data >  >> NoSQL >> MongoDB

MongoDB :Kerangka kerja agregasi :Dapatkan dokumen bertanggal terakhir per ID pengelompokan

Untuk langsung menjawab pertanyaan Anda, ya itu cara yang paling efisien. Tapi saya pikir kita perlu mengklarifikasi mengapa demikian.

Seperti yang disarankan dalam alternatif, satu hal yang dilihat orang adalah "mengurutkan" hasil Anda sebelum meneruskan ke $group panggung dan apa yang mereka lihat adalah nilai "stempel waktu", jadi Anda ingin memastikan bahwa semuanya dalam urutan "cap waktu", jadi bentuknya:

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

Dan seperti yang dinyatakan, Anda tentu ingin indeks mencerminkan hal itu untuk membuat pengurutan menjadi efisien:

Namun, dan inilah poin sebenarnya. Apa yang tampaknya telah diabaikan oleh orang lain ( jika tidak demikian untuk Anda sendiri ) adalah bahwa semua data ini kemungkinan telah dimasukkan sudah dalam urutan waktu, di mana setiap bacaan dicatat sebagai ditambahkan.

Jadi keindahan ini adalah _id bidang ( dengan default ObjectId ) sudah dalam urutan "stempel waktu", karena ia sendiri sebenarnya berisi nilai waktu dan ini memungkinkan pernyataan:

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

Dan itu adalah lebih cepat. Mengapa? Anda tidak perlu memilih indeks (kode tambahan untuk dipanggil), Anda juga tidak perlu "memuat" indeks selain dokumen.

Kita sudah tahu bahwa dokumen-dokumennya sudah rapi ( by _id ) jadi $last batas benar-benar valid. Anda tetap memindai semuanya, dan Anda juga dapat "menjangkau" kueri di _id nilai yang sama-sama valid untuk antara dua tanggal.

Satu-satunya hal yang nyata untuk dikatakan di sini, adalah bahwa dalam penggunaan "dunia nyata", mungkin lebih praktis bagi Anda untuk $match antara rentang tanggal saat melakukan akumulasi semacam ini sebagai lawan untuk mendapatkan _id "pertama" dan "terakhir" nilai untuk mendefinisikan "rentang" atau sesuatu yang serupa dalam penggunaan Anda yang sebenarnya.

Jadi mana buktinya ini? Reproduksinya cukup mudah, jadi saya melakukannya dengan membuat beberapa contoh 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
    });

}

Pada perangkat keras saya (laptop 8GB dengan disk berputar, yang tidak luar biasa, tetapi tentu saja memadai) menjalankan setiap bentuk pernyataan dengan jelas menunjukkan jeda yang mencolok dengan versi yang menggunakan indeks dan pengurutan (kunci yang sama pada indeks sebagai pernyataan pengurutan). Ini hanya jeda kecil, tetapi perbedaannya cukup signifikan untuk diperhatikan.

Bahkan dengan melihat output yang dijelaskan ( versi 2.6 dan lebih tinggi, atau sebenarnya ada di 2.4.9 meskipun tidak didokumentasikan ), Anda dapat melihat perbedaannya, meskipun $sort dioptimalkan karena adanya indeks, waktu yang dibutuhkan tampaknya dengan pemilihan indeks dan kemudian memuat entri yang diindeks. Menyertakan semua bidang untuk "tercakup" kueri indeks tidak ada bedanya.

Juga sebagai catatan, pengindeksan tanggal murni dan hanya pengurutan pada nilai tanggal memberikan hasil yang sama. Mungkin sedikit lebih cepat, tetapi masih lebih lambat daripada bentuk indeks alami tanpa pengurutan.

Jadi selama Anda bisa dengan senang hati "berjarak" di pertama dan terakhir _id nilai, maka memang benar bahwa menggunakan indeks alami pada urutan penyisipan sebenarnya adalah cara yang paling efisien untuk melakukan ini. Jarak tempuh dunia nyata Anda dapat bervariasi tergantung pada apakah ini praktis untuk Anda atau tidak dan mungkin akan lebih mudah untuk menerapkan indeks dan pengurutan pada tanggal.

Tetapi jika Anda senang menggunakan _id rentang atau lebih besar dari _id "terakhir" dalam kueri Anda, lalu mungkin satu tweak untuk mendapatkan nilai bersama dengan hasil Anda sehingga Anda sebenarnya dapat menyimpan dan menggunakan informasi itu dalam kueri yang berurutan:

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

Dan jika Anda benar-benar "mengikuti" hasil seperti itu maka Anda dapat menentukan nilai maksimum ObjectId dari hasil Anda dan gunakan dalam kueri berikutnya.

Bagaimanapun, bersenang-senanglah bermain dengan itu, tetapi sekali lagi Ya, dalam hal ini kueri itu adalah cara tercepat.



  1. Redis
  2.   
  3. MongoDB
  4.   
  5. Memcached
  6.   
  7. HBase
  8.   
  9. CouchDB
  1. Cara Menginstal MongoDB 4.2 pada sistem RedHat/ CentOS 7

  2. Grup Pengemudi MongoDB .NET Berdasarkan Rentang Waktu

  3. Praktik terbaik untuk mempertahankan sesi mgo

  4. Temukan luwak () tidak mengembalikan hasil

  5. Bagaimana cara mengecualikan satu bidang tertentu dari koleksi di Mongoose?