
本文介绍在 go 中使用 mgo 驱动时,如何避免冗长的 if-else 组合,通过动态构造 bson.m 查询映射实现灵活、可维护的 mongodb 聚合匹配逻辑。
本文介绍在 go 中使用 mgo 驱动时,如何避免冗长的 if-else 组合,通过动态构造 bson.m 查询映射实现灵活、可维护的 mongodb 聚合匹配逻辑。
在开发数据聚合视图(如小时级日志统计)时,常需根据前端传入的多个可选参数(如 site、size、network、region、code 等)动态生成 MongoDB 的 $match 阶段。原始实现中采用 30+ 分支的嵌套 if-else 判断,不仅代码臃肿、难以维护,还极易因漏判或顺序错误引入逻辑缺陷。
更优解在于:将 bson.M 视为可扩展的 map,按需注入过滤字段。因为 bson.M 本质是 map[string]interface{},天然支持键值动态增删,无需预定义所有组合路径。
以下为重构后的清晰、健壮实现:
func GetAllHourly(dbsession *mgo.Session, year, month, day, site, size, network, region string, code int) (items []MassAggregation, err error) {
defer dbsession.Close()
// 步骤1:初始化基础查询条件(必选时间维度)
query := bson.M{
"year": year,
"month": month,
"day": day,
}
// 步骤2:按需添加可选过滤字段(语义清晰,无重复逻辑)
if site != "" {
query["site"] = site
}
if size != "" {
query["size"] = size
}
if region != "" {
query["aws_region"] = region
}
if code != -1 {
query["http_request_status"] = code
}
if network != "" {
// 兼容旧别名:openx3 → openx
if network == "openx3" {
network = "openx"
}
query["network"] = &bson.RegEx{Pattern: network, Options: "i"}
}
// 步骤3:构建聚合管道(单个 $match + $group)
pipeline := []bson.M{
{"$match": query},
{
"$group": bson.M{
"_id": bson.M{
"aws_region": "$aws_region",
"http_request_status": "$http_request_status",
"hour": "$hour",
"network": "$network",
"site": "$site",
"size": "$size",
"zone": "$zone",
"extra": "$extra",
},
"total": bson.M{"$sum": "$count"},
},
},
}
err = dbsession.DB("logs").C("prod").Pipe(pipeline).All(&items)
return
}✅ 优势总结:
-
零分支爆炸:从 30+ if-else 降至 5 个独立
if,逻辑正交、互不干扰; - 高可读性与可维护性:每个条件职责单一,新增/删除字段仅需增减一行;
-
天然防错:空字符串或默认值(如
code == -1)自动跳过,无需显式排除; -
兼容正则与类型安全:
&bson.RegEx可直接赋值,类型系统保障结构正确。
⚠️ 注意事项:
- 若需支持模糊搜索(如
site包含某子串),可将query["site"] = site替换为query["site"] = bson.M{"$regex": fmt.Sprintf(".*%s.*", regexp.QuoteMeta(site)), "$options": "i"}; - 对于数值范围查询(如
code在区间内),应使用"$gte"/"$lte"而非等值匹配; - 生产环境建议对用户输入的
network、region等字段做白名单校验,防范正则注入或无效值导致全表扫描。
该模式不仅适用于 mgo,也广泛适配官方 mongo-go-driver(使用 bson.D 或 bson.M 同理),是构建动态查询服务的通用范式。


















