DocumentDB - 快速指南



DocumentDB - 简介

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

NoSQL 文档数据库

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

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

  • 将 NoSQL 数据库视为非关系型数据库更有帮助,因此 NoSQL 实际上表示非关系型。

NoSQL 数据库有不同类型,包括:

  • Azure 表存储。
  • 基于列的存储,例如 Cassandra。
  • 图形数据库,例如 NEO4。
  • 文档数据库,例如 MongoDB 和 Azure DocumentDB。

Azure DocumentDB

微软于 2015 年 4 月 8 日正式发布了 Azure DocumentDB,它当然可以被归类为典型的 NoSQL 文档数据库。它具有可扩展性,并使用无模式的 JSON 文档。

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

  • 它还提供始终如一的快速读写、模式灵活性以及能够根据需要轻松扩展数据库的能力。

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

  • DocumentDB 会在文档添加到数据库时自动索引文档中的每个属性。

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

DocumentDB – 定价

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

  • 集合不仅是扩展单元,而且是成本单元,因此在 DocumentDB 中,您按集合付费,每个集合的存储容量高达 10 GB。

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

  • 随着数据库规模的增长并超过 10 GB,您需要购买另一个集合来容纳额外的数据。

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

  • 您还可以将其完全扩展到 S3 并每月支付约 100 美元。

DocumentDB - 优势

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

无模式

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

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

SQL 语法

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

可调一致性

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

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

  • 强一致性
  • 有界陈旧
  • 会话一致性
  • 最终一致性

弹性伸缩

可扩展性是 NoSQL 的核心,而 DocumentDB 则提供了这一点。DocumentDB 已经证明了其可扩展性。

  • 像 Office OneNote 和 Xbox 这样的主要服务已经由 DocumentDB 支持,数据库包含数十 TB 的 JSON 文档,超过一百万活跃用户,并且始终如一地保持 99.95% 的可用性。

  • 您可以通过创建更多单元来弹性扩展 DocumentDB,并获得可预测的性能,以满足应用程序增长的需求。

完全托管

DocumentDB 作为 Azure 上运行的完全托管的基于云的平台即服务提供。

  • 您无需安装或管理任何内容。

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

  • 微软会完成所有这些工作并保持服务的运行。

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

DocumentDB - 环境设置

微软提供 Visual Studio 的免费版本,其中还包含 SQL Server,您可以从 https://www.visualstudio.com 下载

安装

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

Installer

步骤 2 − 单击“安装”按钮,它将启动安装过程。

Installation Process

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

Restart Now

步骤 4 − 关闭此对话框,如果需要,重新启动计算机。

步骤 5 − 现在从“开始”菜单打开 Visual Studio,这将打开以下对话框。第一次仅需准备一段时间。

open visual studio

完成后,您将看到 Visual Studio 的主窗口。

Main Window

步骤 6 − 让我们从“文件”→“新建”→“项目”创建新项目。

Create New Project

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

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

Right click on project

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

Manage NuGet Packages

步骤 10 − 通过单击“安装”按钮安装最新版本。

License Acceptance

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

Start Application

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

DocumentDB - 创建账户

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

步骤 1 − 如果您已有 Azure 订阅,请登录到联机 https://portal.azure.com,否则您需要先登录。

您将看到主仪表板。它是完全可自定义的,因此您可以根据需要排列这些磁贴,调整其大小,添加和删除您经常使用或不再使用的磁贴。

Dashbord

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

Select New Option

步骤 3 − 现在选择“数据 + 存储”>“Azure DocumentDB”选项,您将看到以下“新建 DocumentDB 帐户”部分。

Azure DocumentDB

我们需要想出一个全局唯一的名称(ID),它与 .documents.azure.com 组合后,就是我们 DocumentDB 帐户的公共可访问端点。我们在此帐户下创建的所有数据库都可以使用此端点通过互联网访问。

步骤 4 − 我们将其命名为 azuredocdbdemo,然后单击“资源组”→“new_resource”。

Resource Group

步骤 5 − 选择位置,即您希望此帐户托管在哪个 Microsoft 数据中心。选择位置并选择您的区域。

Select Location

步骤 6 − 选中“固定到仪表板”复选框,然后继续单击“创建”按钮。

Create Button

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

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

Account Dashboard

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

Created Documentdb

DocumentDB - 连接账户

当您开始针对 DocumentDB 进行编程时,第一步就是连接。因此,要连接到您的 DocumentDB 帐户,您需要两样东西:

  • 端点
  • 授权密钥

端点

端点是您 DocumentDB 帐户的 URL,它通过将您的 DocumentDB 帐户名称与 .documents.azure.com 组合来构建。让我们转到仪表板。

Endpoint

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

click on created DocumentDB

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

select keys option

授权密钥

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

主密钥

  • 使用主密钥,您几乎可以做任何事情。如果您愿意,可以使用主密钥删除整个数据库。

  • 因此,您绝对不希望共享主密钥或将其分发到客户端环境。作为额外的安全措施,最好经常更改它。

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

资源令牌

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

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

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

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

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

让我们转到第 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 门户,您将看到仪表板。

Login to portal

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

click on created DocumentDB

步骤 3 - 选择“添加数据库”选项并为您的数据库提供 ID。

Select Add Database

步骤 4 - 单击“确定”。

Database added

您可以看到数据库已添加。目前,它没有任何集合,但我们稍后可以添加集合,这些集合是将存储我们的 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 帐户,您现在将看到两个数据库。

Two Databases

您还可以使用 .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 帐户。为了演示目的,我在以下屏幕截图中添加了两个数据库。

Drop Databases

步骤 2 - 要删除任何数据库,您需要单击该数据库。让我们选择 tempdb,您将看到以下页面,选择“删除数据库”选项。

Delete Database

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

Confirmation Message

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

TempDB Deleted

您还可以使用 .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);
}

步骤 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 门户上的主仪表板。

Create Collection

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

myfirstdb

步骤 3 - 单击“添加集合”选项并为集合指定 ID。为不同的选项选择定价层。

Add Collection

步骤 4 - 让我们选择 S1 标准并单击“选择”→“确定”按钮。

select S1 Standard

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

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

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

步骤 2 - 要创建集合,首先通过其 ID 在 CreateDocumentClient 任务中检索 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,该方法还接受我们在此处用于设置新集合性能层的 options 参数,我们将其称为 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 帐户。为了演示目的,我在以下屏幕截图中添加了两个集合。

Delete Collection

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

Select Collection

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

Delete Collection Message

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

Collection Deleted

您还可以使用 .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 参数在此分配给此 SqlQuerySpec 的参数属性的 SqlParameterCollection 中定义。

然后,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 定价层 Families 集合。

Insert Document

步骤 2 − 选择 Families 集合,然后单击“创建文档”选项以打开“新建文档”边栏。

Families Collection

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

simple text editor

步骤 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
}

输入上述文档后,您将看到以下屏幕。

Document

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

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

还要注意文档的分层结构,它具有一些顶级属性,例如必需的 id、lastName 和 isRegistered,但它也具有嵌套属性。

例如,parents 属性作为 JSON 数组提供,如方括号所示。我们还有另一个用于 children 的数组,即使在此示例中数组中只有一个子项。

步骤 4 − 单击“保存”按钮保存文档,我们就创建了第一个文档。

如您所见,已对我们的 JSON 应用漂亮的格式设置,它将每个属性都放在自己的行上,并使用空格缩进以传达每个属性的嵌套级别。

Save Document

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

Retrieve Document

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

choose a Database

默认情况下,文档浏览器显示集合中未过滤的文档列表,但您也可以按 ID 搜索任何特定文档,或根据部分 ID 的通配符搜索搜索多个文档。

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

步骤 6 − 单击 ID 以查看文档。

Click on ID

使用 .NET SDK 创建文档

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

  • 文档与其他资源之间的一个主要区别是,当然,它们是无模式的。

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

  • 您还可以使用真实的 C# 对象或称为实体的对象,这些对象可能是您的业务域类。

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

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

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。我们返回一个带有 resource 属性的响应,在这种情况下,它表示具有其系统生成属性的新文档。

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

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。

使用门户查询文档

Azure 门户有一个查询浏览器,允许您对 DocumentDB 数据库运行任何 SQL 查询。

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

步骤 1 − 在数据库边栏中,单击以打开查询浏览器边栏。

Query Explorer Blade

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

Run Query

步骤 2 − 选择 Families 集合,该集合是之前使用门户创建的。

查询浏览器将打开此简单的查询 SELECT * FROM c,它只是从集合中检索所有文档。

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

Document in Result Blade

使用 .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,这将在一行代码中发出必要的请求以拉取所有结果。

步骤 2 − 相反,调用 AsDocumentQuery,此方法返回一个具有 HasMoreResults 属性的查询对象。

步骤 3 − 如果 HasMoreResults 为 true,则调用 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 门户,您可以通过在文档浏览器中打开文档并在编辑器中将其更新为文本文件来轻松更新文档。

Update Document

单击“保存”按钮。现在,当您需要使用 .Net SDK 更改文档时,您只需替换它即可。您无需删除并重新创建它,除了繁琐之外,这还会更改资源 ID,而您在修改文档时不希望这样做。以下是如何使用 .Net SDK 更新文档的步骤。

让我们看看以下 ReplaceDocuments 任务,我们将在其中查询 isNew 属性为 true 的文档,但我们将无法获取任何文档,因为没有任何文档。因此,让我们修改我们之前添加的文档,即名称以“新客户”开头的那些文档。

步骤 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 门户,您可以通过在文档浏览器中打开文档并单击“删除”选项来轻松删除任何文档。

Delete Document

Delete Document Dialog

它将显示确认消息。现在按下“是”按钮,您将看到该文档不再存在于您的 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 数组的形式提供,如方括号所示。

  • 我们还有一个用于 children 的数组,即使在本例中数组中只有一个子项。因此,这就是您在文档中模拟一对多关系的方式。

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

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

  • 对于 location 属性,我们将三个相关属性(州、县和城市)组合到一个对象中。

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

嵌入数据

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

  • 规范化数据通常涉及获取一个实体(例如客户)并将其分解成离散的数据块,例如联系方式和地址。

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

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

{
   "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 集合,我们将其存储在此私有变量 collection 中,以便在整个类中都可以访问它。

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。我们得到一个包含 resource 属性的响应,在这种情况下,该属性表示具有其系统生成属性的新文档。

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

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

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,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。尽管我们的 customer 对象确实有一个 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 对象表示法是一种轻量级的基于文本的开放标准,旨在用于人类可读的数据交换,并且易于机器解析和生成。JSON 是 DocumentDB 的核心。我们通过网络传输 JSON,我们将 JSON 存储为 JSON,并索引 JSON 树,允许查询完整的 JSON 文档。

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

序号 类型和描述
1

数字

JavaScript 中的双精度浮点格式

2

字符串

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

3

布尔值

真或假

4

数组

值的排序序列

5

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

6

对象

键值对的无序集合

7

空格

它可以在任何一对标记之间使用

8

让我们看一个简单的 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 关键字,这使得查询运行速度更快并消耗更少的资源,提高了查询运算符的限制,并在 .NET SDK 中添加了对其他 LINQ 运算符的支持。

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

Limiting Records

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

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

SELECT * FROM c 
WHERE c.magnitude > 2.5 

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

Retrieved Records

步骤 2 - 现在使用相同查询的 TOP 关键字。这里我们指定了 TOP 关键字,'2' 表示我们只需要两条记录。

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

步骤 3 - 现在运行此查询,您将看到只检索了两条记录。

Two records retrieved

类似地,您可以在使用 .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 支持使用 JSON 文档上的 SQL 查询文档。您可以使用查询中的 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 只索引添加到集合的每个文档的这些特定部分来减少索引开销。

自动索引

让我们看一个自动索引的简单例子。

步骤 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 的文档提供任何特殊请求选项,因为集合的索引策略,此文档将不会被索引。

步骤 3 − 现在当我们为 Mark 添加第二个文档时,我们使用带有 IndexingDirective.Include 的 RequestOptions 来告诉 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 中容纳大量纯文本文档。

在可扩展性方面,存储并不是唯一需要考虑的问题。集合上可用的最大吞吐量是每秒 2500 个请求单元,这是使用 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 值进行编码。否则,第一个范围将不会匹配任何 M,除了单个 M,并且第二个范围中的 Z 也是如此。因此,您可以将此处的编码值视为匹配分区键的通配符。

步骤 4 − 创建解析器后,使用当前 DocumentClient 为数据库注册它。为此,只需将其分配给 PartitionResolver 的 dictionary 属性。

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

现在让我们创建一些文档。首先,我们将为 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 文件。

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

Executable Files

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

让我们启动 GUI 版本。

Launch GUI version

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

Source Information page

在这里,您可以配置您的数据源,并且您可以从下拉菜单中看到许多受支持的选择。

Specify Source Information

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

使用 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 方法,在本例中,我们调用 CreateUserAsync 方法,并传入数据库的 SelfLink 和 userDefinition。我们从其 resource 属性获取结果,并从中获取新创建的用户对象。

现在,让我们在数据库中查看这两个新用户。

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 和一个 permissionMode(Permission.All 或 Permission.Read),以及受权限保护的资源的 SelfLink。

步骤 5 − 调用 CreatePermissionAsync 并从结果的 resource 属性中获取创建的权限。

要查看创建的权限,以下是 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 Desktop。

launch Power BI

步骤 2 − 点击“获取数据”选项,该选项位于“主页”选项卡下的“外部数据”组中,它将显示“获取数据”页面。

Click Get Data

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

Click Connect

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

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

Account Key

步骤 5 − 输入帐户密钥(主密钥),该密钥对于 Azure 门户上可用的每个 DocumentDB 帐户都是唯一的,然后点击“连接”。

list of Record

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

步骤 6 − 点击“编辑”按钮,这将启动查询编辑器。

Click Edit button

步骤 7 − 在 Power BI 查询编辑器中,您应该在中间窗格中看到一个“文档”列,点击“文档”列标题右侧的展开器,然后选择您要显示的列。

Click on expander

您可以看到我们已将纬度和经度作为单独的列,但我们希望以纬度、经度坐标的形式可视化数据。

步骤 8 − 为此,点击“添加列”选项卡。

click Add Column

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

Add Custom Column

步骤 10 − 指定新列名,例如 LatLong,以及将纬度和经度组合到一个以逗号分隔的列中的公式。以下为公式。

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

步骤 11 − 点击“确定”继续,您将看到已添加新列。

New Column added

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

click close and apply

步骤 13 − 您可以通过将字段拖放到报表画布中来创建报表。您可以在右侧看到两个窗格:一个“可视化效果”窗格,另一个是“字段”窗格。

create reports

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

步骤 14 − 从“可视化效果”窗格中拖动地图视觉类型。

步骤 15 − 现在,将“LatLong”字段从“字段”窗格拖放到“可视化效果”窗格中的“位置”属性。然后,将“强度”字段拖放到“值”属性。

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

Depth Field

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

广告