我在MongoDB中有一个名为Document的集合。此集合中的文档具有以ISO日期类型存储的名为CreationDate的字段。我的任务是计算每天创建的文档数量,并按异步数量排序。输出格式必须是 [{_id:' yyyy-MM-dd',cnt:x}]。我尝试使用如下聚合框架。
db.Document.aggregate(
, {$project: {_id:1, Year:{$year:'$CreationDate'}, Month:{$month:'$CreationDate'}, Date:{$dayOfMonth:'$CreationDate'}}}
, {$group: {_id:{$concat:['$Year', '-', '$Month', '-', '$Date']}, cnt:{$sum:1}}}
, {$sort:{'cnt':-1}}
);
代码给出了如下错误:
$concat only supports strings, not NumberInt32
我理解这是因为$ year,$ month和$ dayOfMonth都是返回号码。可以将_id字段组合为对象,并在应用程序级别以所需格式重新格式化它。
但从技术角度来看,我有两个问题:
如何在MongoDB shell中将数字转换为字符串?在这种情况下,$ year的输出可以转换为字符串并在$ concat中使用。
有没有更好的方法将ISODate输出格式化为各种日期格式?在许多情况下,我们只需要ISODate的某些部分,例如:日期组件或时间部分。是否有任何MongoDb内置运营商来实现这一目标?
提前感谢任何建议。
答案 0 :(得分:28)
您可以使用$concat
执行此操作,但首先需要通过$substr
转换为字符串,同时处理双位数字的情况:
db.Document.aggregate([
{ "$group": {
"_id":{
"$concat": [
{ "$substr": [ { "$year": "$CreationDate" }, 0, 4 ] },
"-",
{ "$cond": [
{ "$gt": [ { "$month": "$CreationDate" }, 9 ] },
{ "$substr": [ { "$month": "$CreationDate" }, 0, 2 ] },
{ "$concat": [
"0",
{ "$substr": [ { "$month": "$CreationDate" }, 0, 1 ] },
]},
]},
"-",
{ "$cond": [
{ "$gt": [ { "$dayOfMonth": "$CreationDate" }, 9 ] },
{ "$substr": [ { "$dayOfMonth": "$CreationDate" }, 0, 2 ] },
{ "$concat": [
"0",
{ "$substr": [ { "$dayOfMonth": "$CreationDate" }, 0, 1 ] },
]}
]}
]
},
{ "cnt": { "$sum": 1 } }
}}
{ "$sort":{ "cnt" :-1 }}
]);
可能更好的是只使用日期数学,这会返回一个纪元时间戳值,但在后期处理中很容易使用日期对象:
db.Document.aggregate([
{ "$group": {
"_id": {
"$subtract": [
{ "$subtract": [ "$CreationDate", new Date("1970-01-01") ] },
{ "$mod": [
{ "$subtract": [ "$CreationDate", new Date("1970-01-01") ] },
1000 * 60 * 60 * 24
]}
]
},
"cnt": { "$sum": 1 }
}},
{ "$sort": { "cnt": -1 } }
])
答案 1 :(得分:13)
将ISODate转换为各种日期格式的另一种更简单方法是使用$dateToString aggregation operator。
db.collection.aggregate([
{ $group: {
_id: { $dateToString: { format: "%Y-%m-%d %H:%M", date: "$CreationDate" } },
count: { $sum: 1 }
}},
{ $sort : { count: -1 }}
])
答案 2 :(得分:4)
Neil回答了它的权利,但有一点点错误。在条件中(检查月份和日期的数量是否需要加上0)不是$gt
而是$lte
。
使用$lte
时,只有一个数字的月份和日期将以0开头。
例如:2014-10- 0 3,2014- 0 8- 0 3.
答案 3 :(得分:4)
如果一些可怜的灵魂在2017年偶然遇到这个问题,就像我一样: 从Mongo 3.0开始,现在有一个dateToString运算符
这意味着如果您有正确的日期(),您应该能够做到:
db.Document.aggregate(
, {$project: {_id:1, CreationDate:1}
, {$group: {
_id : { $dateToString: { format: "%Y-%m-$d", date: "$CreationDate" } },
cnt:{$sum:1}}}
, {$sort:{'cnt':-1}}
);
对于我们这些碰巧将日期存储在非日期字段中的人(快乐!),您可以在项目步骤中创建一个新的Date()对象。 在我的例子中,日期存储为毫秒(整数),我将毫秒数添加到0日期。
db.Document.aggregate(
, {$project: {
_id:1,
CreationDate: {"$add": [ new Date(0), "$CreatedOn" ]}
}
, {$group: {
_id : { $dateToString: { format: "%Y-%m-$d", date: "$CreationDate" } },
cnt:{$sum:1}}}
, {$sort:{'cnt':-1}}
);
答案 4 :(得分:2)
将日期格式转换为“xxxx-xx-xx”
db.getCollection('analytics').aggregate([
{$project: {
day: {$dayOfMonth: "$createdAt"},
month: {$month: "$createdAt"},
year: {$year: "$createdAt"},
}
},
{$project: {
day: {$concat:["0", {$substr:["$day",0, 2]}]},
month: {$concat:["0", {$substr:["$month",0, 2]}]},
year: {$substr:["$year",0, 4]},
}
},
{$project: {date: {$concat: [{$substr:["$year",0, 4]},"-", {$substr:["$month",0, 2]},"-", {$substr:["$day",0, 2]}]}}},
]);