MongoDB大数据集下高效过滤嵌套数组label=1数据及C#实现
MongoDB 按条件过滤数组元素并保留原结构的查询方案
问题场景
现有按areaId划分的1分钟粒度登录BSON数据,示例结构如下:
[ { "_id": "", "at": ISODate("2022-11-19T10:00:00Z"), "areaId": 3, "data": [ {"label": 1,"name": "a","sec": 34,"x": 10.3,"y": 23.3}, {"label": 1,"name": "a","sec": 36,"x": 10.3,"y": 23.3}, {"label": 1,"name": "c","sec": 37,"x": 10.3,"y": 23.3} ] }, { "_id": "", "at": ISODate("2022-11-19T10:01:00Z"), "areaId": 3, "data": [ {"name": "a","label": 1,"sec": 10,"x": 10.3,"y": 23.3}, {"label": 2,"name": "b","sec": 12,"x": 10.3,"y": 23.3} ] } ]
因数据量极大,需在指定日期范围内以最优性能获取所有label=1的数据,要求返回文档结构不变,仅data数组保留符合条件的元素。
一、方案可行性
完全可行。要保证最优性能,需提前创建复合索引:{at: 1, areaId: 1}。该索引能让MongoDB快速定位指定日期范围和区域的文档,避免全表扫描,再配合数组过滤操作,整体性能最优。
二、MongoDB查询语句
推荐使用聚合管道实现,因为它能返回数组中所有符合条件的元素(而find的$elemMatch仅返回第一个匹配项),且灵活性更高。
聚合管道查询代码
db.your_collection.aggregate([ // 第一步:快速筛选指定日期范围的文档(利用索引) { $match: { at: { $gte: ISODate("2022-11-19T00:00:00Z"), $lte: ISODate("2022-11-19T23:59:59Z") }, areaId: 3 // 可选:如果需要限定区域,添加此条件 } }, // 第二步:过滤data数组中label=1的元素,保留原文档结构 { $project: { _id: 1, at: 1, areaId: 1, data: { $filter: { input: "$data", cond: { $eq: ["$$this.label", 1] } } } } }, // 可选:过滤掉data数组为空的文档(不需要则删除此阶段) { $match: { "data.0": { $exists: true } } } ])
注意事项
如果使用find方法搭配$elemMatch,仅能返回数组中第一个label=1的元素,无法满足“获取所有符合条件元素”的需求,因此不推荐。
三、C# 基于BsonDocument的实现
使用MongoDB官方驱动MongoDB.Driver,通过构建聚合管道实现,代码示例如下:
using MongoDB.Bson; using MongoDB.Driver; using System; using System.Collections.Generic; namespace MongoExample { class Program { static void Main(string[] args) { // 初始化Mongo客户端 var client = new MongoClient("mongodb://localhost:27017"); var db = client.GetDatabase("your_database_name"); var collection = db.GetCollection<BsonDocument>("your_collection_name"); // 指定日期范围(注意使用UTC时间,匹配MongoDB存储的ISODate) var startDate = new DateTime(2022, 11, 19, 0, 0, 0, DateTimeKind.Utc); var endDate = new DateTime(2022, 11, 19, 23, 59, 59, DateTimeKind.Utc); // 构建聚合管道 var pipeline = new List<BsonDocument> { // $match阶段:筛选日期和区域 new BsonDocument("$match", new BsonDocument { ["at"] = new BsonDocument { ["$gte"] = startDate, ["$lte"] = endDate }, ["areaId"] = 3 }), // $project阶段:过滤data数组 new BsonDocument("$project", new BsonDocument { ["_id"] = 1, ["at"] = 1, ["areaId"] = 1, ["data"] = new BsonDocument("$filter", new BsonDocument { ["input"] = "$data", ["cond"] = new BsonDocument("$eq", new BsonArray { "$$this.label", 1 }) }) }), // 可选:过滤空data数组的文档 new BsonDocument("$match", new BsonDocument { ["data.0"] = new BsonDocument("$exists", true) }) }; // 执行查询并获取结果 var result = collection.Aggregate<BsonDocument>(pipeline).ToList(); // 处理结果(示例:打印第一个文档) if (result.Count > 0) { Console.WriteLine(result[0].ToJson()); } } } }
内容的提问来源于stack exchange,提问作者Mehmet Serkan Ekinci
相关产品推荐
相关产品推荐

