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

Urutkan dan batas agregat Mongodb dalam grup

Masalah Dasar

Ini bukan ide yang paling bijaksana untuk mencoba dan melakukan ini dalam kerangka agregasi saat ini dalam waktu dekat. Masalah utama tentu saja berasal dari baris ini dalam kode yang sudah Anda miliki:

"items" : { "$push": "$$ROOT" }

Dan itu berarti persis bahwa, yang pada dasarnya perlu terjadi adalah bahwa semua objek di dalam kunci pengelompokan perlu didorong ke dalam larik untuk mendapatkan hasil "N teratas" dalam kode selanjutnya.

Ini jelas tidak berskala karena pada akhirnya ukuran array itu sendiri dapat melampaui batas BSON 16MB, dan terlepas dari sisa data dalam dokumen yang dikelompokkan. Tangkapan utama di sini adalah bahwa tidak mungkin untuk "membatasi dorongan" hanya pada sejumlah item tertentu. Ada masalah JIRA yang sudah lama ada tentang hal seperti itu.

Untuk alasan itu saja, pendekatan paling praktis untuk ini adalah menjalankan kueri individual untuk item "N teratas" untuk setiap kunci pengelompokan. Ini bahkan tidak perlu .aggregate() statments ( tergantung pada data ) dan benar-benar dapat berupa apa saja yang hanya membatasi nilai "N teratas" yang Anda inginkan.

Pendekatan Terbaik

Arsitektur Anda tampaknya berada di node.js dengan mongoose , tetapi apa pun yang mendukung async IO dan eksekusi paralel kueri akan menjadi opsi terbaik. Idealnya sesuatu dengan pustaka API-nya sendiri yang mendukung penggabungan hasil kueri tersebut menjadi satu respons.

Misalnya ada daftar contoh yang disederhanakan ini menggunakan arsitektur Anda dan perpustakaan yang tersedia ( terutama async ) yang melakukan hasil paralel dan gabungan ini dengan tepat:

var async = require('async'),
    mongoose = require('mongoose'),
    Schema = mongoose.Schema;

mongoose.connect('mongodb://localhost/test');

var data = [
  { "merchant": 1, "rating": 1 },
  { "merchant": 1, "rating": 2 },
  { "merchant": 1, "rating": 3 },
  { "merchant": 2, "rating": 1 },
  { "merchant": 2, "rating": 2 },
  { "merchant": 2, "rating": 3 }
];

var testSchema = new Schema({
  merchant: Number,
  rating: Number
});

var Test = mongoose.model( 'Test', testSchema, 'test' );

async.series(
  [
    function(callback) {
      Test.remove({},callback);
    },
    function(callback) {
      async.each(data,function(item,callback) {
        Test.create(item,callback);
      },callback);
    },
    function(callback) {
      async.waterfall(
        [
          function(callback) {
            Test.distinct("merchant",callback);
          },
          function(merchants,callback) {
            async.concat(
              merchants,
              function(merchant,callback) {
                Test.find({ "merchant": merchant })
                  .sort({ "rating": -1 })
                  .limit(2)
                  .exec(callback);
              },
              function(err,results) {
                console.log(JSON.stringify(results,undefined,2));
                callback(err);
              }
            );
          }
        ],
        callback
      );
    }
  ],
  function(err) {
    if (err) throw err;
    mongoose.disconnect();
  }
);

Ini hanya menghasilkan 2 hasil teratas untuk setiap pedagang di output:

[
  {
    "_id": "560d153669fab495071553ce",
    "merchant": 1,
    "rating": 3,
    "__v": 0
  },
  {
    "_id": "560d153669fab495071553cd",
    "merchant": 1,
    "rating": 2,
    "__v": 0
  },
  {
    "_id": "560d153669fab495071553d1",
    "merchant": 2,
    "rating": 3,
    "__v": 0
  },
  {
    "_id": "560d153669fab495071553d0",
    "merchant": 2,
    "rating": 2,
    "__v": 0
  }
]

Ini benar-benar cara paling efisien untuk memproses ini meskipun akan membutuhkan sumber daya karena masih banyak kueri. Namun tidak ada sumber daya yang habis di saluran agregasi jika Anda mencoba menyimpan semua dokumen dalam larik dan memprosesnya.

Masalah Agregat, sekarang dan dalam waktu dekat

Untuk itu, dimungkinkan mengingat jumlah dokumen tidak menyebabkan pelanggaran batas BSON sehingga hal ini dapat dilakukan. Metode dengan rilis MongoDB saat ini tidak bagus untuk ini, tetapi rilis yang akan datang ( pada saat penulisan, 3.1.8 cabang dev melakukan ini ) setidaknya memperkenalkan $slice operator ke pipa agregasi. Jadi, jika Anda lebih pintar tentang operasi agregasi dan menggunakan $sort pertama, maka item yang sudah diurutkan dalam array dapat dipilih dengan mudah:

var async = require('async'),
    mongoose = require('mongoose'),
    Schema = mongoose.Schema;

mongoose.connect('mongodb://localhost/test');

var data = [
  { "merchant": 1, "rating": 1 },
  { "merchant": 1, "rating": 2 },
  { "merchant": 1, "rating": 3 },
  { "merchant": 2, "rating": 1 },
  { "merchant": 2, "rating": 2 },
  { "merchant": 2, "rating": 3 }
];

var testSchema = new Schema({
  merchant: Number,
  rating: Number
});

var Test = mongoose.model( 'Test', testSchema, 'test' );

async.series(
  [
    function(callback) {
      Test.remove({},callback);
    },
    function(callback) {
      async.each(data,function(item,callback) {
        Test.create(item,callback);
      },callback);
    },
    function(callback) {
      Test.aggregate(
        [
          { "$sort": { "merchant": 1, "rating": -1 } },
          { "$group": {
            "_id": "$merchant",
            "items": { "$push": "$$ROOT" }
          }},
          { "$project": {
            "items": { "$slice": [ "$items", 2 ] }
          }}
        ],
        function(err,results) {
          console.log(JSON.stringify(results,undefined,2));
          callback(err);
        }
      );
    }
  ],
  function(err) {
    if (err) throw err;
    mongoose.disconnect();
  }
);

Yang menghasilkan hasil dasar yang sama karena 2 item teratas "diiris" dari larik setelah diurutkan terlebih dahulu.

Ini juga sebenarnya "mungkin" dalam rilis saat ini, tetapi dengan batasan dasar yang sama karena ini masih melibatkan mendorong semua konten ke dalam array setelah menyortir konten terlebih dahulu. Itu hanya membutuhkan pendekatan "berulang". Anda dapat membuat kode ini untuk menghasilkan alur agregasi untuk entri yang lebih besar, tetapi hanya menunjukkan "dua" akan menunjukkan bahwa itu bukan ide yang bagus untuk dicoba:

var async = require('async'),
    mongoose = require('mongoose'),
    Schema = mongoose.Schema;

mongoose.connect('mongodb://localhost/test');

var data = [
  { "merchant": 1, "rating": 1 },
  { "merchant": 1, "rating": 2 },
  { "merchant": 1, "rating": 3 },
  { "merchant": 2, "rating": 1 },
  { "merchant": 2, "rating": 2 },
  { "merchant": 2, "rating": 3 }
];

var testSchema = new Schema({
  merchant: Number,
  rating: Number
});

var Test = mongoose.model( 'Test', testSchema, 'test' );

async.series(
  [
    function(callback) {
      Test.remove({},callback);
    },
    function(callback) {
      async.each(data,function(item,callback) {
        Test.create(item,callback);
      },callback);
    },
    function(callback) {
      Test.aggregate(
        [
          { "$sort": { "merchant": 1, "rating": -1 } },
          { "$group": {
            "_id": "$merchant",
            "items": { "$push": "$$ROOT" }
          }},
          { "$unwind": "$items" },
          { "$group": {
            "_id": "$_id",
            "first": { "$first": "$items" },
            "items": { "$push": "$items" }
          }},
          { "$unwind": "$items" },
          { "$redact": {
            "$cond": [
              { "$eq": [ "$items", "$first" ] },
              "$$PRUNE",
              "$$KEEP"
            ]
          }},
          { "$group": {
            "_id": "$_id",
            "first": { "$first": "$first" },
            "second": { "$first": "$items" }
          }},
          { "$project": {
            "items": {
              "$map": {
                "input": ["A","B"],
                "as": "el",
                "in": {
                  "$cond": [
                    { "$eq": [ "$$el", "A" ] },
                    "$first",
                    "$second"
                  ]
                }
              }
            }
          }}
        ],
        function(err,results) {
          console.log(JSON.stringify(results,undefined,2));
          callback(err);
        }
      );
    }
  ],
  function(err) {
    if (err) throw err;
    mongoose.disconnect();
  }
);

Dan lagi sementara "mungkin" di versi sebelumnya ( ini menggunakan 2.6 fitur yang diperkenalkan untuk mempersingkat karena Anda sudah memberi tag $$ROOT ), langkah dasarnya adalah menyimpan larik dan kemudian mengeluarkan setiap item "dari tumpukan" menggunakan $first dan membandingkannya ( dan kemungkinan lainnya ) dengan item dalam larik untuk menghapusnya dan kemudian mengeluarkan item "berikutnya dulu" dari tumpukan itu hingga "N teratas" Anda akhirnya selesai.

Kesimpulan

Sampai suatu hari ada operasi yang memungkinkan item dalam $push agregasi akumulator terbatas pada hitungan tertentu, maka ini sebenarnya bukan operasi praktis untuk agregat.

Anda dapat melakukannya, jika data yang Anda miliki dalam hasil ini cukup kecil, dan bahkan mungkin lebih efisien daripada pemrosesan sisi klien jika server database memiliki spesifikasi yang cukup untuk memberikan keuntungan nyata. Namun kemungkinan besar keduanya tidak akan menjadi kasus di sebagian besar aplikasi nyata dari penggunaan yang wajar.

Taruhan terbaik adalah menggunakan opsi "kueri paralel" yang ditunjukkan terlebih dahulu. Itu selalu akan skala dengan baik, dan tidak perlu "mengkodekan" logika sedemikian rupa sehingga pengelompokan tertentu mungkin tidak mengembalikan setidaknya total item "N teratas" yang diperlukan dan mencari cara untuk mempertahankannya (contoh yang lebih lama dari yang dihilangkan ) karena hanya melakukan setiap kueri dan menggabungkan hasilnya.

Gunakan kueri paralel. Ini akan menjadi lebih baik daripada pendekatan kode yang Anda miliki, dan itu akan mengungguli pendekatan agregasi yang ditunjukkan dengan cara yang jauh. Sampai ada pilihan yang lebih baik setidaknya.



  1. Redis
  2.   
  3. MongoDB
  4.   
  5. Memcached
  6.   
  7. HBase
  8.   
  9. CouchDB
  1. Indeks parsial di mongodb / luwak

  2. MongoDB:Cara menemukan dokumen dengan id di dalam dokumen bersarang

  3. Tumbuh Signifikansi MongoDB di Bidang Ilmu Data

  4. gangguan mongoDB pada array

  5. MongoDB perkiraanDocumentCount()