MongoDB中如何筛选文档并仅返回数组中指定值的元素
问题:MongoDB文档数组中筛选指定值并仅返回符合条件的数组元素
需要在MongoDB的文档数组中查找指定值,仅返回包含该值的数组元素及外层文档数据。
示例文档
集合中的文档结构如下:
{ "_id": { "$oid": "63452345f14f98447b792327e9c" }, "TID": "695742332", "DID": "WW*DHD", "IndexedSpansUsageCost": [ { "month": { "$numberInt": "9" }, "year": "2022", "IndexedSpansCost": { "$numberDouble": "1.11" }, "IndexedSpansCount": { "$numberDouble": "418334.0" } }, { "month": { "$numberInt": "10" }, "year": "2022", "IndexedSpansCost": { "$numberDouble": "1.11" }, "IndexedSpansCount": { "$numberDouble": "432516.0" } } ] }
当前尝试的代码及问题
用以下Go代码查询时,能找到包含month=9的文档,但返回的是整个文档(包括month=10的数组元素),无法仅保留匹配的数组项:
multifilter := bson.A{{"IndexedSpansUsageCost.month", bson.D{{"$eq", 9}}}} cursor, err := collection.Find(context.TODO(), multifilter) if err != nil { panic(err) } var resultsTest3 []bson.M if err = cursor.All(context.TODO(), &resultsTest3); err != nil { panic(err) } for _, resultTest3 := range resultsTest3 { output, err := json.MarshalIndent(resultTest3, "", " ") if err != nil { panic(err) } fmt.Printf("%s\n", output) }
期望结果
希望返回的文档仅包含符合条件的数组元素:
{ "_id": { "$oid": "63452345f14f98447b792327e9c" }, "TID": "695742332", "DID": "WW*DHD", "IndexedSpansUsageCost": [ { "month": { "$numberInt": "9" }, "year": "2022", "IndexedSpansCost": { "$numberDouble": "1.11" }, "IndexedSpansCount": { "$numberDouble": "418334.0" } } ] }
解决方案
方法1:使用聚合管道
通过$match筛选出包含目标元素的文档,再用$project结合$filter过滤数组,只保留符合条件的元素:
MongoDB Shell命令示例
db.collection.aggregate([ // 第一步:筛选出包含month=9的文档 { $match: { "IndexedSpansUsageCost.month": 9 } }, // 第二步:过滤数组,仅保留month=9的元素 { $project: { TID: 1, DID: 1, IndexedSpansUsageCost: { $filter: { input: "$IndexedSpansUsageCost", as: "item", cond: { $eq: ["$$item.month", 9] } } } } } ])
对应的Go代码实现
pipeline := bson.A{ // $match阶段:匹配包含目标month的文档 bson.D{{"$match", bson.D{{"IndexedSpansUsageCost.month", 9}}}}, // $project阶段:过滤数组 bson.D{ {"$project", bson.D{ {"TID", 1}, {"DID", 1}, {"IndexedSpansUsageCost", bson.D{ {"$filter", bson.D{ {"input", "$IndexedSpansUsageCost"}, {"as", "item"}, {"cond", bson.D{{"$eq", bson.A{"$$item.month", 9}}}}, }}, }}, }}, }, } cursor, err := collection.Aggregate(context.TODO(), pipeline) if err != nil { panic(err) } var results []bson.M if err = cursor.All(context.TODO(), &results); err != nil { panic(err) } for _, result := range results { output, err := json.MarshalIndent(result, "", " ") if err != nil { panic(err) } fmt.Printf("%s\n", output) }
方法2:Find查询时使用投影(MongoDB 3.2+支持)
直接在Find的选项中设置投影,用$filter处理数组,无需聚合:
filter := bson.D{{"IndexedSpansUsageCost.month", 9}} projection := bson.D{ {"TID", 1}, {"DID", 1}, {"IndexedSpansUsageCost", bson.D{ {"$filter", bson.D{ {"input", "$IndexedSpansUsageCost"}, {"as", "item"}, {"cond", bson.D{{"$eq", bson.A{"$$item.month", 9}}}}, }}, }}, } cursor, err := collection.Find(context.TODO(), filter, options.Find().SetProjection(projection)) if err != nil { panic(err) } var results []bson.M if err = cursor.All(context.TODO(), &results); err != nil { panic(err) } for _, result := range results { output, err := json.MarshalIndent(result, "", " ") if err != nil { panic(err) } fmt.Printf("%s\n", output) }
关键说明
$match:先筛选出包含目标元素的文档,减少后续处理的数据量$filter:遍历数组,仅保留满足cond条件的元素,这里判断数组项的month是否等于9
内容的提问来源于stack exchange,提问作者Spring_Reboot
相关产品推荐
相关产品推荐

