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

Array pembaruan massal dari sub dokumen yang cocok di Mongodb

Dalam jawaban terpendek, itu adalah "ya" dan "tidak".

Memang ada cara untuk mencocokkan elemen array individual dan memperbaruinya dengan nilai terpisah dalam satu pernyataan, karena Anda sebenarnya dapat memberikan "beberapa" arrayFilters kondisi dan gunakan pengidentifikasi tersebut dalam pernyataan pembaruan Anda.

Masalah dengan sampel khusus Anda di sini adalah bahwa salah satu entri di "set perubahan" Anda ( yang terakhir ) sebenarnya tidak cocok dengan anggota larik mana pun yang ada saat ini. Tindakan "diduga" di sini adalah $push anggota baru yang tidak cocok itu ke dalam larik di mana ia tidak ditemukan. Namun tindakan tertentu itu tidak bisa dilakukan dalam "operasi tunggal" , tetapi Anda dapat menggunakan bulkWrite() untuk mengeluarkan "beberapa" pernyataan untuk menutupi kasus itu.

Mencocokkan Kondisi Array yang Berbeda

Menjelaskan bahwa dalam poin, pertimbangkan dua item pertama di "set perubahan" Anda. Anda dapat menerapkan "tunggal" perbarui pernyataan dengan beberapa arrayFilters seperti ini:

db.avail_rates_copy.updateOne(
  { "_id": 12345 },
  { 
    "$set": {
      "rates.$[one]": {
        "productId" : NumberInt(1234), 
        "rate" : 400.0, 
        "rateCardId": NumberInt(1),
        "month" : NumberInt(201801)
      },
      "rates.$[two]": {
        "productId" : NumberInt(1234), 
        "rate" : 500.0, 
        "rateCardId": NumberInt(1),
        "month" : NumberInt(201802)
      } 
    }
  },
  { 
    "arrayFilters": [
      {
        "one.productId": NumberInt(1234),
        "one.rateCardId": NumberInt(1),
        "one.month": NumberInt(201801)
      },
      {
        "two.productId": NumberInt(1234),
        "two.rateCardId": NumberInt(1),
        "two.month": NumberInt(201802)
      }
    ]
  }
)

Jika Anda menjalankannya, Anda akan melihat dokumen yang dimodifikasi menjadi:

{
        "_id" : 12345,
        "_class" : "com.example.ProductRates",
        "rates" : [
                {                             // Matched and changed this by one
                        "productId" : 1234,
                        "rate" : 400,
                        "rateCardId" : 1,
                        "month" : 201801
                },
                {                            // And this as two
                        "productId" : 1234,
                        "rate" : 500,
                        "rateCardId" : 1,
                        "month" : 201802
                },
                {
                        "productId" : 1234,
                        "rate" : 400,
                        "rateCardId" : 2,
                        "month" : 201803
                },
                {
                        "productId" : 1235,
                        "rate" : 500,
                        "rateCardId" : 1,
                        "month" : 201801
                },
                {
                        "productId" : 1235,
                        "rate" : 234,
                        "rateCardId" : 2,
                        "month" : 201803
                }
        ]
}

Perhatikan di sini bahwa Anda menentukan setiap "pengidentifikasi" dalam daftar arrayFilters dengan beberapa kondisi untuk mencocokkan elemen seperti:

  {
    "one.productId": NumberInt(1234),
    "one.rateCardId": NumberInt(1),
    "one.month": NumberInt(201801)
  },

Jadi setiap "kondisi" secara efektif dipetakan sebagai:

  <identifier>.<property>

Jadi tahu untuk melihat "rates" array dengan pernyataan di blok pembaruan oleh $[<indentifier>] :

 "rates.$[one]"

Dan perhatikan setiap elemen "rates" untuk mencocokkan kondisi. Jadi "one" pengenal akan cocok dengan kondisi yang diawali dengan "one" dan juga untuk kumpulan kondisi lainnya yang diawali dengan "two" , oleh karena itu pernyataan pembaruan yang sebenarnya hanya berlaku untuk yang cocok dengan kondisi yang ditetapkan untuk pengidentifikasi.

Jika Anda hanya ingin "rates" properti sebagai lawan dari seluruh objek, maka Anda cukup memberi notasi sebagai:

{ "$set": { "rates.$[one].rate": 400, "rates.$[two].rate": 500 } }

Menambahkan Objek yang Tidak Cocok

Jadi bagian pertama relatif sederhana untuk dipahami, tetapi seperti yang dinyatakan melakukan $push untuk "elemen yang tidak ada" adalah masalah yang berbeda, karena pada dasarnya kita memerlukan kondisi kueri pada level "dokumen" untuk menentukan bahwa elemen array "hilang".

Pada dasarnya ini berarti bahwa Anda perlu mengeluarkan pembaruan dengan $push mencari setiap elemen array untuk melihat apakah ada atau tidak. Jika tidak ada, maka dokumen tersebut cocok dan $push dilakukan.

Di sinilah bulkWrite() ikut bermain, dan Anda menggunakannya dengan menambahkan pembaruan tambahan ke operasi pertama kami di atas untuk setiap elemen dalam "set perubahan":

db.avail_rates_copy.bulkWrite(
  [
    { "updateOne": {
      "filter": { "_id": 12345 },
      "update": {
        "$set": {
          "rates.$[one]": {
            "productId" : NumberInt(1234), 
            "rate" : 400.0, 
            "rateCardId": NumberInt(1),
            "month" : NumberInt(201801)
          },
          "rates.$[two]": {
            "productId" : NumberInt(1234), 
            "rate" : 500.0, 
            "rateCardId": NumberInt(1),
            "month" : NumberInt(201802)
          },
          "rates.$[three]": {
            "productId" : NumberInt(1235), 
            "rate" : 700.0, 
            "rateCardId": NumberInt(1),
            "month" : NumberInt(201802)
          }
        }
      },
      "arrayFilters": [
        {
          "one.productId": NumberInt(1234),
          "one.rateCardId": NumberInt(1),
          "one.month": NumberInt(201801)
        },
        {
          "two.productId": NumberInt(1234),
          "two.rateCardId": NumberInt(1),
          "two.month": NumberInt(201802)
        },
        {
          "three.productId": NumberInt(1235),
          "three.rateCardId": NumberInt(1),
          "three.month": NumberInt(201802)
        }
      ]    
    }},
    { "updateOne": {
      "filter": {
        "_id": 12345,
        "rates": {
          "$not": {
            "$elemMatch": {
              "productId" : NumberInt(1234), 
              "rateCardId": NumberInt(1),
              "month" : NumberInt(201801)
            }
          }
        }
      },
      "update": {
        "$push": {
          "rates": {
            "productId" : NumberInt(1234), 
            "rate" : 400.0, 
            "rateCardId": NumberInt(1),
            "month" : NumberInt(201801)
          }
        }
      }
    }},
    { "updateOne": {
      "filter": {
        "_id": 12345,
        "rates": {
          "$not": {
            "$elemMatch": {
              "productId" : NumberInt(1234), 
              "rateCardId": NumberInt(1),
              "month" : NumberInt(201802)
            }
          }
        }
      },
      "update": {
        "$push": {
          "rates": {
            "productId" : NumberInt(1234), 
            "rate" : 500.0, 
            "rateCardId": NumberInt(1),
            "month" : NumberInt(201802)
          }
        }
      }
    }},
    { "updateOne": {
      "filter": {
        "_id": 12345,
        "rates": {
          "$not": {
            "$elemMatch": {
              "productId" : NumberInt(1235),
              "rateCardId": NumberInt(1),
              "month" : NumberInt(201802)
            }
          }
        }
      },
      "update": {
        "$push": {
          "rates": {
            "productId" : NumberInt(1235),
            "rate" : 700.0, 
            "rateCardId": NumberInt(1),
            "month" : NumberInt(201802)
          }
        }
      }
    }}
  ],
  { "ordered": true }
)

Perhatikan $elemMatch dalam filter kueri, karena ini adalah persyaratan untuk mencocokkan elemen array dengan "beberapa kondisi". Kami tidak membutuhkannya di arrayFilters entri karena mereka hanya lihat setiap item array yang sudah menerapkannya, tetapi sebagai "permintaan" kondisinya memerlukan $elemMatch sesederhana "notasi titik" akan mengembalikan kecocokan yang salah.

Lihat juga $not operator digunakan di sini untuk "meniadakan" $elemMatch , karena kondisi kami yang sebenarnya hanya cocok dengan dokumen yang "tidak cocok dengan elemen larik" dengan kondisi yang disediakan, dan itulah yang membenarkan pemilihan untuk menambahkan elemen baru.

Dan pernyataan tunggal yang dikeluarkan ke server pada dasarnya mencoba empat operasi pembaruan sebagai satu untuk mencoba memperbarui elemen larik yang cocok, dan satu lagi untuk masing-masing tiga "ubah set" mencoba $push di mana dokumen ditemukan tidak sesuai dengan ketentuan untuk elemen larik di "set perubahan".

Karena itu, hasilnya seperti yang diharapkan:

{
        "_id" : 12345,
        "_class" : "com.example.ProductRates",
        "rates" : [
                {                               // matched and updated
                        "productId" : 1234,
                        "rate" : 400,
                        "rateCardId" : 1,
                        "month" : 201801
                },
                {                               // matched and updated
                        "productId" : 1234,
                        "rate" : 500,
                        "rateCardId" : 1,
                        "month" : 201802
                },
                {
                        "productId" : 1234,
                        "rate" : 400,
                        "rateCardId" : 2,
                        "month" : 201803
                },
                {
                        "productId" : 1235,
                        "rate" : 500,
                        "rateCardId" : 1,
                        "month" : 201801
                },
                {
                        "productId" : 1235,
                        "rate" : 234,
                        "rateCardId" : 2,
                        "month" : 201803
                },
                {                              // This was appended
                        "productId" : 1235,
                        "rate" : 700,
                        "rateCardId" : 1,
                        "month" : 201802
                }
        ]
}

Bergantung pada berapa banyak elemen yang sebenarnya tidak cocok dengan bulkWrite() respon akan melaporkan berapa banyak dari pernyataan tersebut yang benar-benar cocok dan mempengaruhi dokumen. Dalam hal ini 2 cocok dan dimodifikasi, karena operasi pembaruan "pertama" cocok dengan entri larik yang ada, dan pembaruan perubahan "terakhir" cocok dengan dokumen yang tidak berisi entri larik dan menjalankan $push untuk dimodifikasi.

Kesimpulan

Jadi, Anda memiliki pendekatan gabungan, di mana:

  • Bagian pertama dari "memperbarui" dalam pertanyaan Anda sangat mudah dan dapat dilakukan dalam satu pernyataan , seperti yang ditunjukkan di bagian pertama.

  • Bagian kedua di mana ada elemen array yang "saat ini tidak ada" dalam larik dokumen saat ini, ini sebenarnya mengharuskan Anda menggunakan bulkWrite() untuk mengeluarkan "beberapa" operasi dalam satu permintaan.

Oleh karena itu perbarui , adalah "YA" untuk satu operasi. Tapi menambahkan perbedaan berarti beberapa operasi. Tetapi Anda dapat menggabungkan kedua pendekatan seperti yang ditunjukkan di sini.

Ada banyak cara "mewah" di mana Anda dapat membuat pernyataan ini berdasarkan isi larik "ubah set" dengan kode, jadi Anda tidak perlu "meng-hardcode" setiap anggota.

Sebagai kasus dasar untuk JavaScript dan kompatibel dengan rilis shell mongo saat ini (yang agak mengganggu tidak mendukung operator penyebaran objek):

db.getCollection('avail_rates_copy').drop();
db.getCollection('avail_rates_copy').insert(
  {
    "_id" : 12345,
    "_class" : "com.example.ProductRates",
    "rates" : [
      {
        "productId" : 1234,
        "rate" : 100,
        "rateCardId" : 1,
        "month" : 201801
      },
      {
        "productId" : 1234,
        "rate" : 200,
        "rateCardId" : 1,
        "month" : 201802
      },
      {
        "productId" : 1234,
        "rate" : 400,
        "rateCardId" : 2,
        "month" : 201803
      },
      {
        "productId" : 1235,
        "rate" : 500,
        "rateCardId" : 1,
        "month" : 201801
      },
      {
        "productId" : 1235,
        "rate" : 234,
        "rateCardId" : 2,
        "month" : 201803
      }
    ]
  }
);

var changeSet = [
  {
      "productId" : 1234, 
      "rate" : 400.0, 
      "rateCardId": 1,
      "month" : 201801
  }, 
  {
      "productId" : 1234, 
      "rate" : 500.0, 
      "rateCardId": 1,
      "month" : 201802
  }, 
  {

      "productId" : 1235, 
      "rate" : 700.0, 
      "rateCardId": 1,
      "month" : 201802
  }
];

var arrayFilters = changeSet.map((obj,i) => 
  Object.keys(obj).filter(k => k != 'rate' )
    .reduce((o,k) => Object.assign(o, { [`u${i}.${k}`]: obj[k] }) ,{})
);

var $set = changeSet.reduce((o,r,i) =>
  Object.assign(o, { [`rates.$[u${i}].rate`]: r.rate }), {});

var updates = [
  { "updateOne": {
    "filter": { "_id": 12345 },
    "update": { $set },
    arrayFilters
  }},
  ...changeSet.map(obj => (
    { "updateOne": {
      "filter": {
        "_id": 12345,
        "rates": {
          "$not": {
            "$elemMatch": Object.keys(obj).filter(k => k != 'rate')
              .reduce((o,k) => Object.assign(o, { [k]: obj[k] }),{})
          }
        }
      },
      "update": {
        "$push": {
          "rates": obj
        }
      }
    }}
  ))
];

db.getCollection('avail_rates_copy').bulkWrite(updates,{ ordered: true });

Ini akan secara dinamis membuat daftar operasi pembaruan "Massal" yang akan terlihat seperti:

[
  {
    "updateOne": {
      "filter": {
        "_id": 12345
      },
      "update": {
        "$set": {
          "rates.$[u0].rate": 400,
          "rates.$[u1].rate": 500,
          "rates.$[u2].rate": 700
        }
      },
      "arrayFilters": [
        {
          "u0.productId": 1234,
          "u0.rateCardId": 1,
          "u0.month": 201801
        },
        {
          "u1.productId": 1234,
          "u1.rateCardId": 1,
          "u1.month": 201802
        },
        {
          "u2.productId": 1235,
          "u2.rateCardId": 1,
          "u2.month": 201802
        }
      ]
    }
  },
  {
    "updateOne": {
      "filter": {
        "_id": 12345,
        "rates": {
          "$not": {
            "$elemMatch": {
              "productId": 1234,
              "rateCardId": 1,
              "month": 201801
            }
          }
        }
      },
      "update": {
        "$push": {
          "rates": {
            "productId": 1234,
            "rate": 400,
            "rateCardId": 1,
            "month": 201801
          }
        }
      }
    }
  },
  {
    "updateOne": {
      "filter": {
        "_id": 12345,
        "rates": {
          "$not": {
            "$elemMatch": {
              "productId": 1234,
              "rateCardId": 1,
              "month": 201802
            }
          }
        }
      },
      "update": {
        "$push": {
          "rates": {
            "productId": 1234,
            "rate": 500,
            "rateCardId": 1,
            "month": 201802
          }
        }
      }
    }
  },
  {
    "updateOne": {
      "filter": {
        "_id": 12345,
        "rates": {
          "$not": {
            "$elemMatch": {
              "productId": 1235,
              "rateCardId": 1,
              "month": 201802
            }
          }
        }
      },
      "update": {
        "$push": {
          "rates": {
            "productId": 1235,
            "rate": 700,
            "rateCardId": 1,
            "month": 201802
          }
        }
      }
    }
  }
]

Seperti yang dijelaskan dalam "bentuk panjang" dari jawaban umum, tetapi tentu saja hanya menggunakan konten "array" input untuk menyusun semua pernyataan tersebut.

Anda dapat melakukan konstruksi objek dinamis seperti itu dalam bahasa apa pun, dan semua driver MongoDB menerima masukan dari beberapa jenis struktur yang diizinkan untuk "dimanipulasi" yang kemudian diubah menjadi BSON sebelum benar-benar dikirim ke server untuk dieksekusi.




  1. Redis
  2.   
  3. MongoDB
  4.   
  5. Memcached
  6.   
  7. HBase
  8.   
  9. CouchDB
  1. Bagaimana cara menggabungkan hasil string dari beberapa catatan MongoDB menjadi satu hasil di MongoDB?

  2. Bagaimana cara mengatasi:'MongoError:$where is notallowed in this atlas tier'?

  3. mongodb $dalam batas

  4. Cara menyemai wadah buruh pelabuhan di Windows

  5. SQL NULLIF() Dijelaskan