在上篇文章我们介绍了一些驱动程序相关的基础知识,以及如何将文档插入到集合中。在这篇文章中,我们将学习如何从数据库中检索文档。

作者:依乐祝

译文地址:https://www.cnblogs.com/yilezhu/p/13520021.html

英文地址:https://www.codementor.io/@pmbanugo/working-with-mongodb-in-net-2-retrieving-mrlbeanm5

任何文档都属于集合,因此所有CRUD操作都是在单个集合范围中完成的。若要从集合中检索文档,可以使用Find, FindSync,和FindAsync等方法。

FindSync&FindAsync

FindSync和FindAsync两者都有两个带有三个参数的重载。FindSync和FindAsync很相似,只是FindSync是同步的,并阻塞直到它的调用完成。FindSync返回IAsyncCursor ,而FindAsync返回一个IAsyncCursor的任务.

什么是IAsyncCursor

MongoDB以批形式返回查询结果,批处理大小不会超过BSON文档的最大大小。从版本3.2开始,BSON文档的最大大小为16 MB。最大文档大小有助于确保单个文档在传输过程中不能使用过多的RAM或过多的带宽。此约束在将文档添加到集合时也适用,但是为了存储更大的文档,MongoDB已经将GridFS API作为一项规定。对于大多数查询,第一批将返回101个文档或刚好超过1MB的文档,随后的批处理将为4MB。我们可以在驱动程序中通过设置FindOptions 的BatchSize 属性来覆盖默认的批大小,该属性作为第二个参数传递给任何find方法。所以基本上,游标是指向查询结果集的指针。

默认情况下,服务器将在不活动10分钟后或客户端耗尽游标后自动关闭游标。若要重写此行为,可以指定在查询中使用FindOptions类的NoCursorTimeout属性值设置为false。但是,如果你这样做您应该手动关闭游标或耗尽游标。

驱动程序中的这个IAsyncCursor表示异步游标。要访问文档,我们需要手动迭代游标。

检索文件

让我们构建我们的第一个Read查询,这个查询返回我们数据库中books中的所有数据。更新MainAsync方法如下:

static async Task Main(string[] args)
{
await TestFindAsync();
Console.ReadLine();
} static async Task TestFindAsync()
{
var connectionString = "mongodb://localhost:27017";
var client = new MongoClient(connectionString);
var database = client.GetDatabase("bookstore");
var collection = database.GetCollection<BsonDocument>("books");
using IAsyncCursor<BsonDocument> cursor = await collection.FindAsync(new BsonDocument());
while (await cursor.MoveNextAsync())
{
IEnumerable<BsonDocument> batch = cursor.Current;
foreach (BsonDocument document in batch)
{
Console.WriteLine(document);
Console.WriteLine();
}
}
}

任何find方法的第一个重载都有3个参数:FilterDefinition (用于定义查询的筛选器)、一个可选的FindOptions(用于指定查询的选项(例如游标超时、批处理大小等)和一个可选的cancellationToken。

在上面的代码中,我们通过向方法传递一个空的BsonDocument来指定一个空的过滤器定义。另一种编写方法是使用FilterDefinition<BsonDocument>.Empty来表示一个空的过滤器。有了空过滤器,我们基本上是告诉它返回给我们集合中的所有文档。然后,我们迭代游标以成批获取文档(while循环中的MoveNextAsync),并调用cursor.Current获取当前批中的文档,然后将其打印出来。

运行上面的代码应该可以为我们提供该集合中已有的所有文档

{ "_id" : ObjectId("5f33630f9e7b20e7e29208f3"), "bookname" : ".net core3.1 with mongodb", "description" : "这是一本关于 在.net core3.1中使用mongodb进行开发的教程", "tags" : [".net core", "mongodb"], "remark" : "C#是世界上最好的语言", "publishyear" : 2020 }

{ "_id" : ObjectId("5f3367482d2d59d358e1219b"), "bookname" : ".net core3.1 with mongodb1", "description" : "这是一本关于在.net core3.1中使用mongodb进行开发的教程1", "tags" : [".net core", "mongodb"], "remark" : "C#是世界上最好的语言", "publishyear" : 2020 }

{ "_id" : ObjectId("5f3367482d2d59d358e1219c"), "bookname" : ".net core3.1 with mongodb2", "description" : "这是一本关于在.net core3.1中使用mongodb进行开发的教程2", "tags" : [".net core", "mongodb"], "remark" : "C#是世界上最好的语言", "publishyear" : 2020 }

{ "_id" : ObjectId("5f3367482d2d59d358e1219d"), "bookname" : ".net core3.1 with mongodb2", "description" : "这是一本关于在.net core3.1中使用mongodb进行开发的教程2", "tags" : [".net core", "mongodb"], "remark" : "C#是世界上最好的语言", "publishyear" : 2020 }

{ "_id" : ObjectId("5f33850d467bf3877966f1ea"), "BookName" : ".net core3.1 with mongodb21", "Description" : "这是一本关 于在.net core3.1中使用mongodb进行开发的教程21", "Tags" : [".net core", "mongodb"], "Remark" : "C#是世界上最好的语言", "PublishYear" : 2020 }

{ "_id" : ObjectId("5f33850d467bf3877966f1eb"), "BookName" : ".net core3.1 with mongodb22", "Description" : "这是一本关 于在.net core3.1中使用mongodb进行开发的教程22", "Tags" : [".net core", "mongodb"], "Remark" : "C#是世界上最好的语言", "PublishYear" : 2020 }

{ "_id" : ObjectId("5f33850d467bf3877966f1ec"), "BookName" : ".net core3.1 with mongodb23", "Description" : "这是一本关 于在.net core3.1中使用mongodb进行开发的教程23", "Tags" : [".net core", "mongodb"], "Remark" : "C#是世界上最好的语言", "PublishYear" : 2020 }

我们可以看到返回的数据跟我们在上一篇文章中添加的文档基本一样,除了多了一个_id属性,所有集合在这个字段上都有一个唯一的主索引,如果您在创建文档时没有提供主索引,那么MongoDB会默认提供一个主索引。它的类型是ObjectId,这是在Bson规范中定义。

为了演示FindOptions,我将添加一个将批大小限制为2的选项,该选项将显示我们在控制台中循环的批。使用以下内容更新代码

static async Task Main(string[] args)
{
await TestFindAsync();
Console.ReadLine();
} static async Task TestFindAsync()
{
var connectionString = "mongodb://localhost:27017";
var client = new MongoClient(connectionString);
var database = client.GetDatabase("bookstore");
var collection = database.GetCollection<BsonDocument>("books");
FilterDefinition<BsonDocument> filter = FilterDefinition<BsonDocument>.Empty;
FindOptions<BsonDocument> options = new FindOptions<BsonDocument> {
BatchSize = 2,
NoCursorTimeout = false
};
using IAsyncCursor<BsonDocument> cursor = await collection.FindAsync(filter,options);
var batch = 0;
while (await cursor.MoveNextAsync())
{
batch++;
Console.WriteLine($"Batch: {batch}");
IEnumerable<BsonDocument> documents = cursor.Current;
foreach (BsonDocument document in documents)
{
Console.WriteLine(document);
Console.WriteLine();
}
}
Console.WriteLine($"Total Batch: { batch}");
}

并运行它以获得以下结果:

Batch: 1
{ "_id" : ObjectId("5f33630f9e7b20e7e29208f3"), "bookname" : ".net core3.1 with mongodb", "description" : "这是一本关于 在.net core3.1中使用mongodb进行开发的教程", "tags" : [".net core", "mongodb"], "remark" : "C#是世界上最好的语言", "publishyear" : 2020 } { "_id" : ObjectId("5f3367482d2d59d358e1219b"), "bookname" : ".net core3.1 with mongodb1", "description" : "这是一本关于在.net core3.1中使用mongodb进行开发的教程1", "tags" : [".net core", "mongodb"], "remark" : "C#是世界上最好的语言", "publishyear" : 2020 } Batch: 2
{ "_id" : ObjectId("5f3367482d2d59d358e1219c"), "bookname" : ".net core3.1 with mongodb2", "description" : "这是一本关于在.net core3.1中使用mongodb进行开发的教程2", "tags" : [".net core", "mongodb"], "remark" : "C#是世界上最好的语言", "publishyear" : 2020 } { "_id" : ObjectId("5f3367482d2d59d358e1219d"), "bookname" : ".net core3.1 with mongodb2", "description" : "这是一本关于在.net core3.1中使用mongodb进行开发的教程2", "tags" : [".net core", "mongodb"], "remark" : "C#是世界上最好的语言", "publishyear" : 2020 } Batch: 3
{ "_id" : ObjectId("5f33850d467bf3877966f1ea"), "BookName" : ".net core3.1 with mongodb21", "Description" : "这是一本关 于在.net core3.1中使用mongodb进行开发的教程21", "Tags" : [".net core", "mongodb"], "Remark" : "C#是世界上最好的语言", "PublishYear" : 2020 } { "_id" : ObjectId("5f33850d467bf3877966f1eb"), "BookName" : ".net core3.1 with mongodb22", "Description" : "这是一本关 于在.net core3.1中使用mongodb进行开发的教程22", "Tags" : [".net core", "mongodb"], "Remark" : "C#是世界上最好的语言", "PublishYear" : 2020 } Batch: 4
{ "_id" : ObjectId("5f33850d467bf3877966f1ec"), "BookName" : ".net core3.1 with mongodb23", "Description" : "这是一本关 于在.net core3.1中使用mongodb进行开发的教程23", "Tags" : [".net core", "mongodb"], "Remark" : "C#是世界上最好的语言", "PublishYear" : 2020 } Total Batch: 4

我们还可以通过调用ToListAsync或ForEachAsync从光标中获取所有文档并将它们放入内存中,从而以更简洁的方式编写此代码。在IAsyncCursor上有扩展方法可以这么做。下面是一些代码:

collection.FindSync(filter).ToList();

await collection.FindSync(filter).ToListAsync();

await collection.FindSync(filter).ForEachAsync(doc => Console.WriteLine());

collection.FindSync(filter).FirstOrDefault();

collection.FindSync(filter).FirstOrDefault();

await collection.FindSync(filter).FirstOrDefaultAsync();

从代码角度看,这看起来既简洁又简短,但它所做的是强制所有文档都保存在内存中。在某些情况下,这可能不太理想,当查询结果很大时,游标很有用,我们可以通过调用MoveNextAsync或MoveNext来移动光标。

Find

此方法与其对应方法相似,但它返回IFindFluent接口。这是一个流畅的接口,它为我们提供了一些简单的语法:Count ,Skip ,Sort,和Limit(关于这些,下篇文章中会有更多的介绍)。从IFindFluent中我们也可以返回一个游标(通过调用ToCursor或ToCursorAsync或一个列表(通过调用ToList或ToListAsync)。通过下面的代码,我们可以使用Find方法获取所有文档并将它们打印到控制台

await collection.Find(FilterDefinition<BsonDocument>.Empty)
.ForEachAsync(doc => Console.WriteLine(doc));

结果

{ "_id" : ObjectId("5f33630f9e7b20e7e29208f3"), "bookname" : ".net core3.1 with mongodb", "description" : "这是一本关于 在.net core3.1中使用mongodb进行开发的教程", "tags" : [".net core", "mongodb"], "remark" : "C#是世界上最好的语言", "publishyear" : 2020 }
{ "_id" : ObjectId("5f3367482d2d59d358e1219b"), "bookname" : ".net core3.1 with mongodb1", "description" : "这是一本关于在.net core3.1中使用mongodb进行开发的教程1", "tags" : [".net core", "mongodb"], "remark" : "C#是世界上最好的语言", "publishyear" : 2020 }
{ "_id" : ObjectId("5f3367482d2d59d358e1219c"), "bookname" : ".net core3.1 with mongodb2", "description" : "这是一本关于在.net core3.1中使用mongodb进行开发的教程2", "tags" : [".net core", "mongodb"], "remark" : "C#是世界上最好的语言", "publishyear" : 2020 }
{ "_id" : ObjectId("5f3367482d2d59d358e1219d"), "bookname" : ".net core3.1 with mongodb2", "description" : "这是一本关于在.net core3.1中使用mongodb进行开发的教程2", "tags" : [".net core", "mongodb"], "remark" : "C#是世界上最好的语言", "publishyear" : 2020 }
{ "_id" : ObjectId("5f33850d467bf3877966f1ea"), "BookName" : ".net core3.1 with mongodb21", "Description" : "这是一本关 于在.net core3.1中使用mongodb进行开发的教程21", "Tags" : [".net core", "mongodb"], "Remark" : "C#是世界上最好的语言", "PublishYear" : 2020 }
{ "_id" : ObjectId("5f33850d467bf3877966f1eb"), "BookName" : ".net core3.1 with mongodb22", "Description" : "这是一本关 于在.net core3.1中使用mongodb进行开发的教程22", "Tags" : [".net core", "mongodb"], "Remark" : "C#是世界上最好的语言", "PublishYear" : 2020 }
{ "_id" : ObjectId("5f33850d467bf3877966f1ec"), "BookName" : ".net core3.1 with mongodb23", "Description" : "这是一本关 于在.net core3.1中使用mongodb进行开发的教程23", "Tags" : [".net core", "mongodb"], "Remark" : "C#是世界上最好的语言", "PublishYear" : 2020 }

查找特定的文件

大多数情况下,我们不想检索所有文档,而是指定一个筛选器,它返回与特定筛选器匹配的文档。现在让我们看看为查询指定筛选器的方法。

使用BsonDocument或String

我们可以将BsonDocument定义为一个过滤器,查询将找到与文档中定义的字段相匹配的文档。将以下代码添加到您的方法中,并运行它以检索description为“这是一本关于在.net core3.1中使用mongodb进行开发的教程1”的书籍

var filter = new BsonDocument("description", "这是一本关于在.net core3.1中使用mongodb进行开发的教程1");
await collection.Find(filter).ForEachAsync(doc => Console.WriteLine(doc));

这样只返回符合条件的一个文档

var filter = new BsonDocument("description", "这是一本关于在.net core3.1中使用mongodb进行开发的教程1");
await collection.Find(filter).ForEachAsync(doc => Console.WriteLine(doc));

您可能会有些困惑,因为这些方法接受FilterDefinition,但是我们给了它一个BsonDocument,它没有出异常。之所以会发生这种情况,是因为它被隐式转换,而且我们也可以通过字符串进行转换。要使用字符串,我们需要定义一个有效的JSON字符串来指定过滤器。我们可以使用下面的代码对字符串执行上述相同的操作,仍然会得到相同的结果:

var filter= "{description:'这是一本关于在.net core3.1中使用mongodb进行开发的教程1'}";
await collection.Find(filter).ForEachAsync(doc => Console.WriteLine(doc));

我们也可以指定比较或逻辑运算符。例如,查找2020年出版的书。我们可以如下所示构建查询:

var filter = new BsonDocument("publishyear", new BsonDocument("$eq", 2020));

或者使用字符串

var filter = "{ Age: {'$eq': 23}}";

我们所做的是为操作符添加一个标识符,在我们的例子中是$eq。

使用FilterDefinitionBuilder

您可以使用FilterDefinitionBuilder,它是FilterDefinition的构建器。它提供了一套方法来构建查询,而Lt作为其中之一,指定了小于比较。因此,我们可以使用FilterDefinitionBuilder定义过滤器,如下所示:

var filter = new FilterDefinitionBuilder<BsonDocument>().Lt("publishyear", 2020);

或者使用接受LINQ表达式的重载方法:

var filter = new FilterDefinitionBuilder<Book>().Lt( book => book.PublishYear, 2020);

此外,您还可以使用静态Builders类来构建过滤器定义,该类还具有用于构建其他内容的静态帮助方法,如投影定义、排序定义和其他一些方法。

var filter = Builders<BsonDocument>.Filter.Lt("publishyear", 2020);
var filter = Builders<Book>.Filter.Lt(book => book.PublishYear, 2020);

驱动程序还为过滤器定义重载了3个操作符。这个and (&), or (|)和not (!)操作。例如,我们希望得到出版年份是2020年且描述信息为这是一本关于在.net core3.1中使用mongodb进行开发的教程1的书籍信息,我们可以使用构建器帮助方法和&重载操作符如下

var builder = Builders<BsonDocument>.Filter;
var filter = builder.Eq("publishyear", 2020) & builder.Eq("description", "`这是一本关于在.net core3.1中使用mongodb进行开发的教程1");

Linq表达 式

最后一部分我们没有讨论的是这些方法的重载,这些方法采用LINQ表达式,当我们有一个强类型对象时,我们可以使用LINQ表达式构建一个过滤器查询。假设我们想让出版年份为2020,描述信息为这是一本关于在.net core3.1中使用mongodb进行开发的教程1的书籍信息打印出他们。我们使用以下代码:

  var collection = database.GetCollection<Book>("books");
await collection.Find(book => book.PublishYear == 2020 && book.Description == "这是一本关于在.net core3.1中使用mongodb进行开发的教程1").ForEachAsync(doc => Console.WriteLine(doc));

我们将集合类型更改为Book并运行以下代码,我们将在控制台上得到一个错误:

错误描述显示_id不匹配任何类型的字段或属性。这是因为Book对象无法映射_id字段从数据库到“学生”类型的任何属性。这是在我们创建文档时自动添加的。让我们通过在Book类中添加Bson类型的ID属性。

internal class Book
{
public ObjectId Id { get; set; }
public string BookName { get; set; }
public string Description { get; set; }
public IEnumerable<string> Tags { get; set; }
public string Remark { get; set; }
public int PublishYear { get; set; }
}

并运行发现正常了。当然你也可以通过设置IgnoreExtraElement为true来规避这个问题

它只是起作用了。所以很多时候,你会想用表达式树语法来构建您的查询。在需要更多粒度的情况下,可以使用其他方法。

在下一个教程中,我们将看到如何进行 projections, sort, skip, limit and sort. 。

在.NET Core中使用MongoDB明细教程(2):使用Filter语句检索文档的更多相关文章

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

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

  2. 在.NET Core中使用MongoDB明细教程(1):驱动基础及文档插入

    MongoDB,被归类为NoSQL数据库,是一个以类JSON格式存储数据的面向文档的数据库系统.MongoDB在底层以名为bson的二进制编码格式表示JSON文档,MongoDB bson实现是轻量级 ...

  3. MongoDB 教程(七):插入文档、更新文档、删除文档

    MongoDB 插入文档 文档的数据结构和JSON基本一样. 所有存储在集合中的数据都是BSON格式 —— BSON是一种类json的二进制形式的存储格式,简称Binary JSON. MongoDB ...

  4. 在.Net Core中使用MongoDB的入门教程(二)

    在上一篇文章中,讲到了MongoDB在导入驱动.MongoDB的连接,数据的插入等. 在.Net Core中使用MongoDB的入门教程(一) 本篇文章将接着上篇文章进行介绍MongoDB在.Net ...

  5. 在.Net Core中使用MongoDB的入门教程(一)

    首先,我们在MongoDB的官方文档中看到,MongoDb的2.4以上的For .Net的驱动是支持.Net Core 2.0的. 所以,在我们安装好了MangoDB后,就可以开始MangoDB的.N ...

  6. Asp.Net Core中使用MongoDB的入门教程,控制台程序使用 MongoDB

    内容来源  https://blog.csdn.net/only_yu_yy/article/details/78882446 首先,创建一个.Net Core的控制台应用程序.然后使用NuGet导入 ...

  7. MongoDB中的映射,限制记录和记录拼排序 文档的插入查询更新删除操作

    映射 在 MongoDB 中,映射(Projection)指的是只选择文档中的必要数据,而非全部数据.如果文档有 5 个字段,而你只需要显示 3 个,则只需选择 3 个字段即可. find() 方法 ...

  8. Mongodb 笔记02 创建、更新和删除文档

    创建.更新和删除文档          1. 插入并保存: 1). 单条插入,insert : db.foo.insert({"bar":"baz"}) 2). ...

  9. 转:在 C# 中使用 P/Invoke 调用 Mupdf 函数库显示 PDF 文档

    在 C# 中使用 P/Invoke 调用 Mupdf 函数库显示 PDF 文档 一直以来,我都想为 PDF 补丁丁添加一个 PDF 渲染引擎.可是,目前并没有可以在 .NET 框架上运行的免费 PDF ...

随机推荐

  1. [spring] -- AOP、IOC、DI篇

    AOP(面向切面编程) 怎么理解这个切面编程的概念及优点? 概念: 横切关注点与业务逻辑相分离,切面能帮助我们模块化横切关注点: 优点: 现在每个关注点都集中于一个地方,而不是分散到多处代码中: 服务 ...

  2. Salt组件(一)

    一.管理对象属性(Grains) Grains里面记录着每台Minion的一些常用属性,比如CPU.内存.磁盘.网信息等,我们可以通过grains.items查看某台Minion的所有Grains信息 ...

  3. Python之filter、map、reduce函数

    简介三函数: 高阶函数:一个函数可以接收另一个函数作为参数,这种函数称之为高阶函数. filter.map.reduce三个函数都是高阶函数,且语法都一致:filter/map/reduce(func ...

  4. Android Zero (开篇)

    Android Zero == 从零开始 本文章主要打算给将要入门Android或刚刚入门Android的小伙伴适用的,开篇的几个案例都会抛弃所有现在用到的新技术和第三方框架,用最原生的方法从0演示, ...

  5. 再见了Antirez永远的Redis之神

    其实antirez(Redis作者)退出Redis维护一发布我就在很多咨询网站上面看到了,当时也没太多感慨. 今天比较有空想去看看霉霉Twitter的,然后看到了antirez,我就又一次回顾了他的退 ...

  6. PHP 变量讲解

    PHP 变量 变量是用于存储信息的"容器": 实例 <?php $x=5; $y=6; $z=$x+$y; echo $z; ?> 运行实例 » 与代数类似 x=5y= ...

  7. PHP array_push() 函数

    实例 向数组尾部插入 "blue" 和 "yellow": <?php$a=array("red","green" ...

  8. PHP set_file_buffer() 函数

    定义和用法 set_file_buffer() 函数设置打开文件的缓冲大小. 使用 fwrite() 函数输出结果,缓冲的大小通常为 8K.因此,如果要将两个进程写入同一个文件,那么每个文件一次最多只 ...

  9. luogu P5043 【模板】树同构 hash 最小表示法

    LINK:模板 树同构 题目说的很迷 给了一棵有根树 但是重新标号 言外之意还是一棵无根树 然后要求判断是否重构. 由于时无根的 所以一个比较显然的想法暴力枚举根. 然后做树hash或者树的最小表示法 ...

  10. Armv8-A Memory management

    本文介绍Armv8-A的内存管理.内存管理指的是在系统中,内存访问是如何实现的. 使用内存管理机制,可以让每个应用之间的内存地址分离,即sandbox application,也可以让多个在物理内存上 ...