使用PyMongo和Python按日期过滤查询MongoDB文档失败问题排查
问题描述
我尝试用PyMongo和Python从MongoDB集合查询文档,集合数据如下:
{ "fees": "0.00", "trigger": "immediate", "price": "18.30000000", "recordTimestamp": "2023-01-10T12:03:27.197000Z" }, { "fees": "4.00", "trigger": "immediate", "price": "12.30000000", "recordTimestamp": "2023-02-10T12:03:27.197000Z" }, { "fees": "1.00", "trigger": "immediate", "price": "10.30000000", "recordTimestamp": "2023-03-10T12:03:27.197000Z" }
在MongoDB Compass里用过滤器 {recordTimestamp: {$gte: '2023-01-24',$lte: '2023-03-01'}} 能得到预期结果:
{ "fees": "4.00", "trigger": "immediate", "price": "12.30000000", "recordTimestamp": "2023-02-10T12:03:27.197000Z" }
但用PyMongo执行相同逻辑查询却无结果,代码如下:
stdateval = datetime(2023,1,24) endateval = datetime(2023,3,1) dtFilter = { "recordTimestamp" : {"$gte": stdateval,"$lt": endateval}} collection = DBModule.database[collectionName] jsnObj_list = collection.find(dtFilter)
我已经能用客户端手动转换日期字符串过滤的方式得到结果,但想知道PyMongo的find方法查询失败的原因是什么?
替代方案代码:
stdateval = datetime(2023, 1, 24) endateval = datetime(2023, 3, 1) jsnObj_list = collection.find({}) filtered_list = [] for item in jsnObj_list: recordTimestamp = datetime.strptime(item['recordTimestamp'], "%Y-%m-%dT%H:%M:%S.%fZ") if stdateval <= recordTimestamp < endateval: filtered_list.append(item)
问题原因
核心原因是集合中的recordTimestamp字段存储的是字符串类型,而非MongoDB原生的ISODate日期类型,但PyMongo查询时传入的是Python的datetime对象,两者类型不匹配,导致MongoDB无法正确执行范围匹配:
- MongoDB Compass中用字符串作为过滤条件时,是基于字符串的字典序比较,而你的日期字符串采用
YYYY-MM-DDTHH:MM:SS.fffZ格式,这种格式的字典序和时间顺序一致,因此能得到正确结果。 - PyMongo会自动将Python的
datetime对象转换为MongoDB的ISODate类型,用ISODate去匹配字符串类型的字段,类型不匹配自然无法匹配到任何文档。
解决方案
有两种可行的解决方式:
方式1:将查询条件转为字符串格式
既然字段是字符串类型,直接使用字符串作为过滤条件,和Compass的逻辑保持一致:
stdateval = '2023-01-24T00:00:00.000000Z' endateval = '2023-03-01T23:59:59.999999Z' dtFilter = { "recordTimestamp": {"$gte": stdateval, "$lte": endateval}} collection = DBModule.database[collectionName] jsnObj_list = collection.find(dtFilter)
方式2:修改集合字段类型为ISODate(推荐)
从长期维护和查询效率考虑,建议将recordTimestamp字段统一转换为MongoDB原生的ISODate类型,之后即可正常使用Python的datetime对象查询:
1. 批量转换现有数据
from pymongo import UpdateOne from datetime import datetime updates = [] for doc in collection.find(): # 将字符串转为datetime对象,PyMongo会自动转为ISODate ts = datetime.strptime(doc['recordTimestamp'], "%Y-%m-%dT%H:%M:%S.%fZ") updates.append(UpdateOne( {'_id': doc['_id']}, {'$set': {'recordTimestamp': ts}} )) if updates: collection.bulk_write(updates)
2. 后续插入数据时直接存储datetime对象
确保新插入的文档中,recordTimestamp字段使用Python的datetime对象,PyMongo会自动将其转为MongoDB的ISODate类型,后续查询即可直接用datetime作为条件:
new_doc = { "fees": "5.00", "trigger": "immediate", "price": "15.30000000", "recordTimestamp": datetime.utcnow() # 直接用datetime对象 } collection.insert_one(new_doc)
内容的提问来源于stack exchange,提问作者digitally_inspired
相关产品推荐
相关产品推荐

