группа pymongo по дате и времени

Я пытаюсь выполнить поиск в коллекции и сгруппировать записи по полю даты, которое является датой и временем. Я знаю, что pymongo преобразует их в правильный тип на заднем плане (ISODate или что-то в этом роде).

Вопрос в том, что, поскольку объекты datetime имеют дату, время, часовой пояс... как я могу указать оператору группы использовать только часть даты? Потому что в противном случае я не получу желаемую группировку, поскольку время не позволяет сгруппировать записи с одним и тем же днем, месяцем, годом.

db.test.aggregate([
        {"$group": {
             "_id": "$date", 
             "count": {"$sum": 1}
        }},
        {"$limit": 10}])

Результат:

{u'ok': 1.0,
 u'result': [
  {u'_id': datetime.datetime(2014, 2, 15, 18, 49, 9, tzinfo=<bson.tz_util.FixedOffset object at 0x318f210>),
   u'count': 1},
  {u'_id': datetime.datetime(2014, 2, 15, 18, 36, 38, tzinfo=<bson.tz_util.FixedOffset object at 0x318f210>),
   u'count': 1},
  {u'_id': datetime.datetime(2014, 2, 15, 18, 23, 56, tzinfo=<bson.tz_util.FixedOffset object at 0x318f210>),
   u'count': 1}]}

Было бы неплохо контролировать информацию о дате и времени, используемую для группировки,

  • группировать только по дате
  • группировать по дате и часу
  • группировка по дате, часам и минутам
  • и т. д.

Есть ли что-то вроде: (или какой-то способ сказать, чтобы использовать только дату)

db.test.aggregate([
          {"$group": {
              "_id": "$date.date()",
              "count": {"$sum": 1}
          }},
          {"$sort": "_id"}
])

Или, может быть, есть другой способ справиться с этим, есть идеи? Спасибо.


person Sebastian    schedule 26.02.2014    source источник


Ответы (1)


Да. Вы можете использовать операторы даты с $substr и $concat, чтобы связать все вместе.

db.test.aggregate([
    {"$group": {
        "_id" : { "$concat": [
            {"$substr": [{"$year": "$date"}, 0, 4 ]},
            "-",
            {"$substr": [{"$month": "$date"}, 0, 2 ]},
            "-",
            {"$substr": [{"$dayOfMonth": "$date"}, 0, 2 ]},
        ]},
        "count": {"$sum": 1 }
     }},
     {"$sort": { "_id": 1 }}
])

Вы можете использовать только операторы даты и сделать документ следующим образом:

"day": { 
    "year": {"$year": "$date" },
   "month": {"$month": "$date"}, 
   "day": {"$dayOfYear": "$date"}
}

Это работает так же хорошо. Но это дает вам хорошую строку. Это использует тот факт, что $substr преобразует целое число в строку. Если это когда-нибудь будет добавлено в документацию.

Посмотрите документацию по операторам даты для использования в других временных интервалах, которые можно использовать на свиданиях.


Еще лучше использовать математику даты, чтобы вернуть дату BSON:

import datetime

db.test.aggregate([
    { "$group": {
        "_id": {
            "$add": [
               { "$subtract": [
                   { "$subtract": [ "$date", datetime.datetime.utcfromtimestamp(0) ] },
                   { "$mod": [
                       { "$subtract": [ "$date", datetime.datetime.utcfromtimestamp(0) ] },
                       1000 * 60 * 60 * 24
                   ]}
               ]},
               datetime.datetime.utcfromtimestamp(0)
           ]
        },
        "count": { "$sum": 1 }
    }},
    { "$sort": { "_id": 1 } }
])

Здесь datetime.datetime.utcfromtimestamp(0) будет передаваться в конвейер как BSON Дата, представляющая «эпоху». Когда вы $subtract одну дату BSON от другой, возвращается разница в миллисекундах . Это позволяет «округлить» дату до текущего дня, снова вычитая $mod результат, чтобы получить оставшуюся разницу в миллисекундах со дня.

То же самое относится и к $add, где "добавление" даты BSON к числовое значение приведет к дате BSON.

person Neil Lunn    schedule 26.02.2014
comment
Спасибо! это прекрасно работает (я исправил скобки в $substr) - person Sebastian; 26.02.2014