REST转GraphQL遇Float无法表示Infinity错误求解决方案
问题描述
将站点从REST迁移到GraphQL时,有一个接收period参数的查询:
valuations(period: $period) { low }
Resolver中根据period调用MongoDB聚合查询,返回对应时间范围的结果:
valuations: async ({ collectible_id }, { period }, { prisma }) => { switch (true){ case period === 1: return [await CollectiblePrice.aggregate([ { "$match": { collectibleId: collectible_id, "date": { $gte: new Date(new Date().getTime() - (24 * 60 * 60 * 1000)) } } }, ])] case period === 2: return [await CollectiblePrice.aggregate([ { "$match": { collectibleId: collectible_id, "date": { $gte: new Date(new Date().getTime() - (48 * 60 * 60 * 1000)) } } }, ])] case period === 7: console.log('7 day period...') return [await CollectiblePrice.aggregate([ { "$match": { collectibleId: collectible_id, "date": { $gte: new Date(new Date().getTime() - (7 * 24 * 60 * 60 * 1000)) } } }, ])] case period === 28 || 30: return [await CollectiblePrice.aggregate([ { "$match": { collectibleId: collectible_id, "date": { $gte: new Date(new Date().getTime() - (30 * 24 * 60 * 60 * 1000)) } } }, ])] case period === 90: return [await CollectiblePrice.aggregate([ { "$match": { collectibleId: collectible_id, "date": { $gte: new Date(new Date().getTime() - (90 * 24 * 60 * 60 * 1000)) } } }, ])] default: return [await CollectiblePrice.aggregate([ { "$match": { collectibleId: collectible_id, "date": { $gte: new Date(new Date().getTime() - (24 * 60 * 60 * 1000)) } } }, ])] } }
多数period参数能正常运行,但传入90这类大值时,出现错误:
"message": "Float cannot represent non numeric value: Infinity",
该数据在REST API及图表包中均可正常使用,需解决如何允许或跳过异常值以正常返回结果。
解决方案
1. 在MongoDB聚合阶段过滤异常值
错误核心是返回的low字段存在Infinity值,而GraphQL的Float类型不支持该值。可以在聚合查询中直接过滤掉这类异常数据,在$match时间范围后新增一个过滤阶段:
// 以period=90的场景为例,其他case可同步修改 return [await CollectiblePrice.aggregate([ { "$match": { collectibleId: collectible_id, "date": { $gte: new Date(new Date().getTime() - (90 * 24 * 60 * 60 * 1000)) } } }, { "$match": { "low": { $nin: [Infinity, -Infinity, NaN] } // 排除无限值和非数值 } } ])]
2. 在Resolver中统一清洗返回结果
如果不想修改每个聚合查询,可在获取MongoDB返回结果后,遍历数据替换或移除异常值:
// 以任意case为例,替换原有返回逻辑 const rawResult = await CollectiblePrice.aggregate([/* 原查询逻辑 */]); const cleanedResult = rawResult.map(item => ({ ...item, low: isFinite(item.low) ? item.low : null // 将无限值转为null,也可根据业务替换为默认值(如0) })); return [cleanedResult];
3. 重构Resolver减少冗余(可选)
当前switch逻辑重复度极高,可重构为映射表简化代码,同时统一处理异常值:
valuations: async ({ collectible_id }, { period }, { prisma }) => { // 定义period对应天数的映射表 const periodDaysMap = { 1: 1, 2: 2, 7: 7, 28: 30, 30: 30, 90: 90 }; // 默认返回1天的数据 const targetDays = periodDaysMap[period] || 1; const timeRange = new Date(Date.now() - targetDays * 24 * 60 * 60 * 1000); const rawResult = await CollectiblePrice.aggregate([ { "$match": { collectibleId: collectible_id, "date": { $gte: timeRange } } }, { "$match": { "low": { $nin: [Infinity, -Infinity, NaN] } } } ]); // 最终清洗数据,确保符合GraphQL类型要求 const cleanedResult = rawResult.map(item => ({ ...item, low: isFinite(item.low) ? item.low : null })); return [cleanedResult]; }
4. 调整GraphQL Schema(可选)
如果业务允许low字段为空,可将Schema中的low类型改为可空:
type Valuation { low: Float # 原类型若为Float!,改为Float即可允许null值 }
这样当low为null时,GraphQL不会抛出类型错误。
内容的提问来源于stack exchange,提问作者alienbuild
相关产品推荐
相关产品推荐

