1. 程式人生 > >在.NET Core中使用MongoDB明細教程(3):Skip, Sort, Limit, Projections

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

![](https://img2020.cnblogs.com/blog/1377250/202008/1377250-20200818205326644-42430411.png) 到目前為止,我們已經討論了建立文件, 檢索文件,現在讓我們來研究一下文件排序,指定要跳過或限制返回的文件數量,以及如何進行投影。此篇文章中的例項程式碼摘錄自原文,未像前幾篇文章一樣進行實際程式碼的驗證。 > 作者:依樂祝 > > 譯文地址: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`返回的`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 ``` 為了讓它把結果限制在最多兩個學生,我呼叫了`Limit()`方法,並傳遞值為2的引數: ```csharp 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的所有程式碼,並跳過第一個。 ```csharp 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`。 我們可以使用以下程式碼來完成此操作,並將每個頁面的結果列印到控制檯: ```csharp var client = new MongoClient(); var db = client.GetDatabase("schoool"); var collection = db.GetCollection("students"); int currentPage = 1, pageSize = 2; double totalDocuments = await collection.CountAsync(FilterDefinition.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.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++; } ``` 我們在控制檯視窗中得到以下結果: ```c# 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`作為引數,它可以從`string`或`BsonDocument`隱式轉換,就像`FilterDefinition`一樣。因此,如果我們想使用字串作為排序定義,按姓氏升序排序,那麼它將是: ```csharp await collection.Find(FilterDefinition.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 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`將姓氏按降序排列,則這將是: ```csharp await collection.Find(FilterDefinition.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`。因此,我們可以使用構建器幫助方法更新程式碼以建立一個排序定義,如下所示: ```csharp await collection.Find(FilterDefinition.Empty) .Skip((currentPage - 1) * pageSize) .Limit(pageSize) .Sort(Builders.Sort.Descending("LastName")) .ForEachAsync( student => { Console.WriteLine($"S/N: {count}, \t Id: {student.Id}, FirstName: {student.FirstName}, LastName: {student.LastName}, Age: {student.Age}"); count++; }); ``` 我們仍然可以得到相同的結果,我們還可以組合不同欄位上的升序和降序列表: ```csharp await collection.Find(FilterDefinition.Empty) .Skip((currentPage - 1) * pageSize) .Limit(pageSize) .Sort(Builders.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++; }); ``` 或使用強型別物件時,使用表示式樹: ```csharp await collection.Find(FilterDefinition.Empty) .Skip((currentPage - 1) * pageSize) .Limit(pageSize) .Sort(Builders.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`FLUENT介面的方法。按照前面的示例,這將被定義為: ```csharp await collection.Find(FilterDefinition.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` ```csharp await collection.Find(FilterDefinition.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`。 ```csharp .Project("{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欄位,並對投影定義進行以下更新: ```csharp .Project("{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(Builders.Projection.Include(x => x.FirstName).Exclude(x => x.Id))`這與使用定義生成器進行排序和篩選類似。我們也可以使用表示式樹進行投影,然後將其投影到不同的結果。以下程式碼將只返回first 和last name,並將其對映到匿名型別: ```csharp int count = 1; await collection.Find(FilterDefinition.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,而是與另一種方式不同,這是因為在強型別表示式樹中,它同意只返回您指定的那些欄位,而排除其他欄位。 ### 總結 本文帶著你一起研究了一下文件的排序,指定要跳過或限制返回的文件數量,以及如何進行投影。此篇文章中的例項程式碼摘錄自原文,未像前幾篇文章一樣進行實際程式碼的驗證。希望對你有所