Pertanyaan di sini sebenarnya tentang sesuatu yang berbeda dan tidak perlu $lookup
sama sekali. Tetapi bagi siapa pun yang datang ke sini murni dari judul "filtering after $lookup" maka ini adalah tekniknya untuk Anda:
MongoDB 3.6 - Sub-pipa
db.test.aggregate([
{ "$match": { "id": 100 } },
{ "$lookup": {
"from": "test",
"let": { "id": "$id" },
"pipeline": [
{ "$match": {
"value": "1",
"$expr": { "$in": [ "$$id", "$contain" ] }
}}
],
"as": "childs"
}}
])
Sebelumnya - $lookup + $unwind + $match coalescence
db.test.aggregate([
{ "$match": { "id": 100 } },
{ "$lookup": {
"from": "test",
"localField": "id",
"foreignField": "contain",
"as": "childs"
}},
{ "$unwind": "$childs" },
{ "$match": { "childs.value": "1" } },
{ "$group": {
"_id": "$_id",
"id": { "$first": "$id" },
"value": { "$first": "$value" },
"contain": { "$first": "$contain" },
"childs": { "$push": "$childs" }
}}
])
Jika Anda mempertanyakan mengapa Anda $unwind
dibandingkan dengan menggunakan $filter
pada larik, lalu baca Agregat $lookup Ukuran total dokumen dalam saluran yang cocok melebihi ukuran dokumen maksimum untuk semua detail tentang mengapa hal ini umumnya diperlukan dan jauh lebih optimal.
Untuk rilis MongoDB 3.6 dan seterusnya, maka "sub-pipeline" yang lebih ekspresif umumnya adalah apa yang Anda ingin "filter" hasil koleksi asing sebelum apa pun dikembalikan ke dalam array sama sekali.
Kembali ke jawaban yang sebenarnya menjelaskan mengapa pertanyaan yang diajukan perlu "tidak bergabung" sama sekali....
Asli
Menggunakan $lookup
seperti ini bukan cara yang paling "efisien" untuk melakukan apa yang Anda inginkan di sini. Tapi lebih lanjut tentang ini nanti.
Sebagai konsep dasar, cukup gunakan $filter
pada larik yang dihasilkan:
db.test.aggregate([
{ "$match": { "id": 100 } },
{ "$lookup": {
"from": "test",
"localField": "id",
"foreignField": "contain",
"as": "childs"
}},
{ "$project": {
"id": 1,
"value": 1,
"contain": 1,
"childs": {
"$filter": {
"input": "$childs",
"as": "child",
"cond": { "$eq": [ "$$child.value", "1" ] }
}
}
}}
]);
Atau gunakan $redact
sebagai gantinya:
db.test.aggregate([
{ "$match": { "id": 100 } },
{ "$lookup": {
"from": "test",
"localField": "id",
"foreignField": "contain",
"as": "childs"
}},
{ "$redact": {
"$cond": {
"if": {
"$or": [
{ "$eq": [ "$value", "0" ] },
{ "$eq": [ "$value", "1" ] }
]
},
"then": "$$DESCEND",
"else": "$$PRUNE"
}
}}
]);
Keduanya mendapatkan hasil yang sama:
{
"_id":ObjectId("570557d4094a4514fc1291d6"),
"id":100,
"value":"0",
"contain":[ ],
"childs":[ {
"_id":ObjectId("570557d4094a4514fc1291d7"),
"id":110,
"value":"1",
"contain":[ 100 ]
},
{
"_id":ObjectId("570557d4094a4514fc1291d8"),
"id":120,
"value":"1",
"contain":[ 100 ]
}
]
}
Intinya adalah $lookup
sendiri tidak dapat "belum" meminta untuk hanya memilih data tertentu. Jadi semua "pemfilteran" perlu dilakukan setelah $lookup
Tapi sebenarnya untuk jenis "self join" ini sebaiknya tidak menggunakan $lookup
sama sekali dan menghindari overhead dari pembacaan tambahan dan "gabungan hash" sepenuhnya. Ambil saja item terkait dan $group
sebagai gantinya:
db.test.aggregate([
{ "$match": {
"$or": [
{ "id": 100 },
{ "contain.0": 100, "value": "1" }
]
}},
{ "$group": {
"_id": {
"$cond": {
"if": { "$eq": [ "$value", "0" ] },
"then": "$id",
"else": { "$arrayElemAt": [ "$contain", 0 ] }
}
},
"value": { "$first": { "$literal": "0"} },
"childs": {
"$push": {
"$cond": {
"if": { "$ne": [ "$value", "0" ] },
"then": "$$ROOT",
"else": null
}
}
}
}},
{ "$project": {
"value": 1,
"childs": {
"$filter": {
"input": "$childs",
"as": "child",
"cond": { "$ne": [ "$$child", null ] }
}
}
}}
])
Yang hanya keluar sedikit berbeda karena saya sengaja menghapus bidang asing. Tambahkan sendiri jika Anda benar-benar ingin:
{
"_id" : 100,
"value" : "0",
"childs" : [
{
"_id" : ObjectId("570557d4094a4514fc1291d7"),
"id" : 110,
"value" : "1",
"contain" : [ 100 ]
},
{
"_id" : ObjectId("570557d4094a4514fc1291d8"),
"id" : 120,
"value" : "1",
"contain" : [ 100 ]
}
]
}
Jadi satu-satunya masalah sebenarnya di sini adalah "memfilter" null
apa pun hasil dari larik, dibuat saat dokumen saat ini adalah parent
dalam memproses item menjadi $push
.
Apa yang juga tampaknya Anda lewatkan di sini adalah bahwa hasil yang Anda cari tidak memerlukan agregasi atau "sub-kueri" sama sekali. Struktur yang telah Anda simpulkan atau mungkin ditemukan di tempat lain "dirancang" sehingga Anda bisa mendapatkan "simpul" dan semua "anak" dalam satu permintaan kueri.
Itu berarti hanya "permintaan" yang benar-benar dibutuhkan, dan pengumpulan data (yang merupakan semua yang terjadi karena tidak ada konten yang benar-benar "dikurangi" ) hanyalah fungsi dari pengulangan hasil kursor:
var result = {};
db.test.find({
"$or": [
{ "id": 100 },
{ "contain.0": 100, "value": "1" }
]
}).sort({ "contain.0": 1 }).forEach(function(doc) {
if ( doc.id == 100 ) {
result = doc;
result.childs = []
} else {
result.childs.push(doc)
}
})
printjson(result);
Ini melakukan hal yang persis sama:
{
"_id" : ObjectId("570557d4094a4514fc1291d6"),
"id" : 100,
"value" : "0",
"contain" : [ ],
"childs" : [
{
"_id" : ObjectId("570557d4094a4514fc1291d7"),
"id" : 110,
"value" : "1",
"contain" : [
100
]
},
{
"_id" : ObjectId("570557d4094a4514fc1291d8"),
"id" : 120,
"value" : "1",
"contain" : [
100
]
}
]
}
Dan berfungsi sebagai bukti bahwa yang perlu Anda lakukan di sini hanyalah mengeluarkan kueri "tunggal" untuk memilih induk dan anak-anak. Data yang dikembalikan sama saja, dan semua yang Anda lakukan di server atau klien adalah "memijat" ke format lain yang dikumpulkan.
Ini adalah salah satu kasus di mana Anda bisa "terperangkap" dalam memikirkan bagaimana Anda melakukan sesuatu dalam database "relasional", dan tidak menyadari bahwa karena cara data disimpan telah "berubah", Anda tidak perlu lagi menggunakan pendekatan yang sama.
Itulah inti dari contoh dokumentasi "Struktur Pohon Model dengan Referensi Anak" dalam strukturnya, yang memudahkan untuk memilih orang tua dan anak dalam satu kueri.