CouchDB MapReduce日期键去重reduce返回undefined问题排查
排查与修正方案
最常见的两类报错原因(直接对应你返回undefined的现象)
reduce函数签名不满足引擎要求
CouchDB调用reduce函数时,一定会传入三个固定参数:keys, values, rereduce。函数定义哪怕不用第三个参数,也必须把三个形参位占全。你现在写的函数只接了两个形参,到跨分片二次聚合的环节,引擎会因为函数参数不匹配触发静默报错,最终返回undefined。
先把签名改成function (keys, values, rereduce) { ... }解决最基础的语法校验问题。没有兼容rereduce阶段的输入结构
CouchDB的reduce是分两轮跑的:- 第一轮(
rereduce=false):每个分片本地,把map输出的同key原始业务值做聚合,这部分你的去重逻辑是匹配输入格式的 - 第二轮(
rereduce=true):把各个分片算出来的中间结果再合一次,这时候传进函数的values根本就不是带vimr、prod的原始对象,而是你第一轮返回的{vimr: {prod: true}}结构的聚合结果。
你现在的逻辑完全没判断rereduce状态,到第二轮的时候,对着聚合对象取.vimr、.prod字段,拿到的都是空值,循环处理的时候直接抛错,自然返回undefined。
兼容两轮逻辑的完整可运行代码:
function (keys, values, rereduce) { const result = {}; for (let i = 0; i < values.length; i++) { const item = values[i]; if (rereduce) { // 合并其他分片返回的中间去重结果 for (const vimr in item) { if (!result[vimr]) { result[vimr] = {}; } Object.assign(result[vimr], item[vimr]); } } else { // 处理map输出的原始业务数据,加兜底跳过异常空数据 if (!item || !item.vimr || !item.prod) continue; if (!result[item.vimr]) { result[item.vimr] = {}; } result[item.vimr][item.prod] = true; } } return result; }- 第一轮(
其他容易踩的坑
- 别用
for...in直接遍历数组结构的values入参:for...in是用来遍历对象可枚举属性的,遍历数组时会读到挂载在原型上的非预期属性,容易出现奇怪的逻辑异常,换成索引循环或for...of更靠谱 - 检查查询参数:开了
group=true的话,要确认查询的日期key范围和粒度匹配,跨多层级聚合时数据结构不对也会触发静默错误;另外reduce=true和include_docs=true是冲突参数,不能同时开 - 调试方法:
- 用设计文档自带的视图测试面板,手动传测试用的values、rereduce参数跑逻辑,不用等全量索引更新浪费时间
- 查设计文档的
_info接口返回,所有reduce执行的报错堆栈都会存在这里,不是只有一个undefined就没线索 - 确认视图索引已经构建完成,索引没就绪时查reduce结果也可能返回空值
内容的提问来源于stack exchange,提问作者Liam McNulty
相关产品推荐
相关产品推荐

