Итерация по PyMongo создает курсор InvalidBSON: год вне диапазона - PullRequest
0 голосов
/ 06 ноября 2018

Я использую PyMongo, чтобы просто перебирать коллекцию Mongo, но я борюсь с обработкой больших объектов дат Mongodb.

Например, если у меня есть данные в коллекции, которые выглядят так:

"bad_data" : [ 
            {
                "id" : "id01",
                "label" : "bad_data",
                "value" : "exist",
                "type" : "String",
                "lastModified" : ISODate("2018-06-01T10:04:35.000Z"),
                "expires" : Date(9223372036854775000)
            }
        ]

Я сделаю что-то вроде:

from pymongo import MongoClient, database, cursor, collection
client = MongoClient('localhost')
db = client['db1']
db.authenticate('user', 'pass', source='admin')
collection = db['collection']
for i in collection:
    # do something with i

и получите ошибку InvalidBSON: year 292278994 is out of range

Есть ли способ справиться с этим нелепым Date() объектом без падения bson? Я понимаю, что иметь такую ​​дату в Mongodb - это безумие, но я ничего не могу с этим поделать, поскольку это не мои данные.

1 Ответ

0 голосов
/ 06 ноября 2018

На самом деле есть раздел в FAQ по PyMongo на эту тему:

Почему я получаю даты декодирования OverflowError, сохраненные драйвером другого языка?

PyMongo декодирует значения даты и времени BSON в экземпляры Python datetime.datetime. Экземпляры datetime.datetime ограничены годами от datetime.MINYEAR (обычно 1) до datetime.MAXYEAR (обычно 9999). Некоторые драйверы MongoDB (например, драйвер PHP) могут хранить значения времени BSON со значениями года, намного превышающими значения, поддерживаемые datetime.datetime.

Таким образом, основное ограничение здесь относится к типу datetime.datetime, который реализован для отображения из BSON драйвером, и, хотя это может быть «нелепым», оно допустимо для других языков для создания такого значения даты.

Как указано в FAQ, ваши общие обходные пути:

  1. Сделка с оскорбительной датой BSON. Хотя он действителен для хранения, он, возможно, не был «истинным» намерением того, кто / что бы ни хранил его в первую очередь.

  2. Добавьте условие «диапазон дат» в свой код для фильтрации дат «вне диапазона»:

    result = db['collection'].find({ 
      'expires': { '$gte': datetime.min, '$lte': datetime.max }
    })
    for i in result:
      # do something with i
    
  3. Пропустите поле даты, в котором возникла ошибка, в проекции, если вам не нужны данные для дальнейшей обработки:

    result = db['collection'].find({  }, projection={ 'expires': False })
    for i in result:
      # do something with i
    

Конечно, 'expires' как следует из названия, первоначальное намерение значения было датой настолько далекой в ​​будущем, что никогда не наступит, поскольку первоначальный автор этих данных (и, возможно, текущий код все еще пишет его). не зная об ограничении даты "Python". Так что, вероятно, вполне безопасно «опускать» это число во всех документах и ​​там, где его все еще пишет какой-либо код.

...