Bạn cần một vài giai đoạn bổ sung để trả về giá trị mặc định. Trước hết, bạn cần sử dụng $group
với _id
đặt thành null
để thu thập tất cả các kết quả trong một tài liệu. Sau đó, bạn có thể sử dụng $ map với một mảng ngày làm đầu vào. Bên trong $map
đó bạn có thể sử dụng $ indexOfArray để tìm xem ngày đó có tồn tại trong tập kết quả hiện tại của bạn hay không. Nếu có (index != -1
) thì bạn có thể trả về giá trị đó, nếu không, bạn cần trả về tài liệu con mặc định với views
đặt thành 0
. Sau đó, bạn có thể sử dụng $ unwind để lấy lại danh sách tài liệu và $ ReplaceRoot để quảng bá các thống kê stats
lồng nhau lên cấp cao nhất.
ProductView.aggregate([
{ $match: { productId: '5b8c0f3204a10228b00a1745' } },
{ $project: { day: { $substr: ["$createdAt", 0, 10] } } },
{
$group: {
_id: "$day",
count: { $sum: 1 },
time: { $avg: "$createdAt" },
}
},
{ $sort: { _id: 1 } },
{
$project: {
date: '$_id',
views: '$count',
},
},
{
$group: {
_id: null,
stats: { $push: "$$ROOT" }
}
},
{
$project: {
stats: {
$map: {
input: [ "2018-09-01", "2018-09-02", "2018-09-03", "2018-09-04", "2018-09-05" ],
as: "date",
in: {
$let: {
vars: { dateIndex: { "$indexOfArray": [ "$stats._id", "$$date" ] } },
in: {
$cond: {
if: { $ne: [ "$$dateIndex", -1 ] },
then: { $arrayElemAt: [ "$stats", "$$dateIndex" ] },
else: { _id: "$$date", date: "$$date", views: 0 }
}
}
}
}
}
}
}
},
{
$unwind: "$stats"
},
{
$replaceRoot: {
newRoot: "$stats"
}
}
]).exec((err, result) => ...)
Bạn có thể tạo danh sách ngày tháng tĩnh trong logic ứng dụng của mình bằng cách sử dụng vòng lặp đơn giản. Tôi tin rằng điều đó cũng có thể xảy ra trong MongoDB (sử dụng $ range) nhưng nó có thể làm phức tạp đường ống tổng hợp này. Hãy cho tôi biết nếu bạn ổn với điều đó hoặc bạn muốn thử tạo mảng ngày tháng đó trong MongoDB.