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

По дате в mongodb

Я работаю над проектом, в котором я отслеживаю количество кликов по теме.

Я использую mongodb, и мне нужно сгруппировать число кликов по дате (я хочу сгруппировать данные в течение 15 дней).

У меня есть хранилище данных в следующем формате в mongodb

{ 
   "_id" : ObjectId("4d663451d1e7242c4b68e000"), 
  "date" : "Mon Dec 27 2010 18:51:22 GMT+0000 (UTC)", 
  "topic" : "abc", 
  "time" : "18:51:22"
}
{ 
    "_id" : ObjectId("4d6634514cb5cb2c4b69e000"), 
    "date" : "Mon Dec 27 2010 18:51:23 GMT+0000 (UTC)", 
    "topic" : "bce", 
    "time" : "18:51:23"
}

Я хочу группировать число кликов по теме: abc by days (в течение 15 дней).. Я знаю, как сгруппировать это, но как я могу группировать по дате, которые хранятся в моей базе данных

Я ищу результат в следующем формате

[
  {
    "date" : "date in log",
    "click" : 9 
  },  
  {
    "date" : "date in log",
    "click" : 19
  },  
]

Я написал код, но он будет работать только в том случае, если дата находится в строке (код здесь http://pastebin.com/2wm1n1ix) ... пожалуйста, назовите меня, как мне сгруппировать его

4b9b3361

Ответ 1

Новый ответ с использованием механизма агрегирования Mongo

После того, как на этот вопрос был задан и получен ответ, 10gen выпустила Mongodb версии 2.2 со структурой агрегации, которая теперь является лучшим способом выполнить такой запрос. Этот запрос немного сложен, потому что вы хотите сгруппировать по дате, а сохраненные значения являются временными метками, поэтому вам нужно что-то сделать, чтобы преобразовать временные метки в совпадающие даты. Для целей примера я просто напишу запрос, который получает правильные значения.

db.col.aggregate(
   { $group: { _id: { $dayOfYear: "$date"},
               click: { $sum: 1 } } }
   )

Это вернет что-то вроде:

[
    {
        "_id" : 144,
        "click" : 165
    },
    {
        "_id" : 275,
        "click" : 12
    }
]

Вам нужно использовать $match чтобы ограничить запрос диапазоном дат, который вас интересует, и $project чтобы переименовать _id на date. Как вы конвертируете день года назад в дату, оставлено читателю в качестве упражнения. :-)

10gen имеет удобный график конвертации SQL в Mongo Aggregation, который стоит добавить в закладки. Также есть специальная статья об операторах агрегации дат.

Приобретая немного больше, вы можете использовать:

db.col.aggregate([
  { $group: {
      _id: {
        $add: [
         { $dayOfYear: "$date"}, 
         { $multiply: 
           [400, {$year: "$date"}]
         }
      ]},   
      click: { $sum: 1 },
      first: {$min: "$date"}
    }
  },
  { $sort: {_id: -1} },
  { $limit: 15 },
  { $project: { date: "$first", click: 1, _id: 0} }
])

что даст вам последние 15 дней и вернет некоторое время в течение каждого дня в поле date. Например:

[
    {
        "click" : 431,
        "date" : ISODate("2013-05-11T02:33:45.526Z")
    },
    {
        "click" : 702,
        "date" : ISODate("2013-05-08T02:11:00.503Z")
    },
            ...
    {
        "click" : 814,
        "date" : ISODate("2013-04-25T00:41:45.046Z")
    }
]

Ответ 2

Поздний ответ, но для записи (для всех, кто приходит на эту страницу): вам нужно использовать аргумент "keyf" вместо "key", так как ваш ключ на самом деле будет функцией дата события (т.е. "день", извлеченный с даты), а не сама дата. Это должно делать то, что вы ищете:

db.coll.group(
{
    keyf: function(doc) {
        var date = new Date(doc.date);
        var dateKey = (date.getMonth()+1)+"/"+date.getDate()+"/"+date.getFullYear()+'';
        return {'day':dateKey};
    },
    cond: {topic:"abc"},
    initial: {count:0},
    reduce: function(obj, prev) {prev.count++;}
});

Для получения дополнительной информации просмотрите страницу документа MongoDB по агрегации и группе: http://www.mongodb.org/display/DOCS/Aggregation#Aggregation-Group

Ответ 3

Это может помочь

return new Promise(function(resolve, reject) {
db.doc.aggregate(
            [
                { $match: {} },
                { $group: { _id: { $dateToString: { format: "%Y-%m-%d", date: "$date" } }, count: { $sum: 1 } } },
                { $sort: { _id: 1 } }
            ]
        ).then(doc => {
            /* if you need a date object */
            doc.forEach(function(value, index) {
                  doc[index]._id = new Date(value._id);
              }, this);
            resolve(doc);
        }).catch(reject);
}

Ответ 4

Не так много работал с MongoDB, поэтому я не совсем уверен. Но разве вы не можете использовать полный Javascript?
Таким образом, вы можете проанализировать свою дату с помощью класса Javascript Date, создать свою дату для дня и установить ключ в свойство "выход". И всегда добавляйте один, если ключ уже существует, иначе создайте его new со значением = 1 (первый клик). Ниже приведен код с адаптированной функцией сокращения (непроверенный код!):

db.coll.group(
{
   key:{'date':true},
   initial: {retVal: {}},
   reduce: function(doc, prev){
              var date = new Date(doc.date);
              var dateKey = date.getFullYear()+''+date.getMonth()+''+date.getDate();
              (typeof prev.retVal[dateKey] != 'undefined') ? prev.retVal[dateKey] += 1 : prev.retVal[dateKey] = 1;
            }, 
   cond: {topic:"abc"}
}
)

Ответ 5

Еще один поздний ответ, но все же. Поэтому, если вы хотите сделать это всего за одну итерацию и получить количество кликов, сгруппированных по дате и теме, вы можете использовать следующий код:

db.coll.group(
{
   $keyf : function(doc) {
       return { "date" : doc.date.getDate()+"/"+doc.date.getMonth()+"/"+doc.date.getFullYear(),
                "topic": doc.topic };
    },
    initial: {count:0},
    reduce: function(obj, prev) { prev.count++; }
 })

Также, если вы хотите оптимизировать запрос, как предлагается, вы можете использовать целочисленное значение для даты (подсказка: используйте valueOf() для ключевой даты вместо строки, хотя для моих примеров скорость была одинаковой.

Кроме того, всегда разумно регулярно проверять документы MongoDB, поскольку они постоянно добавляют новые функции. Например, с новой структурой Aggregation, которая будет выпущена в версии 2.2, вы можете добиться тех же результатов гораздо проще http://docs.mongodb.org/manual/applications/aggregation/

Ответ 6

спасибо за @mindthief, ваш ответ поможет решить мою проблему сегодня. Функция ниже может группироваться днем ​​немного легче, надеюсь, может помочь другим.

/**
 * group by day
 * @param query document {key1:123,key2:456}
 */
var count_by_day = function(query){
    return db.action.group(
    {
        keyf: function(doc) {
            var date = new Date(doc.time);
            var dateKey = (date.getMonth()+1)+"/"+date.getDate()+"/"+date.getFullYear();
            return {'date': dateKey};
        },
        cond:query,
        initial: {count:0},
        reduce: function(obj, prev) {
          prev.count++;
        }
    });
}

count_by_day({this:'is',the:'query'})

Ответ 7

Если вы хотите, чтобы Date oject возвращался напрямую

Затем вместо применения операторов агрегирования даты вместо этого примените "Date Math" для округления объекта даты. Это часто бывает желательно, поскольку все драйверы представляют дату BSON в форме, которая обычно используется для манипуляций Date для всех языков, где это возможно:

db.datetest.aggregate([
    { "$group": {
        "_id": {
            "$add": [
                { "$subtract": [
                    { "$subtract": [ "$date", new Date(0) ] },
                    { "$mod": [
                        { "$subtract": [ "$date", new Date(0) ] },
                        1000 * 60 * 60 * 24
                    ]}
                ]},
                new Date(0)
            ]
        },
        "click": { "$sum": 1 }
    }}
])

Или, если подразумевается в вопросе о том, что требуемый интервал группировки "ведра" составляет 15 дней, просто примените это к числовому значению в $mod:

db.datetest.aggregate([
    { "$group": {
        "_id": {
            "$add": [
                { "$subtract": [
                    { "$subtract": [ "$date", new Date(0) ] },
                    { "$mod": [
                        { "$subtract": [ "$date", new Date(0) ] },
                        1000 * 60 * 60 * 24 * 15
                    ]}
                ]},
                new Date(0)
            ]
        },
        "click": { "$sum": 1 }
    }}
])

Приведенная математическая математика заключается в том, что если $subtract два объекта Date, результат будет миллисекундами differnce численно. Таким образом, эпоха представлена ​​Date(0) как базой для преобразования в любом конструкторе языка, который у вас есть.

С числовым значением, применяется "modulo" ($mod) для округления даты (вычесть остаток из деления) до требуемого интервала. Также:

1000 миллисекунд x 60 секунд * 60 минут * 24 часа = 1 день

Или

1000 миллисекунд x 60 секунд * 60 минут * 24 часа * 15 дней = 15 дней

Таким образом, он гибкий для любого требуемого интервала.

Тем же способом $add операция между "числовым" значением и объектом Date возвращает Date объект, эквивалентный значению millseconds обоих объектов (эпоха равна 0, поэтому 0 плюс разница - это преобразованная дата).

Легко представлен и воспроизводится в следующем листинге:

var now = new Date();
var bulk = db.datetest.initializeOrderedBulkOp();

for ( var x = 0; x < 60; x++ ) {
    bulk.insert({ "date": new Date( now.valueOf() + ( 1000 * 60 * 60 * 24 * x ))});
}

bulk.execute();

И запустив второй пример с 15-дневными интервалами:

{ "_id" : ISODate("2016-04-14T00:00:00Z"), "click" : 12 }
{ "_id" : ISODate("2016-03-30T00:00:00Z"), "click" : 15 }
{ "_id" : ISODate("2016-03-15T00:00:00Z"), "click" : 15 }
{ "_id" : ISODate("2016-02-29T00:00:00Z"), "click" : 15 }
{ "_id" : ISODate("2016-02-14T00:00:00Z"), "click" : 3 }

Или аналогичное распределение в зависимости от текущей даты при запуске листинга, и, конечно, 15-дневные интервалы будут согласованы с даты эпохи.

Использование метода "Math" немного легче настраивается, особенно если вы хотите настроить периоды времени для разных часовых поясов в выводе агрегации, где вы можете так же настроить цифру, добавив/вычитая числовое отличие от UTC.

Ответ 8

Конечно, который является хорошим решением. Помимо этого вы можете группировать даты по дням в виде строк (как которые отвечают) или вы можете получить начало дат, указав поле даты (в совокупности) следующим образом:

{'$project': {
    'start_of_day': {'$subtract': [
        '$date',
        {'$add': [
            {'$multiply': [{'$hour': '$date'}, 3600000]},
            {'$multiply': [{'$minute': '$date'}, 60000]},
            {'$multiply': [{'$second': '$date'}, 1000]},
            {'$millisecond': '$date'}
        ]}
    ]},
}}

Это дает вам следующее:

{
    "start_of_day" : ISODate("2015-12-03T00:00:00.000Z")
},
{
    "start_of_day" : ISODate("2015-12-04T00:00:00.000Z")
}

У него есть несколько плюсов: вы можете манипулировать своими днями в типе даты (не числом или строкой), он позволяет использовать все агрегацию дат операторов в следующих операциях агрегации и выдает тип даты на выходе.

Ответ 9

На этот вопрос уже есть много ответов, но я не был доволен ни одним из них. MongoDB улучшился за эти годы, и теперь есть более простые способы сделать это. Ответ Джонаса Томанги дает правильный ответ, но он слишком сложный.

Если вы используете MongoDB 3.0 или более поздней версии, здесь вы можете сгруппировать по дате. Я начинаю с агрегации $match потому что автор также спросил, как ограничить результаты.

db.yourCollection.aggregate([
  { $match: { date: { $gte: ISODate("2019-05-01") } } },
  { $group: { _id: { $dateToString: { format: "%Y-%m-%d", date: "$date"} }, count: { $sum: 1 } } },
  { $sort: { _id: 1} }
])