使用Mongoose通过_id(ObjectId)查询返回空数组的问题排查
问题:Mongoose通过_id查询客户返回空数组的排查与解决
我编写了如下findCustomer函数:
export const findCustomer = async (query?: FindCustomerParams) => { try { await database.connect(); } catch (err: any) { throw new Error("Could not connect to the database. Root error: " + err); } if (!query) { return await customer.find(); } findCustomerParams.parse(query); let returnCustomers; try { const fetchedCustomers: (Omit<Customer, "address"> & { address?: FindOneCustomerParams["address"] })[] | null = await customer.find(query ? removeFalsyValues({ ...query, _id: new mongoose.Types.ObjectId(query.id) }) : {}); if (!fetchedCustomers) { throw new Error(); } if (query.address) { const fetchedAddresses = await addressMethods.find(query.address); for (let i = 0; i < fetchedAddresses.length; i++) { if (!fetchedAddresses || !fetchedAddresses[i].customers.includes(fetchedCustomers[i]._id)) { throw new Error(`Could find address on customer ${fetchedCustomers[i]._id}`); } fetchedCustomers[i].address = fetchedAddresses[i]; } } returnCustomers = fetchedCustomers; } catch (err: any) { throw new Error("Failed to find a customer. Root error: " + err); } await database.close(); return returnCustomers; };
该函数除通过_id查询外,其他查询均正常工作。当通过_id查询时,会返回空数组,而实际应返回单个客户数据。
可正常返回结果的查询示例:
await findCustomer({firstName: "Ethan"})
无法返回结果的查询示例:
await findCustomer({_id: id})
我已尝试以下操作:
- 验证ID正确性
- 确认已连接Atlas集群
- 尝试将
_id从string类型转为ObjectId类型 - 分别使用
id和_id查询均无效 - 控制台无报错信息
解决方法
问题根源
核心bug出在查询构造逻辑:当传入{_id: id}时,代码里写的是new mongoose.Types.ObjectId(query.id),但你传入的参数里只有_id字段,query.id是undefined,转成ObjectId后会生成一个无效的空ID,自然查不到数据。
另外还有两个潜在问题:
- 用
customer.find()查单个ID会返回数组,更适合用customer.findOne(),性能更好且符合单文档查询的预期。 - 每次查询都手动执行
connect()和close(),Mongoose默认会维护连接池,频繁开闭连接会导致性能损耗甚至连接异常。
修复后的代码
export const findCustomer = async (query?: FindCustomerParams) => { // 移除手动连接/关闭逻辑:Mongoose会自动维护连接池,在应用启动时初始化一次即可 if (!query) { return await customer.find(); } findCustomerParams.parse(query); let returnCustomers; try { let queryObj = { ...query }; // 正确处理ID查询:判断传入的是_id还是id字段,转换为ObjectId if (query._id) { queryObj._id = new mongoose.Types.ObjectId(query._id); delete queryObj.id; // 清理冗余字段 } else if (query.id) { queryObj._id = new mongoose.Types.ObjectId(query.id); delete queryObj.id; } // 清理空值 queryObj = removeFalsyValues(queryObj); // 单个ID查询用findOne,其他条件用find if (queryObj._id) { const fetchedCustomer = await customer.findOne(queryObj); if (!fetchedCustomer) { throw new Error("Customer not found"); } // 关联查询地址(按客户ID匹配,而非原逻辑的索引匹配) if (query.address) { const fetchedAddress = await addressMethods.find({ ...query.address, customers: fetchedCustomer._id }); if (!fetchedAddress?.length) { throw new Error(`Could not find address on customer ${fetchedCustomer._id}`); } fetchedCustomer.address = fetchedAddress[0]; } returnCustomers = [fetchedCustomer]; // 保持返回数组格式兼容原有逻辑 } else { const fetchedCustomers = await customer.find(queryObj); if (!fetchedCustomers) { throw new Error(); } if (query.address) { const fetchedAddresses = await addressMethods.find(query.address); // 按客户ID匹配地址,避免原逻辑索引错位的问题 for (let customer of fetchedCustomers) { const matchedAddress = fetchedAddresses.find(addr => addr.customers.includes(customer._id)); if (!matchedAddress) { throw new Error(`Could not find address on customer ${customer._id}`); } customer.address = matchedAddress; } } returnCustomers = fetchedCustomers; } } catch (err: any) { throw new Error("Failed to find a customer. Root error: " + err); } return returnCustomers; };
关键修复点
- 修正
_id转换逻辑:根据传入的字段(_id或id)正确获取ID值并转换为ObjectId - 针对单个ID查询使用
findOne,优化性能和结果准确性 - 移除冗余的连接/关闭逻辑,改用Mongoose连接池管理
- 优化地址关联查询的匹配逻辑,避免原代码中按索引匹配的潜在错误
内容的提问来源于stack exchange,提问作者Ethan
相关产品推荐
相关产品推荐

