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



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

    作者:依乐祝

    译文地址: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} 	 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} 	 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}, 	 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

    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}, 	 Id: {student.Id}, FirstName: {student.FirstName}, LastName: {student.LastName}, Age: {student.Age}");
                count++;
            });
    

    在字符串中,我们有{LastName:1},其中1告诉它升序排序,而-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
    

    如果我们希望使用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}, 	 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}, 	 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}, 	 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}, 	 Id: {student.Id}, FirstName: {student.FirstName}, LastName: {student.LastName}, Age: {student.Age}");
                count++;
            });
    

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

    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}, 	 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}, 	 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 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,但是FirstNameId被返回,而其他的则保持默认值。为了解决这个问题,我们显式地告诉它排除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))这与使用定义生成器进行排序和筛选类似。我们也可以使用表达式树进行投影,然后将其投影到不同的结果。以下代码将只返回first 和last name,并将其映射到匿名类型:

    int count = 1;
    await collection.Find(FilterDefinition<Student>.Empty)
        .Project(x => new {x.FirstName, x.LastName})
        .ForEachAsync(
            student =>
            {
                Console.WriteLine($"{count}. 	 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,而是与另一种方式不同,这是因为在强类型表达式树中,它同意只返回您指定的那些字段,而排除其他字段。

    总结

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

  • 相关阅读:
    python os.path
    ant的基本说明
    gcc的基本使用方法
    java逻辑运算符小节
    awk 简单教程
    推荐:恢复Ext3下被删除的文件
    python读取excel
    ant的简明教程,后面运行写的不错
    WinForm中快捷键与组合按键的设置
    InstallShield 2010集成.net Framework 4的安装包制作
  • 原文地址:https://www.cnblogs.com/yilezhu/p/13525942.html
Copyright © 2020-2023  润新知