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

Kelompokkan Nilai dan Hitungan Berbeda untuk Setiap Properti dalam Satu Kueri

Ada pendekatan yang berbeda tergantung pada versi yang tersedia, tetapi semuanya pada dasarnya memecah untuk mengubah bidang dokumen Anda menjadi dokumen terpisah dalam "array", lalu "melepas" array itu dengan $unwind dan melakukan $group berturut-turut tahapan untuk mengakumulasi total output dan larik.

MongoDB 3.4.4 dan yang lebih baru

Rilis terbaru memiliki operator khusus seperti $arrayToObject dan $objectToArray yang dapat membuat transfer ke "array" awal dari dokumen sumber lebih dinamis daripada rilis sebelumnya:

db.profile.aggregate([
  { "$project": { 
     "_id": 0,
     "data": { 
       "$filter": {
         "input": { "$objectToArray": "$$ROOT" },
         "cond": { "$in": [ "$$this.k", ["gender","caste","education"] ] }
       }   
     }
  }},
  { "$unwind": "$data" },
  { "$group": {
    "_id": "$data",
    "total": { "$sum": 1 }  
  }},
  { "$group": {
    "_id": "$_id.k",
    "v": {
      "$push": { "name": "$_id.v", "total": "$total" } 
    }  
  }},
  { "$group": {
    "_id": null,
    "data": { "$push": { "k": "$_id", "v": "$v" } }
  }},
  { "$replaceRoot": {
    "newRoot": {
      "$arrayToObject": "$data"
    }
  }}
])

Jadi gunakan $objectToArray Anda membuat dokumen awal menjadi larik dari kunci dan nilainya sebagai "k" dan "v" kunci dalam array objek yang dihasilkan. Kami menerapkan $filter di sini untuk memilih dengan "kunci". Di sini menggunakan $in dengan daftar kunci yang kita inginkan, tapi ini bisa lebih dinamis digunakan sebagai daftar kunci untuk "mengecualikan" di mana yang lebih pendek. Ini hanya menggunakan operator logika untuk mengevaluasi kondisinya.

Tahap akhir di sini menggunakan $replaceRoot dan karena semua manipulasi dan "pengelompokan" kami di antaranya masih menyimpan "k" dan "v" formulir, kami kemudian menggunakan $arrayToObject di sini untuk mempromosikan "array objek" kami sebagai "kunci" dari dokumen tingkat atas dalam output.

MongoDB 3.6 $mergeObjects

Sebagai tambahan kerutan di sini, MongoDB 3.6 menyertakan $mergeObjects yang dapat digunakan sebagai "akumulator " dalam $group tahap pipeline juga, sehingga menggantikan $push dan membuat $replaceRoot final cukup dengan menggeser "data" kunci ke "root" dari dokumen yang dikembalikan sebagai gantinya:

db.profile.aggregate([
  { "$project": { 
     "_id": 0,
     "data": { 
       "$filter": {
         "input": { "$objectToArray": "$$ROOT" },
         "cond": { "$in": [ "$$this.k", ["gender","caste","education"] ] }
       }   
     }
  }},
  { "$unwind": "$data" },
  { "$group": { "_id": "$data", "total": { "$sum": 1 } }},
  { "$group": {
    "_id": "$_id.k",
    "v": {
      "$push": { "name": "$_id.v", "total": "$total" } 
    }  
  }},
  { "$group": {
    "_id": null,
    "data": {
      "$mergeObjects": {
        "$arrayToObject": [
          [{ "k": "$_id", "v": "$v" }]
        ] 
      }
    }  
  }},
  { "$replaceRoot": { "newRoot": "$data"  } }
])

Ini tidak terlalu berbeda dengan apa yang ditunjukkan secara keseluruhan, tetapi hanya menunjukkan bagaimana $mergeObjects dapat digunakan dengan cara ini dan mungkin berguna dalam kasus di mana kunci pengelompokan adalah sesuatu yang berbeda dan kami tidak ingin "penggabungan" terakhir itu ke ruang akar objek.

Perhatikan bahwa $arrayToObject masih diperlukan untuk mengubah "nilai" kembali menjadi nama "kunci", tetapi kami hanya melakukannya selama akumulasi daripada setelah pengelompokan, karena akumulasi baru memungkinkan "penggabungan" kunci.

MongoDB 3.2

Mengambil kembali versi atau bahkan jika Anda memiliki MongoDB 3.4.x yang kurang dari rilis 3.4.4, kami masih dapat menggunakan sebagian besar dari ini tetapi sebaliknya kami menangani pembuatan array dengan cara yang lebih statis, juga karena menangani "transformasi" akhir pada output secara berbeda karena operator agregasi yang tidak kita miliki:

db.profile.aggregate([
  { "$project": {
    "data": [
      { "k": "gender", "v": "$gender" },
      { "k": "caste", "v": "$caste" },
      { "k": "education", "v": "$education" }
    ]
  }},
  { "$unwind": "$data" },
  { "$group": {
    "_id": "$data",
    "total": { "$sum": 1 }  
  }},
  { "$group": {
    "_id": "$_id.k",
    "v": {
      "$push": { "name": "$_id.v", "total": "$total" } 
    }  
  }},
  { "$group": {
    "_id": null,
    "data": { "$push": { "k": "$_id", "v": "$v" } }
  }},
  /*
  { "$replaceRoot": {
    "newRoot": {
      "$arrayToObject": "$data"
    }
  }}
  */
]).map( d => 
  d.data.map( e => ({ [e.k]: e.v }) )
    .reduce((acc,curr) => Object.assign(acc,curr),{})
)

Ini adalah hal yang persis sama, kecuali alih-alih memiliki transformasi dinamis dari dokumen ke dalam array, kami sebenarnya "secara eksplisit" menetapkan setiap anggota array dengan "k" yang sama dan "v" notasi. Benar-benar hanya menyimpan nama-nama kunci untuk konvensi pada saat ini karena tidak ada operator agregasi di sini yang bergantung pada itu sama sekali.

Juga daripada menggunakan $replaceRoot , kami hanya melakukan hal yang persis sama seperti yang dilakukan oleh implementasi tahap pipeline sebelumnya di sana tetapi dalam kode klien sebagai gantinya. Semua driver MongoDB memiliki beberapa implementasi cursor.map() untuk mengaktifkan "transformasi kursor". Di sini dengan shell kami menggunakan fungsi JavaScript dasar Array.map() dan Array.reduce() untuk mengambil output itu dan sekali lagi mempromosikan konten array menjadi kunci dari dokumen tingkat atas yang dikembalikan.

MongoDB 2.6

Dan kembali ke MongoDB 2.6 untuk menutupi versi di antaranya, satu-satunya hal yang berubah di sini adalah penggunaan $map dan $literal untuk input dengan deklarasi array:

db.profile.aggregate([
  { "$project": {
    "data": {
      "$map": {
        "input": { "$literal": ["gender","caste", "education"] },
        "as": "k",
        "in": {
          "k": "$$k",
          "v": {
            "$cond": {
              "if": { "$eq": [ "$$k", "gender" ] },
              "then": "$gender",
              "else": {
                "$cond": {
                  "if": { "$eq": [ "$$k", "caste" ] },
                  "then": "$caste",
                  "else": "$education"
                }
              }    
            }
          }    
        }
      }
    }
  }},
  { "$unwind": "$data" },
  { "$group": {
    "_id": "$data",
    "total": { "$sum": 1 }  
  }},
  { "$group": {
    "_id": "$_id.k",
    "v": {
      "$push": { "name": "$_id.v", "total": "$total" } 
    }  
  }},
  { "$group": {
    "_id": null,
    "data": { "$push": { "k": "$_id", "v": "$v" } }
  }},
  /*
  { "$replaceRoot": {
    "newRoot": {
      "$arrayToObject": "$data"
    }
  }}
  */
])
.map( d => 
  d.data.map( e => ({ [e.k]: e.v }) )
    .reduce((acc,curr) => Object.assign(acc,curr),{})
)

Karena ide dasarnya di sini adalah untuk "mengulangi" array yang disediakan dari nama bidang, penetapan nilai yang sebenarnya datang dengan "menyarangkan" $cond pernyataan. Untuk tiga kemungkinan hasil, ini berarti hanya satu sarang untuk "bercabang" untuk setiap hasil.

MongoDB modern dari 3.4 memiliki $switch yang membuat percabangan ini lebih sederhana, namun ini menunjukkan bahwa logika selalu memungkinkan dan $cond operator telah ada sejak kerangka kerja agregasi diperkenalkan di MongoDB 2.2.

Sekali lagi, transformasi yang sama pada hasil kursor berlaku karena tidak ada yang baru di sana dan sebagian besar bahasa pemrograman memiliki kemampuan untuk melakukan ini selama bertahun-tahun, jika tidak sejak awal.

Tentu saja proses dasar bahkan dapat dilakukan kembali ke MongoDB 2.2, tetapi hanya menerapkan pembuatan array dan $unwind dengan cara yang berbeda. Tetapi tidak seorang pun boleh menjalankan MongoDB di bawah 2.8 pada saat ini, dan dukungan resmi bahkan dari 3.0 bahkan cepat habis.

Keluaran

Untuk visualisasi, output dari semua pipeline yang didemonstrasikan di sini memiliki bentuk berikut sebelum "transform" terakhir dilakukan:

/* 1 */
{
    "_id" : null,
    "data" : [ 
        {
            "k" : "gender",
            "v" : [ 
                {
                    "name" : "Male",
                    "total" : 3.0
                }, 
                {
                    "name" : "Female",
                    "total" : 2.0
                }
            ]
        }, 
        {
            "k" : "education",
            "v" : [ 
                {
                    "name" : "M.C.A",
                    "total" : 1.0
                }, 
                {
                    "name" : "B.E",
                    "total" : 3.0
                }, 
                {
                    "name" : "B.Com",
                    "total" : 1.0
                }
            ]
        }, 
        {
            "k" : "caste",
            "v" : [ 
                {
                    "name" : "Lingayath",
                    "total" : 3.0
                }, 
                {
                    "name" : "Vokkaliga",
                    "total" : 2.0
                }
            ]
        }
    ]
}

Dan kemudian dengan $replaceRoot atau kursor berubah seperti yang ditunjukkan hasilnya menjadi:

/* 1 */
{
    "gender" : [ 
        {
            "name" : "Male",
            "total" : 3.0
        }, 
        {
            "name" : "Female",
            "total" : 2.0
        }
    ],
    "education" : [ 
        {
            "name" : "M.C.A",
            "total" : 1.0
        }, 
        {
            "name" : "B.E",
            "total" : 3.0
        }, 
        {
            "name" : "B.Com",
            "total" : 1.0
        }
    ],
    "caste" : [ 
        {
            "name" : "Lingayath",
            "total" : 3.0
        }, 
        {
            "name" : "Vokkaliga",
            "total" : 2.0
        }
    ]
}

Jadi sementara kami dapat menempatkan beberapa operator baru dan mewah ke dalam pipa agregasi di mana kami memilikinya, kasus penggunaan yang paling umum adalah dalam "transformasi akhir pipa" ini di mana kami mungkin juga melakukan transformasi yang sama pada setiap dokumen di hasil kursor kembali sebagai gantinya.




  1. Redis
  2.   
  3. MongoDB
  4.   
  5. Memcached
  6.   
  7. HBase
  8.   
  9. CouchDB
  1. bagaimana saya bisa memvalidasi kata sandi nama pengguna untuk otentikasi mongodb melalui pymongo?

  2. Menggunakan mongodb map/reduce di php

  3. Opsi konfigurasi runtime ClusterControl

  4. Kueri MongoDB Mengembalikan Array Kosong

  5. Object.keys, cara mendapatkan daftar kunci di mongodb