Подтвердить что ты не робот

Преобразование типа данных в 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 для достижения этого?

Заранее благодарим за любой совет.

4b9b3361

Ответ 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}}
);