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

Agregasi MongoDB - $grup menurut tanggal meskipun tidak ada

Daripada mencoba memaksa database untuk mengembalikan hasil untuk data yang tidak ada, praktik yang lebih baik adalah menghasilkan data kosong di luar kueri dan menggabungkan hasil ke dalamnya. Dengan cara itu Anda memiliki entri "0" di mana tidak ada data dan memungkinkan database mengembalikan apa yang ada di sana.

Penggabungan adalah proses dasar untuk membuat tabel hash dari kunci unik dan hanya mengganti salah satu nilai yang ditemukan dalam hasil agregasi dalam tabel hash tersebut. Dalam JavaScript, objek dasar cocok dengan semua kunci yang unik.

Saya juga lebih suka untuk benar-benar mengembalikan Date objek dari hasil agregasi dengan menggunakan matematika tanggal untuk memanipulasi dan "membulatkan" tanggal ke interval yang diperlukan daripada menggunakan operator agregasi tanggal. Anda dapat memanipulasi tanggal dengan menggunakan $subtract untuk mengubah nilai menjadi representasi stempel waktu numerik dengan mengurangi tanggal lain dengan nilai tanggal Epoch, dan $mod operator untuk mendapatkan sisa dan membulatkan tanggal ke interval yang diperlukan.

Sebaliknya menggunakan $add dengan objek tanggal Epoch yang serupa akan mengubah nilai integer kembali menjadi Tanggal BSON. Dan tentu saja jauh lebih efisien untuk memproses langsung ke $group daripada menggunakan $project yang terpisah tahap karena Anda dapat memproses tanggal yang dimodifikasi langsung ke dalam pengelompokan _id tetap bernilai.

Sebagai contoh shell:

var sample = 30,
    Days = 30,
    OneDay = ( 1000 * 60 * 60 * 24 ),
    now = Date.now(),
    Today = now - ( now % OneDay ) ,
    nDaysAgo = Today - ( OneDay * Days ),
    startDate = new Date( nDaysAgo ),
    endDate = new Date( Today + OneDay ),
    store = {};

var thisDay = new Date( nDaysAgo );
while ( thisDay < endDate ) {
    store[thisDay] = 0;
    thisDay = new Date( thisDay.valueOf() + OneDay );
}

db.datejunk.aggregate([
    { "$match": { "when": { "$gte": startDate } }},
    { "$group": {
        "_id": {
            "$add": [
                { "$subtract": [
                    { "$subtract": [ "$when", new Date(0) ] },
                    { "$mod": [
                        { "$subtract": [ "$when", new Date(0) ] },
                        OneDay
                    ]}
                ]},
                new Date(0)
            ]
        },
        "count": { "$sum": 1 }
    }}
]).forEach(function(result){
    store[result._id] = result.count;
});

Object.keys(store).forEach(function(k) {
    printjson({ "date": k, "count": store[k] })
});

Yang akan mengembalikan semua hari dalam interval termasuk 0 nilai di mana tidak ada data, seperti:

{ "date" : "Tue Sep 22 2015 10:00:00 GMT+1000 (AEST)", "count" : 0 }
{ "date" : "Wed Sep 23 2015 10:00:00 GMT+1000 (AEST)", "count" : 1 }
{ "date" : "Thu Sep 24 2015 10:00:00 GMT+1000 (AEST)", "count" : 0 }
{ "date" : "Fri Sep 25 2015 10:00:00 GMT+1000 (AEST)", "count" : 1 }
{ "date" : "Sat Sep 26 2015 10:00:00 GMT+1000 (AEST)", "count" : 1 }
{ "date" : "Sun Sep 27 2015 10:00:00 GMT+1000 (AEST)", "count" : 0 }
{ "date" : "Mon Sep 28 2015 10:00:00 GMT+1000 (AEST)", "count" : 1 }
{ "date" : "Tue Sep 29 2015 10:00:00 GMT+1000 (AEST)", "count" : 1 }
{ "date" : "Wed Sep 30 2015 10:00:00 GMT+1000 (AEST)", "count" : 0 }
{ "date" : "Thu Oct 01 2015 10:00:00 GMT+1000 (AEST)", "count" : 1 }
{ "date" : "Fri Oct 02 2015 10:00:00 GMT+1000 (AEST)", "count" : 2 }
{ "date" : "Sat Oct 03 2015 10:00:00 GMT+1000 (AEST)", "count" : 0 }
{ "date" : "Sun Oct 04 2015 11:00:00 GMT+1100 (AEST)", "count" : 1 }
{ "date" : "Mon Oct 05 2015 11:00:00 GMT+1100 (AEDT)", "count" : 0 }
{ "date" : "Tue Oct 06 2015 11:00:00 GMT+1100 (AEDT)", "count" : 1 }
{ "date" : "Wed Oct 07 2015 11:00:00 GMT+1100 (AEDT)", "count" : 2 }
{ "date" : "Thu Oct 08 2015 11:00:00 GMT+1100 (AEDT)", "count" : 2 }
{ "date" : "Fri Oct 09 2015 11:00:00 GMT+1100 (AEDT)", "count" : 1 }
{ "date" : "Sat Oct 10 2015 11:00:00 GMT+1100 (AEDT)", "count" : 1 }
{ "date" : "Sun Oct 11 2015 11:00:00 GMT+1100 (AEDT)", "count" : 1 }
{ "date" : "Mon Oct 12 2015 11:00:00 GMT+1100 (AEDT)", "count" : 0 }
{ "date" : "Tue Oct 13 2015 11:00:00 GMT+1100 (AEDT)", "count" : 3 }
{ "date" : "Wed Oct 14 2015 11:00:00 GMT+1100 (AEDT)", "count" : 2 }
{ "date" : "Thu Oct 15 2015 11:00:00 GMT+1100 (AEDT)", "count" : 2 }
{ "date" : "Fri Oct 16 2015 11:00:00 GMT+1100 (AEDT)", "count" : 0 }
{ "date" : "Sat Oct 17 2015 11:00:00 GMT+1100 (AEDT)", "count" : 3 }
{ "date" : "Sun Oct 18 2015 11:00:00 GMT+1100 (AEDT)", "count" : 0 }
{ "date" : "Mon Oct 19 2015 11:00:00 GMT+1100 (AEDT)", "count" : 0 }
{ "date" : "Tue Oct 20 2015 11:00:00 GMT+1100 (AEDT)", "count" : 0 }
{ "date" : "Wed Oct 21 2015 11:00:00 GMT+1100 (AEDT)", "count" : 2 }
{ "date" : "Thu Oct 22 2015 11:00:00 GMT+1100 (AEDT)", "count" : 1 }

Memperhatikan bahwa semua nilai "tanggal" sebenarnya masih tanggal BSON, tetapi hanya merangkai seperti itu di ouput dari .printjson() sebagai metode shell.

Contoh yang lebih ringkas dapat ditampilkan menggunakan nodejs tempat Anda dapat menggunakan operasi seperti async.parallel untuk memproses konstruksi hash dan kueri agregasi secara bersamaan, serta utilitas berguna lainnya di nedb yang mengimplementasikan "hash" menggunakan fungsi yang familiar untuk menggunakan koleksi MongoDB. Ini juga menunjukkan bagaimana ini dapat menskalakan untuk hasil yang besar dengan menggunakan koleksi MongoDB nyata jika Anda juga mengubah penanganan ke pemrosesan streaming kursor yang dikembalikan dari .aggregate() :

var async = require('async'),
    mongodb = require('mongodb'),
    MongoClient = mongodb.MongoClient,
    nedb = require('nedb'),
    DataStore = new nedb();

// Setup vars
var sample = 30,
    Days = 30,
    OneDay = ( 1000 * 60 * 60 * 24 ),
    now = Date.now(),
    Today = now - ( now % OneDay ) ,
    nDaysAgo = Today - ( OneDay * Days ),
    startDate = new Date( nDaysAgo ),
    endDate = new Date( Today + OneDay );

MongoClient.connect('mongodb://localhost/test',function(err,db) {

  var coll = db.collection('datejunk');

  async.series(
    [
      // Clear test collection
      function(callback) {
        coll.remove({},callback)
      },

      // Generate a random sample
      function(callback) {
        var bulk = coll.initializeUnorderedBulkOp();

        while (sample--) {
          bulk.insert({
            "when": new Date(
              Math.floor(
                Math.random()*(Today-nDaysAgo+OneDay)+nDaysAgo
              )
            )
          });
        }
        bulk.execute(callback);
      },

      // Aggregate data and dummy data
      function(callback) {
        console.log("generated");
        async.parallel(
          [
            // Dummy data per day
            function(callback) {
              var thisDay = new Date( nDaysAgo );
              async.whilst(
                function() { return thisDay < endDate },
                function(callback) {
                  DataStore.update(
                    { "date": thisDay },
                    { "$inc": { "count": 0 } },
                    { "upsert": true },
                    function(err) {
                      thisDay = new Date( thisDay.valueOf() + OneDay );
                      callback(err);
                    }
                  );
                },
                callback
              );
            },
            // Aggregate data in collection
            function(callback) {
              coll.aggregate(
                [
                  { "$match": { "when": { "$gte": startDate } } },
                  { "$group": {
                    "_id": {
                      "$add": [
                        { "$subtract": [
                          { "$subtract": [ "$when", new Date(0) ] },
                          { "$mod": [
                            { "$subtract": [ "$when", new Date(0) ] },
                            OneDay
                          ]}
                        ]},
                        new Date(0)
                      ]
                    },
                    "count": { "$sum": 1 }
                  }}
                ],
                function(err,results) {
                  if (err) callback(err);
                  async.each(results,function(result,callback) {
                    DataStore.update(
                      { "date": result._id },
                      { "$inc": { "count": result.count } },
                      { "upsert": true },
                      callback
                    );
                  },callback);
                }
              );
            }
          ],
          callback
        );
      }
    ],
    // Return result or error
    function(err) {
      if (err) throw err;
      DataStore.find({},{ "_id": 0 })
        .sort({ "date": 1 })
        .exec(function(err,results) {
        if (err) throw err;
        console.log(results);
        db.close();
      });
    }
  );

});

Ini sangat cocok untuk data bagan dan grafik. Prosedur dasarnya sama untuk implementasi bahasa apa pun, dan idealnya dilakukan dalam pemrosesan paralel untuk kinerja terbaik, jadi lingkungan asinkron atau berulir memberi Anda bonus nyata meskipun untuk sampel kecil seperti ini tabel hash dasar dapat dibuat di memori dengan sangat cepat lingkungan Anda memerlukan operasi berurutan.

Jadi jangan mencoba dan memaksa database untuk melakukan ini. Tentu saja ada contoh kueri SQL yang melakukan "penggabungan" ini di server basis data, tetapi itu tidak pernah benar-benar ide yang bagus di sana dan harus benar-benar ditangani dengan proses penggabungan "klien" yang serupa karena hanya membuat overhead basis data yang sebenarnya tidak ' t diperlukan.

Semuanya sangat efisien dan praktis untuk tujuan tersebut, dan tentu saja tidak memerlukan pemrosesan kueri agregasi terpisah untuk setiap hari dalam periode tersebut, yang tidak akan efisien sama sekali.




  1. Redis
  2.   
  3. MongoDB
  4.   
  5. Memcached
  6.   
  7. HBase
  8.   
  9. CouchDB
  1. Efisiensi saat memasukkan ke mongodb (pymongo)

  2. Bagaimana mengubah bidang kolom objek dtype tertentu menjadi kolom bingkai data di panda

  3. Mengapa memulai instans MongoDB solo sebagai set replika tidak disarankan dalam produksi?

  4. Kelas 'Jenssegers\Mongodb\MongodbServiceProvider' tidak ditemukan

  5. Ambil hanya elemen yang ditanyakan dalam array objek di koleksi MongoDB