到目前为止,我们已经讨论了创建文档,检索文档,现在让我们来研究一下文档排序,指定要跳过或限制返回的文档数量,以及如何进行投影。此篇文章中的实例代码摘录自原文,未像前几篇文章一样进行实际代码的验证。
作者:依乐祝
Limit
当我们查询一个文档时,我们有时不想返回所有符合过滤条件的文档,而只返回其中的一部分。这就是limit
方法的具体应用。对于MongoDB,可以通过调用Find
返回的IFindFluent
的limit
方法来限制文档的数量。因此,如果我查询数据库中年龄小于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
@H_404_29@为了让它把结果限制在最多两个学生,我调用了
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++; });
@H_404_29@然后得到以下输出,它只返回两个文档:
S/N: 1,Id: 582489339798f091295b9094,LastName: Felix S/N: 2,Id: 582489339798f091295b9095,LastName: Elkberg
@H_404_29@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},LastName: {student.LastName}"); count++; });
@H_404_29@S/N: 1,LastName: Elkberg S/N: 2,Id: 582489339798f091295b9096,LastName: Sandal S/N: 3,Id: 583da304f03a84d4d4f4678d,LastName: Cyborg
@H_404_29@
-
跟踪当前页面和要检索的最大文档数。
-
确定总页数。
-
然后检索文档,同时相应地应用
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},LastName: {student.LastName}"); count++; }); Console.WriteLine(); currentPage++; }
@H_404_29@我们在控制台窗口中得到以下结果:
Page 1 S/N: 1,Id: 58469c732adc9f5370e50c9c,Id: 58469c732adc9f5370e50c9d,LastName: Elkberg Page 2 S/N: 1,Id: 58469c732adc9f5370e50c9e,LastName: Sandal S/N: 2,Id: 58469c732adc9f5370e50c9f,LastName: Cyborg Page 3 S/N: 1,Id: 58469c732adc9f5370e50ca0,FirstName: James,LastName: Cyborg
@H_404_29@这样,我们得到三个页面,因为我们总共有五个记录,每页最多检索两个文档。
Sort
fluent
接口的Sort
方法采用SortDeFinition
作为参数,它可以从string
或BsonDocument
隐式转换,就像FilterDeFinition
一样。因此,如果我们想使用字符串作为排序定义,按姓氏升序排序,那么它将是:await collection.Find(FilterDeFinition<Student>.Empty) .Skip((currentPage - 1) * pageSize) .Limit(pageSize) .sort("{LastName: 1}") .ForEachAsync( student => { Console.WriteLine($"S/N: {count},LastName: {student.LastName},Age: {student.Age}"); count++; });
@H_404_29@在字符串中,我们有
{LastName:1}
,其中1告诉它升序排序,而-1告诉它按降序排序。如果我们使用前面更新的代码运行应用程序,它会在第一页返回James和Peter作为结果,如下所示:Page 1 S/N: 1,LastName: Cyborg,Age: 39 S/N: 2,Age: 39 Page 2 S/N: 1,LastName: Elkberg,Age: 23 S/N: 2,LastName: Felix,Age: 23 Page 3 S/N: 1,LastName: Sandal,Age: 25
@H_404_29@如果我们希望使用
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},Age: {student.Age}"); count++; });
@H_404_29@给出了与之前结果相反的结果:
Page 1 S/N: 1,Age: 25 S/N: 2,Age: 23 Page 2 S/N: 1,Age: 39 Page 3 S/N: 1,Age: 39
@H_404_29@我们也可以使用
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},Age: {student.Age}"); count++; });
@H_404_29@我们仍然可以得到相同的结果,我们还可以组合不同字段上的升序和降序列表:
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},Age: {student.Age}"); count++; });
@H_404_29@或使用强类型对象时,使用表达式树:
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},Age: {student.Age}"); count++; });
@H_404_29@我们还可以使用表达式树来指定对
SortBy
,SortByDescending
,ThenBy
和ThenByDescending
FLUENT接口的方法。按照前面的示例,这将被定义为: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},Age: {student.Age}"); count++; });
@H_404_29@大多数情况下,我们将使用强类型对象,因为使用表达式树构建查询要容易得多。
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},Age: {student.Age}"); count++; });
@H_404_29@使用更新的代码,我们的应用程序无法编译。给我们带来了另一个区别:通过投影定义,它隐式地将文档类型从
Student
转换为bsondocument
,因此我们得到的是一个fluent对象,其结果将是一个BsonDocument
(即使我们使用的是Student类型)。如果我们想和Student一起工作,我们必须指出我们仍然希望将类型保留为Student
。.Project<Student>("{FirstName: 1}")
@H_404_29@因此,通过将
Student
设置为方法的类型来更新我们的代码,将得到以下输出:Page 1 S/N: 1,LastName:,Age: 0 S/N: 2,Age: 0 Page 2 S/N: 1,Age: 0 Page 3 S/N: 1,Age: 0
@H_404_29@您可以看到,虽然我们只需要
FirstName
,但是FirstName
和Id
被返回,而其他的则保持默认值。为了解决这个问题,我们显式地告诉它排除Id字段,并对投影定义进行以下更新:.Project<Student>("{FirstName: 1,_id: 0}")
@H_404_29@然后运行它,我们只返回
FirstName
,而其他值保持默认值:Page 1 S/N: 1,Id: 000000000000000000000000,Age: 0
@H_404_29@我们也可以使用投影生成器。
.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();
@H_404_29@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
@H_404_29@您可能已经注意到,我们并没有显式地指明要排除Id,而是与另一种方式不同,这是因为在强类型表达式树中,它同意只返回您指定的那些字段,而排除其他字段。
总结
本文带着你一起研究了一下文档的排序,指定要跳过或限制返回的文档数量,以及如何进行投影。此篇文章中的实例代码摘录自原文,未像前几篇文章一样进行实际代码的验证。希望对你有所帮助。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。