Konsep dasarnya di sini adalah Anda memerlukan kerangka kerja agregasi untuk menerapkan kondisi untuk "memfilter" elemen array ke kondisi. Tergantung pada versi yang tersedia, ada beberapa teknik berbeda yang dapat diterapkan.
Dalam semua kasus inilah hasilnya:
{
"_id" : ObjectId("593921425ccc8150f35e7664"),
"user1" : 1,
"user2" : 4,
"messages" : {
"sender" : 1,
"datetime" : ISODate("2017-06-09T10:04:50Z"),
"body" : "hiii 1"
}
}
{
"_id" : ObjectId("593921425ccc8150f35e7663"),
"user1" : 1,
"user2" : 3,
"messages" : {
"sender" : 1,
"datetime" : ISODate("2017-06-10T10:04:50Z"),
"body" : "hiii 2"
}
}
{
"_id" : ObjectId("593921425ccc8150f35e7662"),
"user1" : 1,
"user2" : 2,
"messages" : {
"sender" : 1,
"datetime" : ISODate("2017-06-08T10:04:50Z"),
"body" : "hiii 0"
}
}
MongoDB 3.4 dan di atasnya
db.chat.aggregate([
{ "$match": { "messages.sender": 1 } },
{ "$replaceRoot": {
"newRoot": {
"$let": {
"vars": {
"messages": {
"$filter": {
"input": "$messages",
"as": "m",
"cond": { "$eq": [ "$$m.sender", 1 ] }
}
},
"maxDate": {
"$max": {
"$map": {
"input": {
"$filter": {
"input": "$messages",
"as": "m",
"cond": { "$eq": [ "$$m.sender", 1 ] }
}
},
"as": "m",
"in": "$$m.datetime"
}
}
}
},
"in": {
"_id": "$_id",
"user1": "$user1",
"user2": "$user2",
"messages": {
"$arrayElemAt": [
{ "$filter": {
"input": "$$messages",
"as": "m",
"cond": { "$eq": [ "$$m.datetime", "$$maxDate" ] }
}},
0
]
}
}
}
}
}}
])
Ini adalah cara paling efisien yang memanfaatkan $replaceRoot
yang memungkinkan kita mendeklarasikan variabel untuk digunakan dalam struktur "diganti" menggunakan $let
. Keuntungan utama di sini adalah bahwa ini hanya membutuhkan "dua" tahap pipa.
Untuk mencocokkan konten larik, Anda menggunakan $filter
tempat Anda menerapkan $eq
operasi logis untuk menguji nilai "sender"
. Jika kondisinya cocok, maka hanya entri larik yang cocok yang dikembalikan.
Menggunakan $filter
yang sama
sehingga hanya entri "pengirim" yang cocok yang dipertimbangkan, kami kemudian ingin menerapkan $max
di atas daftar "difilter" ke nilai dalam "datetime"
. $max
]5
nilai adalah tanggal "terbaru" menurut ketentuan.
Kami menginginkan nilai ini sehingga nanti kami dapat membandingkan hasil yang dikembalikan dari larik "terfilter" dengan "maxDate" ini. Itulah yang terjadi di dalam "in"
blok $let
di mana dua "variabel" yang dideklarasikan sebelumnya untuk konten yang difilter dan "maxDate" diterapkan lagi ke $filter
untuk mengembalikan apa yang seharusnya menjadi satu-satunya nilai yang memenuhi kedua kondisi yang memiliki "tanggal terbaru" juga.
Karena Anda hanya menginginkan hasil "satu", kami menggunakan $arrayElemAt
untuk menggunakan nilai daripada array.
MongoDB 3.2
db.chat.aggregate([
{ "$match": { "messages.sender": 1 } },
{ "$project": {
"user1": 1,
"user2": 1,
"messages": {
"$filter": {
"input": "$messages",
"as": "m",
"cond": { "$eq": [ "$$m.sender", 1 ] }
}
},
"maxDate": {
"$max": {
"$map": {
"input": {
"$filter": {
"input": "$messages",
"as": "m",
"cond": { "$eq": [ "$$m.sender", 1 ] }
}
},
"as": "m",
"in": "$$m.datetime"
}
}
}
}},
{ "$project": {
"user1": 1,
"user2": 1,
"messages": {
"$arrayElemAt":[
{ "$filter": {
"input": "$messages",
"as": "m",
"cond": { "$eq": [ "$$m.datetime", "$maxDate" ] }
}},
0
]
}
}}
])
Ini pada dasarnya adalah proses yang sama seperti yang dijelaskan, tetapi tanpa $replaceRoot
tahap pipa, kita perlu menerapkan dalam dua $project
tahapan. Alasan untuk ini adalah kita memerlukan "nilai yang dihitung" dari "maxDate" untuk melakukan itu $filter
, dan itu tidak tersedia untuk dilakukan dalam pernyataan majemuk, jadi alih-alih kami membagi jalur pipa. Ini berdampak kecil pada keseluruhan biaya operasi.
Di MongoDB 2.6 hingga 3.0 kita dapat menggunakan sebagian besar teknik di sini kecuali untuk $arrayElemAt
dan terima hasil "array" dengan satu entri atau masukkan $unwind
tahap untuk menangani apa yang sekarang seharusnya menjadi satu entri.
MongoDB versi sebelumnya
db.chat.aggregate([
{ "$match": { "messages.sender": 1 } },
{ "$unwind": "$messages" },
{ "$match": { "messages.sender": 1 } },
{ "$sort": { "_id": 1, "messages.datetime": -1 } },
{ "$group": {
"_id": "$_id",
"user1": { "$first": "$user1" },
"user2": { "$first": "$user2" },
"messages": { "$first": "$messages" }
}}
])
Meskipun terlihat singkat, ini adalah operasi yang paling mahal. Di sini Anda harus menggunakan $unwind
untuk menerapkan kondisi ke elemen array. Ini adalah proses yang sangat mahal karena menghasilkan salinan setiap dokumen untuk setiap entri array, dan pada dasarnya digantikan oleh operator modern yang menghindari hal ini dalam kasus "pemfilteran".
$match
kedua
stage di sini membuang elemen apa pun ( sekarang "dokumen" ) yang tidak cocok dengan kondisi "pengirim". Kemudian kami menerapkan $sort
untuk menempatkan tanggal "terbaru" di atas setiap dokumen dengan _id
, maka dua kunci "sort".
Akhirnya kami menerapkan $group
untuk hanya merujuk ke dokumen asli, gunakan $first
sebagai akumulator untuk mendapatkan elemen yang "di atas".