到目前为止,我们已经讨论了创建文档, 检索文档,现在让我们来研究一下文档排序,指定要跳过或限制返回的文档数量,以及如何进行投影。此篇文章中的实例代码摘录自原文,未像前几篇文章一样进行实际代码的验证。

作者:依乐祝

译文地址:https://www.cnblogs.com/yilezhu/p/13525942.html

英文地址:https://www.codementor.io/@pmbanugo/working-with-mongodb-in-net-part-3-skip-sort-limit-and-projections-oqfwncyka

Limit

当我们查询一个文档时,我们有时不想返回所有符合过滤条件的文档,而只返回其中的一部分。这就是limit 方法的具体应用。对于MongoDB,可以通过调用Find返回的IFindFluentlimit方法来限制文档的数量。因此,如果我查询数据库中年龄小于40岁的学生,我会得到以下信息:

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

Skip

如果我们想告诉数据库要跳过多少文档,我们使用fluent接口中的skip方法。因此,它类似于我们之前使用的代码,但是告诉数据库返回年龄小于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被跳过了。使用skipsort,我们可以将分页添加到应用程序中。

假设我们要检索集合中的每个学生,一个页面上最多显示两个学生。我们可以通过如下过程实现:

  • 跟踪当前页面和要检索的最大文档数。

  • 确定总页数。

  • 然后检索文档,同时相应地应用skiplimit

我们可以使用以下代码来完成此操作,并将每个页面的结果打印到控制台:

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 1S/N: 1,      Id: 58469c732adc9f5370e50c9c, FirstName: Gregor, LastName: Felix
S/N: 2,      Id: 58469c732adc9f5370e50c9d, FirstName: Machiko, LastName: ElkbergPage 2S/N: 1,    Id: 58469c732adc9f5370e50c9e, FirstName: Julie, LastName: Sandal
S/N: 2,      Id: 58469c732adc9f5370e50c9f, FirstName: Peter, LastName: CyborgPage 3S/N: 1,   Id: 58469c732adc9f5370e50ca0, FirstName: James, LastName: Cyborg

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

Sort

fluent接口的Sort方法采用SortDefinition作为参数,它可以从stringBsonDocument隐式转换,就像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},其中1告诉它升序排序,而-1告诉它按降序排序。如果我们使用前面更新的代码运行应用程序,它会在第一页返回James和Peter作为结果,如下所示:

Page 1S/N: 1,    Id: 58469c732adc9f5370e50ca0, FirstName: James, LastName: Cyborg, Age: 39
S/N: 2,      Id: 58469c732adc9f5370e50c9f, FirstName: Peter, LastName: Cyborg, Age: 39Page 2S/N: 1,      Id: 58469c732adc9f5370e50c9d, FirstName: Machiko, LastName: Elkberg, Age: 23
S/N: 2,      Id: 58469c732adc9f5370e50c9c, FirstName: Gregor, LastName: Felix, Age: 23Page 3S/N: 1,      Id: 58469c732adc9f5370e50c9e, FirstName: Julie, LastName: Sandal, Age: 25

如果我们希望使用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 1S/N: 1,    Id: 58469c732adc9f5370e50c9e, FirstName: Julie, LastName: Sandal, Age: 25
S/N: 2,      Id: 58469c732adc9f5370e50c9c, FirstName: Gregor, LastName: Felix, Age: 23Page 2S/N: 1,      Id: 58469c732adc9f5370e50c9d, FirstName: Machiko, LastName: Elkberg, Age: 23
S/N: 2,      Id: 58469c732adc9f5370e50ca0, FirstName: James, LastName: Cyborg, Age: 39Page 3S/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++;});

我们还可以使用表达式树来指定对SortBySortByDescendingThenByThenByDescendingFLUENT接口的方法。按照前面的示例,这将被定义为:

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++;});

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

Projection投影

我们也可以使用fluent接口的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,因此我们得到的是一个fluent对象,其结果将是一个BsonDocument(即使我们使用的是Student类型)。如果我们想和Student一起工作,我们必须指出我们仍然希望将类型保留为Student

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

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

Page 1S/N: 1,     Id: 58469c732adc9f5370e50c9e, FirstName: Julie, LastName: , Age: 0
S/N: 2,      Id: 58469c732adc9f5370e50c9c, FirstName: Gregor, LastName: , Age: 0Page 2S/N: 1,    Id: 58469c732adc9f5370e50c9d, FirstName: Machiko, LastName: , Age: 0
S/N: 2,      Id: 58469c732adc9f5370e50ca0, FirstName: James, LastName: , Age: 0Page 3S/N: 1,     Id: 58469c732adc9f5370e50c9f, FirstName: Peter, LastName: , Age: 0

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

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

然后运行它,我们只返回FirstName,而其他值保持默认值:

Page 1S/N: 1,    Id: 000000000000000000000000, FirstName: Julie, LastName: , Age: 0
S/N: 2,      Id: 000000000000000000000000, FirstName: Gregor, LastName: , Age: 0Page 2S/N: 1,    Id: 000000000000000000000000, FirstName: Machiko, LastName: , Age: 0
S/N: 2,      Id: 000000000000000000000000, FirstName: James, LastName: , Age: 0Page 3S/N: 1,     Id: 000000000000000000000000, FirstName: Peter, LastName: , Age: 0

我们也可以使用投影生成器。.Project<Student>(Builders<Student>.Projection.Include(x => x.FirstName).Exclude(x => x.Id))这与使用定义生成器进行排序和筛选类似。我们也可以使用表达式树进行投影,然后将其投影到不同的结果。以下代码将只返回first 和last name,并将其映射到匿名类型:

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,而是与另一种方式不同,这是因为在强类型表达式树中,它同意只返回您指定的那些字段,而排除其他字段。

总结

本文带着你一起研究了一下文档的排序,指定要跳过或限制返回的文档数量,以及如何进行投影。此篇文章中的实例代码摘录自原文,未像前几篇文章一样进行实际代码的验证。希望对你有所帮助。

往期精彩回顾

【推荐】.NET Core开发实战视频课程 ★★★

.NET Core实战项目之CMS 第一章 入门篇-开篇及总体规划

【.NET Core微服务实战-统一身份认证】开篇及目录索引

Redis基本使用及百亿数据量中的使用技巧分享(附视频地址及观看指南)

.NET Core中的一个接口多种实现的依赖注入与动态选择看这篇就够了

10个小技巧助您写出高性能的ASP.NET Core代码

用abp vNext快速开发Quartz.NET定时任务管理界面

在ASP.NET Core中创建基于Quartz.NET托管服务轻松实现作业调度

现身说法:实际业务出发分析百亿数据量下的多表查询优化

关于C#异步编程你应该了解的几点建议

C#异步编程看这篇就够了

给我好看
您看此文用  · 秒,转发只需1秒呦~
好看你就点点我

在.NET Core中使用MongoDB明细教程(3):Skip, Sort, Limit, Projections相关推荐

  1. 在.NET Core中使用MongoDB明细教程(1):驱动基础及文档插入

    MongoDB,被归类为NoSQL数据库(其实我更觉得MongoDb是介于关系型数据库和非关系型数据库之间,此外,在我看来在很多场景中MongoDb都可以取代关系型数据库.),是一个以类JSON格式存 ...

  2. java skip函数_【Java必修课】图说Stream中的skip()和limit()方法及组合使用

    1 简介 本文将讲解Java 8 Stream中的两个方法:skip()和limit().这两个方法是Stream很常用的,不仅各自会被高频使用,还可以组合出现,并能实现一些小功能,如subList和 ...

  3. 图说Stream中的skip()和limit()方法及组合使用

    1 简介 本文将讲解Java 8 Stream中的两个方法:skip()和limit().这两个方法是Stream很常用的,不仅各自会被高频使用,还可以组合出现,并能实现一些小功能,如subList和 ...

  4. 解决.NET Core中MailKit无法使用阿里云邮件推送服务的问题

    在博问中(.net core怎么实现邮件发送)知道了MailKit无法使用阿里云邮件推送服务发送邮件的问题,自已实测也遇到同样的问题,而用自己搭建的邮件服务器没这个问题. 于是,向阿里云提交了工单.. ...

  5. .NET Core也可以使用MongoDB了

    可能是由于.NET Core还不是正式版的缘故吧,MongoDB的官方Driver(http://mongodb.github.io/mongo-csharp-driver/)一直不支持.NET Co ...

  6. java访问mongodb_Java中获取MongoDB连接的方法详解

    首先是所需jar包,Maven中的配置如下: org.mongodb mongodb-driver 3.4.1 org.mongodb bson 3.4.1 org.springframework.d ...

  7. 路漫漫其修远兮:js的成长经历(二十五)—— Node.js中的MongoDB

    目录 MongoDB简介 MongoDB安装教程 mongoDB基本组成 mongoDB的基本指令 安装可视化操作软件 熟悉使用MongoDB的基本指令(增删改查等) Node进阶-Mongoose ...

  8. 如何使用C#在ASP.NET Core中轻松实现QRCoder

    by Yogi 由瑜伽士 如何使用C#在ASP.NET Core中轻松实现QRCoder (How to easily implement QRCoder in ASP.NET Core using ...

  9. ASP .NET Core Web Razor Pages系列教程四:使用数据库进行交互 entity-framework(MySQL/MariaDB 版)

    系列文章目录:系列教程:使用ASP.NET Core创建Razor Pages Web应用程序 - zhangpeterx的博客 系列教程代码的GitHub地址:ASP .Net Core Razor ...

最新文章

  1. 如何让fragment每次都重新加载_每次都能正确判罚丨网球是如何电子化的
  2. Wpf拖动按钮实现(二)
  3. 需要进一步学习和思考的速度问题
  4. 写给后端程序员的HTTP缓存原理介绍
  5. 通过zookeeper连接hive beeline
  6. 详解:设计模式之-策略设计模式
  7. JAVA对时间的几个处理小方法
  8. Applet、Scriptlet与Servlet
  9. php转型mysql dba_MySQL_DBA整理(转)
  10. linux ACL应用学习
  11. C++算法学习(力扣:1269. 停在原地的方案数)
  12. 软件测试--用例编写
  13. linu安装mysql5.7
  14. HDU2058 The sum problem(数学问题)
  15. 数据挖掘与数据分析的主要区别是什么?
  16. GotW#63 狂乱的代码
  17. 动态规划!!!动态规划!!!
  18. 线程、进程、多线程、多进程 和 多任务
  19. BIM模型文件下载——售楼中心室内装饰Revit模型
  20. 数学建模的常用模型和方法

热门文章

  1. stm32串口通讯问题
  2. jQuery.ajax实现根据不同的Content-Type做出不同的响应
  3. 02 JRE与JDK
  4. 做程序员的纠结在哪里
  5. 博客园2013年5月份第1周源码发布详情
  6. wpf中的datagrid中如何显示图片
  7. C#——一个简单的文件管理器
  8. vue-typescript
  9. linux批量分发必会面试题,通过邮件反馈结果。
  10. 关于一个js栈溢出的异常