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

Kembalikan Dokumen dengan Sub Dokumen Maks

Pertama, mari tunjukkan data Anda dengan cara yang dapat digunakan orang lain dan menghasilkan hasil yang diinginkan:

{ value: 1,
  _id: ObjectId('5cb9ea0c75c61525e0176f96'),
  name: 'Test',
  category: 'Development',
  subcategory: 'Programming Languages',
  status: 'Supported',
  description: 'Test',
  change:
   [ { version: 1,
       who: 'ATL User',
       when: new Date('2019-04-19T15:30:39.912Z'),
       what: 'Item Creation' },
     { version: 2,
       who: 'ATL Other User',
       when: new Date('2019-04-19T15:31:39.912Z'),
       what: 'Name Change' } ],
}

Perhatikan bahwa "when" tanggal sebenarnya berbeda sehingga akan ada $max nilai dan mereka tidak hanya sama. Sekarang kita bisa menjalankan kasusnya

Kasus 1 - Ambil "tunggal" $max nilai

Kasus dasar di sini adalah menggunakan $arrayElemAt dan $indexOfArray operator untuk mengembalikan $max yang cocok nilai:

db.items.aggregate([
  { "$match": {
    "subcategory": "Programming Languages", "name": { "$exists": true }
  }}, 
  { "$addFields": {
    "change": {
      "$arrayElemAt": [
        "$change",
        { "$indexOfArray": [
          "$change.when",
          { "$max": "$change.when" }
        ]}
      ]
    }
  }}
])

Pengembalian:

{
        "_id" : ObjectId("5cb9ea0c75c61525e0176f96"),
        "value" : 1,
        "name" : "Test",
        "category" : "Development",
        "subcategory" : "Programming Languages",
        "status" : "Supported",
        "description" : "Test",
        "change" : {
                "version" : 2,
                "who" : "ATL Other User",
                "when" : ISODate("2019-04-19T15:31:39.912Z"),
                "what" : "Name Change"
        }
}

Pada dasarnya "$max": "$change.when" mengembalikan nilai yang merupakan "maksimum" dari dalam array nilai tersebut. Anda kemudian menemukan "indeks" yang cocok dari larik nilai tersebut melalui $indexOfArray yang mengembalikan pertama indeks yang cocok ditemukan. Posisi "indeks" itu (dari sebenarnya hanya array "when" nilai dialihkan dalam urutan yang sama ) kemudian digunakan dengan $arrayElemAt untuk mengekstrak "seluruh objek" dari "change" array pada posisi indeks yang ditentukan.

Kasus 2 - Mengembalikan "multiple" $max entri

Hampir sama dengan $max , kecuali kali ini kami $filter untuk mengembalikan beberapa "mungkin" nilai yang cocok dengan $max nilai:

db.items.aggregate([
  { "$match": {
    "subcategory": "Programming Languages", "name": { "$exists": true }
  }}, 
  { "$addFields": {
    "change": {
      "$filter": {
        "input": "$change",
        "cond": {
          "$eq": [ "$$this.when", { "$max": "$change.when" } ]
        }
      }       
    }
  }}
])

Pengembalian:

{
        "_id" : ObjectId("5cb9ea0c75c61525e0176f96"),
        "value" : 1,
        "name" : "Test",
        "category" : "Development",
        "subcategory" : "Programming Languages",
        "status" : "Supported",
        "description" : "Test",
        "change" : [
                {
                        "version" : 2,
                        "who" : "ATL Other User",
                        "when" : ISODate("2019-04-19T15:31:39.912Z"),
                        "what" : "Name Change"
                }
        ]
}

Jadi $max tentu saja sama tetapi kali ini nilai tunggal yang dikembalikan oleh operator tersebut digunakan dalam $eq perbandingan dalam $filter . Ini memeriksa setiap elemen array dan melihat saat ini "when" nilai ( "$$this.when" ). Dimana "sama" maka elemen tersebut dikembalikan.

Pada dasarnya sama dengan pendekatan pertama tetapi dengan pengecualian bahwa $filter memungkinkan "banyak" elemen yang akan dikembalikan. Oleh karena itu semuanya dengan sama $max nilai.

Kasus 3 - Pra-sortir konten array.

Sekarang Anda mungkin memperhatikan bahwa dalam contoh data yang saya sertakan ( diadaptasi dari data Anda sendiri tetapi dengan tanggal "maks" yang sebenarnya ) nilai "maks" sebenarnya adalah terakhir nilai dalam larik. Ini mungkin terjadi secara alami sebagai akibat $push ( secara default ) "tambahkan" ke akhir konten array yang ada. Jadi "lebih baru" entri akan cenderung berada di akhir dari larik.

Ini tentu saja default perilaku, tetapi ada alasan bagus mengapa Anda "mungkin" ingin mengubah itu. Singkatnya terbaik cara untuk mendapatkan "paling baru" entri array sebenarnya mengembalikan elemen pertama dari larik.

Yang perlu Anda lakukan hanyalah memastikan "terbaru" sebenarnya ditambahkan pertama daripada terakhir . Ada dua pendekatan:

  1. Gunakan $position ke item larik yang "ditunda dulu": Ini adalah pengubah sederhana untuk $push menggunakan 0 posisi untuk selalu menambah depan :

    db.items.updateOne(
      { "_id" : ObjectId("5cb9ea0c75c61525e0176f96") },
      { "$push": {
          "change": {
            "$each": [{
              "version": 3,
              "who": "ATL User",
              "when": new Date(),
              "what": "Another change"
            }],
            "$position": 0
          }
       }}
    )
    

    Ini akan mengubah dokumen menjadi:

    {
        "_id" : ObjectId("5cb9ea0c75c61525e0176f96"),
        "value" : 1,
        "name" : "Test",
        "category" : "Development",
        "subcategory" : "Programming Languages",
        "status" : "Supported",
        "description" : "Test",
        "change" : [
                {
                        "version" : 3,
                        "who" : "ATL User",
                        "when" : ISODate("2019-04-20T02:40:30.024Z"),
                        "what" : "Another change"
                },
                {
                        "version" : 1,
                        "who" : "ATL User",
                        "when" : ISODate("2019-04-19T15:30:39.912Z"),
                        "what" : "Item Creation"
                },
                {
                        "version" : 2,
                        "who" : "ATL Other User",
                        "when" : ISODate("2019-04-19T15:31:39.912Z"),
                        "what" : "Name Change"
                }
        ]
    }
    

Perhatikan bahwa ini akan mengharuskan Anda untuk benar-benar pergi dan "membalikkan" semua elemen array Anda sebelumnya sehingga "terbaru" sudah ada di depan sehingga urutannya dipertahankan. Untungnya ini agak tercakup dalam pendekatan kedua...

  1. Gunakan $sort untuk mengubah dokumen secara berurutan pada setiap $push : Dan ini adalah pengubah lain yang sebenarnya "mengurutkan ulang" secara atom pada setiap penambahan item baru. Penggunaan normal pada dasarnya sama dengan item baru apa pun ke $each seperti di atas, atau bahkan hanya larik "kosong" untuk menerapkan $sort hanya untuk data yang ada:

    db.items.updateOne(
      { "_id" : ObjectId("5cb9ea0c75c61525e0176f96") },
      { "$push": {
          "change": {
            "$each": [],
            "$sort": { "when": -1 } 
          }
       }}
    )
    

    Hasil dalam:

    {
            "_id" : ObjectId("5cb9ea0c75c61525e0176f96"),
            "value" : 1,
            "name" : "Test",
            "category" : "Development",
            "subcategory" : "Programming Languages",
            "status" : "Supported",
            "description" : "Test",
            "change" : [
                    {
                            "version" : 3,
                            "who" : "ATL User",
                            "when" : ISODate("2019-04-20T02:40:30.024Z"),
                            "what" : "Another change"
                    },
                    {
                            "version" : 2,
                            "who" : "ATL Other User",
                            "when" : ISODate("2019-04-19T15:31:39.912Z"),
                            "what" : "Name Change"
                    },
                    {
                            "version" : 1,
                            "who" : "ATL User",
                            "when" : ISODate("2019-04-19T15:30:39.912Z"),
                            "what" : "Item Creation"
                    }
            ]
    }
    

    Mungkin perlu beberapa saat untuk memahami mengapa Anda $push untuk $sort larik seperti ini, tetapi maksud umumnya adalah ketika modifikasi dapat dilakukan pada larik yang "mengubah" properti seperti Date nilai sedang diurutkan dan Anda akan menggunakan pernyataan seperti itu untuk mencerminkan perubahan tersebut. Atau memang hanya menambahkan item baru dengan $sort dan biarkan berhasil.

Jadi mengapa "menyimpan" array dipesan seperti ini? Seperti yang disebutkan sebelumnya, Anda menginginkan yang pertama item sebagai "paling baru" , lalu kueri untuk mengembalikan yang menjadi:

db.items.find(
  {
    "subcategory": "Programming Languages",
    "name": { "$exists": true }
  },
  { "change": { "$slice": 1 } }
)

Pengembalian:

{
        "_id" : ObjectId("5cb9ea0c75c61525e0176f96"),
        "value" : 1,
        "name" : "Test",
        "category" : "Development",
        "subcategory" : "Programming Languages",
        "status" : "Supported",
        "description" : "Test",
        "change" : [
                {
                        "version" : 3,
                        "who" : "ATL User",
                        "when" : ISODate("2019-04-20T02:40:30.024Z"),
                        "what" : "Another change"
                }
        ]
}

Jadi $slice kemudian dapat digunakan hanya untuk mengekstrak item array dengan indeks yang diketahui. Secara teknis Anda bisa menggunakan -1 di sana untuk mengembalikan terakhir item dari array, tetapi penataan ulang di mana yang terbaru adalah yang pertama memungkinkan hal-hal lain seperti mengonfirmasi modifikasi terakhir dibuat oleh pengguna tertentu, dan/atau kondisi lain seperti batasan rentang tanggal. yaitu:

db.items.find(
  {
    "subcategory": "Programming Languages",
    "name": { "$exists": true },
    "change.0.who": "ATL User",
    "change.0.when": { "$gt": new Date("2018-04-01") }
  },
  { "change": { "$slice": 1 } }
)

Perhatikan di sini bahwa sesuatu seperti "change.-1.when" adalah pernyataan ilegal, itulah sebabnya kami menyusun ulang array sehingga Anda dapat menggunakan legal 0 untuk pertama bukannya -1 untuk terakhir .

Kesimpulan

Jadi ada beberapa hal berbeda yang dapat Anda lakukan, baik dengan menggunakan pendekatan agregasi untuk memfilter konten array atau melalui formulir kueri standar setelah melakukan beberapa modifikasi pada bagaimana data sebenarnya disimpan. Yang mana yang akan digunakan bergantung pada keadaan Anda sendiri, tetapi perlu diperhatikan bahwa salah satu formulir kueri standar akan berjalan lebih cepat daripada manipulasi apa pun melalui kerangka kerja agregasi atau operator terkomputasi mana pun.




  1. Redis
  2.   
  3. MongoDB
  4.   
  5. Memcached
  6.   
  7. HBase
  8.   
  9. CouchDB
  1. Kesalahan [ERR_HTTP_HEADERS_SENT]:Tidak dapat mengatur header setelah dikirim ke klien

  2. Gunakan satu MongoClient di seluruh layanan web JavaEE

  3. Di MongoDb, bagaimana cara menerapkan sortir bidang internal yang ada dalam dokumen?

  4. Codec MongoDB BSON tidak digunakan saat menyandikan objek

  5. Gunakan mongoexport dengan --query untuk ISODate