MongoDB聚合查询报错TypeError: 'NameError'不可迭代问题求助
MongoDB聚合查询报错排查与饼图过滤功能修复
集合结构
{ "_id" : ObjectId("63295d64ba562f5f8842e097"), "device_euid" : "D8:14:40:E6:DF:5C:62:E8", "created_on" : ISODate("2022-09-20T06:27:48.831+0000"), "last_seen" : 1663841855.0 }
原查询逻辑(可生成饼图但无法过滤)
devicesbylastseen_query = [ {"$group": {"_id": "$last_seen", "count": {"$sum": 1}}} ] last_seen_devices = execute_query(Device, devicesbylastseen_query) # execute query是独立的MongoDB聚合执行函数 counts_dict = {'lessthan5minutes': 0, '5minutesto1hours': 0, '1hourto1day': 0, 'greaterthan1day': 0, 'preapproved': 0} # 获取当前日期时间 current_datetime = datetime.datetime.now() # 计算用于比较的时间戳 cd_minus_5_min_epoch = int(round(time.mktime( (current_datetime - datetime.timedelta( minutes=5)).timetuple()))) cd_minus_1_hour_epoch = int(round(time.mktime( (current_datetime - datetime.timedelta( minutes=60)).timetuple()))) cd_minus_1_day_epoch = int(round(time.mktime( (current_datetime - datetime.timedelta( days=1)).timetuple()))) # 检查查询结果是否有数据 if last_seen_devices: for last_seen_device in last_seen_devices: if last_seen_device.get("_id").isdigit(): last_seen = int(last_seen_device.get("_id")) # 5分钟内 if last_seen >= cd_minus_5_min_epoch: counts_dict["lessthan5minutes"] = \ counts_dict["lessthan5minutes"] + int( last_seen_device.get("count")) # 5分钟到1小时 elif last_seen < cd_minus_5_min_epoch \ and last_seen >= cd_minus_1_hour_epoch: counts_dict["5minutesto1hours"] = \ counts_dict["5minutesto1hours"] + \ int(last_seen_device.get("count")) # 1小时到1天 elif last_seen < cd_minus_1_hour_epoch \ and last_seen >= cd_minus_1_day_epoch: counts_dict["1hourto1day"] = counts_dict[ "1hourto1day"] + int( last_seen_device.get("count")) # 超过1天 else: counts_dict["greaterthan1day"] = \ counts_dict["greaterthan1day"] + int( last_seen_device.get("count")) else: counts_dict["preapproved"] = counts_dict[ "preapproved"] + int( last_seen_device.get("count")) # 组装结果,无数据时所有计数为0 devicesbylastseen_result = { "lessthan5minutes": counts_dict["lessthan5minutes"], "5minutesto1hours": counts_dict["5minutesto1hours"], "1hourto1day": counts_dict["1hourto1day"], "greaterthan1day": counts_dict["greaterthan1day"], "preapproved": counts_dict["preapproved"] } return devicesbylastseen_result
修改后的$facet查询逻辑(报错版本)
counts_dict = {'lessthan5minutes': 0, '5minutesto1hours': 0, '1hourto1day': 0, 'greaterthan1day': 0, 'preapproved': 0} # 获取当前时间戳 current_time = int(time.time()) # 计算用于比较的时间戳 cd_minus_5_min_epoch = current_time-300 cd_minus_1_hour_epoch = current_time-3600 cd_minus_1_day_epoch = current_time-86400 devicesbylastseen_query = [ {"$facet": { "lessthan5minutes": [{"$match": {"last_seen": {"$gt": cd_minus_5_min_epoch, "$lt": current_time}}}, {"$group": {"_id": None, "count": {"$sum": 1}}}], "5minutesto1hours": [ {"$match": {"last_seen": {"$gt": cd_minus_1_hour_epoch, "$lt": cd_minus_5_min_epoch}}}, {"$group": {"_id": None, "count": {"$sum": 1}}}], "1hourto1day": [ {"$match": {"last_seen": {"$gt": cd_minus_1_day_epoch, "$lt": cd_minus_1_hour_epoch}}}, {"$group": {"_id": None, "count": {"$sum": 1}}}], "greaterthan1day": [{"$match": {"last_seen": {"$lt": cd_minus_1_day_epoch}}}, {"$group": {"_id": None, "count": {"$sum": 1}}}], "preapproved": [{"$match": {"last_seen": {"$exists": False}}}, {"$group": {"_id": None, "count": {"$sum": 1}}} ] } } ] last_seen_devices = list(execute_query(Device, devicesbylastseen_query)) if last_seen_devices[0]: for key in counts_dict: if key in last_seen_devices[0].keys() and last_seen_devices[0][key]: counts_dict[key] = last_seen_devices[0][key][0]["count"] # 组装结果,无数据时所有计数为0 devicesbylastseen_result = { "lessthan5minutes": counts_dict["lessthan5minutes"], "5minutesto1hours": counts_dict["5minutesto1hours"], "1hourto1day": counts_dict["1hourto1day"], "greaterthan1day": counts_dict["greaterthan1day"], "preapproved": counts_dict["preapproved"] } return devicesbylastseen_result
报错信息
Traceback (most recent call last): File "/usr/local/lib/python3.8/dist-packages/django/core/handlers/exception.py", line 34, in inner response = get_response(request) File "/usr/local/lib/python3.8/dist-packages/django/core/handlers/base.py", line 145, in _get_response response = self.process_exception_by_middleware(e, request) File "/usr/local/lib/python3.8/dist-packages/django/core/handlers/base.py", line 143, in _get_response response = response.render() File "/usr/local/lib/python3.8/dist-packages/django/template/response.py", line 106, in render self.content = self.rendered_content File "/usr/local/lib/python3.8/dist-packages/rest_framework/response.py", line 70, in rendered_content ret = renderer.render(self.data, accepted_media_type, context) TypeError: argument of type 'NameError' is not iterable
解决方案
1. 修复$facet查询语法错误
原查询中preapproved的聚合数组缺失闭合方括号,导致JSON格式非法,同时调整匹配条件贴合原逻辑(兼容last_seen不存在或为字符串的情况):
"preapproved": [ {"$match": {"$or": [{"last_seen": {"$exists": False}}, {"last_seen": {"$type": "string"}}]}}, {"$group": {"_id": None, "count": {"$sum": 1}}} ]
2. 修正结果处理逻辑
当facet无匹配数据时返回空数组,直接取[0]会触发IndexError,需先判断数组长度:
if last_seen_devices: facet_result = last_seen_devices[0] for key in counts_dict: if key in facet_result and facet_result[key]: counts_dict[key] = facet_result[key][0]["count"] if len(facet_result[key]) > 0 else 0
3. 添加异常捕获
避免聚合查询错误直接传递到DRF渲染层,同时记录错误日志:
try: last_seen_devices = list(execute_query(Device, devicesbylastseen_query)) except Exception as e: import logging logging.error(f"Last seen aggregation error: {str(e)}") last_seen_devices = []
4. 统一时间戳类型
集合中last_seen为float类型,将时间戳计算改为float类型,确保比较逻辑严谨:
current_time = time.time() cd_minus_5_min_epoch = current_time - 300 cd_minus_1_hour_epoch = current_time - 3600 cd_minus_1_day_epoch = current_time - 86400
最终修复后的完整代码
counts_dict = {'lessthan5minutes': 0, '5minutesto1hours': 0, '1hourto1day': 0, 'greaterthan1day': 0, 'preapproved': 0} # 获取当前时间戳(匹配集合中last_seen的float类型) current_time = time.time() # 计算用于比较的时间戳 cd_minus_5_min_epoch = current_time - 300 cd_minus_1_hour_epoch = current_time - 3600 cd_minus_1_day_epoch = current_time - 86400 devicesbylastseen_query = [ {"$facet": { "lessthan5minutes": [ {"$match": {"last_seen": {"$gt": cd_minus_5_min_epoch, "$lt": current_time}}}, {"$group": {"_id": None, "count": {"$sum": 1}}} ], "5minutesto1hours": [ {"$match": {"last_seen": {"$gt": cd_minus_1_hour_epoch, "$lt": cd_minus_5_min_epoch}}}, {"$group": {"_id": None, "count": {"$sum": 1}}} ], "1hourto1day": [ {"$match": {"last_seen": {"$gt": cd_minus_1_day_epoch, "$lt": cd_minus_1_hour_epoch}}}, {"$group": {"_id": None, "count": {"$sum": 1}}} ], "greaterthan1day": [ {"$match": {"last_seen": {"$lt": cd_minus_1_day_epoch}}}, {"$group": {"_id": None, "count": {"$sum": 1}}} ], "preapproved": [ {"$match": {"$or": [{"last_seen": {"$exists": False}}, {"last_seen": {"$type": "string"}}]}}, {"$group": {"_id": None, "count": {"$sum": 1}}} ] }} ] try: last_seen_devices = list(execute_query(Device, devicesbylastseen_query)) except Exception as e: import logging logging.error(f"Last seen aggregation error: {str(e)}") last_seen_devices = [] if last_seen_devices: facet_result = last_seen_devices[0] for key in counts_dict: if key in facet_result and facet_result[key]: counts_dict[key] = facet_result[key][0]["count"] if len(facet_result[key]) > 0 else 0 devicesbylastseen_result = { "lessthan5minutes": counts_dict["lessthan5minutes"], "5minutesto1hours": counts_dict["5minutesto1hours"], "1hourto1day": counts_dict["1hourto1day"], "greaterthan1day": counts_dict["greaterthan1day"], "preapproved": counts_dict["preapproved"] } return devicesbylastseen_result
饼图过滤功能实现说明
修改后的$facet查询将每个时间区间的设备数据单独分组,用户点击饼图某一区间时,只需根据对应键(如lessthan5minutes)执行对应的$match条件,即可获取该区间的所有设备详情,实现过滤功能。
内容的提问来源于stack exchange,提问作者roguepanda
相关产品推荐
相关产品推荐

