迭代 PyMongo 游标抛出 InvalidBSON:年份超出范围

问题描述 投票:0回答:4

我正在使用 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 中进行这样的日期是疯狂的,但我对此无能为力,因为这不是我的数据。

python mongodb mongodb-query pymongo
4个回答
7
投票

PyMongo FAQ 中实际上有一个关于这个主题的部分:

为什么我会收到另一种语言驱动程序存储的解码日期溢出错误?

PyMongo 将 BSON 日期时间值解码为 Python

datetime.datetime
的实例。
datetime.datetime
的实例仅限于
datetime.MINYEAR
(通常为 1)和
datetime.MAXYEAR
(通常为 9999)之间的年份。一些 MongoDB 驱动程序(例如 PHP 驱动程序)可以存储 BSON 日期时间,其年份值远远超出
datetime.datetime
支持的范围。

因此,这里的基本约束是驱动程序为从 BSON 进行映射而实现的

datetime.datetime
类型,尽管这可能很“荒谬”,但对于其他语言创建这样的日期值来说是有效的。

如常见问题解答中所述,您的一般解决方法是:

  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”日期限制。因此,在所有文档中以及任何代码仍在编写该数字的地方“降低”该数字可能是相当安全的。


1
投票

我对 pymongo 查询也有同样的问题。 当我尝试查询我的数据时,我收到此错误:

year 53427 is out of range

因为我的一些值是 Unix 时间,一些是 IOS 时间。

我的解决方案:

  1. 首先根据日期大于未来日期(2023)的条件,将 UNIX 时间标记为 1,将正常时间标记为 0
{$addFields: {
    Unix_time_index: {$cond: 
        { if: { $gt: [ "$mytime", ISODate("2023-12-12T00:00:00.000Z") ] },
          then: 1,
          else: 0 }},
        }},
  1. 然后将我的时间更改为 Long 类型数字(仅适用于 UNIX 时间):
{$addFields:{
    mytime: {$cond:
        { if: { $eq: [ "$Unix_time_index", 1 ] },
          then: {$toLong: "$mytime"},
          else: "$mytime" }},
        }},
  1. 然后将长unix时间除以1000:
{$addFields:{
    mytime: {$cond:
        { if: { $eq: [ "$Unix_time_index", 1 ] },
          then: { $divide: [ "$mytime",1000 ] },
          else: "$mytime" }},
                }},
  1. 最后将长unix数字转换为常规数字:
{$addFields:{
mytime: {$cond: 
        { if: { $eq: [ "$Unix_time_index", 1 ] },
          then: {$toDate: "$mytime"},
          else: "$mytime" }}
                }},

结果:

/* 1 */
{
    "_id" : ObjectId("606b5d48ab86b4002130f944"),
    "mytime" : ISODate("2021-06-18T21:52:51.000Z"),
    "before" : Date(1624053171000000),
    "Unix_time_index" : 1.0
}

/* 2 */
{
    "_id" : ObjectId("606b5d48ab86b4002130f944"),
    "mytime" : ISODate("2021-06-18T21:52:47.000Z"),
    "before" : Date(1624053167000000),
    "Unix_time_index" : 1.0
}

/* 3 */
{
    "_id" : ObjectId("606b5d48ab86b4002130f944"),
    "mytime" : ISODate("2022-01-27T23:44:46.000Z"),
    "before" : ISODate("2022-01-27T23:44:46.000Z"),
    "Unix_time_index" : 0.0
}

0
投票

我刚刚遇到了同样的问题:

正如尼尔提到的,我引用:

PyMongo 将 BSON 日期时间值解码为 Python datetime.datetime 的实例。 datetime.datetime 的实例是有限的

所以仔细观察我的对象后,我注意到一些日期对象是这样的:

0000:12:30T00:00:00

就我而言,最好的方法是在将文档插入集合之前设置验证,因此如果日期为 0 ,将其设置为 1970 可以解决问题


0
投票

这让我发疯。我在 Amazon Linux 上运行了一个类似的脚本并且不断得到

20245 年超出范围

然后我在 Windows 11 上的 Ubuntu WSL 上运行它并得到相同的错误,但有一些有用的信息:

20245 年超出范围(考虑使用 CodecOptions(datetime_conversion=DATETIME_AUTO) 或 MongoClient(datetime_conversion='DATETIME_AUTO'))。请参阅:https://pymongo.readthedocs.io/en/stable/examples/datetimes.html#handling-out-of-range-datetimes

事实上,使用选项 datetime_conversion="DATETIME_AUTO" 创建 MongoClient 解决了我的问题:

client = MongoClient(CONNECTION_STRING,datetime_conversion="DATETIME_AUTO")
© www.soinside.com 2019 - 2024. All rights reserved.