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 屬性,我們將三個相關的屬性(state、county 和 city)組合到一個物件中。

  • 以這種方式嵌入物件而不是嵌入物件的陣列類似於在關係資料庫中兩個單獨表中的兩行之間具有一對一關係。

嵌入資料

當您開始在文件儲存(例如 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": "mark.upston@xyz.com"}, 
      {"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,因此它具有所有通用的 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,它是另一個巢狀物件。

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
廣告

© . All rights reserved.