DocumentDB - Моделирование данных

Хотя базы данных без схемы, такие как DocumentDB, упрощают внесение изменений в вашу модель данных, вам все же следует потратить некоторое время на размышления о своих данных.

  • У вас есть много вариантов. Естественно, вы можете просто работать с графами объектов JSON или даже с необработанными строками текста JSON, но вы также можете использовать динамические объекты, которые позволяют выполнять привязку к свойствам во время выполнения без определения класса во время компиляции.

  • Вы также можете работать с реальными объектами C # или Entities, как они называются, которые могут быть классами вашей бизнес-области.

Отношения

Давайте посмотрим на иерархическую структуру документа. Он имеет несколько свойств верхнего уровня, таких как требуемый идентификатор, а также 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 
}
  • Например, свойство parent предоставляется в виде массива JSON, обозначенного квадратными скобками.

  • У нас также есть еще один массив для дочерних элементов, хотя в этом примере в массиве только один дочерний элемент. Вот как вы моделируете в документе эквивалент отношений «один ко многим».

  • Вы просто используете массивы, где каждый элемент в массиве может быть простым значением или другим сложным объектом, даже другим массивом.

  • Таким образом, одна семья может иметь несколько родителей и несколько детей, и если вы посмотрите на дочерние объекты, у них есть свойство домашнего животного, которое само по себе является вложенным массивом для одно-многих отношений между детьми и домашними животными.

  • Для свойства местоположения мы объединяем три связанных свойства: штат, округ и город в объект.

  • Встраивание объекта таким образом, а не встраивание массива объектов аналогично взаимно однозначному отношению между двумя строками в отдельных таблицах в реляционной базе данных.

Встраивание данных

Когда вы начинаете моделировать данные в хранилище документов, таком как DocumentDB, попробуйте рассматривать свои сущности как автономные документы, представленные в JSON. При работе с реляционными базами данных мы всегда нормализуем данные.

  • Нормализация данных обычно включает взятие объекта, например клиента, и разбиение его на отдельные фрагменты данных, такие как контактные данные и адреса.

  • Чтобы прочитать клиента со всеми его контактными данными и адресами, вам необходимо использовать JOINS для эффективного агрегирования ваших данных во время выполнения.

Теперь давайте посмотрим, как мы моделируем те же данные как автономную сущность в базе данных документов.

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

Как видите, мы денормализовали запись о клиенте, где вся информация о клиенте встроена в один документ JSON.

В NoSQL у нас есть бесплатная схема, поэтому вы также можете добавлять контактные данные и адреса в другом формате. В NoSQL вы можете получить запись о клиенте из базы данных за одну операцию чтения. Точно так же обновление записи также является одной операцией записи.

Ниже приведены шаги по созданию документов с помощью .Net SDK.

Step 1- Создать экземпляр DocumentClient. Затем мы будем запрашивать базу данных myfirstdb, а также запрашивать коллекцию MyCollection, которую мы храним в этой частной коллекции переменных, чтобы она была доступна для всего класса.

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

}

Step 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 для этого документа.

Step 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; 
}

Step 4- На этот раз мы вызываем CreateDocumentAsync, указывая SelfLink коллекции, в которую мы хотим добавить документ. Мы получаем ответ со свойством ресурса, которое в данном случае представляет новый документ с его свойствами, созданными системой.

В следующей задаче CreateDocuments мы создали три документа.

  • В первом документе объект Document - это определенный класс в SDK, который наследуется от ресурса, поэтому он имеет все общие свойства ресурса, но также включает динамические свойства, которые определяют сам документ без схемы.

private async static Task CreateDocuments(DocumentClient client) {
   Console.WriteLine(); 
   Console.WriteLine("**** Create Documents ****"); 
   Console.WriteLine();
	
   dynamic document1Definition = new {
      name = "New Customer 1", address = new {
         addressType = "Main Office", 
         addressLine1 = "123 Main Street", 
         location = new {
            city = "Brooklyn", stateProvinceName = "New York" 
         }, 
         postalCode = "11229", 
         countryRegionName = "United States" 
      }, 
   };
	
   Document document1 = await CreateDocument(client, document1Definition); 
   Console.WriteLine("Created document {0} from dynamic object", document1.Id); 
   Console.WriteLine();
	
   var document2Definition = @" {
      ""name"": ""New Customer 2"", 
		
      ""address"": { 
         ""addressType"": ""Main Office"", 
         ""addressLine1"": ""123 Main Street"", 
         ""location"": { 
            ""city"": ""Brooklyn"", ""stateProvinceName"": ""New York"" 
         }, 
         ""postalCode"": ""11229"", 
         ""countryRegionName"": ""United States"" 
      } 
   }"; 
	
   Document document2 = await CreateDocument(client, document2Definition); 
   Console.WriteLine("Created document {0} from JSON string", document2.Id);
   Console.WriteLine();
	
   var document3Definition = new Customer {
      Name = "New Customer 3", 
		
      Address = new Address {
         AddressType = "Main Office", 
         AddressLine1 = "123 Main Street", 
         Location = new Location {
            City = "Brooklyn", StateProvinceName = "New York" 
         }, 
         PostalCode = "11229", 
         CountryRegionName = "United States" 
      }, 
   };
	
   Document document3 = await CreateDocument(client, document3Definition); 
   Console.WriteLine("Created document {0} from typed object", document3.Id); 
   Console.WriteLine(); 
}
  • Этот второй документ работает только с необработанной строкой JSON. Теперь мы переходим к перегрузке для CreateDocument, которая использует JavaScriptSerializer для десериализации строки в объект, который затем передается тому же методу CreateDocument, который мы использовали для создания первого документа.

  • В третьем документе мы использовали объект C # Customer, который определен в нашем приложении.

Давайте посмотрим на этого клиента, у него есть свойства Id и address, где адрес - это вложенный объект со своими собственными свойствами, включая местоположение, которое является еще одним вложенным объектом.

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