Golang中MongoDB时间范围查询返回全部子文档问题排查
解决MongoDB查询返回整个数组而非匹配子项的问题
当前查询的filter仅用于判断整个员工文档是否存在符合时间条件的排班记录,一旦匹配成功,MongoDB会返回整个员工文档(包含所有schedules子文档),而非仅筛选符合时间范围的子项。这就是查询5月时返回所有排班(因存在符合条件的子项,整个文档被返回)、查询8月时无匹配文档返回空的原因。
方案1:使用聚合管道筛选所有匹配的排班子文档
聚合管道可精确筛选数组中符合条件的所有子文档,是最可靠的方案:
layout := "2006-01-02T15:04:05.999999" startTime, err := time.Parse(layout, scheduleRequest.StartDate) if err != nil { log.Fatal(err) } endTime, err := time.Parse(layout, scheduleRequest.EndDate) if err != nil { log.Fatal(err) } mongoStartTime := primitive.NewDateTimeFromTime(startTime) mongoEndTime := primitive.NewDateTimeFromTime(endTime) pipeline := []bson.M{ // 匹配目标员工,且存在符合条件的排班 { "$match": bson.M{ "_id": scheduleRequest.EmployeeID, "schedules.end_time": bson.M{"$gte": mongoStartTime, "$lte": mongoEndTime}, }, }, // 筛选schedules数组中符合时间条件的子文档 { "$project": bson.M{ "first_name": 1, "last_name": 1, // 按需保留其他字段 "schedules": bson.M{ "$filter": bson.M{ "input": "$schedules", "as": "schedule", "cond": bson.M{ "$and": []bson.M{ {"$gte": []interface{}{"$$schedule.end_time", mongoStartTime}}, {"$lte": []interface{}{"$$schedule.end_time", mongoEndTime}}, }, }, }, }, }, }, } ctx := context.TODO() cursor, err := employeeCollection.Aggregate(ctx, pipeline) if err != nil { log.Fatal(err) } defer cursor.Close(ctx) var results []bson.M if err := cursor.All(ctx, &results); err != nil { log.Fatal(err) } // results中的文档仅包含符合时间范围的schedules子项
方案2:使用投影$elemMatch(仅返回第一个匹配项)
若只需返回数组中第一个符合条件的子文档,可采用此简化方式,但无法返回多个匹配项:
layout := "2006-01-02T15:04:05.999999" startTime, err := time.Parse(layout, scheduleRequest.StartDate) endTime, err := time.Parse(layout, scheduleRequest.EndDate) mongoStartTime := primitive.NewDateTimeFromTime(startTime) mongoEndTime := primitive.NewDateTimeFromTime(endTime) filter := bson.M{ "_id": scheduleRequest.EmployeeID, "schedules.end_time": bson.M{"$gte": mongoStartTime, "$lte": mongoEndTime}, } // 投影仅返回匹配的第一个schedules子文档 projection := bson.M{ "schedules": bson.M{"$elemMatch": bson.M{"end_time": bson.M{"$gte": mongoStartTime, "$lte": mongoEndTime}}}, "first_name": 1, "last_name": 1, } ctx := context.TODO() cursor, err := employeeCollection.Find(ctx, filter, options.Find().SetProjection(projection)) if err != nil { log.Fatal(err) } defer cursor.Close(ctx) var results []bson.M if err := cursor.All(ctx, &results); err != nil { log.Fatal(err) }
补充说明
- 若你的时间范围逻辑是「排班与查询月份有重叠」(而非仅
end_time在范围内),需调整$filter的cond条件为:"$and": []bson.M{ {"$lte": []interface{}{"$$schedule.start_time", mongoEndTime}}, {"$gte": []interface{}{"$$schedule.end_time", mongoStartTime}}, }, } - 处理时间解析时务必检查错误,避免因解析失败导致的逻辑异常。
内容的提问来源于stack exchange,提问作者Shawn Niedetzki
相关产品推荐
相关产品推荐

