#mongodb #aggregation-framework
#mongodb #агрегация-фреймворк
Вопрос:
Давайте предположим, что у меня есть этот набор данных:
{ ValidFrom: "2019-03-25T16:01:55.714 0000", ValidTo: "2019-03-25T16:01:55.714 0000" },
{ ValidFrom: "2019-03-26T16:01:55.714 0000", ValidTo: "2019-03-25T16:01:55.714 0000" },
{ ValidFrom: "2019-03-25T16:01:55.714 0000", ValidTo: "2019-03-27T16:01:55.714 0000" }
Я хотел бы видеть этот результат с помощью одного запроса:
{ "Day": "2019-03-25", ValidFromCount: 2, ValidToCount: 2 },
{ "Day": "2019-03-26", ValidFromCount: 1, ValidToCount: 0 },
{ "Day": "2019-03-27", ValidFromCount: 0, ValidToCount: 1 }
В настоящее время я написал эту агрегацию, но теперь я застрял:
{
$addFields: {
ValidFromDay: { $dateToString: { format: "%Y-%m-%d", date: "$ValidFrom" } },
ValidUntilDay: { $dateToString: { format: "%Y-%m-%d", date: "$ValidUntil" } }
}
},
{
$group : {
_id: { FromDate: '$ValidFromDay', ToDate: '$ValidUntilDay' },
Count: { "$sum": 1 },
}
},
{
$group : {
_id: null,
FromDates: { "$addToSet": { "Date": "$_id.FromDate", "FromCount": { "$sum": "$Count" } } },
ToDate: { "$addToSet": { "Date": "$_id.ToDate", "UntilCount": "$Count" } }
}
}
Возможно ли каким-либо образом получить результаты, которые я ищу?
Ответ №1:
Вам нужно добавить массив из 2 полей, а не просто 2 поля. Это позволит вам развернуть его и посчитать по дате:
{
$addFields: {
boundary: [
{ day: {$dateToString: { format: "%Y-%m-%d", date: "$ValidFrom" } }, from: 1 },
{ day: { $dateToString: { format: "%Y-%m-%d", date: "$ValidTo" } } , to: 1 }
]
}
},
{
$unwind: "$boundary"
},
{
$group: {
_id: "$boundary.day",
ValidFromCount: {$sum: "$boundary.from"},
ValidToCount: {$sum: "$boundary.to"},
}
}
Ответ №2:
Я думаю, это сделает то, что вы хотите. Конвейер состоит из трех этапов. $project
Который создает отдельные поля дня, месяца и года.
> projector
{
"$project" : {
"day" : {
"$dayOfMonth" : "$ValidFrom"
},
"month" : {
"$month" : "$ValidFrom"
},
"year" : {
"$year" : "$ValidFrom"
},
"ValidFrom" : 1
}
}
Затем $group
создать итоговые данные и посчитать их по отдельным дням с помощью _id
of {year, month, day}
.
> grouper
{
"$group" : {
"_id" : {
"year" : "$year",
"month" : "$month",
"day" : "$day"
},
"ValidFromCount" : {
"$sum" : 1
},
"ValidToCount" : {
"$sum" : 1
}
}
}
Наконец, проекция для устранения ложных полей, а также приведения Day
поля в нужный вам формат.
> converter
{
"$project" : {
"_id" : 0,
"Day" : {
"$concat" : [
{
"$toString" : "$_id.year"
},
"-",
{
"$toString" : "$_id.month"
},
"-",
{
"$toString" : "$_id.day"
}
]
},
"ValidFromCount" : 1,
"ValidToCount" : 1
}
}
для запуска просто выполните (я создал ваши данные в коллекции so2):
> db.so2.find()
{ "_id" : ObjectId("5ca75adfd1a64a2919883a8d"), "ValidFrom" : "2019-03-25T16:01:55.714 0000", "ValidTo" : "2019-03-25T16:01:55.714 0000" }
{ "_id" : ObjectId("5ca75adfd1a64a2919883a8e"), "ValidFrom" : "2019-03-26T16:01:55.714 0000", "ValidTo" : "2019-03-25T16:01:55.714 0000" }
{ "_id" : ObjectId("5ca75adfd1a64a2919883a8f"), "ValidFrom" : "2019-03-25T16:01:55.714 0000", "ValidTo" : "2019-03-27T16:01:55.714 0000" }
>
> db.so3.aggregate([projector,grouper,converter])
{ "ValidFromCount" : 1, "ValidToCount" : 1, "Day" : "2019-3-26" }
{ "ValidFromCount" : 2, "ValidToCount" : 2, "Day" : "2019-3-25" }
>
Я не уверен, что предоставленные вами тестовые данные верны, потому что второй документ, похоже, возвращается в прошлое, поэтому ValidTo
находится перед ValidFrom
.