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

如何在Mongoose聚合的$match阶段未匹配到文档时抛出错误?

解决方案

要实现指定userId对应用户不存在时抛出错误并提示客户端,你需要先区分「用户不存在」和「用户存在但无登录尝试」这两种情况——直接使用aggregate的话,两种场景都会返回空数组,无法直接判断原因。下面提供两种可行方案:

方法一:先验证用户存在性,再执行聚合查询

这种方式逻辑直观,代码复杂度低,额外的主键查询性能开销极小:

app.get('/users/:userId/login-attempts', async(req,res,next)=>{
    try {
        // 基于_id主键快速验证用户是否存在
        const userExists = await User.exists({ _id: mongoose.Types.ObjectId(req.params.userId) });
        if (!userExists) {
            const error = new Error(`用户ID ${req.params.userId} 不存在`);
            error.statusCode = 404;
            throw error;
        }

        // 用户存在时,执行聚合查询提取登录记录
        const loginAttempts = await User.aggregate([
            { $match: { _id: mongoose.Types.ObjectId(req.params.userId) } },
            { $unwind: '$loginAttempts' },
            { $replaceRoot: { newRoot: '$loginAttempts' } },
        ]);

        // 返回结果,空数组代表用户存在但无登录尝试
        res.status(200).json({ loginAttempts });
    } catch (err) {
        // 将错误传递到Express全局错误处理中间件
        next(err);
    }
})

方法二:在聚合管道中保留用户存在标记(单次数据库请求)

如果希望减少数据库请求次数,可以修改聚合管道,通过$group和$addFields保留用户存在的状态:

app.get('/users/:userId/login-attempts', async(req,res,next)=>{
    try {
        const result = await User.aggregate([
            { $match: { _id: mongoose.Types.ObjectId(req.params.userId) } },
            // 添加标记字段,确认用户存在
            { $addFields: { userExists: true } },
            // 保留空数组的情况,避免直接过滤掉存在但无登录记录的用户
            { $unwind: { path: '$loginAttempts', preserveNullAndEmptyArrays: true } },
            // 聚合用户信息和登录记录
            { $group: {
                _id: '$_id',
                userExists: { $first: '$userExists' },
                loginAttempts: { $push: '$loginAttempts' }
            } },
            // 过滤掉空的登录记录,整理返回结构
            { $project: {
                _id: 0,
                userExists: 1,
                loginAttempts: {
                    $filter: {
                        input: '$loginAttempts',
                        cond: { $ne: ['$$this', null] }
                    }
                }
            } }
        ]);

        if (result.length === 0) {
            // 无聚合结果说明用户不存在
            const error = new Error(`用户ID ${req.params.userId} 不存在`);
            error.statusCode = 404;
            throw error;
        }

        // 返回整理后的登录记录
        res.status(200).json({ loginAttempts: result[0].loginAttempts });
    } catch (err) {
        next(err);
    }
})

补充:全局错误处理

最后需要配置Express全局错误处理中间件,统一将错误返回给客户端:

app.use((err, req, res, next) => {
    const statusCode = err.statusCode || 500;
    const message = err.message || '服务器内部错误';
    res.status(statusCode).json({ error: message });
});

内容的提问来源于stack exchange,提问作者Islam Y-

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.25 09:06:36