.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终端运算符:

1
2
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属性然后具有大量方法来帮助我们构建查询。 我们以前已经看过一个例子:

1
2
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运算符的例子:

1
2
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条件的简短符号。 这是一个替代解决方案:

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

And函数接受带有任意数量的过滤器定义的集合或参数数组,并将它们与AND结合在一起。 作为旁注,您可能会猜到有一个Or函数接受相同的过滤器定义参数,并将它们与逻辑OR链接在一起。 短的符号是单个管道字符“|”。

以下是一个例子:

1
2
3
4
5
6
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风格的解决方案:

1
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函数接受集合字段,然后接收集合子属性上的过滤器,如下所示:

1
2
3
4
5
6
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);

这是一个与过滤器匹配的餐厅:

1
2
3
4
5
6
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
{
   "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中写入的过滤器。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值