2

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

Вопрос в том, что объекты 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}]} 

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

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

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

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

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

ответ

3

Да. Вы можете использовать Date Operators с $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 будет отличать от целых чисел до строки. Если это когда-нибудь добавится в документацию.

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


еще лучше, использовать дату математики для возвращения Даты 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 Date из другой, возвращается разница в миллисекундах. Это позволяет вам «округлить» дату до текущего дня, снова вычитая результат $mod, чтобы получить остаток в миллисекундах от дня.

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

+0

спасибо! это отлично работает (я исправил скобки на $ substr) – Sebastian