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