DocumentDB – 快速指南

DocumentDB – 快速指南


DocumentDB – 简介

在本章中,我们将简要讨论围绕 NoSQL 和文档数据库的主要概念。我们还将快速了解 DocumentDB。

NoSQL 文档数据库

DocumentDB 是微软最新的 NoSQL 文档数据库,那么当您说 NoSQL 文档数据库时,我们所说的 NoSQL 和文档数据库究竟指的是什么?

  • SQL是结构化查询语言,是关系数据库的传统查询语言。SQL 通常等同于关系数据库。

  • 将 NoSQL 数据库视为非关系型数据库确实更有帮助,因此 NoSQL 真正意味着非关系型。

有不同类型的 NoSQL 数据库,其中包括键值存储,例如 –

  • Azure 表存储。
  • 像 Cassandra 这样的列式商店。
  • 图数据库,如 NEO4。
  • 文档数据库,如 MongoDB 和 Azure DocumentDB。

Azure 文档数据库

微软于 4 月 8 日正式推出 Azure DocumentDBth, 2015,它当然可以被称为典型的 NoSQL 文档数据库。它具有大规模可扩展性,并且可以处理无模式的 JSON 文档。

  • DocumentDB 是一种真正的无模式 NoSQL 文档数据库服务,专为现代移动和 Web 应用程序而设计。

  • 它还提供始终如一的快速读取和写入、架构灵活性以及根据需要轻松扩展和缩减数据库的能力。

  • 它不为它索引的 JSON 文档假定或要求任何模式。

  • 一旦文档被添加到数据库中,DocumentDB 就会自动索引文档中的每个属性。

  • DocumentDB 支持使用 SQL 语言进行复杂的临时查询,并且每个文档在创建时都可以立即进行查询,并且您可以在文档层次结构中的任何位置搜索任何属性。

DocumentDB – 定价

DocumentDB 根据数据库帐户中包含的集合数量计费。每个帐户可以拥有一个或多个数据库,并且每个数据库可以拥有几乎无限数量的集合,尽管初始默认配额为 100。可以通过联系 Azure 支持来取消此配额。

  • 一个集合不仅是一个规模单位,也是一个成本单位,所以在 DocumentDB 中,你要为每个集合付费,它的存储容量高达 10 GB。

  • 您至少需要一个 S1 集合来将文档存储在一个数据库中,该数据库的费用约为每月 25 美元,根据您的 Azure 订阅收费。

  • 随着数据库大小的增长并超过 10 GB,您将需要购买另一个集合来包含额外的数据。

  • 每个 S1 集合每秒将为您提供 250 个请求单位,如果这还不够,那么您可以将集合扩展到 S2,并以每月约 50 美元的价格获得每秒 1000 个请求单位。

  • 您也可以将其升级为 S3,每月支付约 100 美元。

DocumentDB – 优势

DocumentDB 以一些非常独特的功能脱颖而出。Azure DocumentDB 提供以下关键功能和优势。

架构免费

在关系数据库中,每个表都有一个模式,用于定义表中的每一行必须符合的列和数据类型。

相比之下,文档数据库没有定义的模式,每个文档都可以有不同的结构。

SQL 语法

DocumentDB 支持使用 SQL 语言进行复杂的临时查询,并且每个文档在创建时都可以立即查询。您可以在文档层次结构中的任何位置搜索任何属性。

可调一致性

它提供了一些细化的、定义明确的一致性级别,允许您在一致性、可用性和延迟之间进行合理的权衡。

您可以从四个明确定义的一致性级别中进行选择,以实现一致性和性能之间的最佳平衡。对于查询和读取操作,DocumentDB 提供四种不同的一致性级别 –

  • 强的
  • 有界陈旧
  • 会议
  • 最终

弹性伸缩

可扩展性是 NoSQL 游戏的名称,而 DocumentDB 提供了这一点。DocumentDB 已经证明了其规模。

  • Office OneNote 和 Xbox 等主要服务已经得到 DocumentDB 的支持,其数据库包含数十 TB 的 JSON 文档、超过一百万的活跃用户,并且以 99.95% 的可用性始终如一地运行。

  • 您可以随着应用程序的增长创建更多单元,从而以可预测的性能弹性扩展 DocumentDB。

完全托管

DocumentDB 可用作完全托管的基于云的平台,即在 Azure 上运行的服务。

  • 您无需安装或管理任何东西。

  • 无需服务器、电缆、操作系统或更新,无需设置副本。

  • Microsoft 完成所有这些工作并保持服务运行。

  • 在短短几分钟内,您只需使用浏览器和 Azure 订阅即可开始使用 DocumentDB。

DocumentDB – 环境设置

Microsoft 提供了一个免费版本的 Visual Studio,其中也包含 SQL Server,可以从https://www.visualstudio.com下载

安装

步骤 1 – 下载完成后,运行安装程序。将显示以下对话框。

安装程序

步骤 2 – 单击安装按钮,它将开始安装过程。

安装过程

步骤 3 – 安装过程成功完成后,您将看到以下对话框。

现在重启

步骤 4 – 关闭此对话框并根据需要重新启动计算机。

第 5 步– 现在从开始菜单打开 Visual Studio,这将打开以下对话框。第一次只需要一些时间来准备。

开放视觉工作室

完成所有操作后,您将看到 Visual Studio 的主窗口。

主窗口

第 6 步– 让我们从文件 → 新建 → 项目创建一个新项目。

创建新项目

步骤 7 – 选择控制台应用程序,在名称字段中输入 DocumentDBDemo,然后单击确定按钮。

步骤 8 – 在解决方案资源管理器中,右键单击您的项目。

右键单击项目

步骤 9 – 选择管理 NuGet 包,这将在 Visual Studio 中打开以下窗口,并在“在线搜索”输入框中搜索 DocumentDB 客户端库。

管理 NuGet 包

步骤 10 – 单击安装按钮安装最新版本。

许可接受

步骤 11 – 单击“我接受”。安装完成后,您将在输出窗口中看到消息。

开始申请

您现在可以开始您的应用程序了。

DocumentDB – 创建帐户

要使用 Microsoft Azure DocumentDB,您必须创建一个 DocumentDB 帐户。在本章中,我们将使用 Azure 门户创建一个 DocumentDB 帐户。

第 1 步如果您已经拥有 Azure 订阅,登录在线https://portal.azure.com,否则您需要先登录。

您将看到主仪表板。它是完全可定制的,因此您可以按照自己喜欢的方式排列这些磁贴、调整它们的大小、添加和删除经常使用或不再使用的磁贴。

仪表盘

步骤 2 – 选择页面左上角的“新建”选项。

选择新选项

步骤 3 – 现在选择 Data &plus Storage > Azure DocumentDB 选项,您会看到以下新建 DocumentDB 帐户部分。

Azure 文档数据库

我们需要提出一个全球唯一的名称 (ID),它与 ​​.documents.azure.com 相结合是我们 DocumentDB 帐户的可公开寻址的端点。我们在该帐户下创建的所有数据库都可以使用此端点通过 Internet 访问。

第 4 步– 让我们将其命名为 azuredocdbdemo,然后单击资源组 → new_resource。

资源组

步骤 5 – 选择位置,即您希望托管此帐户的 Microsoft 数据中心。选择位置并选择您所在的地区。

选择位置

步骤 6 – 检查固定到仪表板复选框,然后继续并单击创建按钮。

创建按钮

您可以看到磁贴已添加到仪表板,它让我们知道正在创建帐户。实际上,在 DocumentDB 分配端点、供应副本和在后台执行其他工作时,为新帐户设置内容可能需要几分钟时间。

完成后,您将看到仪表板。

帐户仪表板

步骤 7 – 现在单击创建的 DocumentDB 帐户,您将看到如下图所示的详细屏幕。

创建的文档数据库

DocumentDB – 连接帐户

当您开始针对 DocumentDB 进行编程时,第一步是连接。因此,要连接到您的 DocumentDB 帐户,您需要做两件事;

  • 端点
  • 授权密钥

端点

Endpoint 是指向您的 DocumentDB 帐户的 URL,它是通过将您的 DocumentDB 帐户名称与 .documents.azure.com 组合而成的。让我们转到仪表板。

端点

现在,单击创建的 DocumentDB 帐户。您将看到如下图所示的详细信息。

单击创建的 DocumentDB

当您选择“键”选项时,它将显示如下图所示的附加信息。您还将看到您的 DocumentDB 帐户的 URL,您可以将其用作端点。

选择键选项

授权密钥

授权密钥包含您的凭据,并且有两种类型的密钥。主密钥允许完全访问帐户内的所有资源,而资源令牌允许对特定资源的受限访问。

万能钥匙

  • 使用万能钥匙,您无所不能。如果需要,您可以使用主密钥炸毁整个数据库。

  • 出于这个原因,您绝对不想共享主密钥或将其分发到客户端环境。作为一项额外的安全措施,经常更改它是一个好主意。

  • 每个数据库帐户实际上有两个主密钥,如上面屏幕截图中突出显示的主密钥和辅助密钥。

资源令牌

  • 您还可以使用资源令牌而不是主密钥。

  • 基于资源令牌的连接只能访问令牌指定的资源,不能访问其他资源。

  • 资源令牌基于用户权限,因此首先创建一个或多个用户,这些是在数据库级别定义的。

  • 您可以根据您希望允许每个用户访问的资源为每个用户创建一个或多个权限。

  • 每个权限都会生成一个资源令牌,允许对给定资源进行只读或完全访问,并且可以是数据库中的任何用户资源。

让我们转到第 3 章中创建的控制台应用程序。

步骤 1 – 在 Program.cs 文件中添加以下引用。

using Microsoft.Azure.Documents; 
using Microsoft.Azure.Documents.Client; 
using Microsoft.Azure.Documents.Linq; 
using Newtonsoft.Json;

第 2 步– 现在添加端点 URL 和授权密钥。在这个例子中,我们将使用主键作为授权键。

请注意,在您的情况下,端点 URL 和授权密钥应该不同。

private const string EndpointUrl = "https://azuredocdbdemo.documents.azure.com:443/"; 
private const string AuthorizationKey = 
   "BBhjI0gxdVPdDbS4diTjdloJq7Fp4L5RO/StTt6UtEufDM78qM2CtBZWbyVwFPSJIm8AcfDu2O+AfV T+TYUnBQ==";

步骤 3 – 在名为 CreateDocumentClient 的异步任务中创建 DocumentClient 的新实例并实例化新的 DocumentClient。

第 4 步– 从 Main 方法调用异步任务。

以下是目前为止完整的 Program.cs 文件。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text; 
using System.Threading.Tasks;

using Microsoft.Azure.Documents;
using Microsoft.Azure.Documents.Client;
using Microsoft.Azure.Documents.Linq;
using Newtonsoft.Json;

namespace DocumentDBDemo { 

   class Program {
      private const string EndpointUrl = "https://azuredocdbdemo.documents.azure.com:443/";
		
      private const string AuthorizationKey = "BBhjI0gxdVPdDbS4diTjdloJq7Fp4L5RO/
         StTt6UtEufDM78qM2CtBZWbyVwFPSJIm8AcfDu2O+AfV T+TYUnBQ==";
			
      static void Main(string[] args) {
         try {
            CreateDocumentClient().Wait();
         } catch (Exception e) {
            Exception baseException = e.GetBaseException();
            Console.WriteLine("Error: {0}, Message: {1}", e.Message, baseException.Message);
         }
			
         Console.ReadKey();
      }
		
      private static async Task CreateDocumentClient() {
         // Create a new instance of the DocumentClient
         var client = new DocumentClient(new Uri(EndpointUrl), AuthorizationKey);
      }
		
   }
}

在本章中,我们学习了如何连接到 DocumentDB 帐户并创建 DocumentClient 类的实例。

DocumentDB – 创建数据库

在本章中,我们将学习如何创建数据库。要使用 Microsoft Azure DocumentDB,您必须拥有 DocumentDB 帐户、数据库、集合和文档。我们已经有一个 DocumentDB 帐户,现在要创建数据库,我们有两个选项 –

  • Microsoft Azure 门户或
  • .Net SDK

使用 Microsoft Azure 门户为 DocumentDB 创建数据库

要使用门户创建数据库,请执行以下步骤。

步骤 1 – 登录 Azure 门户,您将看到仪表板。

登录门户

第 2 步– 现在单击创建的 DocumentDB 帐户,您将看到详细信息,如下面的屏幕截图所示。

单击创建的 DocumentDB

步骤 3 – 选择添加数据库选项并提供数据库的 ID。

选择添加数据库

步骤 4 – 单击确定。

添加了数据库

可以看到添加了数据库。目前,它没有集合,但我们可以稍后添加集合,这些集合是存储 JSON 文档的容器。请注意,它同时具有 ID 和资源 ID。

使用 .Net SDK 为 DocumentDB 创建数据库

要使用 .Net SDK 创建数据库,请执行以下步骤。

步骤 1 – 从上一章中打开 Visual Studio 中的控制台应用程序。

步骤 2 – 通过创建一个新的数据库对象来创建新的数据库。要创建新数据库,我们只需要分配 Id 属性,我们在 CreateDatabase 任务中将其设置为“mynewdb”。

private async static Task CreateDatabase(DocumentClient client) {
   Console.WriteLine(); 
   Console.WriteLine("******** Create Database *******");
	
   var databaseDefinition = new Database { Id = "mynewdb" }; 
   var result = await client.CreateDatabaseAsync(databaseDefinition); 
   var database = result.Resource;
	
   Console.WriteLine(" Database Id: {0}; Rid: {1}", database.Id, database.ResourceId); 
   Console.WriteLine("******** Database Created *******"); 
} 

第 3 步– 现在将此 databaseDefinition 传递给 CreateDatabaseAsync,并使用 Resource 属性获取结果。所有创建对象方法都返回一个 Resource 属性,该属性描述了所创建的项目,在本例中是一个数据库。

我们从 Resource 属性获取新的数据库对象,并将它与 DocumentDB 分配给它的资源 ID 一起显示在控制台上。

步骤 4 – 现在在 DocumentClient 实例化后从 CreateDocumentClient 任务调用 CreateDatabase 任务。

using (var client = new DocumentClient(new Uri(EndpointUrl), AuthorizationKey)) { 
   await CreateDatabase(client); 
} 

以下是目前为止完整的 Program.cs 文件。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using Microsoft.Azure.Documents;
using Microsoft.Azure.Documents.Client;
using Microsoft.Azure.Documents.Linq;
using Newtonsoft.Json;

namespace DocumentDBDemo {

   class Program {
      private const string EndpointUrl = "https://azuredocdbdemo.documents.azure.com:443/";
		
      private const string AuthorizationKey = "BBhjI0gxdVPdDbS4diTjdloJq7Fp4L5RO/
         StTt6UtEufDM78qM2CtBZWbyVwFPSJIm8AcfDu2O+AfV T+TYUnBQ==";
			
      static void Main(string[] args) {
         try {
            CreateDocumentClient().Wait();
         } catch (Exception e) {
            Exception baseException = e.GetBaseException();
            Console.WriteLine("Error: {0}, Message: {1}", e.Message, baseException.Message);
         }
         Console.ReadKey();
      }
		
      private static async Task CreateDocumentClient() {
         // Create a new instance of the DocumentClient
         using (var client = new DocumentClient(new Uri(EndpointUrl), AuthorizationKey)) {
            await CreateDatabase(client);
         } 
      }
		
      private async static Task CreateDatabase(DocumentClient client) {
         Console.WriteLine();
         Console.WriteLine("******** Create Database *******");
			
         var databaseDefinition = new Database { Id = "mynewdb" };
         var result = await client.CreateDatabaseAsync(databaseDefinition);
         var database = result.Resource;
			
         Console.WriteLine(" Database Id: {0}; Rid: {1}", database.Id, database.ResourceId);
         Console.WriteLine("******** Database Created *******");
      }
		
   } 
}

编译并执行上述代码后,您将收到以下输出,其中包含数据库和资源 ID。

******** Create Database ******* 
 Database Id: mynewdb; Rid: ltpJAA== 
******** Database Created ******* 

DocumentDB – 列出数据库

到目前为止,我们已经在 DocumentDB 帐户中创建了两个数据库,第一个是使用 Azure 门户创建的,而第二个数据库是使用 .Net SDK 创建的。现在要查看这些数据库,您可以使用 Azure 门户。

转到 Azure 门户上的 DocumentDB 帐户,您现在将看到两个数据库。

两个数据库

您还可以使用 .Net SDK 从代码中查看或列出数据库。以下是涉及的步骤。

步骤 1 – 发出不带参数的数据库查询,返回完整列表,但您也可以传入查询以查找特定数据库或特定数据库。

private static void GetDatabases(DocumentClient client) {
   Console.WriteLine();
   Console.WriteLine();
   Console.WriteLine("******** Get Databases List ********");
	
   var databases = client.CreateDatabaseQuery().ToList(); 
	
   foreach (var database in databases) { 
      Console.WriteLine(" Database Id: {0}; Rid: {1}", database.Id, database.ResourceId);
   }
	
   Console.WriteLine(); 
   Console.WriteLine("Total databases: {0}", databases.Count);
}

您将看到有许多用于定位集合、文档、用户和其他资源的 CreateQuery 方法。这些方法实际上并不执行查询,它们只是定义查询并返回一个可迭代的对象。

实际上是调用 ToList() 来执行查询,迭代结果,并在列表中返回它们。

步骤 2 – 在 DocumentClient 实例化后,从 CreateDocumentClient 任务调用 GetDatabases 方法。

第 3 步– 您还需要注释 CreateDatabase 任务或更改数据库 ID,否则您将收到数据库存在的错误消息。

using (var client = new DocumentClient(new Uri(EndpointUrl), AuthorizationKey)) {
   //await CreateDatabase(client); 
   GetDatabases(client); 
}

以下是目前为止完整的 Program.cs 文件。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using Microsoft.Azure.Documents;
using Microsoft.Azure.Documents.Client;
using Microsoft.Azure.Documents.Linq;
using Newtonsoft.Json; 

namespace DocumentDBDemo {

   class Program {
      private const string EndpointUrl = "https://azuredocdbdemo.documents.azure.com:443/";
		
      private const string AuthorizationKey = "BBhjI0gxdVPdDbS4diTjdloJq7Fp4L5RO/
         StTt6UtEufDM78qM2CtBZWbyVwFPSJIm8AcfDu2O+AfV T+TYUnBQ==";
			
      static void Main(string[] args) {
         try {
            CreateDocumentClient().Wait();
         } catch (Exception e) {
            Exception baseException = e.GetBaseException();
            Console.WriteLine("Error: {0}, Message: {1}", e.Message, baseException.Message);
         }
         Console.ReadKey();
      }
		
      private static async Task CreateDocumentClient() {
         // Create a new instance of the DocumentClient
         using (var client = new DocumentClient(new Uri(EndpointUrl), AuthorizationKey)) {
            await CreateDatabase(client);
            GetDatabases(client);
         } 
      }
		
      private async static Task CreateDatabase(DocumentClient client) {
         Console.WriteLine();
         Console.WriteLine("******** Create Database *******");
			
         var databaseDefinition = new Database { Id = "mynewdb" };
         var result = await client.CreateDatabaseAsync(databaseDefinition);
         var database = result.Resource;
			
         Console.WriteLine(" Database Id: {0}; Rid: {1}", database.Id, database.ResourceId);
         Console.WriteLine("******** Database Created *******");
      }
		
      private static void GetDatabases(DocumentClient client) {
         Console.WriteLine();
         Console.WriteLine();
         Console.WriteLine("******** Get Databases List ********");
			
         var databases = client.CreateDatabaseQuery().ToList();
			
         foreach (var database in databases) {
            Console.WriteLine(" Database Id: {0}; Rid: {1}",
               database.Id, database.ResourceId);
         }  
			
         Console.WriteLine(); 
         Console.WriteLine("Total databases: {0}", databases.Count);
      }
		
   } 
}

编译并执行上述代码后,您将收到以下输出,其中包含两个数据库的数据库和资源 ID。最后,您还将看到数据库的总数。

******** Get Databases List ******** 
 Database Id: myfirstdb; Rid: Ic8LAA== 
 Database Id: mynewdb; Rid: ltpJAA==  
Total databases: 2 

DocumentDB – 删除数据库

您可以使用 .Net SDK 从门户和代码中删除一个或多个数据库。在这里,我们将逐步讨论如何在 DocumentDB 中删除数据库。

步骤 1 – 转到 Azure 门户上的 DocumentDB 帐户。出于演示的目的,我添加了另外两个数据库,如下面的屏幕截图所示。

删除数据库

步骤 2 – 要删除任何数据库,您需要单击该数据库。我们选择tempdb,你会看到如下页面,选择’Delete Database’选项。

删除数据库

步骤 3 – 它将显示确认消息,现在单击“是”按钮。

确认讯息

您将看到仪表板中的 tempdb 不再可用。

TempDB 已删除

您还可以使用 .Net SDK 从您的代码中删除数据库。要执行以下步骤。

步骤 1 – 让我们通过指定要删除的数据库的 ID 来删除数据库,但我们需要它的 SelfLink。

第 2 步– 我们像以前一样调用 CreateDatabaseQuery,但这次我们实际上提供了一个查询,以仅返回 ID 为 tempdb1 的一个数据库。

private async static Task DeleteDatabase(DocumentClient client) {
   Console.WriteLine("******** Delete Database ********");
   Database database = client
      .CreateDatabaseQuery("SELECT * FROM c WHERE c.id = 'tempdb1'")
      .AsEnumerable()
      .First();
   await client.DeleteDatabaseAsync(database.SelfLink);
}

Step 3 – 这次,我们可以调用 AsEnumerable 而不是 ToList() 因为我们实际上不需要列表对象。只期待结果,调用 AsEnumerable 就足够了,这样我们就可以使用 First() 获取查询返回的第一个数据库对象。这是 tempdb1 的数据库对象,它有一个 SelfLink,我们可以使用它来调用 DeleteDatabaseAsync 来删除数据库。

步骤 4 – 您还需要在 DocumentClient 实例化后从 CreateDocumentClient 任务调用 DeleteDatabase 任务。

第 5 步– 要在删除指定数据库后查看数据库列表,让我们再次调用 GetDatabases 方法。

using (var client = new DocumentClient(new Uri(EndpointUrl), AuthorizationKey)) {
   //await CreateDatabase(client);
	
   GetDatabases(client);
   await DeleteDatabase(client);
   GetDatabases(client); 
} 

以下是目前为止完整的 Program.cs 文件。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using Microsoft.Azure.Documents;
using Microsoft.Azure.Documents.Client;
using Microsoft.Azure.Documents.Linq;
using Newtonsoft.Json;

namespace DocumentDBDemo {

   class Program {
	
      private const string EndpointUrl = "https://azuredocdbdemo.documents.azure.com:443/";
		
      private const string AuthorizationKey = "BBhjI0gxdVPdDbS4diTjdloJq7Fp4L5RO/
         StTt6UtEufDM78qM2CtBZWbyVwFPSJIm8AcfDu2O+AfV T+TYUnBQ==";
			
      static void Main(string[] args) {
         try {
            CreateDocumentClient().Wait();
         } catch (Exception e) {
            Exception baseException = e.GetBaseException();
            Console.WriteLine("Error: {0}, Message: {1}", e.Message, baseException.Message);
         }
         Console.ReadKey();
      }
		
      private static async Task CreateDocumentClient() {
         // Create a new instance of the DocumentClient
         using (var client = new DocumentClient(new Uri(EndpointUrl), AuthorizationKey)) {
            //await CreateDatabase(client);
            GetDatabases(client);
            await DeleteDatabase(client);
            GetDatabases(client);
         }
      }
		
      private async static Task CreateDatabase(DocumentClient client) {
         Console.WriteLine();
         Console.WriteLine("******** Create Database *******");
			
         var databaseDefinition = new Database { Id = "mynewdb" };
         var result = await client.CreateDatabaseAsync(databaseDefinition);
         var database = result.Resource;
			
         Console.WriteLine(" Database Id: {0}; Rid: {1}",
            database.Id, database.ResourceId);
         Console.WriteLine("******** Database Created *******");
      }
		
      private static void GetDatabases(DocumentClient client) {
         Console.WriteLine();
         Console.WriteLine();
         Console.WriteLine("******** Get Databases List ********");
			
         var databases = client.CreateDatabaseQuery().ToList();
			
         foreach (var database in databases) {
            Console.WriteLine(" Database Id: {0}; Rid: {1}", database.Id,
               database.ResourceId);
         }
			
         Console.WriteLine();
         Console.WriteLine("Total databases: {0}", databases.Count);
      }
		
      private async static Task DeleteDatabase(DocumentClient client) {
         Console.WriteLine();
         Console.WriteLine("******** Delete Database ********");
			
         Database database = client
            .CreateDatabaseQuery("SELECT * FROM c WHERE c.id = 'tempdb1'")
            .AsEnumerable()
            .First();
         await client.DeleteDatabaseAsync(database.SelfLink);
      }
		
   }
}

编译并执行上述代码后,您将收到以下输出,其中包含三个数据库的数据库和资源 ID 以及数据库总数。

******** Get Databases List ******** 
 Database Id: myfirstdb; Rid: Ic8LAA== 
 Database Id: mynewdb; Rid: ltpJAA== 
 Database Id: tempdb1; Rid: 06JjAA==
 
Total databases: 3  

******** Delete Database ******** 
  
******** Get Databases List ******** 
 Database Id: myfirstdb; Rid: Ic8LAA== 
 Database Id: mynewdb; Rid: ltpJAA==
 
Total databases: 2 

删除数据库后,最后您还会看到DocumentDB帐户中只剩下两个数据库。

DocumentDB – 创建集合

在本章中,我们将学习如何创建集合。它类似于创建数据库。您可以从门户或使用 .Net SDK 的代码创建集合。

步骤 1 – 转到 Azure 门户上的主仪表板。

创建收藏

步骤 2 – 从数据库列表中选择 myfirstdb。

我的第一个数据库

第 3 步– 单击“添加收藏”选项并指定收藏 ID。选择不同选项的定价层。

添加收藏

步骤 4 – 让我们选择 S1 标准,然后单击选择 → 确定按钮。

选择 S1 标准

如您所见,MyCollection 已添加到 myfirstdb 中。

您还可以使用 .Net SDK 从代码创建集合。让我们看一下从代码中添加集合的以下步骤。

步骤 1 – 在 Visual Studio 中打开控制台应用程序。

第 2 步– 要创建一个集合,首先在 CreateDocumentClient 任务中通过其 ID 检索 myfirstdb 数据库。

private static async Task CreateDocumentClient() {

   // Create a new instance of the DocumentClient
   using (var client = new DocumentClient(new Uri(EndpointUrl), AuthorizationKey)) {
      database = client.CreateDatabaseQuery("SELECT * FROM c WHERE c.id =
         'myfirstdb'").AsEnumerable().First();
			
      await CreateCollection(client, "MyCollection1");
      await CreateCollection(client, "MyCollection2", "S2"); 
   }
}

以下是 CreateCollection 任务的实现。

private async static Task CreateCollection(DocumentClient client, string collectionId,
   string offerType = "S1") {
	
   Console.WriteLine();
   Console.WriteLine("**** Create Collection {0} in {1} ****", collectionId, database.Id);
	
   var collectionDefinition = new DocumentCollection { Id = collectionId };
   var options = new RequestOptions { OfferType = offerType };
   var result = await client.CreateDocumentCollectionAsync(database.SelfLink,
      collectionDefinition, options);
   var collection = result.Resource;
	
   Console.WriteLine("Created new collection");
   ViewCollection(collection);
}

我们创建了一个新的 DocumentCollection 对象,该对象使用 CreateDocumentCollectionAsync 方法所需的 Id 定义新集合,该方法还接受我们在此处使用的选项参数来设置新集合的性能层,我们将其称为 offerType。

这默认为 S1,因为我们没有为 MyCollection1 传递 offerType,所以这将是一个 S1 集合,而对于 MyCollection2,我们已经传递了 S2,这使得这个集合成为 S2,如上所示。

以下是 ViewCollection 方法的实现。

private static void ViewCollection(DocumentCollection collection) {
   Console.WriteLine("Collection ID: {0} ", collection.Id); 
   Console.WriteLine("Resource ID: {0} ", collection.ResourceId); 
   Console.WriteLine("Self Link: {0} ", collection.SelfLink); 
   Console.WriteLine("Documents Link: {0} ", collection.DocumentsLink); 
   Console.WriteLine("UDFs Link: {0} ", collection.UserDefinedFunctionsLink); 
   Console.WriteLine(" StoredProcs Link: {0} ", collection.StoredProceduresLink); 
   Console.WriteLine("Triggers Link: {0} ", collection.TriggersLink); 
   Console.WriteLine("Timestamp: {0} ", collection.Timestamp);
}

以下是集合的 program.cs 文件的完整实现。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using Microsoft.Azure.Documents;
using Microsoft.Azure.Documents.Client;
using Microsoft.Azure.Documents.Linq;

using Newtonsoft.Json;

namespace DocumentDBDemo {

   class Program {
	
      private const string EndpointUrl = "https://azuredocdbdemo.documents.azure.com:443/";
		
      private const string AuthorizationKey = "BBhjI0gxdVPdDbS4diTjdloJq7Fp4L5RO/
         StTt6UtEufDM78qM2CtBZWbyVwFPSJIm8AcfDu2O+AfV T+TYUnBQ==";
			
      private static Database database;
		
      static void Main(string[] args) {
         try {
            CreateDocumentClient().Wait();
         } catch (Exception e) {
            Exception baseException = e.GetBaseException();
            Console.WriteLine("Error: {0}, Message: {1}", e.Message, baseException.Message);
         }
         Console.ReadKey();
      }
		
      private static async Task CreateDocumentClient() {
         // Create a new instance of the DocumentClient
         using (var client = new DocumentClient(new Uri(EndpointUrl), AuthorizationKey)) {
            database = client.CreateDatabaseQuery("SELECT * FROM c WHERE c.id =
               'myfirstdb'").AsEnumerable().First();
            await CreateCollection(client, "MyCollection1");
            await CreateCollection(client, "MyCollection2", "S2");
				
            //await CreateDatabase(client);
            //GetDatabases(client);
            //await DeleteDatabase(client);
            //GetDatabases(client);
         }
      }
		
      private async static Task CreateCollection(DocumentClient client,
         string collectionId, string offerType = "S1") {
			
         Console.WriteLine();
         Console.WriteLine("**** Create Collection {0} in {1} ****", collectionId,
            database.Id);

         var collectionDefinition = new DocumentCollection { Id = collectionId };
         var options = new RequestOptions { OfferType = offerType };
         var result = await 
			
			client.CreateDocumentCollectionAsync(database.SelfLink,
            collectionDefinition, options);
         var collection = result.Resource;

         Console.WriteLine("Created new collection");
         ViewCollection(collection);
      }
		
      private static void ViewCollection(DocumentCollection collection) {
         Console.WriteLine("Collection ID: {0} ", collection.Id);
         Console.WriteLine("Resource ID: {0} ", collection.ResourceId);
         Console.WriteLine("Self Link: {0} ", collection.SelfLink);
         Console.WriteLine("Documents Link: {0} ", collection.DocumentsLink);
         Console.WriteLine("UDFs Link: {0} ", collection.UserDefinedFunctionsLink);
         Console.WriteLine("StoredProcs Link: {0} ", collection.StoredProceduresLink);
         Console.WriteLine("Triggers Link: {0} ", collection.TriggersLink);
         Console.WriteLine("Timestamp: {0} ", collection.Timestamp);
      }
		
   }
}

编译并执行上述代码后,您将收到以下输出,其中包含与集合相关的所有信息。

**** Create Collection MyCollection1 in myfirstdb **** 
Created new collection
   Collection ID: MyCollection1
      Resource ID: Ic8LAPPvnAA=
         Self Link: dbs/Ic8LAA==/colls/Ic8LAPPvnAA=/
   Documents Link: dbs/Ic8LAA==/colls/Ic8LAPPvnAA=/docs/
         UDFs Link: dbs/Ic8LAA==/colls/Ic8LAPPvnAA=/udfs/
   StoredProcs Link: dbs/Ic8LAA==/colls/Ic8LAPPvnAA=/sprocs/
      Triggers Link: dbs/Ic8LAA==/colls/Ic8LAPPvnAA=/triggers/
         Timestamp: 12/10/2015 4:55:36 PM
		  
**** Create Collection MyCollection2 in myfirstdb ****
Created new collection
   Collection ID: MyCollection2
      Resource ID: Ic8LAKGHDwE=
         Self Link: dbs/Ic8LAA==/colls/Ic8LAKGHDwE=/
   Documents Link: dbs/Ic8LAA==/colls/Ic8LAKGHDwE=/docs/
         UDFs Link: dbs/Ic8LAA==/colls/Ic8LAKGHDwE=/udfs/
   StoredProcs Link: dbs/Ic8LAA==/colls/Ic8LAKGHDwE=/sprocs/
      Triggers Link: dbs/Ic8LAA==/colls/Ic8LAKGHDwE=/triggers/
         Timestamp: 12/10/2015 4:55:38 PM

DocumentDB – 删除集合

要删除一个或多个集合,您可以使用 .Net SDK 从门户和代码执行相同的操作。

步骤 1 – 转到 Azure 门户上的 DocumentDB 帐户。出于演示的目的,我添加了另外两个集合,如下面的屏幕截图所示。

删除收藏

第 2 步– 要删除任何集合,您需要单击该集合。让我们选择 TempCollection1。您将看到以下页面,选择“删除收藏”选项。

选择收藏

步骤 3 – 它将显示确认消息。现在单击“是”按钮。

删除收藏信息

您将看到 TempCollection1 在您的仪表板上不再可用。

集合已删除

您还可以使用 .Net SDK 从您的代码中删除集合。为此,请执行以下步骤。

步骤 1 – 让我们通过指定要删除的集合的 ID 来删除集合。

这是通过 Id 查询获取删除资源所需的 selfLinks 的常用模式。

private async static Task DeleteCollection(DocumentClient client, string collectionId) {
   Console.WriteLine();
   Console.WriteLine("**** Delete Collection {0} in {1} ****", collectionId, database.Id);
	
   var query = new SqlQuerySpec {
      QueryText = "SELECT * FROM c WHERE c.id = @id",
         Parameters = new SqlParameterCollection {
         new SqlParameter {
            Name = "@id", Value = collectionId
         }
      }
   };
	
   DocumentCollection collection = client.CreateDocumentCollectionQuery(database.SelfLink,
      query).AsEnumerable().First();
		
   await client.DeleteDocumentCollectionAsync(collection.SelfLink);
   Console.WriteLine("Deleted collection {0} from database {1}", collectionId,
      database.Id);
}

在这里,我们看到了构造参数化查询的首选方法。我们没有对 collectionId 进行硬编码,因此此方法可用于删除任何集合。我们正在通过 Id 查询特定集合,其中 Id 参数在此 SqlParameterCollection 中定义,该 SqlParameterCollection 分配给此 SqlQuerySpec 的参数属性。

然后,SDK 会为 DocumentDB 构建最终查询字符串,并将 collectionId 嵌入其中。

步骤 2 – 运行查询,然后使用其 SelfLink 从 CreateDocumentClient 任务中删除集合。

private static async Task CreateDocumentClient() {
   // Create a new instance of the DocumentClient 
	
   using (var client = new DocumentClient(new Uri(EndpointUrl), AuthorizationKey)) {
      database = client.CreateDatabaseQuery("SELECT * FROM c WHERE c.id =
         'myfirstdb'").AsEnumerable().First(); 
      await DeleteCollection(client, "TempCollection"); 
   } 
}

以下是 Program.cs 文件的完整实现。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using Microsoft.Azure.Documents;
using Microsoft.Azure.Documents.Client;
using Microsoft.Azure.Documents.Linq;

using Newtonsoft.Json;

namespace DocumentDBDemo {

   class Program {
	
      private const string EndpointUrl = "https://azuredocdbdemo.documents.azure.com:443/";
		
      private const string AuthorizationKey = "BBhjI0gxdVPdDbS4diTjdloJq7Fp4L5RO/
         StTt6UtEufDM78qM2CtBZWbyVwFPSJIm8AcfDu2O+AfV T+TYUnBQ==";
			
      private static Database database;

      static void Main(string[] args) {
         try {
            CreateDocumentClient().Wait();
         } catch (Exception e) {
            Exception baseException = e.GetBaseException();
            Console.WriteLine("Error: {0}, Message: {1}", e.Message, baseException.Message);
         }
         Console.ReadKey();
      }

      private static async Task CreateDocumentClient() {
         // Create a new instance of the DocumentClient
         using (var client = new DocumentClient(new Uri(EndpointUrl), AuthorizationKey)) {
            database = client.CreateDatabaseQuery("SELECT * FROM c WHERE c.id =
               'myfirstdb'").AsEnumerable().First();
            await DeleteCollection(client, "TempCollection");
				
            //await CreateCollection(client, "MyCollection1");
            //await CreateCollection(client, "MyCollection2", "S2");
            ////await CreateDatabase(client);
            //GetDatabases(client);
            //await DeleteDatabase(client);
            //GetDatabases(client);
         }
      }
		
      private async static Task CreateCollection(DocumentClient client,
         string collectionId, string offerType = "S1") {
			
         Console.WriteLine();
         Console.WriteLine("**** Create Collection {0} in {1} ****", collectionId,
            database.Id);
         
         var collectionDefinition = new DocumentCollection { Id = collectionId };
         var options = new RequestOptions { OfferType = offerType };
         var result = await client.CreateDocumentCollectionAsync(database.SelfLink,
            collectionDefinition, options);
				
         var collection = result.Resource; 
         
         Console.WriteLine("Created new collection"); 
         ViewCollection(collection); 
      }

      private static void ViewCollection(DocumentCollection collection) {
         Console.WriteLine("Collection ID: {0} ", collection.Id); 
         Console.WriteLine("Resource ID: {0} ", collection.ResourceId); 
         Console.WriteLine("Self Link: {0} ", collection.SelfLink); 
         Console.WriteLine("Documents Link: {0} ", collection.DocumentsLink); 
         Console.WriteLine("UDFs Link: {0} ", collection.UserDefinedFunctionsLink); 
         Console.WriteLine("StoredProcs Link: {0} ", collection.StoredProceduresLink); 
         Console.WriteLine("Triggers Link: {0} ", collection.TriggersLink); 
         Console.WriteLine("Timestamp: {0} ", collection.Timestamp); 
      }
		
      private async static Task DeleteCollection(DocumentClient client,
         string collectionId) {
			
         Console.WriteLine();
         Console.WriteLine("**** Delete Collection {0} in {1} ****", collectionId,
            database.Id);
				
         var query = new SqlQuerySpec {
            QueryText = "SELECT * FROM c WHERE c.id = @id", Parameters = new
               SqlParameterCollection {
               new SqlParameter {
                  Name = "@id", Value = collectionId
               }
            }
         };
			
         DocumentCollection collection = client.CreateDocumentCollectionQuery
            (database.SelfLink, query).AsEnumerable().First();
				
         await client.DeleteDocumentCollectionAsync(collection.SelfLink);
         Console.WriteLine("Deleted collection {0} from database {1}", collectionId,
            database.Id); 
      }
		
   } 
}

编译并执行上述代码后,您将收到以下输出。

**** Delete Collection TempCollection in myfirstdb **** 
Deleted collection TempCollection from database myfirstdb

DocumentDB – 插入文档

在本章中,我们将开始处理集合中的实际文档。可以使用 Azure 门户或 .Net SDK 创建文档。

使用 Azure 门户创建文档

让我们看看以下将文档添加到您的集合的步骤。

步骤 1 – 在 myfirstdb 中添加新的 S1 定价层集合系列。

插入文档

步骤 2 – 选择家庭集合并单击创建文档选项以打开新文档刀片。

家庭收藏

这只是一个简单的文本编辑器,可让您为新文档键入任何 JSON。

简单的文本编辑器

第 3 步– 由于这是原始数据输入,让我们输入我们的第一个文档。

{
   "id": "AndersenFamily", 
   "lastName": "Andersen", 
	
   "parents": [ 
      { "firstName": "Thomas", "relationship": "father" }, 
      { "firstName": "Mary Kay", "relationship": "mother" } 
   ], 
	
   "children": [ 
      { 
         "firstName": "Henriette Thaulow", 
         "gender": "female", 
         "grade": 5, 
         "pets": [ { "givenName": "Fluffy", "type": "Rabbit" } ] 
      } 
   ], 
	
   "location": { "state": "WA", "county": "King", "city": "Seattle"}, 
   "isRegistered": true
}

当您输入上述文件时,您将看到以下屏幕。

文档

请注意,我们已经为文档提供了一个 id。id 值始终是必需的,并且它在同一集合中的所有其他文档中必须是唯一的。当您忽略它时,DocumentDB 将使用 GUID 或全局唯一标识符为您自动生成一个。

id 始终是字符串,不能是数字、日期、布尔值或其他对象,并且不能超过 255 个字符。

还要注意文档的层次结构,它有一些顶级属性,比如所需的 id,以及 lastName 和 isRegistered,但它也有嵌套的属性。

例如,parents 属性作为方括号表示的 JSON 数组提供。我们还有另一个子数组,尽管在这个例子中数组中只有一个子数组。

第 4 步– 单击“保存”按钮保存文档,我们已经创建了第一个文档。

正如您所看到的,我们的 JSON 应用了漂亮的格式,它将每个属性分解成一行,并用空格缩进以传达每个属性的嵌套级别。

保存文件

该门户包含一个文档资源管理器,所以现在让我们使用它来检索我们刚刚创建的文档。

检索文件

步骤 5 – 选择一个数据库和数据库中的任何集合以查看该集合中的文档。我们目前只有一个名为 myfirstdb 的数据库和一个名为 Families 的集合,这两个集合都已在此处的下拉列表中预先选择。

选择一个数据库

默认情况下,文档资源管理器显示集合中未过滤的文档列表,但您也可以按 ID 搜索任何特定文档或基于对部分 ID 的通配符搜索来搜索多个文档。

到目前为止,我们的集合中只有一个文档,我们在下面的屏幕上看到它的 ID,AndersonFamily。

步骤 6 – 单击 ID 查看文档。

点击身份证

使用 .NET SDK 创建文档

如您所知,文档只是另一种类型的资源,并且您已经熟悉如何使用 SDK 处理资源。

  • 文档和其他资源之间的一大区别当然是它们是无模式的。

  • 因此有很多选择。当然,您可以只处理 JSON 对象图甚至 JSON 文本的原始字符串,但您也可以使用动态对象,让您在运行时绑定到属性,而无需在编译时定义类。

  • 您还可以使用真正的 C# 对象或实体,它们可能是您的业务领域类。

让我们开始使用 .Net SDK 创建文档。以下是步骤。

步骤 1 – 实例化 DocumentClient 然后我们将查询 myfirstdb 数据库,然后查询 MyCollection 集合,我们将其存储在这个私有变量集合中,以便在整个类中都可以访问它。

private static async Task CreateDocumentClient() {
   // Create a new instance of the DocumentClient
	
   using (var client = new DocumentClient(new Uri(EndpointUrl), AuthorizationKey)) {
      database = client.CreateDatabaseQuery("SELECT * FROM c WHERE c.id =
         'myfirstdb'").AsEnumerable().First(); 
			
      collection = client.CreateDocumentCollectionQuery(database.CollectionsLink,
         "SELECT * FROM c WHERE c.id = 'MyCollection'").AsEnumerable().First();  
			
      await CreateDocuments(client); 
   } 
}

步骤 2 – 在 CreateDocuments 任务中创建一些文档。

private async static Task CreateDocuments(DocumentClient client) {
   Console.WriteLine(); 
   Console.WriteLine("**** Create Documents ****"); 
   Console.WriteLine();
	
   dynamic document1Definition = new {
      name = "New Customer 1", address = new {
         addressType = "Main Office", 
         addressLine1 = "123 Main Street", 
         location = new {
            city = "Brooklyn", stateProvinceName = "New York" 
         }, postalCode = "11229", countryRegionName = "United States"
      }, 
   };
	
   Document document1 = await CreateDocument(client, document1Definition); 
   Console.WriteLine("Created document {0} from dynamic object", document1.Id); 
   Console.WriteLine(); 
} 

将从这个动态对象生成第一个文档。这可能看起来像 JSON,但当然不是。这是 C# 代码,我们正在创建一个真正的 .NET 对象,但没有类定义。相反,属性是从对象初始化的方式推断出来的。

请注意,我们尚未为此文档提供 Id 属性。

现在让我们来看看 CreateDocument。它看起来与我们在创建数据库和集合时看到的模式相同。

private async static Task<Document> CreateDocument(DocumentClient client,
   object documentObject) {
	
   var result = await client.CreateDocumentAsync(collection.SelfLink, documentObject); 
   var document = result.Resource;
	
   Console.WriteLine("Created new document: {0}\r\n{1}", document.Id, document); 
   return result; 
}

第 3 步– 这次我们调用 CreateDocumentAsync,指定我们要将文档添加到的集合的 SelfLink。我们得到一个带有资源属性的响应,在这种情况下,它用系统生成的属性表示新文档。

Document 对象是 SDK 中定义的类,它继承自资源,因此它具有所有公共资源属性,但它还包括定义无架构文档本身的动态属性。

private async static Task CreateDocuments(DocumentClient client) {
   Console.WriteLine(); 
   Console.WriteLine("**** Create Documents ****"); 
   Console.WriteLine();  
	
   dynamic document1Definition = new {
      name = "New Customer 1", address = new { 
         addressType = "Main Office",
         addressLine1 = "123 Main Street", 
         location = new {
            city = "Brooklyn", stateProvinceName = "New York" 
         }, postalCode = "11229", countryRegionName = "United States" 
      }, 
   };
	
   Document document1 = await CreateDocument(client, document1Definition); 
   Console.WriteLine("Created document {0} from dynamic object", document1.Id); 
   Console.WriteLine();
}

编译并执行上述代码后,您将收到以下输出。

**** Create Documents ****  
Created new document: 34e9873a-94c8-4720-9146-d63fb7840fad {
   "name": "New Customer 1", 
	
   "address": { 
      "addressType": "Main Office", 
      "addressLine1": "123 Main Street", 
      "location": { 
         "city": "Brooklyn", "stateProvinceName": "New York" 
      }, 
      "postalCode": "11229", "countryRegionName": "United States"
   }, 
	
   "id": "34e9873a-94c8-4720-9146-d63fb7840fad", 
   "_rid": "Ic8LAMEUVgACAAAAAAAAAA==", 
   "_ts": 1449812756, 
   "_self": "dbs/Ic8LAA==/colls/Ic8LAMEUVgA=/docs/Ic8LAMEUVgACAAAAAAAAAA==/", 
   "_etag": "\"00001000-0000-0000-0000-566a63140000\"", 
   "_attachments": "attachments/" 
} 
Created document 34e9873a-94c8-4720-9146-d63fb7840fad from dynamic object 

如您所见,我们没有提供 ID,但是 DocumentDB 为我们为新文档生成了这个 ID。

DocumentDB – 查询文档

在 DocumentDB 中,我们实际上是使用 SQL 来查询文档的,所以本章都是关于使用 DocumentDB 中的特殊 SQL 语法进行查询的。尽管如果您正在进行 .NET 开发,也可以使用 LINQ 提供程序,它可以从 LINQ 查询生成适当的 SQL。

使用Portal查询文档

Azure 门户有一个查询资源管理器,可让您对 DocumentDB 数据库运行任何 SQL 查询。

我们将使用查询资源管理器从最简单的查询开始演示查询语言的许多不同功能和特性。

步骤 1 – 在数据库刀片中,单击以打开查询资源管理器刀片。

查询资源管理器刀片

请记住,查询在集合范围内运行,因此查询资源管理器允许您在此下拉列表中选择集合。

运行查询

步骤 2 – 选择之前使用门户创建的家庭集合。

查询资源管理器通过这个简单的查询 SELECT * FROM c 打开,它只是从集合中检索所有文档。

步骤 3 – 通过单击“运行查询”按钮执行此查询。然后您将看到在结果边栏选项卡中检索了完整的文档。

结果刀片中的文档

使用 .Net SDK 查询文档

以下是使用 .Net SDK 运行一些文档查询的步骤。

在这个例子中,我们要查询刚刚添加的新创建的文档。

步骤 1 – 调用 CreateDocumentQuery,传入集合以通过其 SelfLink 和查询文本运行查询。

private async static Task QueryDocumentsWithPaging(DocumentClient client) {
   Console.WriteLine(); 
   Console.WriteLine("**** Query Documents (paged results) ****"); 
   Console.WriteLine();  
   Console.WriteLine("Quering for all documents"); 
	
   var sql = "SELECT * FROM c";  
   var query = client.CreateDocumentQuery(collection.SelfLink, sql).AsDocumentQuery();
	
   while (query.HasMoreResults) {
      var documents = await query.ExecuteNextAsync(); 
		
      foreach (var document in documents) { 
         Console.WriteLine(" Id: {0}; Name: {1};", document.id, document.name); 
      } 
   }
	
   Console.WriteLine(); 
} 

此查询还返回整个集合中的所有文档,但我们不像以前那样在 CreateDocumentQuery 上调用 .ToList,它会根据需要发出尽可能多的请求以在一行代码中提取所有结果。

Step 2 – 相反,调用 AsDocumentQuery 并且此方法返回一个具有 HasMoreResults 属性的查询对象。

第 3 步– 如果 HasMoreResults 为真,则调用 ExecuteNextAsync 以获取下一个块,然后转储该块的所有内容。

第 4 步– 如果您愿意,也可以使用 LINQ 而不是 SQL 进行查询。在这里,我们在 q 中定义了一个 LINQ 查询,但在我们对其运行 .ToList 之前它不会执行。

private static void QueryDocumentsWithLinq(DocumentClient client) {
   Console.WriteLine(); 
   Console.WriteLine("**** Query Documents (LINQ) ****"); 
   Console.WriteLine();  
   Console.WriteLine("Quering for US customers (LINQ)");
	
   var q = 
      from d in client.CreateDocumentQuery<Customer>(collection.DocumentsLink) 
      where d.Address.CountryRegionName == " United States" 
      select new {
         Id = d.Id, 
         Name = d.Name, 
         City = d.Address.Location.City 
      };  
		
   var documents = q.ToList();  
   Console.WriteLine("Found {0} UK customers", documents.Count);
	
   foreach (var document in documents) {
      var d = document as dynamic; 
      Console.WriteLine(" Id: {0}; Name: {1}; City: {2}", d.Id, d.Name, d.City); 
   } 
	
   Console.WriteLine(); 
}

SDK 会将我们的 LINQ 查询转换为 DocumentDB 的 SQL 语法,根据我们的 LINQ 语法生成一个 SELECT 和 WHERE 子句

第 5 步– 现在从 CreateDocumentClient 任务调用上述查询。

private static async Task CreateDocumentClient() { 
   // Create a new instance of the DocumentClient 
	
   using (var client = new DocumentClient(new Uri(EndpointUrl), AuthorizationKey)) {
      database = client.CreateDatabaseQuery("SELECT * FROM c WHERE c.id =
         'myfirstdb'").AsEnumerable().First(); 
			
      collection = client.CreateDocumentCollectionQuery(database.CollectionsLink,
         "SELECT * FROM c WHERE c.id = 'MyCollection'").AsEnumerable().First();  
			
      //await CreateDocuments(client); 
      await QueryDocumentsWithPaging(client); 
      QueryDocumentsWithLinq(client); 
   } 
	
}

执行上述代码后,您将收到以下输出。

**** Query Documents (paged results) ****  
Quering for all documents 
 Id: 7e9ad4fa-c432-4d1a-b120-58fd7113609f; Name: New Customer 1; 
 Id: 34e9873a-94c8-4720-9146-d63fb7840fad; Name: New Customer 1;  
 
**** Query Documents (LINQ) **** 
Quering for US customers (LINQ) 
Found 2 UK customers 
 Id: 7e9ad4fa-c432-4d1a-b120-58fd7113609f; Name: New Customer 1; City: Brooklyn 
 Id: 34e9873a-94c8-4720-9146-d63fb7840fad; Name: New Customer 1; City: Brooklyn

DocumentDB – 更新文档

在本章中,我们将学习如何更新文档。使用 Azure 门户,您可以通过在文档资源管理器中打开文档并在编辑器中像文本文件一样更新它来轻松更新文档。

更新文件

单击“保存”按钮。现在,当您需要使用 .Net SDK 更改文档时,您只需替换它即可。您不需要删除并重新创建它,这不仅很乏味,而且还会更改资源 ID,这在您只是修改文档时是不想做的。以下是使用 .Net SDK 更新文档的以下步骤。

让我们看一下下面的 ReplaceDocuments 任务,我们将在其中查询 isNew 属性为 true 的文档,但我们不会得到任何文档,因为没有。因此,让我们修改之前添加的文档,名称以 New Customer 开头的文档。

步骤 1 – 将 isNew 属性添加到这些文档并将其值设置为 true。

private async static Task ReplaceDocuments(DocumentClient client) {

   Console.WriteLine(); 
   Console.WriteLine(">>> Replace Documents <<<"); 
   Console.WriteLine();  
   Console.WriteLine("Quering for documents with 'isNew' flag");
	
   var sql = "SELECT * FROM c WHERE c.isNew = true"; 
   var documents = client.CreateDocumentQuery(collection.SelfLink, sql).ToList();
	
   Console.WriteLine("Documents with 'isNew' flag: {0} ", documents.Count); 
   Console.WriteLine();  
   Console.WriteLine("Quering for documents to be updated"); 
	
   sql = "SELECT * FROM c WHERE STARTSWITH(c.name, 'New Customer') = true"; 
   documents = client.CreateDocumentQuery(collection.SelfLink, sql).ToList(); 
   Console.WriteLine("Found {0} documents to be updated", documents.Count); 
	
   foreach (var document in documents) {
      document.isNew = true; 
      var result = await client.ReplaceDocumentAsync(document._self, document); 
      var updatedDocument = result.Resource; 
      Console.WriteLine("Updated document 'isNew' flag: {0}", updatedDocument.isNew); 
   }
	
   Console.WriteLine();  
   Console.WriteLine("Quering for documents with 'isNew' flag");
	
   sql = "SELECT * FROM c WHERE c.isNew = true"; 
   documents = client.CreateDocumentQuery(collection.SelfLink, sql).ToList(); 
   Console.WriteLine("Documents with 'isNew' flag: {0}: ", documents.Count); 
   Console.WriteLine(); 
}

第 2 步– 使用相同的 STARTSWITH 查询获取要更新的文档,这为我们提供了文档,我们将这些文档作为动态对象返回此处。

步骤 3 – 附加 isNew 属性并将其设置为 true 为每个文档。

第 4 步– 调用 ReplaceDocumentAsync,传入文档的 SelfLink 以及更新的文档。

现在只是为了证明这是有效的,查询 isNew 等于 true 的文档。让我们从 CreateDocumentClient 任务调用上述查询。

private static async Task CreateDocumentClient() {
   // Create a new instance of the DocumentClient
	
   using (var client = new DocumentClient(new Uri(EndpointUrl), AuthorizationKey)) {
      database = client.CreateDatabaseQuery("SELECT * FROM c WHERE c.id =
         'myfirstdb'").AsEnumerable().First(); 
			
      collection = client.CreateDocumentCollectionQuery(database.CollectionsLink,
         "SELECT * FROM c WHERE c.id = 'MyCollection'").AsEnumerable().First();
			
      //await CreateDocuments(client);  
      //QueryDocumentsWithSql(client); 
      //await QueryDocumentsWithPaging(client); 
      //QueryDocumentsWithLinq(client); 
      await ReplaceDocuments(client); 
   }
	
}

编译并执行上述代码后,您将收到以下输出。

**** Replace Documents ****  
Quering for documents with 'isNew' flag 
Documents with 'isNew' flag: 0 
Quering for documents to be updated 
Found 2 documents to be updated 
Updated document ‘isNew’ flag: True 
Updated document ‘isNew’ flag: True 
Quering for documents with 'isNew' flag 
Documents with 'isNew' flag: 2 

DocumentDB – 删除文档

在本章中,我们将学习如何从您的 DocumentDB 帐户中删除文档。使用 Azure 门户,您可以通过在文档资源管理器中打开文档并单击“删除”选项轻松删除任何文档。

删除文档

删除文档对话框

它将显示确认消息。现在按是按钮,您将看到该文档在您的 DocumentDB 帐户中不再可用。

现在,当您想使用 .Net SDK 删除文档时。

第 1 步– 这与我们之前看到的模式相同,我们将首先查询以获取每个新文档的 SelfLink。我们在这里不使用 SELECT * ,它会完整地返回我们不需要的文档。

第 2 步– 相反,我们只是将 SelfLink 选择到一个列表中,然后我们只需为每个 SelfLink 调用 DeleteDocumentAsync,一次一个,以从集合中删除文档。

private async static Task DeleteDocuments(DocumentClient client) {
   Console.WriteLine();
   Console.WriteLine(">>> Delete Documents <<<");
   Console.WriteLine();
   Console.WriteLine("Quering for documents to be deleted");
	
   var sql =
      "SELECT VALUE c._self FROM c WHERE STARTSWITH(c.name, 'New Customer') = true";
		
   var documentLinks =
      client.CreateDocumentQuery<string>(collection.SelfLink, sql).ToList();
		
   Console.WriteLine("Found {0} documents to be deleted", documentLinks.Count);

   foreach (var documentLink in documentLinks) {
      await client.DeleteDocumentAsync(documentLink);
   }
	
   Console.WriteLine("Deleted {0} new customer documents", documentLinks.Count);
   Console.WriteLine();
}

第 3 步– 现在让我们从 CreateDocumentClient 任务调用上述 DeleteDocuments。

private static async Task CreateDocumentClient() {
   // Create a new instance of the DocumentClient 
   using (var client = new DocumentClient(new Uri(EndpointUrl), AuthorizationKey)) {
      database = client.CreateDatabaseQuery("SELECT * FROM c WHERE c.id =
         'myfirstdb'").AsEnumerable().First(); 
			
      collection = client.CreateDocumentCollectionQuery(database.CollectionsLink,
         "SELECT * FROM c WHERE c.id = 'MyCollection'").AsEnumerable().First();  
			
      await DeleteDocuments(client); 
   } 
}

执行上述代码后,您将收到以下输出。

***** Delete Documents *****  
Quering for documents to be deleted 
Found 2 documents to be deleted 
Deleted 2 new customer documents 

DocumentDB – 数据建模

虽然像 DocumentDB 这样的无模式数据库可以非常容易地接受对数据模型的更改,但您仍然应该花一些时间来考虑您的数据。

  • 你有很多选择。当然,您可以只处理 JSON 对象图甚至 JSON 文本的原始字符串,但您也可以使用动态对象,让您在运行时绑定到属性,而无需在编译时定义类。

  • 您还可以使用真正的 C# 对象或实体,它们可能是您的业务领域类。

关系

让我们来看看文档的层次结构。它有一些顶级属性,比如所需的 id,以及 lastName 和 isRegistered,但它也有嵌套的属性。

{ 
   "id": "AndersenFamily", 
   "lastName": "Andersen", 
	
   "parents": [ 
      { "firstName": "Thomas", "relationship": "father" }, 
      { "firstName": "Mary Kay", "relationship": "mother" } 
   ],
	
   "children": [ 
      { 
         "firstName": "Henriette Thaulow", 
         "gender": "female", 
         "grade": 5, 
         "pets": [ { "givenName": "Fluffy", "type": "Rabbit" } ] 
      } 
   ], 
	
   "location": { "state": "WA", "county": "King", "city": "Seattle"}, 
   "isRegistered": true 
}
  • 例如,parents 属性作为方括号表示的 JSON 数组提供。

  • 我们还有另一个子数组,尽管在这个例子中数组中只有一个子数组。所以这就是您在文档中模拟一对多关系的方式。

  • 您只需使用数组,其中数组中的每个元素可以是一个简单值或另一个复杂对象,甚至是另一个数组。

  • 所以一个家庭可以有多个父母和多个孩子,如果你查看子对象,他们有一个宠物的属性,它本身就是一个嵌套数组,用于孩子和宠物之间的一对多关系。

  • 对于位置属性,我们将州、县和城市这三个相关属性组合成一个对象。

  • 以这种方式嵌入对象而不是嵌入对象数组类似于在关系数据库中不同表中的两行之间建立一对一关系。

嵌入数据

当您开始对文档存储(例如 DocumentDB)中的数据进行建模时,尝试将您的实体视为以 JSON 表示的自包含文档。在使用关系数据库时,我们总是对数据进行规范化。

  • 规范化您的数据通常涉及获取实体(例如客户)并将其分解为谨慎的数据片段,例如联系方式和地址。

  • 要读取客户及其所有联系方式和地址,您需要使用 JOINS 在运行时有效地聚合您的数据。

现在让我们看看如何将相同的数据建模为文档数据库中的自包含实体。

{
   "id": "1", 
   "firstName": "Mark", 
   "lastName": "Upston", 
	
   "addresses": [ 
      {             
         "line1": "232 Main Street", 
         "line2": "Unit 1", 
         "city": "Brooklyn", 
         "state": "NY", 
         "zip": 11229
      }
   ],
	
   "contactDetails": [ 
      {"email": "[email protected]"}, 
      {"phone": "+1 356 545-86455", "extension": 5555} 
   ]
} 

如您所见,我们对客户记录进行了非规范化处理,其中客户的所有信息都嵌入到单个 JSON 文档中。

在 NoSQL 中,我们有一个免费模式,因此您也可以添加不同格式的联系方式和地址。在 NoSQL 中,您可以通过一次读取操作从数据库中检索客户记录。同样,更新一条记录也是一次写操作。

以下是使用 .Net SDK 创建文档的步骤。

步骤 1 – 实例化 DocumentClient。然后我们将查询 myfirstdb 数据库并查询 MyCollection 集合,我们将其存储在这个私有变量集合中,以便在整个类中都可以访问它。

private static async Task CreateDocumentClient() { 
   // Create a new instance of the DocumentClient
	
   using (var client = new DocumentClient(new Uri(EndpointUrl), AuthorizationKey)) { 
      database = client.CreateDatabaseQuery("SELECT * FROM c WHERE c.id =
         'myfirstdb'").AsEnumerable().First(); 
			
      collection = client.CreateDocumentCollectionQuery(database.CollectionsLink,
         "SELECT * FROM c WHERE c.id = 'MyCollection'").AsEnumerable().First();  
			
      await CreateDocuments(client); 
   }

}

步骤 2 – 在 CreateDocuments 任务中创建一些文档。

private async static Task CreateDocuments(DocumentClient client) {
   Console.WriteLine(); 
   Console.WriteLine("**** Create Documents ****"); 
   Console.WriteLine();
	
   dynamic document1Definition = new {
      name = "New Customer 1", address = new { 
         addressType = "Main Office", 
         addressLine1 = "123 Main Street", 
         location = new { 
            city = "Brooklyn", stateProvinceName = "New York"
         }, 
         postalCode = "11229", countryRegionName = "United States" 
      }, 
   };
	
   Document document1 = await CreateDocument(client, document1Definition); 
   Console.WriteLine("Created document {0} from dynamic object", document1.Id); 
   Console.WriteLine(); 
}

将从这个动态对象生成第一个文档。这可能看起来像 JSON,但当然不是。这是 C# 代码,我们正在创建一个真正的 .NET 对象,但没有类定义。相反,属性是从对象初始化的方式推断出来的。您还可以注意到我们没有为此文档提供 Id 属性。

第 3 步– 现在让我们看看 CreateDocument,它看起来与我们在创建数据库和集合时看到的模式相同。

private async static Task<Document> CreateDocument(DocumentClient client,
   object documentObject) {
   var result = await client.CreateDocumentAsync(collection.SelfLink, documentObject); 
	
   var document = result.Resource; 
   Console.WriteLine("Created new document: {0}\r\n{1}", document.Id, document); 
	
   return result; 
}

第 4 步– 这次我们调用 CreateDocumentAsync,指定我们要将文档添加到的集合的 SelfLink。我们得到一个带有资源属性的响应,在这种情况下,它用系统生成的属性表示新文档。

在下面的 CreateDocuments 任务中,我们创建了三个文档。

  • 在第一个文档中,Document 对象是 SDK 中定义的类,它继承自资源,因此它具有所有公共资源属性,但它还包括定义无模式文档本身的动态属性。

private async static Task CreateDocuments(DocumentClient client) {
   Console.WriteLine(); 
   Console.WriteLine("**** Create Documents ****"); 
   Console.WriteLine();
	
   dynamic document1Definition = new {
      name = "New Customer 1", address = new {
         addressType = "Main Office", 
         addressLine1 = "123 Main Street", 
         location = new {
            city = "Brooklyn", stateProvinceName = "New York" 
         }, 
         postalCode = "11229", 
         countryRegionName = "United States" 
      }, 
   };
	
   Document document1 = await CreateDocument(client, document1Definition); 
   Console.WriteLine("Created document {0} from dynamic object", document1.Id); 
   Console.WriteLine();
	
   var document2Definition = @" {
      ""name"": ""New Customer 2"", 
		
      ""address"": { 
         ""addressType"": ""Main Office"", 
         ""addressLine1"": ""123 Main Street"", 
         ""location"": { 
            ""city"": ""Brooklyn"", ""stateProvinceName"": ""New York"" 
         }, 
         ""postalCode"": ""11229"", 
         ""countryRegionName"": ""United States"" 
      } 
   }"; 
	
   Document document2 = await CreateDocument(client, document2Definition); 
   Console.WriteLine("Created document {0} from JSON string", document2.Id);
   Console.WriteLine();
	
   var document3Definition = new Customer {
      Name = "New Customer 3", 
		
      Address = new Address {
         AddressType = "Main Office", 
         AddressLine1 = "123 Main Street", 
         Location = new Location {
            City = "Brooklyn", StateProvinceName = "New York" 
         }, 
         PostalCode = "11229", 
         CountryRegionName = "United States" 
      }, 
   };
	
   Document document3 = await CreateDocument(client, document3Definition); 
   Console.WriteLine("Created document {0} from typed object", document3.Id); 
   Console.WriteLine(); 
}
  • 第二个文档仅适用于原始 JSON 字符串。现在我们进入 CreateDocument 的重载,它使用 JavaScriptSerializer 将字符串反序列化为一个对象,然后将其传递给我们用来创建第一个文档的相同 CreateDocument 方法。

  • 在第三个文档中,我们使用了在我们的应用程序中定义的 C# 对象 Customer。

让我们来看看这个客户,它有一个 Id 和 address 属性,其中 address 是一个嵌套对象,它有自己的属性,包括 location,这是另一个嵌套对象。

using Newtonsoft.Json; 

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Threading.Tasks;

namespace DocumentDBDemo {
 
   public class Customer { 
      [JsonProperty(PropertyName = "id")] 
      public string Id { get; set; }
      // Must be nullable, unless generating unique values for new customers on client  
      [JsonProperty(PropertyName = "name")] 
      public string Name { get; set; }  
      [JsonProperty(PropertyName = "address")] 
      public Address Address { get; set; } 
   }
	
   public class Address {
      [JsonProperty(PropertyName = "addressType")] 
      public string AddressType { get; set; }  
		
      [JsonProperty(PropertyName = "addressLine1")] 
      public string AddressLine1 { get; set; }  
		
      [JsonProperty(PropertyName = "location")] 
      public Location Location { get; set; }  
		
      [JsonProperty(PropertyName = "postalCode")] 
      public string PostalCode { get; set; }  
		
      [JsonProperty(PropertyName = "countryRegionName")] 
      public string CountryRegionName { get; set; } 
   }
	
   public class Location { 
      [JsonProperty(PropertyName = "city")] 
      public string City { get; set; }  
		
      [JsonProperty(PropertyName = "stateProvinceName")]
      public string StateProvinceName { get; set; } 
   } 
}

我们还准备了 JSON 属性属性,因为我们希望在围栏的两侧保持适当的约定。

所以我只是创建了我的 New Customer 对象及其嵌套的子对象,并再次调用 CreateDocument。尽管我们的客户对象确实有一个 Id 属性,但我们没有为其提供值,因此 DocumentDB 根据 GUID 生成了一个,就像它为前两个文档所做的那样。

编译并执行上述代码后,您将收到以下输出。

**** Create Documents ****  
Created new document: 575882f0-236c-4c3d-81b9-d27780206b2c 
{ 
  "name": "New Customer 1", 
  "address": { 
    "addressType": "Main Office", 
    "addressLine1": "123 Main Street", 
    "location": { 
      "city": "Brooklyn", 
      "stateProvinceName": "New York" 
    }, 
    "postalCode": "11229", 
    "countryRegionName": "United States" 
  }, 
  "id": "575882f0-236c-4c3d-81b9-d27780206b2c", 
  "_rid": "kV5oANVXnwDGPgAAAAAAAA==", 
  "_ts": 1450037545, 
  "_self": "dbs/kV5oAA==/colls/kV5oANVXnwA=/docs/kV5oANVXnwDGPgAAAAAAAA==/", 
  "_etag": "\"00006fce-0000-0000-0000-566dd1290000\"", 
  "_attachments": "attachments/" 
} 
Created document 575882f0-236c-4c3d-81b9-d27780206b2c from dynamic object  
Created new document: 8d7ad239-2148-4fab-901b-17a85d331056 
{ 
  "name": "New Customer 2", 
  "address": {
    "addressType": "Main Office", 
    "addressLine1": "123 Main Street", 
    "location": { 
      "city": "Brooklyn", 
      "stateProvinceName": "New York" 
    }, 
    "postalCode": "11229", 
    "countryRegionName": "United States" 
  }, 
  "id": "8d7ad239-2148-4fab-901b-17a85d331056", 
  "_rid": "kV5oANVXnwDHPgAAAAAAAA==", 
  "_ts": 1450037545, 
  "_self": "dbs/kV5oAA==/colls/kV5oANVXnwA=/docs/kV5oANVXnwDHPgAAAAAAAA==/", 
  "_etag": "\"000070ce-0000-0000-0000-566dd1290000\"", 
  "_attachments": "attachments/" 
} 
Created document 8d7ad239-2148-4fab-901b-17a85d331056 from JSON string  
Created new document: 49f399a8-80c9-4844-ac28-cd1dee689968 
{ 
  "id": "49f399a8-80c9-4844-ac28-cd1dee689968", 
  "name": "New Customer 3", 
  "address": { 
    "addressType": "Main Office", 
    "addressLine1": "123 Main Street", 
    "location": { 
      "city": "Brooklyn", 
      "stateProvinceName": "New York" 
    }, 
    "postalCode": "11229", 
    "countryRegionName": "United States" 
  }, 
  "_rid": "kV5oANVXnwDIPgAAAAAAAA==", 
  "_ts": 1450037546, 
  "_self": "dbs/kV5oAA==/colls/kV5oANVXnwA=/docs/kV5oANVXnwDIPgAAAAAAAA==/", 
  "_etag": "\"000071ce-0000-0000-0000-566dd12a0000\"", 
  "_attachments": "attachments/" 
}
Created document 49f399a8-80c9-4844-ac28-cd1dee689968 from typed object

DocumentDB – 数据类型

JSON 或 JavaScript Object Notation 是一种轻量级的基于文本的开放标准,专为人类可读的数据交换而设计,也易于机器解析和生成。JSON 是 DocumentDB 的核心。我们通过网络传输 JSON,我们将 JSON 存储为 JSON,我们索引 JSON 树,允许对完整的 JSON 文档进行查询。

JSON 格式支持以下数据类型 –

S.No. 类型和描述
1

Number

JavaScript 中的双精度浮点格式

2

String

带有反斜杠转义的双引号 Unicode

3

Boolean

对或错

4

Array

有序的值序列

5

Value

它可以是字符串、数字、真假、空值等。

6

Object

键值对的无序集合

7

Whitespace

它可以在任何一对令牌之间使用

8

Null

空的

让我们看一个简单的示例 DateTime 类型。将出生日期添加到客户类。

public class Customer {
   [JsonProperty(PropertyName = "id")] 
   public string Id { get; set; }
	
   // Must be nullable, unless generating unique values for new customers on client  
   [JsonProperty(PropertyName = "name")] 
   public string Name { get; set; }  
	
   [JsonProperty(PropertyName = "address")] 
   public Address Address { get; set; }  
	
   [JsonProperty(PropertyName = "birthDate")] 
   public DateTime BirthDate { get; set; } 
}

我们可以使用 DateTime 存储、检索和查询,如下面的代码所示。

private async static Task CreateDocuments(DocumentClient client) {
   Console.WriteLine(); 
   Console.WriteLine("**** Create Documents ****"); 
   Console.WriteLine();
	
   var document3Definition = new Customer { 
      Id = "1001", 
      Name = "Luke Andrew", 
		
      Address = new Address { 
         AddressType = "Main Office", 
         AddressLine1 = "123 Main Street", 
         Location = new Location {
            City = "Brooklyn",
            StateProvinceName = "New York" 
         }, 
         PostalCode = "11229",
         CountryRegionName = "United States" 
      },
		
      BirthDate = DateTime.Parse(DateTime.Today.ToString()), 
   };
	
   Document document3 = await CreateDocument(client, document3Definition); 
   Console.WriteLine("Created document {0} from typed object", document3.Id); 
   Console.WriteLine(); 
}

编译执行上述代码,创建文档后,你会看到现在添加了出生日期。

**** Create Documents ****  
Created new document: 1001 
{ 
   "id": "1001", 
   "name": "Luke Andrew", 
   "address": { 
      "addressType": "Main Office", 
      "addressLine1": "123 Main Street", 
      "location": { 
         "city": "Brooklyn", 
         "stateProvinceName": "New York" 
      }, 
      "postalCode": "11229", 
      "countryRegionName": "United States" 
   }, 
   "birthDate": "2015-12-14T00:00:00", 
   "_rid": "Ic8LAMEUVgAKAAAAAAAAAA==", 
   "_ts": 1450113676, 
   "_self": "dbs/Ic8LAA==/colls/Ic8LAMEUVgA=/docs/Ic8LAMEUVgAKAAAAAAAAAA==/", 
   "_etag": "\"00002d00-0000-0000-0000-566efa8c0000\"", 
   "_attachments": "attachments/" 
} 
Created document 1001 from typed object

DocumentDB – 限制记录

Microsoft 最近在如何查询 Azure DocumentDB 方面添加了许多改进,例如 SQL 语法的 TOP 关键字,这使查询运行速度更快并消耗更少资源,增加了对查询运算符的限制,并添加了对其他 LINQ 运算符的支持.NET SDK。

让我们看一个简单的例子,在这个例子中我们将只检索前两条记录。如果您有许多记录并且只想检索其中的一些记录,那么您可以使用 Top 关键字。在这个例子中,我们有很多地震记录。

限制记录

现在我们只想显示前两条记录

步骤 1 – 转到查询资源管理器并运行此查询。

SELECT * FROM c 
WHERE c.magnitude > 2.5 

您将看到它已检索到 4 条记录,因为我们尚未指定 TOP 关键字。

检索记录

第 2 步– 现在使用具有相同查询的 TOP 关键字。这里我们指定了 TOP 关键字,“2”表示我们只需要两条记录。

SELECT TOP 2 * FROM c 
WHERE c.magnitude > 2.5

第 3 步– 现在运行此查询,您将看到仅检索到两条记录。

检索到两条记录

同样,您可以在使用 .Net SDK 的代码中使用 TOP 关键字。下面是实现。

private async static Task QueryDocumentsWithPaging(DocumentClient client) {
   Console.WriteLine(); 
   Console.WriteLine("**** Query Documents (paged results) ****"); 
   Console.WriteLine();  
   Console.WriteLine("Quering for all documents"); 
	
   var sql = "SELECT TOP 3 * FROM c";  
   var query = client 
      .CreateDocumentQuery(collection.SelfLink, sql) 
      .AsDocumentQuery(); 
		
   while (query.HasMoreResults) {
      var documents = await query.ExecuteNextAsync(); 
		
      foreach (var document in documents) { 
         Console.WriteLine(" PublicId: {0}; Magnitude: {1};", document.publicid,
            document.magnitude); 
      } 
   } 
	
   Console.WriteLine(); 
}

以下是 CreateDocumentClient 任务,其中实例化了 DocumentClient 和地震数据库。

private static async Task CreateDocumentClient() {
   // Create a new instance of the DocumentClient 
   using (var client = new DocumentClient(new Uri(EndpointUrl), AuthorizationKey)) {
      database = client.CreateDatabaseQuery("SELECT * FROM c WHERE c.id =
         'earthquake'").AsEnumerable().First(); 
			
      collection = client.CreateDocumentCollectionQuery(database.CollectionsLink,
         "SELECT * FROM c WHERE c.id = 'earthquakedata'").AsEnumerable().First(); 
			
      await QueryDocumentsWithPaging(client); 
   } 
}

当上面的代码编译执行后,你会看到只检索到了三个记录。

**** Query Documents (paged results) **** 
 
Quering for all documents 
PublicId: 2015p947400; Magnitude: 2.515176918; 
PublicId: 2015p947373; Magnitude: 1.506774108; 
PublicId: 2015p947329; Magnitude: 1.593394461;

DocumentDB – 排序记录

Microsoft Azure DocumentDB 支持使用 SQL over JSON 文档查询文档。您可以在查询中使用 ORDER BY 子句根据数字和字符串对集合中的文档进行排序。该子句可以包含一个可选的 ASC/DESC 参数来指定必须检索结果的顺序。

让我们看一下以下示例,其中有一个 JSON 文档。

{ 
   "id": "Food Menu",
   "description": "Grapes, red or green (European type, such as Thompson seedless), raw",
	
   "tags": [
      {
         "name": "grapes"
      },
		
      {
         "name": "red or green (european type"
      },
		
      {
         "name": "such as thompson seedless)"
      },
		
      {
         "name": "raw"
      }
   ],
	
   "foodGroup": "Fruits and Fruit Juices",
	
   "servings": [
      {
         "amount": 1,
         "description": "cup",
         "weightInGrams": 151
      },
		
      {
         "amount": 10,
         "description": "grapes",
         "weightInGrams": 49
      },
		
      {
         "amount": 1,
         "description": "NLEA serving",
         "weightInGrams": 126
      }
   ]
	
}

以下是按降序对结果进行排序的 SQL 查询。

SELECT f.description, f.foodGroup,  
   f.servings[2].description AS servingDescription,  
   f.servings[2].weightInGrams AS servingWeight  
	
FROM f  
ORDER BY f.servings[2].weightInGrams DESC 

执行上述查询时,您将收到以下输出。

[
   {
      "description": "Grapes, red or green (European type, such as Thompson
         seedless), raw",
      "foodGroup": "Fruits and Fruit Juices",
      "servingDescription": "NLEA serving",
      "servingWeight": 126
   }
]

DocumentDB – 索引记录

默认情况下,只要将文档添加到数据库中,DocumentDB 就会自动索引文档中的每个属性。但是,您可以控制并微调您自己的索引策略,从而在存在永远不需要索引的特定文档和/或属性时减少存储和处理开销。

告诉 DocumentDB 自动索引每个属性的默认索引策略适用于许多常见场景。但是,您也可以实施自定义策略,以精确控制索引的内容和未编入索引的内容以及与索引相关的其他功能。

DocumentDB 支持以下类型的索引 –

  • 哈希
  • 范围

哈希

散列索引能够有效地查询相等性,即在搜索给定属性等于精确值的文档时,而不是在诸如小于、大于或介于之类的值范围上进行匹配。

您可以使用散列索引执行范围查询,但 DocumentDB 将无法使用散列索引来查找匹配的文档,而是需要依次扫描每个文档以确定范围查询是否应该选择它。

您将无法在只有哈希索引的属性上使用 ORDER BY 子句对文档进行排序。

范围

为属性定义的范围索引,DocumentDB 允许针对一系列值有效查询文档。它还允许您使用 ORDER BY 对该属性的查询结果进行排序。

DocumentDB 允许您在任何或所有属性上定义散列和范围索引,从而实现高效的等式和范围查询,以及 ORDER BY。

索引政策

每个集合都有一个索引策略,它规定了每个文档的每个属性中的数字和字符串使用哪种类型的索引。

  • 您还可以控制文档在添加到集合时是否自动索引。

  • 默认情况下启用自动索引,但您可以在添加文档时覆盖该行为,告诉 DocumentDB 不要索引该特定文档。

  • 您可以禁用自动索引,以便默认情况下,文档在添加到集合时不会被索引。同样,您可以在文档级别覆盖它,并在将特定文档添加到集合时指示 DocumentDB 为其建立索引。这称为手动索引。

包括/排除索引

索引策略还可以定义应该在索引中包含或排除哪些路径。如果您知道文档的某些部分您从不查询并且您查询了某些部分,这将非常有用。

在这些情况下,您可以通过告诉 DocumentDB 只索引添加到集合中的每个文档的那些特定部分来减少索引开销。

自动索引

我们来看一个简单的自动索引示例。

Step 1 – 首先我们创建一个名为 autoindexing 的集合,并且没有明确提供策略,这个集合使用默认的索引策略,这意味着在这个集合上启用了自动索引。

这里我们对数据库自链接使用基于 ID 的路由,因此在创建集合之前我们不需要知道它的资源 ID 或查询它。我们可以只使用数据库 ID,即 mydb。

第 2 步– 现在让我们创建两个文档,都使用 Upston 的姓氏。

private async static Task AutomaticIndexing(DocumentClient client) {
   Console.WriteLine();
   Console.WriteLine("**** Override Automatic Indexing ****");

   // Create collection with automatic indexing

   var collectionDefinition = new DocumentCollection {
      Id = "autoindexing"
   };
	
   var collection = await client.CreateDocumentCollectionAsync("dbs/mydb",
      collectionDefinition);

   // Add a document (indexed)
   dynamic indexedDocumentDefinition = new {
      id = "MARK",
      firstName = "Mark",
      lastName = "Upston",
      addressLine = "123 Main Street",
      city = "Brooklyn",
      state = "New York",
      zip = "11229",
   };
	
   Document indexedDocument = await client
      .CreateDocumentAsync("dbs/mydb/colls/autoindexing", indexedDocumentDefinition);
		
   // Add another document (request no indexing)
   dynamic unindexedDocumentDefinition = new {
      id = "JANE",
      firstName = "Jane",
      lastName = "Upston",
      addressLine = "123 Main Street",
      city = "Brooklyn",
      state = "New York",
      zip = "11229",
   };
	
   Document unindexedDocument = await client
      .CreateDocumentAsync("dbs/mydb/colls/autoindexing", unindexedDocumentDefinition,
      new RequestOptions { IndexingDirective = IndexingDirective.Exclude });

   //Unindexed document won't get returned when querying on non-ID (or selflink) property

   var doeDocs = client.CreateDocumentQuery("dbs/mydb/colls/autoindexing", "SELECT *
      FROM c WHERE c.lastName = 'Doe'").ToList();
		
   Console.WriteLine("Documents WHERE lastName = 'Doe': {0}", doeDocs.Count);

   // Unindexed document will get returned when using no WHERE clause

   var allDocs = client.CreateDocumentQuery("dbs/mydb/colls/autoindexing",
      "SELECT * FROM c").ToList();
   Console.WriteLine("All documents: {0}", allDocs.Count);
	
   // Unindexed document will get returned when querying by ID (or self-link) property
	
   Document janeDoc = client.CreateDocumentQuery("dbs/mydb/colls/autoindexing",
      "SELECT * FROM c WHERE c.id = 'JANE'").AsEnumerable().FirstOrDefault();
   Console.WriteLine("Unindexed document self-link: {0}", janeDoc.SelfLink);
	
   // Delete the collection
	
   await client.DeleteDocumentCollectionAsync("dbs/mydb/colls/autoindexing");
}

对于 Mark Upston 来说,第一个被添加到集合中,然后根据默认索引策略立即自动索引。

但是当 Mark Upston 的第二个文档被添加时,我们已经通过 IndexingDirective.Exclude 传递了请求选项,它明确指示 DocumentDB 不要索引这个文档,尽管集合的索引策略。

我们对最后的两个文档都有不同类型的查询。

第 3 步– 让我们从 CreateDocumentClient 调用 AutomaticIndexing 任务。

private static async Task CreateDocumentClient() {
   // Create a new instance of the DocumentClient 
   using (var client = new DocumentClient(new Uri(EndpointUrl), AuthorizationKey)) { 
      await AutomaticIndexing(client); 
   } 
}

编译并执行上述代码后,您将收到以下输出。

**** Override Automatic Indexing **** 
Documents WHERE lastName = 'Upston': 1 
All documents: 2 
Unindexed document self-link: dbs/kV5oAA==/colls/kV5oAOEkfQA=/docs/kV5oAOEkfQACA 
AAAAAAAAA==/

正如您所看到的,我们有两个这样的文档,但是查询只返回 Mark 的一个,因为 Mark 的一个没有被索引。如果我们再次查询,没有 WHERE 子句来检索集合中的所有文档,那么我们会得到一个包含两个文档的结果集,这是因为未索引的文档总是由没有 WHERE 子句的查询返回。

我们还可以通过 ID 或自链接检索未索引的文档。因此,当我们通过他的 ID MARK 查询 Mark 的文档时,我们看到 DocumentDB 返回该文档,即使它没有在集合中编入索引。

手动索引

让我们看一个通过覆盖自动索引进行手动索引的简单示例。

第 1 步– 首先,我们将创建一个名为 manualindexing 的集合,并通过显式禁用自动索引来覆盖默认策略。这意味着,除非我们另有要求,否则添加到此集合中的新文档将不会被索引。

private async static Task ManualIndexing(DocumentClient client) {
   Console.WriteLine();
   Console.WriteLine("**** Manual Indexing ****");
   // Create collection with manual indexing

   var collectionDefinition = new DocumentCollection {
      Id = "manualindexing",
      IndexingPolicy = new IndexingPolicy {
         Automatic = false,
      },
   };
	
   var collection = await client.CreateDocumentCollectionAsync("dbs/mydb",
      collectionDefinition);
		
   // Add a document (unindexed)
   dynamic unindexedDocumentDefinition = new {
      id = "MARK",
      firstName = "Mark",
      lastName = "Doe",
      addressLine = "123 Main Street",
      city = "Brooklyn",
      state = "New York",
      zip = "11229",
   }; 
	
   Document unindexedDocument = await client
      .CreateDocumentAsync("dbs/mydb/colls/manualindexing", unindexedDocumentDefinition);
  
   // Add another document (request indexing)
   dynamic indexedDocumentDefinition = new {
      id = "JANE",
      firstName = "Jane",
      lastName = "Doe",
      addressLine = "123 Main Street",
      city = "Brooklyn",
      state = "New York",
      zip = "11229",
   };
	
   Document indexedDocument = await client.CreateDocumentAsync
      ("dbs/mydb/colls/manualindexing", indexedDocumentDefinition, new RequestOptions {
      IndexingDirective = IndexingDirective.Include });

   //Unindexed document won't get returned when querying on non-ID (or selflink) property

   var doeDocs = client.CreateDocumentQuery("dbs/mydb/colls/manualindexing",
      "SELECT * FROM c WHERE c.lastName = 'Doe'").ToList();
   Console.WriteLine("Documents WHERE lastName = 'Doe': {0}", doeDocs.Count);
	
   // Unindexed document will get returned when using no WHERE clause
	
   var allDocs = client.CreateDocumentQuery("dbs/mydb/colls/manualindexing",
      "SELECT * FROM c").ToList();
   Console.WriteLine("All documents: {0}", allDocs.Count);
	
   // Unindexed document will get returned when querying by ID (or self-link) property
	
   Document markDoc = client
      .CreateDocumentQuery("dbs/mydb/colls/manualindexing",
      "SELECT * FROM c WHERE c.id = 'MARK'")
      .AsEnumerable().FirstOrDefault();
   Console.WriteLine("Unindexed document self-link: {0}", markDoc.SelfLink);
   await client.DeleteDocumentCollectionAsync("dbs/mydb/colls/manualindexing");
}

第 2 步– 现在我们将再次创建与以前相同的两个文档。这次我们不会为 Mark 的文档提供任何特殊的请求选项,因为集合的索引策略,该文档不会被索引。

Step 3 – 现在,当我们为 Mark 添加第二个文档时,我们使用 RequestOptions 和 IndexingDirective.Include 来告诉 DocumentDB 它应该索引这个文档,这会覆盖集合的索引策略,它不应该。

我们对最后的两个文档都有不同类型的查询。

第 4 步– 让我们从 CreateDocumentClient 调用 ManualIndexing 任务。

private static async Task CreateDocumentClient() {
   // Create a new instance of the DocumentClient 
   using (var client = new DocumentClient(new Uri(EndpointUrl), AuthorizationKey)) {
      await ManualIndexing(client); 
   } 
}

编译并执行上述代码后,您将收到以下输出。

**** Manual Indexing **** 
Documents WHERE lastName = 'Upston': 1 
All documents: 2 
Unindexed document self-link: dbs/kV5oAA==/colls/kV5oANHJPgE=/docs/kV5oANHJPgEBA 
AAAAAAAAA==/

同样,查询仅返回两个文档中的一个,但这一次,它返回 Jane Doe,我们明确要求将其编入索引。但是和以前一样,没有 WHERE 子句的查询会检索集合中的所有文档,包括 Mark 的未索引文档。我们还可以通过它的 ID 查询未索引的文档,即使它没有被索引,DocumentDB 也会返回它。

DocumentDB – 地理空间数据

Microsoft 添加了地理空间支持,可让您将位置数据存储在文档中,并对点和多边形之间的距离和交点执行空间计算。

  • 空间数据描述了物体在空间中的位置和形状。

  • 通常,它可以用来表示一个人的位置、一个感兴趣的地方、一个城市的边界或一个湖泊。

  • 常见用例通常涉及邻近查询。例如,“查找我当前位置附近的所有大学”。

一个表示空间中的一个位置,它代表确切的位置,例如特定大学的街道地址。一个点在 DocumentDB 中使用其坐标对(经度和纬度)表示。以下是 JSON 点的示例。

{ 
   "type":"Point", 
   "coordinates":[ 28.3, -10.7 ] 
} 

让我们看一个包含大学位置的简单示例。

{ 
   "id":"case-university", 
   "name":"CASE: Center For Advanced Studies In Engineering", 
   "city":"Islamabad", 
	
   "location": { 
      "type":"Point", 
      "coordinates":[ 33.7194136, -73.0964862 ] 
   } 
}

要根据位置检索大学名称,您可以使用以下查询。

SELECT c.name FROM c 

WHERE c.id = "case-university" AND ST_ISVALID({ 
      "type":"Point", 
      "coordinates":[ 33.7194136, -73.0964862 ]})

执行上述查询时,您将收到以下输出。

[ 
   { 
      "name": "CASE: Center For Advanced Studies In Engineering" 
   } 
] 

在 .NET 中使用地理空间数据创建文档

您可以使用地理空间数据创建文档,让我们看一个创建大学文档的简单示例。

private async static Task CreateDocuments(DocumentClient client) {
   Console.WriteLine(); 
   Console.WriteLine("**** Create Documents ****"); 
   Console.WriteLine();
	
   var uniDocument = new UniversityProfile {
      Id = "nust", 
      Name = "National University of Sciences and Technology", 
      City = "Islamabad", 
      Loc = new Point(33.6455715, 72.9903447) 
   };
	
   Document document = await CreateDocument(client, uniDocument); 
   Console.WriteLine("Created document {0} from typed object", document.Id); 
   Console.WriteLine(); 
}

以下是 UniversityProfile 类的实现。

public class UniversityProfile { 
   [JsonProperty(PropertyName = "id")] 
   public string Id { get; set; }  
	
   [JsonProperty("name")] 
   public string Name { get; set; }
	
   [JsonProperty("city")] 
   public string City { get; set; }  
	
   [JsonProperty("location")] 
   public Point Loc { get; set; } 
} 

编译并执行上述代码后,您将收到以下输出。

**** Create Documents ****  
Created new document: nust 
{ 
   "id": "nust", 
   "name": "National University of Sciences and Technology", 
   "city": "Islamabad", 
   "location": { 
      "type": "Point", 
      "coordinates": [ 
         33.6455715, 
         72.9903447 
      ] 
   }, 
   "_rid": "Ic8LAMEUVgANAAAAAAAAAA==", 
   "_ts": 1450200910, 
   "_self": "dbs/Ic8LAA==/colls/Ic8LAMEUVgA=/docs/Ic8LAMEUVgANAAAAAAAAAA==/", 
   "_etag": "\"00004100-0000-0000-0000-56704f4e0000\"", 
   "_attachments": "attachments/" 
} 
Created document nust from typed object 

DocumentDB – 分区

当您的数据库开始增长超过 10GB 时,您可以简单地通过创建新集合然后在越来越多的集合中传播或分区数据来横向扩展。

一个具有 10GB 容量的集合迟早会不足以容纳您的数据库。现在 10GB 听起来可能不是一个很大的数字,但请记住,我们正在存储 JSON 文档,它只是纯文本,即使您考虑索引的存储开销,您也可以在 10GB 中容纳大量纯文本文档。

在可扩展性方面,存储并不是唯一的问题。集合上可用的最大吞吐量是 S3 集合获得的每秒两个半千请求单位。因此,如果您需要更高的吞吐量,那么您还需要通过使用多个集合进行分区来横向扩展。向外扩展分区也称为水平分区

有多种方法可用于通过 Azure DocumentDB 对数据进行分区。以下是最常见的策略 –

  • 溢出分区
  • 范围分区
  • 查找分区
  • 哈希分区

溢出分区

溢出分区是最简单的策略,因为没有分区键。当您对很多事情不确定时,开始通常是一个不错的选择。您可能不知道您是否甚至需要扩展到单个集合之外,或者您可能需要添加多少集合,或者您可能需要多快地添加它们。

  • 溢出分区从单个集合开始,没有分区键。

  • 集合开始增长,然后再增长一些,然后再增长一些,直到您开始接近 10GB 的限制。

  • 当您达到 90% 的容量时,您会溢出到一个新集合并开始将其用于新文档。

  • 一旦您的数据库扩展到更多的集合,您可能希望转向基于分区键的策略。

  • 执行此操作时,您需要根据要迁移到的任何策略,通过将文档移动到不同集合来重新平衡数据。

范围分区

最常见的策略之一是范围分区。通过这种方法,您可以确定文档的分区键可能落入的值范围,并将文档定向到与该范围对应的集合。

  • 日期通常与此策略一起使用,您可以在该策略中创建一个集合来保存定义的日期范围内的文档。当您定义足够小的范围时,您可以确信任何集合都不会超过其 10GB 的限制。例如,可能存在单个集合可以合理处理整个月的文档的场景。

  • 也可能是大多数用户查询当前数据,可能是本月或上个月的数据,但用户很少搜索更旧的数据。因此,您从 6 月开始使用 S3 系列,这是您可以购买的最昂贵的系列,并提供您可以获得的最佳吞吐量。

  • 在 7 月,您购买另一个 S3 集合来存储 7 月的数据,并且您还将 6 月的数据缩减为更便宜的 S2 集合。然后在 8 月,您将获得另一个 S3 系列,并将 7 月缩小到 S2,6 月一直缩小到 S1。月复一月,您始终保持当前数据可用于高吞吐量,而旧数据以较低吞吐量保持可用。

  • 只要查询提供了分区键,就只会查询需要查询的集合,而不是像溢出分区那样会查询数据库中的所有集合。

查找分区

通过查找分区,您可以定义一个分区映射,该映射根据文档的分区键将文档路由到特定集合。例如,您可以按区域进行分区。

  • 将所有美国文档存储在一个集合中,将所有欧洲文档存储在另一个集合中,并将来自任何其他地区的所有文档存储在第三个集合中。

  • 使用此分区映射和查找分区解析器可以根据分区键(每个文档中包含的区域属性)确定要在哪个集合中创建文档以及要查询哪些集合。

哈希分区

在散列分区中,分区是根据散列函数的值分配的,允许您跨多个分区均匀分布请求和数据。

这通常用于对从大量不同客户端生成或使用的数据进行分区,并且对于存储用户配置文件、目录项等很有用。

让我们看一个使用 .NET SDK 提供的 RangePartitionResolver 进行范围分区的简单示例。

第 1 步– 创建一个新的 DocumentClient,我们将在 CreateCollections 任务中创建两个集合。一个包含用户 ID 以 A 到 M 开头的用户的文档,另一个包含用户 ID N 到 Z 的文档。

private static async Task CreateCollections(DocumentClient client) {
   await client.CreateDocumentCollectionAsync(“dbs/myfirstdb”, new DocumentCollection {
      Id = “CollectionAM” }); 
		
   await client.CreateDocumentCollectionAsync(“dbs/myfirstdb”, new DocumentCollection {
      Id = “CollectionNZ” }); 
}

步骤 2 – 为数据库注册范围解析器。

第 3 步– 创建一个新的 RangePartitionResolver<string>,这是我们分区键的数据类型。构造函数接受两个参数,分区键的属性名称和作为分片映射或分区映射的字典,它只是我们为解析器预定义的范围和相应集合的列表。

private static void RegisterRangeResolver(DocumentClient client) {

   //Note: \uffff is the largest UTF8 value, so M\ufff includes all strings that start with M.
		
   var resolver = new RangePartitionResolver<string>(
      "userId", new Dictionary<Range<string>, string>() {
      { new Range<string>("A", "M\uffff"), "dbs/myfirstdb/colls/CollectionAM" },
      { new Range<string>("N", "Z\uffff"), "dbs/myfirstdb/colls/CollectionNZ" },
   });
	
   client.PartitionResolvers["dbs/myfirstdb"] = resolver;
 }

有必要在此处对最大可能的 UTF-8 值进行编码。否则第一个范围不会与任何 Ms 匹配,除了一个 M 之外,第二个范围中的 Z 也是如此。因此,您可以将这里的编码值视为匹配分区键的通配符。

第 4 步– 创建解析器后,使用当前 DocumentClient 将其注册到数据库。为此,只需将其分配给 PartitionResolver 的字典属性即可。

我们将针对数据库创建和查询文档,而不是您通常所做的集合,解析器将使用此映射将请求路由到适当的集合。

现在让我们创建一些文档。首先,我们将为 userId Kirk 创建一个,然后为 Spock 创建一个。

private static async Task CreateDocumentsAcrossPartitions(DocumentClient client) { 
   Console.WriteLine(); 
   Console.WriteLine("**** Create Documents Across Partitions ****");
	
   var kirkDocument = await client.CreateDocumentAsync("dbs/myfirstdb", new { userId =
      "Kirk", title = "Captain" }); 
   Console.WriteLine("Document 1: {0}", kirkDocument.Resource.SelfLink);
	
   var spockDocument = await client.CreateDocumentAsync("dbs/myfirstdb", new { userId =
      "Spock", title = "Science Officer" });		
   Console.WriteLine("Document 2: {0}", spockDocument.Resource.SelfLink); 
}

这里的第一个参数是数据库的自链接,不是特定的集合。如果没有分区解析器,这是不可能的,但是有了分区解析器,它就可以无缝地工作。

两个文档都保存到数据库 myfirstdb 中,但我们知道 Kirk 存储在 A 到 M 的集合中,Spock 存储在 N 到 Z 的集合中,如果我们的 RangePartitionResolver 工作正常。

让我们从 CreateDocumentClient 任务调用这些,如以下代码所示。

private static async Task CreateDocumentClient() {
   // Create a new instance of the DocumentClient 
   using (var client = new DocumentClient(new Uri(EndpointUrl), AuthorizationKey)) {
      await CreateCollections(client);  
      RegisterRangeResolver(client);  
      await CreateDocumentsAcrossPartitions(client); 
   } 
}

执行上述代码后,您将收到以下输出。

**** Create Documents Across Partitions **** 
Document 1: dbs/Ic8LAA==/colls/Ic8LAO2DxAA=/docs/Ic8LAO2DxAABAAAAAAAAAA==/ 
Document 2: dbs/Ic8LAA==/colls/Ic8LAP12QAE=/docs/Ic8LAP12QAEBAAAAAAAAAA==/

正如所见,两个文档的自链接具有不同的资源 ID,因为它们存在于两个单独的集合中。

DocumentDB – 数据迁移

使用 DocumentDB 数据迁移工具,您可以轻松地将数据迁移到 DocumentDB。DocumentDB 数据迁移工具是一个免费的开源实用程序,您可以从 Microsoft 下载中心https://www.microsoft.com/下载

迁移工具支持许多数据源,其中一些列在下面 –

下载 DocumentDB 数据迁移工具后,解压缩 zip 文件。

您可以在此文件夹中看到两个可执行文件,如下面的屏幕截图所示。

可执行文件

首先是 dt.exe,它是带有命令行界面的控制台版本,然后是 dtui.exe,它是带有图形用户界面的桌面版本。

让我们启动 GUI 版本。

启动 GUI 版本

您可以看到欢迎页面。在源信息页面单击“下一步”。

来源信息页面

您可以在此处配置数据源,您可以从下拉菜单中看到许多受支持的选项。

指定源信息

当您进行选择时,“源信息”页面的其余部分会相应更改。

使用 DocumentDB 数据迁移工具将数据导入 DocumentDB 非常容易。我们建议您练习上述示例并使用其他数据文件。

DocumentDB – 访问控制

DocumentDB 提供了控制对 DocumentDB 资源的访问的概念。对 DocumentDB 资源的访问由主密钥令牌或资源令牌管理。基于资源令牌的连接只能访问令牌指定的资源,不能访问其他资源。资源令牌基于用户权限。

  • 首先创建一个或多个用户,这些用户是在数据库级别定义的。

  • 然后,根据您希望允许每个用户访问的资源,为每个用户创建一个或多个权限。

  • 每个权限都会生成一个资源令牌,允许对给定资源进行只读或完全访问,并且可以是数据库中的任何用户资源。

  • 用户是在数据库级别定义的,并且为每个用户定义权限。

  • 用户和权限适用于数据库中的所有集合。

让我们看一个简单的例子,在这个例子中我们将学习如何定义用户和权限以在 DocumentDB 中实现细粒度的安全性。

我们将从一个新的 DocumentClient 开始并查询 myfirstdb 数据库。

private static async Task CreateDocumentClient() {
   // Create a new instance of the DocumentClient
   using (var client = new DocumentClient(new Uri(EndpointUrl), AuthorizationKey)) {
      database = client.CreateDatabaseQuery("SELECT * FROM c WHERE c.id =
         'myfirstdb'").AsEnumerable().First();
			
      collection = client.CreateDocumentCollectionQuery(database.CollectionsLink,
         "SELECT * FROM c WHERE c.id = 'MyCollection'").AsEnumerable().First();
			
      var alice = await CreateUser(client, "Alice");
      var tom = await CreateUser(client, "Tom");
   }
}

以下是 CreateUser 的实现。

private async static Task<User> CreateUser(DocumentClient client, string userId) {
   Console.WriteLine();
   Console.WriteLine("**** Create User {0} in {1} ****", userId, database.Id);
	
   var userDefinition = new User { Id = userId };
   var result = await client.CreateUserAsync(database.SelfLink, userDefinition);
   var user = result.Resource;
	
   Console.WriteLine("Created new user");
   ViewUser(user);
	
   return user;
}

第 1 步– 创建两个用户 Alice 和 Tom 就像我们创建的任何资源一样,我们构造一个具有所需 Id 的定义对象并调用 create 方法,在这种情况下,我们使用数据库的 SelfLink 和 userDefinition 调用 CreateUserAsync。我们从我们获取新创建的用户对象的资源属性中取回结果。

现在在数据库中看到这两个新用户。

private static void ViewUsers(DocumentClient client) {
   Console.WriteLine(); 
   Console.WriteLine("**** View Users in {0} ****", database.Id);  
	
   var users = client.CreateUserQuery(database.UsersLink).ToList();
   var i = 0;
	
   foreach (var user in users) { 
      i++; 
      Console.WriteLine(); 
      Console.WriteLine("User #{0}", i); 
      ViewUser(user); 
   }
	
   Console.WriteLine();
   Console.WriteLine("Total users in database {0}: {1}", database.Id, users.Count); 
}
  
private static void ViewUser(User user) {
   Console.WriteLine("User ID: {0} ", user.Id); 
   Console.WriteLine("Resource ID: {0} ", user.ResourceId); 
   Console.WriteLine("Self Link: {0} ", user.SelfLink); 
   Console.WriteLine("Permissions Link: {0} ", user.PermissionsLink); 
   Console.WriteLine("Timestamp: {0} ", user.Timestamp); 
}

第 2 步– 针对数据库的 UsersLink 调用 CreateUserQuery 以检索所有用户的列表。然后遍历它们并查看它们的属性。

现在我们必须首先创建它们。因此,假设我们希望允许 Alice 对 MyCollection 集合进行读/写权限,但 Tom 只能读取集合中的文档。

await CreatePermission(client, alice, "Alice Collection Access", PermissionMode.All,
   collection);
	
await CreatePermission(client, tom, "Tom Collection Access", PermissionMode.Read,
   collection);

第 3 步– 创建对 MyCollection 集合资源的权限,因此我们需要为该资源获取 SelfLink。

第 4 步– 然后在此集合上为 Alice 创建一个 Permission.All,并为 Tom 在此集合上创建一个 Permission.Read。

以下是 CreatePermission 的实现。

private async static Task CreatePermission(DocumentClient client, User user,
   string permId, PermissionMode permissionMode, string resourceLink) {
   Console.WriteLine();
   Console.WriteLine("**** Create Permission {0} for {1} ****", permId, user.Id);
	
   var permDefinition = new Permission {
      Id = permId,
      PermissionMode = permissionMode,
      ResourceLink = resourceLink
   };
	
   var result = await client.CreatePermissionAsync(user.SelfLink, permDefinition);
   var perm = result.Resource;
   Console.WriteLine("Created new permission");
   ViewPermission(perm);
}

正如您现在应该期待的那样,我们通过为新权限创建一个定义对象来实现这一点,其中包括一个 Id 和一个权限模式,它可以是 Permission.All 或 Permission.Read,以及被保护的资源的 SelfLink经许可。

第 5 步– 调用 CreatePermissionAsync 并从结果中的资源属性获取创建的权限。

查看创建的权限,下面是ViewPermissions的实现。

private static void ViewPermissions(DocumentClient client, User user) {
   Console.WriteLine(); 
   Console.WriteLine("**** View Permissions for {0} ****", user.Id);
	
   var perms = client.CreatePermissionQuery(user.PermissionsLink).ToList();
   var i = 0; 
	
   foreach (var perm in perms) {
      i++; 
      Console.WriteLine(); 
      Console.WriteLine("Permission #{0}", i); 
      ViewPermission(perm); 
   }  
	
   Console.WriteLine(); 
   Console.WriteLine("Total permissions for {0}: {1}", user.Id, perms.Count); 
}
  
private static void ViewPermission(Permission perm) {
   Console.WriteLine("Permission ID: {0} ", perm.Id); 
   Console.WriteLine("Resource ID: {0} ", perm.ResourceId); 
   Console.WriteLine("Permission Mode: {0} ", perm.PermissionMode);
   Console.WriteLine("Token: {0} ", perm.Token); 
   Console.WriteLine("Timestamp: {0} ", perm.Timestamp); 
}

这一次,它是针对用户权限链接的权限查询,我们只是列出为用户返回的每个权限。

让我们删除 Alice 和 Tom 的权限。

await DeletePermission(client, alice, "Alice Collection Access"); 
await DeletePermission(client, tom, "Tom Collection Access");

以下是 DeletePermission 的实现。

private async static Task DeletePermission(DocumentClient client, User user,
   string permId) {
   Console.WriteLine(); 
   Console.WriteLine("**** Delete Permission {0} from {1} ****", permId, user.Id);
	
   var query = new SqlQuerySpec {
      QueryText = "SELECT * FROM c WHERE c.id = @id", 
      Parameters = new SqlParameterCollection {
         new SqlParameter { Name = "@id", Value = permId }
      } 
   };
	
   Permission perm = client.CreatePermissionQuery(user.PermissionsLink, query)
      .AsEnumerable().First();  
   await client.DeletePermissionAsync(perm.SelfLink);  
   Console.WriteLine("Deleted permission {0} from user {1}", permId, user.Id); 
}

步骤 6 – 要删除权限,通过权限 ID 查询以获取 SelfLink,然后使用 SelfLink 删除权限。

接下来,让我们删除用户本身。让我们删除这两个用户。

await DeleteUser(client, "Alice"); 
await DeleteUser(client, "Tom");

以下是 DeleteUser 的实现。

private async static Task DeleteUser(DocumentClient client, string userId) {
   Console.WriteLine(); 
   Console.WriteLine("**** Delete User {0} in {1} ****", userId, database.Id);
	
   var query = new SqlQuerySpec { 
      QueryText = "SELECT * FROM c WHERE c.id = @id", 
      Parameters = new SqlParameterCollection {
         new SqlParameter { Name = "@id", Value = userId }
      } 
   };
	
   User user = client.CreateUserQuery(database.SelfLink, query).AsEnumerable().First();  
   await client.DeleteUserAsync(user.SelfLink);  
   Console.WriteLine("Deleted user {0} from database {1}", userId, database.Id); 
}

第 7 步– 首先查询以获取她的 SelfLink,然后调用 DeleteUserAsync 删除她的用户对象。

以下是我们调用上述所有任务的 CreateDocumentClient 任务的实现。

private static async Task CreateDocumentClient() {
   // Create a new instance of the DocumentClient
   using (var client = new DocumentClient(new Uri(EndpointUrl), AuthorizationKey)) {
      database = client.CreateDatabaseQuery("SELECT * FROM c WHERE c.id =
         'myfirstdb'").AsEnumerable().First();
			
      collection = client.CreateDocumentCollectionQuery(database.CollectionsLink,
         "SELECT * FROM c WHERE c.id = 'MyCollection'").AsEnumerable().First();
			
      ViewUsers(client);
		
      var alice = await CreateUser(client, "Alice");
      var tom = await CreateUser(client, "Tom");
      ViewUsers(client);
		
      ViewPermissions(client, alice);
      ViewPermissions(client, tom);
		
      string collectionLink = client.CreateDocumentCollectionQuery(database.SelfLink,
         "SELECT VALUE c._self FROM c WHERE c.id = 'MyCollection'")
         .AsEnumerable().First().Value;
			
      await CreatePermission(client, alice, "Alice Collection Access", PermissionMode.All,
         collectionLink);
			
      await CreatePermission(client, tom, "Tom Collection Access", PermissionMode.Read,
         collectionLink);
			
      ViewPermissions(client, alice);
      ViewPermissions(client, tom);
		
      await DeletePermission(client, alice, "Alice Collection Access");
      await DeletePermission(client, tom, "Tom Collection Access");
		
      await DeleteUser(client, "Alice");
      await DeleteUser(client, "Tom");
   }
}

编译并执行上述代码后,您将收到以下输出。

**** View Users in myfirstdb **** 
 
Total users in database myfirstdb: 0 
 
**** Create User Alice in myfirstdb **** 
Created new user 
          User ID: Alice 
      Resource ID: kV5oAC56NwA= 
        Self Link: dbs/kV5oAA==/users/kV5oAC56NwA=/ 
 Permissions Link: dbs/kV5oAA==/users/kV5oAC56NwA=/permissions/ 
        Timestamp: 12/17/2015 5:44:19 PM
		  
**** Create User Tom in myfirstdb **** 
Created new user 
          User ID: Tom 
      Resource ID: kV5oAALxKgA= 
        Self Link: dbs/kV5oAA==/users/kV5oAALxKgA=/ 
 Permissions Link: dbs/kV5oAA==/users/kV5oAALxKgA=/permissions/ 
        Timestamp: 12/17/2015 5:44:21 PM
		  
**** View Users in myfirstdb ****
  
User #1 
          User ID: Tom 
      Resource ID: kV5oAALxKgA= 
        Self Link: dbs/kV5oAA==/users/kV5oAALxKgA=/ 
 Permissions Link: dbs/kV5oAA==/users/kV5oAALxKgA=/permissions/ 
        Timestamp: 12/17/2015 5:44:21 PM 
		  
User #2 
          User ID: Alice 
      Resource ID: kV5oAC56NwA= 
        Self Link: dbs/kV5oAA==/users/kV5oAC56NwA=/ 
 Permissions Link: dbs/kV5oAA==/users/kV5oAC56NwA=/permissions/ 
        Timestamp: 12/17/2015 5:44:19 PM
		  
Total users in database myfirstdb: 2
  
**** View Permissions for Alice **** 
 
Total permissions for Alice: 0  

**** View Permissions for Tom **** 
 
Total permissions for Tom: 0  

**** Create Permission Alice Collection Access for Alice **** 
Created new permission 
    Permission ID: Alice Collection Access 
      Resource ID: kV5oAC56NwDON1RduEoCAA== 
  Permission Mode: All
            Token: type=resource&ver=1&sig=zB6hfvvleC0oGGbq5cc67w==;Zt3Lx 
Ol14h8pd6/tyF1h62zbZKk9VwEIATIldw4ZyipQGW951kirueAKdeb3MxzQ7eCvDfvp7Y/ZxFpnip/D G 
JYcPyim5cf+dgLvos6fUuiKSFSul7uEKqp5JmJqUCyAvD7w+qt1Qr1PmrJDyAIgbZDBFWGe2VT9FaBH o 
PYwrLjRlnH0AxfbrR+T/UpWMSSHtLB8JvNFZNSH8hRjmQupuTSxCTYEC89bZ/pS6fNmNg8=; 
        Timestamp: 12/17/2015 5:44:28 PM
		  
**** Create Permission Tom Collection Access for Tom **** 
Created new permission 
    Permission ID: Tom Collection Access 
      Resource ID: kV5oAALxKgCMai3JKWdfAA== 
  Permission Mode: Read 
            Token: type=resource&ver=1&sig=ieBHKeyi6EY9ZOovDpe76w==;92gwq 
V4AxKaCJ2dLS02VnJiig/5AEbPcfo1xvOjR10uK3a3FUMFULgsaK8nzxdz6hLVCIKUj6hvMOTOSN8Lt 7 
i30mVqzpzCfe7JO3TYSJEI9D0/5HbMIEgaNJiCu0JPPwsjVecTytiLN56FHPguoQZ7WmUAhVTA0IMP6 p 
jQpLDgJ43ZaG4Zv3qWJiO689balD+egwiU2b7RICH4j6R66UVye+GPxq/gjzqbHwx79t54=; 
        Timestamp: 12/17/2015 5:44:30 PM
		  
**** View Permissions for Alice ****
  
Permission #1 
    Permission ID: Alice Collection Access 
      Resource ID: kV5oAC56NwDON1RduEoCAA== 
  Permission Mode: All 
            Token: type=resource&ver=1&sig=BSzz/VNe9j4IPJ9M31Mf4Q==;Tcq/B 
X50njB1vmANZ/4aHj/3xNkghaqh1OfV95JMi6j4v7fkU+gyWe3mJasO3MJcoop9ixmVnB+RKOhFaSxE l 
P37SaGuIIik7GAWS+dcEBWglMefc95L2YkeNuZsjmmW5b+a8ELCUg7N45MKbpzkp5BrmmGVJ7h4Z4pf D 
rdmehYLuxSPLkr9ndbOOrD8E3bux6TgXCsgYQscpIlJHSKCKHUHfXWBP2Y1LV2zpJmRjis=; 
        Timestamp: 12/17/2015 5:44:28 PM
		  
Total permissions for Alice: 1
  
**** View Permissions for Tom ****
Permission #1 
    Permission ID: Tom Collection Access 
      Resource ID: kV5oAALxKgCMai3JKWdfAA== 
  Permission Mode: Read 
            Token: type=resource&ver=1&sig=NPkWNJp1mAkCASE8KdR6PA==;ur/G2 
V+fDamBmzECux000VnF5i28f8WRbPwEPxD1DMpFPqYcu45wlDyzT5A5gBr3/R3qqYkEVn8bU+een6Gl j 
L6vXzIwsZfL12u/1hW4mJT2as2PWH3eadry6Q/zRXHAxV8m+YuxSzlZPjBFyJ4Oi30mrTXbBAEafZhA 5 
yvbHkpLmQkLCERy40FbIFOzG87ypljREpwWTKC/z8RSrsjITjAlfD/hVDoOyNJwX3HRaz4=; 
        Timestamp: 12/17/2015 5:44:30 PM
		  
Total permissions for Tom: 1
  
**** Delete Permission Alice Collection Access from Alice **** 
Deleted permission Alice Collection Access from user Alice
  
**** Delete Permission Tom Collection Access from Tom **** 
Deleted permission Tom Collection Access from user Tom
  
**** Delete User Alice in myfirstdb **** 
Deleted user Alice from database myfirstdb
  
**** Delete User Tom in myfirstdb **** 
Deleted user Tom from database myfirstdb

DocumentDB – 可视化数据

在本章中,我们将学习如何可视化存储在 DocumentDB 中的数据。Microsoft 提供了 Power BI Desktop 工具,可将您的数据转换为丰富的视觉效果。它还使您能够从各种数据源检索数据、合并和转换数据、创建强大的报表和可视化效果,以及将报表发布到 Power BI。

在最新版本的 Power BI Desktop 中,Microsoft 还添加了对 DocumentDB 的支持,您现在可以连接到您的 DocumentDB 帐户。您可以从链接下载此工具,https://powerbi.microsoft.com

让我们看一个示例,在该示例中我们将可视化上一章中导入的地震数据。

步骤 1 – 下载工具后,启动 Power BI 桌面。

启动 Power BI

第 2 步– 单击“获取数据”选项,该选项位于“外部数据”组下的“主页”选项卡上,它将显示“获取数据”页面。

点击获取数据

步骤 3 – 选择 Microsoft Azure DocumentDB(Beta)选项,然后单击“连接”按钮。

点击连接

第 4 步– 输入要从中可视化数据的 Azure DocumentDB 帐户、数据库和集合的 URL,然后按确定。

如果您是第一次连接到此端点,系统将提示您输入帐户密钥。

帐户密钥

第 5 步– 输入 Azure 门户上可用的每个 DocumentDB 帐户唯一的帐户密钥(主密钥),然后单击连接。

记录清单

当账户连接成功时,它会从指定的数据库中检索数据。预览窗格显示记录项目列表,文档在 Power BI 中表示为记录类型。

步骤 6 – 单击“编辑”按钮,将启动查询编辑器。

单击编辑按钮

步骤 7 – 在 Power BI 查询编辑器中,您应该在中央窗格中看到一个文档列,单击文档列标题右侧的扩展器并选择要显示的列。

单击扩展器

如您所见,我们将纬度和经度作为单独的列,但我们以纬度、经度坐标形式可视化数据。

步骤 8 – 为此,请单击“添加列”选项卡。

单击添加列

步骤 9 – 选择添加自定义列,将显示以下页面。

添加自定义列

步骤 10 – 指定新列名,假设 LatLong 以及将纬度和经度组合在以逗号分隔的一列中的公式。以下是公式。

Text.From([latitude])&", "&Text.From([longitude])

步骤 11 – 单击确定继续,您将看到添加了新列。

添加了新列

步骤 12 – 转到“主页”选项卡,然后单击“关闭并应用”选项。

点击关闭并申请

步骤 13 – 您可以通过将字段拖放到报告画布中来创建报告。您可以在右侧看到,有两个窗格 – 一个可视化窗格,另一个是字段窗格。

创建报告

让我们创建一个地图视图,显示每个地震的位置。

步骤 14 – 从可视化窗格中拖动地图视觉类型。

步骤 15 – 现在,将 LatLong 字段从 Fields 窗格拖放到 Visualizations 窗格中的 Location 属性。然后,将幅度字段拖放到 Values 属性。

步骤 16 – 将深度字段拖放到颜色饱和度属性。

景深

您现在将看到地图视觉效果,其中显示了一组指示每次地震位置的气泡。

觉得文章有用?

点个广告表达一下你的爱意吧 !😁