Учебники

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

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

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

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

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

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

Отношения

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

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

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

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

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

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

Например, свойство parent предоставляется в виде массива JSON, обозначенного квадратными скобками.

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

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

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

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

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

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

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

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

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

Для нормализации ваших данных обычно требуется взять объект, например клиента, и разбить его на отдельные части данных, такие как контактные данные и адреса.

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

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

{
   "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, которую мы храним в этой коллекции частных переменных, чтобы она была доступна для всего класса.

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

}

Шаг 2 — Создайте несколько документов в задаче CreateDocuments.

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

Первый документ будет создан из этого динамического объекта. Это может выглядеть как JSON, но, конечно, это не так. Это код C #, и мы создаем настоящий объект .NET, но нет определения класса. Вместо этого свойства выводятся из способа инициализации объекта. Вы также можете заметить, что мы не предоставили свойство Id для этого документа.

Шаг 3 — Теперь давайте посмотрим на CreateDocument, и он выглядит так же, как мы видели при создании баз данных и коллекций.

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

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

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

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

В первом документе объект 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, который определен в нашем приложении.

Этот второй документ работает только с необработанной строкой JSON. Теперь мы вступаем в перегрузку для CreateDocument, который использует JavaScriptSerializer для десериализации строки в объект, который затем передается тому же методу CreateDocument, который мы использовали для создания первого документа.

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

Давайте посмотрим на этого клиента, у него есть свойство Id и 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. Хотя у нашего объекта customer есть свойство Id, мы не указали его значение, поэтому DocumentDB сгенерировал его на основе GUID, как и в предыдущих двух документах.

Когда приведенный выше код скомпилирован и выполнен, вы получите следующий вывод.