如何在MongoDB及C# MongoDB.Driver中获取文档的最后N个子文档
在MongoDB.Driver(C#)中对指定文档的子数组排序并截取最后N条
我来帮你解决这个问题——在C#的MongoDB.Driver中,对指定文档的子数组排序并截取最后N条确实不能直接用普通的find方法,得借助聚合管道来实现,下面分步骤给你详细方案:
一、先理解MongoDB底层聚合逻辑
首先用Mongo Shell写出核心逻辑,方便你理解原理:
db.foo.aggregate([ // 第一步:匹配到你需要的目标文档(ID=A) { $match: { _id: "10000" } }, // 第二步:对channels子数组按指定字段排序(这里按id升序,可按需调整) { $addFields: { channels: { $sortArray: { input: "$channels", sortBy: { id: 1 } // 1=升序,-1=降序 } } }}, // 第三步:截取排序后的最后N条(这里N=2) { $addFields: { channels: { $slice: ["$channels", -2] } // 负数表示从数组末尾取N个 }} ])
注意:
$sortArray是MongoDB 5.2及以上版本支持的语法,性能更优;如果你的MongoDB版本低于5.2,我会在后面补充兼容方案。
二、C# MongoDB.Driver实现代码
首先定义和你的文档结构对应的实体模型:
using MongoDB.Bson; using MongoDB.Bson.Serialization.Attributes; using System.Collections.Generic; public class Customer { [BsonId] public string Id { get; set; } public string Password { get; set; } public string Name { get; set; } public List<Channel> Channels { get; set; } } public class Channel { public string Id { get; set; } public string Name { get; set; } public bool Enabled { get; set; } }
然后编写聚合查询的C#代码:
using MongoDB.Driver; using System.Threading.Tasks; // 假设你已经获取了MongoDatabase实例 var collection = mongoDatabase.GetCollection<Customer>("foo"); string targetDocumentId = "10000"; // 你要查找的ID=A int takeLastN = 2; // 要截取的最后N条子文档 // 构建聚合管道 var pipeline = new BsonDocument[] { // 匹配目标文档 new BsonDocument("$match", new BsonDocument("_id", targetDocumentId)), // 对channels子数组排序 new BsonDocument("$addFields", new BsonDocument("channels", new BsonDocument("$sortArray", new BsonDocument { { "input", "$channels" }, { "sortBy", new BsonDocument("id", 1) } // 可修改排序字段和方向,比如按_id降序就是new BsonDocument("_id", -1) }))), // 截取最后N条 new BsonDocument("$addFields", new BsonDocument("channels", new BsonDocument("$slice", new BsonArray { "$channels", -takeLastN }))) }; // 执行聚合并获取结果 var result = await collection.Aggregate<Customer>(pipeline).FirstOrDefaultAsync();
三、兼容MongoDB 5.2以下版本的方案
如果你的MongoDB版本较低,不支持$sortArray,可以用$unwind+$sort+$group的组合实现:
Mongo Shell示例
db.foo.aggregate([ { $match: { _id: "10000" } }, { $unwind: "$channels" }, // 打散子数组为单个文档 { $sort: { "channels.id": 1 } }, // 对打散后的子文档排序 { $group: { // 重新组合为原文档结构 _id: "$_id", password: { $first: "$password" }, name: { $first: "$name" }, channels: { $push: "$channels" } }}, { $project: { // 截取最后N条 password: 1, name: 1, channels: { $slice: ["$channels", -2] } }} ])
对应的C#代码
var pipelineOldVersion = new BsonDocument[] { new BsonDocument("$match", new BsonDocument("_id", targetDocumentId)), new BsonDocument("$unwind", "$channels"), new BsonDocument("$sort", new BsonDocument("channels.id", 1)), new BsonDocument("$group", new BsonDocument { { "_id", "$_id" }, { "password", new BsonDocument("$first", "$password") }, { "name", new BsonDocument("$first", "$name") }, { "channels", new BsonDocument("$push", "$channels") } }), new BsonDocument("$project", new BsonDocument { { "password", 1 }, { "name", 1 }, { "channels", new BsonDocument("$slice", new BsonArray { "$channels", -takeLastN }) } }) }; var resultOldVersion = await collection.Aggregate<Customer>(pipelineOldVersion).FirstOrDefaultAsync();
小贴士:优先使用
$sortArray的方案,因为$unwind会把子数组打散成多个文档,当子数组数据量很大时,性能会明显下降。
内容的提问来源于stack exchange,提问作者Pooria
相关产品推荐
相关产品推荐

