Преобразование типа данных в MongoDB
У меня есть коллекция под названием Document в MongoDB. Документы в этой коллекции имеют поле под названием CreationDate, хранящееся в типе даты ISO. Моя задача - подсчитать количество документов, созданных за день, и отсортировать по числу асинхронно. Формат вывода должен быть
[{_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? В этом случае вывод $year можно затем преобразовать в строку и использовать в $concat.
-
Есть ли лучший способ форматировать вывод ISODate в различные форматы даты? Во многих случаях нам нужна определенная часть ISODate, например: компонент даты или временная часть. Есть ли встроенные операторы MongoDb для достижения этого?
Заранее благодарим за любой совет.
Ответы
Ответ 1
Вы можете сделать это с помощью $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 } }
])
Ответ 2
Еще один простой способ конвертировать ISODate в различные форматы даты - это использовать оператор $dateToString.
db.collection.aggregate([
{ $group: {
_id: { $dateToString: { format: "%Y-%m-%d %H:%M", date: "$CreationDate" } },
count: { $sum: 1 }
}},
{ $sort : { count: -1 }}
])
Ответ 3
Нейл отвечает своим правом на точку, но имеет небольшую ошибку. В условных выражениях (чтобы проверить, должно ли число месяца и дня должно быть добавлено с 0), это не $gt
, а $lte
.
С $lte
только месяцы и дни с одной цифрой будут добавлены с 0.
например: 2014-10- 0 3, 2014- 0 8- 0 3.
Ответ 4
Чтобы преобразовать формат даты в "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]}]}}},
]);
Ответ 5
ДЛЯ МОНГО >= 3.0 (после ~ 2015)
Если некоторые бедные души наткнутся на этот вопрос в 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}}
);