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

作者:依乐祝

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

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

什么是IAsyncCursor

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

我们还可以通过调用ToListAsyncForEachAsync从光标中获取所有文档并将它们放入内存中,从而以更简洁的方式编写此代码。在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();

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

Find

此方法与其对应方法相似,但它返回IFindFluent接口。这是一个流畅的接口,它为我们提供了一些简单的语法:Count ,Skip ,Sort,和Limit(关于这些,下篇文章中会有更多的介绍)。从IFindFluent中我们也可以返回一个游标(通过调用ToCursorToCursorAsync或一个列表(通过调用ToListToListAsync)。通过下面的代码,我们可以使用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 }

查找特定的文件

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

使用BsonDocumentString

我们可以将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. Python "按位或"和"按位异或"的区别

    首先分别解释一下按位或和按位异或 按位或: 按位或指的是参与运算的两个数分别对应的二进制位进行“或”的操作.只要对应的两个二进制位有一个为1时,结果位就为1.python中运算符为“|” 按位异或: ...

  2. DeviceEventEmmiter使用

    发送广播一个事件 DeviceEventEmitter.emit('updatePlantList', '创建工厂成功');//通知刷新工厂列表 接收处,添加监听(监听要再事件发生之前添加,否则无法回 ...

  3. hadoop2.7.3+spark2.0.1+scala2.11.8集群部署

    一.环境 4.用户 hadoop 5.目录规划 /home/hadoop/app    #程序目录 /home/hadoop/data  #数据目录     #打开文件的最大数 vi /etc/sec ...

  4. Python定位模块_PYTHONPATH变量

    Python定位模块: 导入模块时,系统会根据搜索路径进行寻找模块: 1.在程序当前目录下寻找该模块 2.在环境变量 PYTHONPATH 中指定的路径列表寻找 3.在 Python 安装路径中寻找 ...

  5. PHP is_resource() 函数

    is_resource() 函数用于检测变量是否为资源类型. PHP 版本要求: PHP 4, P+-HP 5, PHP 7高佣联盟 www.cgewang.com 语法 bool is_resour ...

  6. PHP is_callable() 函数

    is_callable() 函数用于检测函数在当前环境中是否可调用.高佣联盟 www.cgewang.com is_callable() 函数验证变量的内容能否作为函数调用. 这可以检查包含有效函数名 ...

  7. bzoj 2989: 数列

    LINK:数列 需要动一点脑子 考虑查询 暴力显然不行 考虑把绝对值拆开. 当x<=y ax<=ay时 有 y-x+ay-ax<=k x+ax>=y+ay-k 可以发现在满足前 ...

  8. x86架构:从实模式进入保护模式

    详细的过程说明参考:(1)  https://www.cnblogs.com/Philip-Tell-Truth/p/5211248.html    (2)x86汇编:从实模式到保护模式 这里简化一下 ...

  9. 在不同网段使用 VLAN 通信 - SVI,单臂路由

    在 VLAN 这篇文章中知道,设置 VLAN 目的是隔离大型的广播域,将其分成很小的广播域,从而更好的管理.但也就带来了一些问题:如流量不能在不同的 VLAN 间通信. 而为了解决这个问题,可以采用如 ...

  10. Idea Live Temlpates 自定义代码

    Idea Live Temlpates 自定义代码 目的 - 加快常用代码的书写 使用## 打开Settings 快捷键Ctrl+Alt+S 选中Live Templates 选中temlpates ...