You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 20:35:18