天天看點

.Net-Mongodb學習大全網址

http://www.yuanjiaocheng.net/csharpmongo/16.html

介紹

在上一篇文章中,我們繼續探索MongoDb .NET驅動程式中的資料序列化。 我們檢視了各種屬性,如BsonIgnore,BsonRepresentation或BsonIgnoreIfNull。 資料序列化不是太複雜,隻要MongoDb中的文檔遵守一些模式,就像在我們的示範餐廳和郵政編碼集合。 一旦我們必須反序列化一組無序的文檔,那麼我們可能需要實作我們自己的序列化,事情變得更加複雜。 這是追蹤模式的另一個原因,即使MongoDb文檔是無模式的。

在這篇文章中,我們将介紹.NET驅動程式中的查詢技術。 我們将基于我們以前開始建構的示範.NET代碼。

Find方法

在撰寫本文時,T界面的IMongoCollection提供了以下Find方法:

  • Find:一種擴充方法,使我們能夠以流暢的方式建構查詢,即我們可以連結與搜尋相關的各種其他方法
  • FindSync:一種同步查找方法,它傳回一個遊标,并不像Find擴充方法那麼靈活
  • FindAsync:一個異步的,即等待版本的FindSync

所有這些功能都接受基本上遵循兩種不同格式的過濾器,我們已經看到了兩個示例:FilterDefinition或傳回true的LINQ語句。 我發現Find擴充方法是上述最靈活的,因為它打開了以流暢的方式(如排序和限制)将其他與搜尋相關的查詢附加到其中的可能性。 是以,我會在這篇文章中堅持下去,但是要注意替代方案是很好的。

findOne和findall

回想一下,findOne()提供了一種快速了解Mongo shell中的文檔結構的方法。 我們如何在C#中寫同一個? 簡單:

1

2

ZipCodeDb firstZip = modelContext.ZipCodes.Find(z => 

true

).FirstOrDefault();

RestaurantDb firstRestaurant = modelContext.Restaurants.Find(r => 

true

).FirstOrDefault();

我們在LINQ語句中沒有任何過濾傳回true。 它相當于SQL中沒有where子句的SELECT * FROM語句。 然後,我們隻需附加熟悉的FirstOrDefault方法來擷取第一個文檔。

相反,如果我們要檢索所有沒有任何過濾器的文檔,那麼我們隻需應用ToList終端運算符:

List<ZipCodeDb> allZipCodes = modelContext.ZipCodes.Find(z => 

true

).ToList();

List<RestaurantDb> allRestaurants = modelContext.Restaurants.Find(r => 

true

).ToList();

當您輕觸IntelliSense提供的功能時,您會注意到FirstOrDefault和ToList方法都具有異步版本:FirstOrDefaultAsync和ToListAsync。 在MongoDb庫中,這是一個很常見的功能,它同時具有異步版本。

過濾基礎知識

如前所述,過濾可以使用特定于MongoDb庫的FilterDefinition對象或更一般的LINQ表達式來建構。 .NET程式員會意識到LINQ我相信我們從FilterDefinitionBuilder開始。 在我的經驗中,有一些複雜的過濾器定義隻能用這個特定的文法來表達,是以即使你是一個LINQ的狂熱者也是很好的。

過濾器定義使用通用Builders靜态類及其Filter屬性建構。 Builders類是建構一些不同定義對象的網關,FilterDefinitions隻是其中之一。 Filter屬性然後具有大量方法來幫助我們建構查詢。 我們以前已經看過一個例子:

ModelContext modelContext = ModelContext.Create(

new

ConfigFileConfigurationRepository(), 

new

AppConfigConnectionStringRepository());

var filter = Builders<RestaurantDb>.Filter.Eq(r => r.Borough, 

"Brooklyn"

);

如果您在Visual Studio中鍵入“Builders.Filter”,則IntelliSense将會提供或多或少對應于本系列中檢視的查詢操作符的多種功能。 例如。 Gt函數映射到$ gt運算符,Not to $ not等。在許多情況下,通過名稱來猜測函數的目的并不難,如Gt = GreaterThan。 Filter屬性具有一個名為Empty的特殊屬性,對應于在Mongo shell中的早期示例中看到的空的JSON過濾器{}。 它也是FilterDefinition等效于上面的Find示例中的“return true”LINQ語句。

Filter方法,如Eq和Gte,然後需要一個或多個參數。 Eq函數與其他類似的函數(如Gte和Lt)一樣,需要一個字段選擇器,形式為LINQ表達式,然後是過濾器值。 上面的例子選擇了一個名為“自治市鎮”的字段,我們要對值“布魯克林”過濾這個字段。

我們來看看一個使用FilterDefinition解決方案的邏輯AND運算符的例子:

3

4

5

6

ModelContext modelContext = ModelContext.Create(

new

ConfigFileConfigurationRepository(), 

new

AppConfigConnectionStringRepository());

var boroughFilter = Builders<RestaurantDb>.Filter.Eq(r => r.Borough, 

"Brooklyn"

);

var cuisineFilter = Builders<RestaurantDb>.Filter.Eq(r => r.Cuisine, 

"Delicatessen"

);

var cuisineAndBoroughFilter = boroughFilter & cuisineFilter;

var firstRes = modelContext.Restaurants.Find(cuisineAndBoroughFilter).First();

Console.WriteLine(firstRes);

我們想在布魯克林找到第一間設有“熟食店”的餐廳。 “&”運算符是鍊條AND條件的簡短符号。 這是一個替代解決方案:

var cuisineAndBoroughFilterAlternative = Builders<RestaurantDb>.Filter.And(boroughFilter, cuisineFilter);

And函數接受帶有任意數量的過濾器定義的集合或參數數組,并将它們與AND結合在一起。 作為旁注,您可能會猜到有一個Or函數接受相同的過濾器定義參數,并将它們與邏輯OR連結在一起。 短的符号是單個管道字元“|”。

以下是一個例子:

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

{

"MongoDbId"

"56edc2ff03a1cd840734dbb1"

,

"Address"

: {

"BuildingNr"

"7114"

,

"Coordinates"

: [

-

73.9068506

,

40.6199034

],

"Street"

"Avenue U"

,

"ZipCode"

"11234"

},

"Borough"

"Brooklyn"

,

"Cuisine"

"Delicatessen"

,

"Grades"

: [

{

"InsertedUtc"

"2014-05-29T00:00:00Z"

,

"Grade"

"A"

,

"Score"

10

},

{

"InsertedUtc"

"2014-01-14T00:00:00Z"

,

"Grade"

"A"

,

"Score"

10

},

{

"InsertedUtc"

"2013-08-03T00:00:00Z"

,

"Grade"

"A"

,

"Score"

8

},

{

"InsertedUtc"

"2012-07-18T00:00:00Z"

,

"Grade"

"A"

,

"Score"

10

},

{

"InsertedUtc"

"2012-03-09T00:00:00Z"

,

"Grade"

"A"

,

"Score"

13

},

{

"InsertedUtc"

"2011-10-14T00:00:00Z"

,

"Grade"

"A"

,

"Score"

9

}

],

"Name"

"Wilken'S Fine Food"

,

"Id"

40356483

}

這是LINQ風格的解決方案:

var firstResWithLinq = modelContext.Restaurants.Find(r => r.Borough == 

"Brooklyn"

&& r.Cuisine == 

"Delicatessen"

).FirstOrDefault();

讓我們看看更複雜的一些:找到第一家餐廳,其等級陣列包括A,B和C級,即每個餐廳中至少有一個。 在這種情況下,我們需要深入對象圖。 Filter屬性具有一個All函數,它為數組字段建構$ all過濾器,但在這種情況下我們不能使用它。 如果等級數組隻是一個簡單的字元串數組,如“grade:[”A“,”B“]”,All方法将會有所幫助。 我們需要在這裡更深一層。

該解決方案以與AND相關聯的三個ElemMatch函數的形式。 ElemMatch函數接受集合字段,然後接收集合子屬性上的過濾器,如下所示:

var arrayFilterGradeA = Builders<RestaurantDb>.Filter.ElemMatch(r => r.Grades, g => g.Grade == 

"A"

);

var arrayFilterGradeB = Builders<RestaurantDb>.Filter.ElemMatch(r => r.Grades, g => g.Grade == 

"B"

);

var arrayFilterGradeC = Builders<RestaurantDb>.Filter.ElemMatch(r => r.Grades, g => g.Grade == 

"C"

);

var arrayFilterWithAllGrades = arrayFilterGradeA & arrayFilterGradeB & arrayFilterGradeC;

var firstResWithAllGrades = modelContext.Restaurants.Find(arrayFilterWithAllGrades).FirstOrDefault();

Console.WriteLine(firstResWithAllGrades);

這是一個與過濾器比對的餐廳:

{

"MongoDbId"

"56edc2ff03a1cd840734dbb5"

,

"Address"

: {

"BuildingNr"

"1269"

,

"Coordinates"

: [

-

73.871194

,

40.6730975

],

"Street"

"Sutter Avenue"

,

"ZipCode"

"11208"

},

"Borough"

"Brooklyn"

,

"Cuisine"

"Chinese"

,

"Grades"

: [

{

"InsertedUtc"

"2014-09-16T00:00:00Z"

,

"Grade"

"B"

,

"Score"

21

},

{

"InsertedUtc"

"2013-08-28T00:00:00Z"

,

"Grade"

"A"

,

"Score"

7

},

{

"InsertedUtc"

"2013-04-02T00:00:00Z"

,

"Grade"

"C"

,

"Score"

56

},

{

"InsertedUtc"

"2012-08-15T00:00:00Z"

,

"Grade"

"B"

,

"Score"

27

},

{

"InsertedUtc"

"2012-03-28T00:00:00Z"

,

"Grade"

"B"

,

"Score"

27

}

],

"Name"

"May May Kitchen"

,

"Id"

40358429

}

該示例表明,有時我們需要在驅動程式中建立我們的查詢,以應用我們想要的,但最終我們可以找到相當于Mongo shell中寫入的過濾器。