如何在Map/Reduce索引中统计特定字段值的文档数量
问题:如何在Map/Reduce索引中统计
activated为true的文档数量 现有文档包含activated字段(值为true或false),示例文档如下:
{ "userId": "user1", "activated": true, "loginDuration": 123 }
已创建的基础Map/Reduce索引函数如下:
Map函数:
from user in docs.Users select new { user.userId, Count = 1, user.loginDuration, AvgLoginDuration = 0 }
Reduce函数:
from r in results group r by new r.userId into g let totalLoginDuration = g.Sum(x => x.loginDuration) let count = g.Sum(x => x.Count) select new { userId = g.Key.userId, Count = count, TotalLoginDuration = totalLoginDuration, AvgLoginDuration = totalLoginDuration / count }
尝试添加统计activated为true的文档数功能时遇到两种问题:
- 第一种方法无法保存:
// map函数 from user in docs.Users select new { user.userId, user.activated, Count = 1, user.loginDuration, AvgLoginDuration = 0, NumActivated = 0 } // reduce函数 from r in results group r by new r.userId into g let totalLoginDuration = g.Sum(x => x.loginDuration) let count = g.Sum(x => x.Count) select new { userId = g.Key.userId, activated = g.Key.activated, Count = count, TotalLoginDuration = totalLoginDuration, AvgLoginDuration = totalLoginDuration / count, NumActivated = g.Sum(x => x.activated == true) }
- 第二种方法可保存但运行时报错(无法将bool转为decimal):
// map函数 from user in docs.Users select new { user.userId, Count = 1, user.loginDuration, AvgLoginDuration = 0, NumActivated = user.activated == true ? 1 : 0 } // reduce函数 from r in results group r by new r.userId into g let totalLoginDuration = g.Sum(x => x.loginDuration) let count = g.Sum(x => x.Count) select new { userId = g.Key.userId, Count = count, TotalLoginDuration = totalLoginDuration, AvgLoginDuration = totalLoginDuration / count, NumActivated = g.Sum(x => x.NumActivated == 1) }
解决方法
错误原因分析
- 第一种方法:分组键仅包含
userId,却在结果中错误引用g.Key.activated;同时Sum(x => x.activated == true)返回布尔值,而Sum方法要求输入数值类型,导致无法保存。 - 第二种方法:Reduce函数中
Sum(x => x.NumActivated == 1)的判断返回布尔值,Sum需要累加数值,因此触发“无法将bool转为decimal”的错误。
正确的Map/Reduce实现
Map函数:
在Map阶段将activated的布尔值转换为数值(true为1,false为0),确保后续Reduce可直接累加:
from user in docs.Users select new { user.userId, Count = 1, user.loginDuration, AvgLoginDuration = 0, NumActivated = user.activated ? 1 : 0 }
Reduce函数:
直接对Map阶段生成的NumActivated字段求和,无需额外判断:
from r in results group r by new r.userId into g let totalLoginDuration = g.Sum(x => x.loginDuration) let count = g.Sum(x => x.Count) let activatedCount = g.Sum(x => x.NumActivated) select new { userId = g.Key.userId, Count = count, TotalLoginDuration = totalLoginDuration, AvgLoginDuration = totalLoginDuration / count, NumActivated = activatedCount }
说明
- Map阶段将布尔值转为数值,保证Reduce阶段的
Sum操作能正常累加统计数量。 - Reduce阶段直接对
NumActivated求和,即可得到当前userId下activated为true的文档总数。
内容的提问来源于stack exchange,提问作者dbblackdiamond
相关产品推荐
相关产品推荐

