如何按一定的日期范围对MongoDB记录进行分组



我对MongoDB有点陌生,在查询时遇到了问题。

假设我有以下数据集,

[
{
_id: '1',
date: "2020-12-31T22:02:11.257Z",
},
{
_id: '2',
date: "2020-12-31T22:05:11.257Z",
},
{
_id: '3',
date: "2021-01-01T22:02:11.257Z",
},
{
_id: '4',
date: "2021-01-02T12:02:11.257Z",
},
{
_id: '5',
date: "2021-01-02T22:02:11.257Z",
}
]

我正在尝试按天对所有记录进行分组。在我的前端,我发送了一个多月的信息,然后基于此运行查询。因此,如果用户选择一月,我将运行以下查询:

router.get('/', async (req, res) => {
const {selectedMonth, selectedYear} = req.query; // january would be '1' here
const data = await db.collection.find({"date": {
"$gt": new Date(selectedYear, parseInt(selectedMonth) - 1, 1),
"$lte": new Date(selectedYear, parseInt(selectedMonth), 1)
}}).sort({ date: -1 })

在这里,我正在获取所选范围内的所有记录。因此,如果用户选择2021年1月,我将获取大于2020年12月31日且小于或等于2021年1日的所有记录。

这里的问题是,我想得到每天所有记录的计数。我可以在指定的日期范围内获取所有记录,但我正在寻找类似以下的东西,以便返回:

[
"2021-01-01": [
{ _id: '3', date: "2021-01-01T22:02:11.257Z" },
],
"2021-01-02": [
{ _id: '4', date: "2021-01-02T12:02:11.257Z" },
{ _id: '5', date: "2021-01-02T22:02:11.257Z" },
]
]

我想循环返回的数据并构建自己的响应对象,但我想知道是否有更好的方法可以做到这一点?以下是我目前正在做的事情,

const result = []
let count = 0;
data.forEach((record, index) => {
// first record will always set the base
if (index === 0) {
result.push({
date: record.date.toLocaleDateString(),
count: 1
})
} else {
// If the record is the same date, then increase counter
if (record.date.toLocaleDateString() === result[count].date) {
result[count].count = result[count].count + 1
} else {
// push a new record and increase count
result.push({
date: record.date.toLocaleDateString(),
count: 1
})
count = count + 1
}
}
});

哪个收益率,

result [
{ date: '1/2/2021', count: 2 },
{ date: '1/1/2021', count: 1 }
]

您需要聚合管道:

db.collection.aggregate([
// First Stage: filter out dates
{
$match: {
date: { $gte: new ISODate("2020-01-01"), $lt: new ISODate("2020-12-31") },
},
},
// Second Stage: group by day of the year
{
$group: {
_id: { $dateToString: { format: "%d-%m-%Y", date: "$date" } },
count: { $sum: 1 },
},
},
// Third Stage, reshape the output documents
{
$project: {
_id: 0,
date: "$_id",
count: 1
},
},
]);

您需要的东西可以使用聚合框架来完成,该框架有许多可以使用的运算符用于不同的管道。第一个管道步骤是过滤,在这里您将$match管道阶段与$expr查询运算符和$month$year日期运算符:

const pipeline = [
// First pipeline step
{ '$match': {
'$expr': {
'$and': [
{ '$eq': [ { '$month': '$date' }, parseInt(selectedMonth) ] },
{ '$eq': [ { '$year': '$date' }, parseInt(selectedYear) ] }
]
}
} }
];

下一步是将筛选后返回的所有文档与$group中的$dateToString按天分组,如下所示:

const pipeline = [
// First pipeline step
{ '$match': {
'$expr': {
'$and': [
{ '$eq': [ { '$month': '$date' }, parseInt(selectedMonth) ] },
{ '$eq': [ { '$year': '$date' }, parseInt(selectedYear) ] }
]
}
} },
// Second pipeline step
{ '$group': {
'_id': { '$dateToString': { 'format': '%Y-%m-%d', 'date': '$date' } },
'data': { '$push': '$$ROOT' },
'count': { '$sum': 1 }
} }
];

接下来的步骤是将文档重塑为您想要的投影,在那里您可以利用$arrayToObject运算符和$replaceRoot管道来获得想要的结果。

const pipeline = [
// First pipeline step
{ '$match': {
'$expr': {
'$and': [
{ '$eq': [ { '$month': '$date' }, parseInt(selectedMonth) ] },
{ '$eq': [ { '$year': '$date' }, parseInt(selectedYear) ] }
]
}
} },
// Second pipeline step
{ '$group': {
'_id': { '$dateToString': { 'format': '%Y-%m-%d', 'date': '$date' } },
'data': { '$push': '$$ROOT' },
'count': { '$sum': 1 }
} },
// Third pipeline step
{  '$group': {
'_id': null,
'counts': {
'$push': {
'k': '$_id',
'v': {
'data': '$data',
'count': '$count'
}
}
}
} },
// Fourth pipeline step
{  '$replaceRoot': {
'newRoot': { '$arrayToObject': '$counts' }
} }
];

然后可以组合并运行如下:

router.get('/', async (req, res) => {
const { selectedMonth, selectedYear } = req.query; // january would be '1' here
const pipeline = [...]; // pipeline above
const data = await db.collection.aggregate(pipeline).toArray();
console.log(data);
}

对于表格的最终结果:

[
{
"2021-01-01": [
{ _id: '3', date: "2021-01-01T22:02:11.257Z" },
],
"2021-01-02": [
{ _id: '4', date: "2021-01-02T12:02:11.257Z" },
{ _id: '5', date: "2021-01-02T22:02:11.257Z" },
]
}
]

将您的第三个管道步骤更新为:

// Third pipeline step
{  '$group': {
'_id': null,
'counts': {
'$push': {
'k': '$_id',
'v': '$data'
}
}
} },

对于表格的最终结果:

[
{
"2021-01-01": 1,
"2021-01-02": 2
}
]

你的第三步应该是:

// Third pipeline step
{  '$group': {
'_id': null,
'counts': {
'$push': {
'k': '$_id',
'v': '$count'
}
}
} },

最新更新