Beginilah cara MongoDB menangani proyeksi dasar dengan elemen array. Meskipun Anda dapat melakukan sesuatu seperti ini:
Model.findOne({}, { "comments.upvotes": 1 },function(err,doc) {
})
Dan itu hanya akan mengembalikan bidang "upvotes" dari dalam sub-dokumen dari array komentar untuk semua dokumen yang cocok dengan kondisi dan semua elemen array tentu saja, Anda tidak dapat menggabungkan ini dengan proyeksi posisi yang dipilih menggunakan posisi $
operator. Ini pada dasarnya berasal dari "teori" bahwa umumnya Anda sebenarnya ingin mengembalikan seluruh array. Jadi, cara ini selalu berhasil dan kemungkinan tidak akan segera berubah.
Untuk mendapatkan apa yang Anda inginkan, Anda memerlukan kemampuan yang diperluas untuk manipulasi dokumen yang ditawarkan oleh kerangka kerja agregasi . Ini memberi Anda lebih banyak kendali atas bagaimana dokumen dikembalikan:
Model.aggregate(
[
// Match the document containing the array element
{ "$match": { "comments._id" : oid } },
// Unwind to "de-normalize" the array content
{ "$unwind": "$comments" },
// Match the specific array element
{ "$match": { "comments._id" : oid } },
// Group back and just return the "upvotes" field
{ "$group": {
"_id": "$_id",
"comments": { "$push": { "upvotes": "$comments.upvotes" } }
}}
],
function(err,docs) {
}
);
Atau di MongoDB versi modern sejak 2.6 Anda bahkan dapat melakukan ini:
Model.aggregate(
[
{ "$match": { "comments._id" : oid } },
{ "$project": {
"comments": {
"$setDifference": [
{ "$map": {
"input": "$comments",
"as": "el",
"in": {
"$cond": [
{ "$eq": [ "$$el._id", oid ] },
{ "upvotes": "$$el.upvotes" },
false
]
}
}},
[false]
]
}}
}}
],
function(err,docs) {
}
)
Dan itu menggunakan $map
dan $setDifference
operator untuk melakukan pemfilteran "in-line" dari konten array tanpa terlebih dahulu memproses $unwind
panggung.
Jadi, jika Anda ingin lebih mengontrol bagaimana dokumen dikembalikan, maka kerangka kerja agregasi adalah cara untuk melakukannya saat bekerja dengan dokumen yang disematkan.