当前位置: 首页 > news >正文

在 C# .NETCore 中使用 MongoDB(第 3 部分):跳过、排序、限制和投影

到目前为止,我们已经了解了创建文档、检索文档,现在让我们了解如何对文档进行排序、指定要跳过或限制的文档数量以及如何进行投影。

 在 C# .NETCore 中使用 MongoDB(第 1 部分):驱动程序基础知识和插入文档:
https://blog.csdn.net/hefeng_aspnet/article/details/150556600

在 C# .NETCore 中使用 MongoDB(第 2 部分):使用过滤子句检索文档:
https://blog.csdn.net/hefeng_aspnet/article/details/150557025

在 C# .NETCore 中使用 MongoDB(第 3 部分):跳过、排序、限制和投影:
https://blog.csdn.net/hefeng_aspnet/article/details/150557338

如果您喜欢此文章,请收藏、点赞、评论,谢谢,祝您快乐每一天。

限制

当我们查询文档时,有时我们并不想返回所有符合过滤条件的文档,而只想返回其中一部分。这时,限制子句就派上用场了。使用 MongoDB,您可以通过调用 的返回Limit方法来限制文档数量。因此,如果我在数据库中查询年龄小于 40 岁的学生,则会得到以下结果:IFindFluentFind

S/N: 1      Id: 582489339798f091295b9094, FirstName: Gregor, LastName: Felix
S/N: 2      Id: 582489339798f091295b9095, FirstName: Machiko, LastName: Elkberg
S/N: 3      Id: 582489339798f091295b9096, FirstName: Julie, LastName: Sandal
S/N: 4      Id: 583da304f03a84d4d4f4678d, FirstName: Peter, LastName: Cyborg

为了告诉它将结果限制为最多两名学生,我调用了Limit()值 2:

int count = 1;
await collection.Find(x => x.Age < 40)
    .Limit(2)
    .ForEachAsync(
        student =>
        {
            Console.WriteLine($"S/N: {count} \t Id: {student.Id}, FirstName: {student.FirstName}, LastName: {student.LastName}");
            count++;
        });

然后我们得到以下输出,它仅返回两个文档:

S/N: 1,      Id: 582489339798f091295b9094, FirstName: Gregor, LastName: Felix
S/N: 2,      Id: 582489339798f091295b9095, FirstName: Machiko, LastName: Elkberg

跳过

如果我们想告诉数据库要跳过多少个文档,可以使用SkipFluent 接口中的方法。这和之前的代码类似,但需要告诉数据库返回所有年龄小于 40 的文档,并跳过第一个文档。

int count = 1;
await collection.Find(x => x.Age < 40)
    .Skip(1)
    .ForEachAsync(
        student =>
        {
            Console.WriteLine($"S/N: {count} \t Id: {student.Id}, FirstName: {student.FirstName}, LastName: {student.LastName}");
            count++;
        });

S/N: 1,      Id: 582489339798f091295b9095, FirstName: Machiko, LastName: Elkberg
S/N: 2,      Id: 582489339798f091295b9096, FirstName: Julie, LastName: Sandal
S/N: 3,      Id: 583da304f03a84d4d4f4678d, FirstName: Peter, LastName: Cyborg

你会注意到Gregor Felix被跳过了。有了skip它,sort我们就可以为应用程序添加分页功能了。

假设我们想检索集合中的每个学生,每页最多显示两个学生。我们可以这样实现:

  • 跟踪当前页面和要检索的最大文档数。
  • 确定总页数。
  • skip然后在申请时检索相应文件limit

我们可以使用以下代码来实现这一点,并将每页的结果打印到控制台:

var client = new MongoClient();

var db = client.GetDatabase("schoool");

var collection = db.GetCollection<Student>("students");

int currentPage = 1, pageSize = 2;

double totalDocuments = await collection.CountAsync(FilterDefinition<Student>.Empty);
var totalPages = Math.Ceiling(totalDocuments / pageSize);

for (int i = 1; i <= totalPages; i++)
{
    Console.WriteLine($"Page {currentPage}");
    Console.WriteLine();

    int count = 1;
    await collection.Find(FilterDefinition<Student>.Empty)
        .Skip((currentPage - 1) * pageSize)
        .Limit(pageSize)
        .ForEachAsync(
            student =>
            {
                Console.WriteLine($"S/N: {count}, \t Id: {student.Id}, FirstName: {student.FirstName}, LastName: {student.LastName}");
                count++;
            });

    Console.WriteLine();
    currentPage++;
}

我们在控制台窗口中得到以下结果:

Page 1

S/N: 1,      Id: 58469c732adc9f5370e50c9c, FirstName: Gregor, LastName: Felix
S/N: 2,      Id: 58469c732adc9f5370e50c9d, FirstName: Machiko, LastName: Elkberg

Page 2

S/N: 1,      Id: 58469c732adc9f5370e50c9e, FirstName: Julie, LastName: Sandal
S/N: 2,      Id: 58469c732adc9f5370e50c9f, FirstName: Peter, LastName: Cyborg

Page 3

S/N: 1,      Id: 58469c732adc9f5370e50ca0, FirstName: James, LastName: Cyborg

这样,我们就得到了三页,因为我们总共有五条记录,每页最多检索两个文档。

种类

Sort流式接口的方法接受一个,SortDefinition它可以像 一样从字符串或 BsonDocument 隐式转换FilterDefinition。因此,如果我们想使用字符串作为排序定义,按姓氏升序排序,则它将是:

await collection.Find(FilterDefinition<Student>.Empty)
    .Skip((currentPage - 1) * pageSize)
    .Limit(pageSize)
    .Sort("{LastName: 1}")
    .ForEachAsync(
        student =>
        {
            Console.WriteLine($"S/N: {count}, \t Id: {student.Id}, FirstName: {student.FirstName}, LastName: {student.LastName}, Age: {student.Age}");
            count++;
        });

在字符串中,我们用{LastName: 1}where1来指示按升序排序和-1按降序排序。如果我们使用之前的更新运行应用程序,它会在第一页返回 James 和 Peter 作为结果,如下所示:

Page 1

S/N: 1,      Id: 58469c732adc9f5370e50ca0, FirstName: James, LastName: Cyborg, Age: 39
S/N: 2,      Id: 58469c732adc9f5370e50c9f, FirstName: Peter, LastName: Cyborg, Age: 39

Page 2

S/N: 1,      Id: 58469c732adc9f5370e50c9d, FirstName: Machiko, LastName: Elkberg, Age: 23
S/N: 2,      Id: 58469c732adc9f5370e50c9c, FirstName: Gregor, LastName: Felix, Age: 23

Page 3

S/N: 1,      Id: 58469c732adc9f5370e50c9e, FirstName: Julie, LastName: Sandal, Age: 25

如果我们想LastName使用 BsonDocument 按降序排列,则如下:

await collection.Find(FilterDefinition<Student>.Empty)
    .Skip((currentPage - 1) * pageSize)
    .Limit(pageSize)
    .Sort(new BsonDocument("LastName", -1))
    .ForEachAsync(
        student =>
        {
            Console.WriteLine($"S/N: {count}, \t Id: {student.Id}, FirstName: {student.FirstName}, LastName: {student.LastName}, Age: {student.Age}");
            count++;
        });

给出与先前结果相反的结果:

Page 1

S/N: 1,      Id: 58469c732adc9f5370e50c9e, FirstName: Julie, LastName: Sandal, Age: 25
S/N: 2,      Id: 58469c732adc9f5370e50c9c, FirstName: Gregor, LastName: Felix, Age: 23

Page 2

S/N: 1,      Id: 58469c732adc9f5370e50c9d, FirstName: Machiko, LastName: Elkberg, Age: 23
S/N: 2,      Id: 58469c732adc9f5370e50ca0, FirstName: James, LastName: Cyborg, Age: 39

Page 3

S/N: 1,      Id: 58469c732adc9f5370e50c9f, FirstName: Peter, LastName: Cyborg, Age: 39

我们也可以使用SortDefinitionBuilder。因此,我们可以使用构建器助手更新代码来创建排序定义,如下所示:

await collection.Find(FilterDefinition<Student>.Empty)
    .Skip((currentPage - 1) * pageSize)
    .Limit(pageSize)
    .Sort(Builders<Student>.Sort.Descending("LastName"))
    .ForEachAsync(
        student =>
        {
            Console.WriteLine($"S/N: {count}, \t Id: {student.Id}, FirstName: {student.FirstName}, LastName: {student.LastName}, Age: {student.Age}");
            count++;
        });

我们仍然会得到相同的结果,并且我们还可以组合按不同字段升序和降序排列的列表:

await collection.Find(FilterDefinition<Student>.Empty)
    .Skip((currentPage - 1) * pageSize)
    .Limit(pageSize)
    .Sort(Builders<Student>.Sort.Descending("LastName").Ascending("FirstName"))
    .ForEachAsync(
        student =>
        {
            Console.WriteLine($"S/N: {count}, \t Id: {student.Id}, FirstName: {student.FirstName}, LastName: {student.LastName}, Age: {student.Age}");
            count++;
        });

或者对于强类型对象,使用表达式树:

await collection.Find(FilterDefinition<Student>.Empty)
    .Skip((currentPage - 1) * pageSize)
    .Limit(pageSize)
    .Sort(Builders<Student>.Sort.Descending(x => x.LastName).Ascending(x => x.FirstName))
    .ForEachAsync(
        student =>
        {
            Console.WriteLine($"S/N: {count}, \t Id: {student.Id}, FirstName: {student.FirstName}, LastName: {student.LastName}, Age: {student.Age}");
            count++;
        });

我们还可以使用表达式树来指定对流式接口的SortBy、SortByDescending和ThenBy方法进行排序。按照我们之前的示例,这将定义为:ThenByDescending

await collection.Find(FilterDefinition<Student>.Empty)
    .Skip((currentPage - 1) * pageSize)
    .Limit(pageSize)
    .SortByDescending(x => x.LastName)
    .ThenBy(x => x.Age)
    .ForEachAsync(
        student =>
        {
            Console.WriteLine($"S/N: {count}, \t Id: {student.Id}, FirstName: {student.FirstName}, LastName: {student.LastName}, Age: {student.Age}");
            count++;
        });

大多数时候,我们会使用强类型对象,因为使用表达式树构建查询要容易得多。

投影

我们也可以使用 Fluent 接口的方法进行投影。我们指定一个投影,类似于sortfilter 的Project方法。

使用表达式树或投影定义会导致略有不同的行为。其中一个区别是,使用投影定义语法时,必须明确指定不要排除该_id字段,否则它会将其作为结果集的一部分返回。让我们更新代码,使其仅返回FirstName

await collection.Find(FilterDefinition<Student>.Empty)
    .Skip((currentPage - 1) * pageSize)
    .Limit(pageSize)
    .SortByDescending(x => x.LastName)
    .ThenBy(x => x.Age)
    .Project("{FirstName: 1}")
    .ForEachAsync(
        student =>
        {
            Debug.WriteLine($"S/N: {count}, \t Id: {student.Id}, FirstName: {student.FirstName}, LastName: {student.LastName}, Age: {student.Age}");
            count++;
        });

使用更新后的代码,我们的应用程序编译失败。这又带来了另一个区别:使用投影定义时,它会隐式地将文档类型从Student转换为BsonDocument,因此我们返回的是一个流式对象,最终结果将是BsonDocument(即使我们处理的是Student类型)。如果我们想要使用Student,则必须指明我们仍然希望将类型保留为Student。

.Project<Student>("{FirstName: 1}")

因此,通过设置Student为方法的类型来更新我们的代码将得到以下输出:

Page 1

S/N: 1,      Id: 58469c732adc9f5370e50c9e, FirstName: Julie, LastName: , Age: 0
S/N: 2,      Id: 58469c732adc9f5370e50c9c, FirstName: Gregor, LastName: , Age: 0

Page 2

S/N: 1,      Id: 58469c732adc9f5370e50c9d, FirstName: Machiko, LastName: , Age: 0
S/N: 2,      Id: 58469c732adc9f5370e50ca0, FirstName: James, LastName: , Age: 0

Page 3

S/N: 1,      Id: 58469c732adc9f5370e50c9f, FirstName: Peter, LastName: , Age: 0

您可以看到,虽然我们只需要FirstName,但返回了FirstName和Id,而其他字段则保留默认值。为了解决这个问题,我们明确地告诉它排除 Id 字段,并对投影定义进行以下更新:

.Project<Student>("{FirstName: 1, _id: 0}")

然后运行它,我们得到了所需的结果,只有FirstName返回,而其他保持默认值:

Page 1

S/N: 1,      Id: 000000000000000000000000, FirstName: Julie, LastName: , Age: 0
S/N: 2,      Id: 000000000000000000000000, FirstName: Gregor, LastName: , Age: 0

Page 2

S/N: 1,      Id: 000000000000000000000000, FirstName: Machiko, LastName: , Age: 0
S/N: 2,      Id: 000000000000000000000000, FirstName: James, LastName: , Age: 0

Page 3

S/N: 1,      Id: 000000000000000000000000, FirstName: Peter, LastName: , Age: 0

我们还可以使用投影构建器.Project<Student>(Builders<Student>.Projection.Include(x => x.FirstName).Exclude(x => x.Id)),它类似于使用定义构建器进行排序和过滤。我们还可以使用表达式树进行投影,然后将其投影到不同的结果。以下代码将仅返回姓氏和名字,并将其映射到匿名类型:

int count = 1;
await collection.Find(FilterDefinition<Student>.Empty)
    .Project(x => new {x.FirstName, x.LastName})
    .ForEachAsync(
        student =>
        {
            Console.WriteLine($"{count}. \t FirstName: {student.FirstName} - LastName {student.LastName}");
            count++;
        });

Console.WriteLine();

1.      FirstName: Gregor - LastName Felix
2.      FirstName: Machiko - LastName Elkberg
3.      FirstName: Julie - LastName Sandal
4.      FirstName: Peter - LastName Cyborg
5.      FirstName: James - LastName Cyborg

您可能已经注意到,我们并没有明确表明我们想要排除Id,但与其他方式不同,这是因为使用强类型表达式树,它同意仅返回您指定的那些字段并排除其他字段。

本系列之前的教程:

  • 在 C# .NETCore 中使用 MongoDB(第 1 部分):驱动程序基础知识和插入文档
  • 在 C# .NETCore 中使用 MongoDB(第 2 部分):使用过滤子句检索文档

如果您喜欢此文章,请收藏、点赞、评论,谢谢,祝您快乐每一天。

http://www.dtcms.com/a/410057.html

相关文章:

  • 建设网站入什么科目最大的商标交易平台
  • esp32墨水屏学习3
  • DOM(二):事件监听、事件类型、事件对象、环境对象、回调函数、Tab栏切换
  • net6.0 WebApi 中使用 Entity Framework Core + Sqlite
  • 前端2.0
  • PostIn入门到实战(4) - 如何使用接口Mock尽早满足前端开发需求
  • 【论文阅读 | TGRS 2025 | DHANet:用于多模态无人机目标检测的双流分层交互网络​​】
  • 零知IDE——STM32F407VET6与ADS1115模数转换器实现多通道数据采集显示系统
  • 门户网站 商城系统青岛建站开发
  • 从零学算法39
  • BIKE算法:后量子密码标准化竞赛中的编解码候选者
  • 【字节跳动】LLM大模型算法面试题:什么是 LangChain?LangChain 包含哪些 核心概念?
  • 降低fullgc停顿时间
  • BatchNorm2d详细原理介绍
  • Spring Boot WebSocket:使用 Java 构建多频道聊天系统
  • 中堂镇仿做网站软文网站有哪些
  • Android 应用配置跳转微信小程序
  • Word和WPS文字中的自动编号和文字间距过大怎么办?
  • 京东零售张泽华:从营销意图到购买转化,AI重塑广告增长
  • Casey‘s EDI 需求分析
  • 网站美工和平面设计师手机网站域名开头
  • 从垂直钻到水平钻:如何用陀螺精准掌控钻井轨迹?
  • yield在Python中的应用
  • Linux配置Java/JDK(解决Kali启动ysoserial.jar JRMPListener报错)暨 Kali安装JAVA8和切换JDK版本的详细过程
  • springboot用jar启动能访问,但是打成war,部署到tomcat却访问不到
  • 免费企业网站建设流程华为公司电子商务网站建设策划书
  • 中国网站备案查询系统东莞seo外包公司哪家好
  • STM32H743-ARM例程6-RS422
  • 倾角传感器厂家为物联网应用提供高效双轴监测解决方案
  • 住宅IP vs 数据中心IP 2025实战性能对决:IPIPGO、天启HTTP、光络云深度横评