DocumentDB - Modellazione dati
Sebbene i database senza schema, come DocumentDB, rendano estremamente semplice accettare le modifiche al modello di dati, dovresti comunque dedicare un po 'di tempo a pensare ai tuoi dati.
Hai molte opzioni. Naturalmente, puoi lavorare solo con grafici a oggetti JSON o anche stringhe non elaborate di testo JSON, ma puoi anche utilizzare oggetti dinamici che ti consentono di collegarti a proprietà in fase di esecuzione senza definire una classe in fase di compilazione.
È inoltre possibile lavorare con oggetti C # reali, o entità come vengono chiamate, che potrebbero essere le classi del dominio aziendale.
Relazioni
Diamo un'occhiata alla struttura gerarchica del documento. Ha alcune proprietà di primo livello come l'id richiesto, lastName e isRegistered, ma ha anche proprietà nidificate.
{
"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
}
Ad esempio, la proprietà parent viene fornita come array JSON come indicato dalle parentesi quadre.
Abbiamo anche un altro array per i bambini, anche se c'è un solo figlio nell'array in questo esempio. Quindi è così che modellate l'equivalente delle relazioni uno-a-molti all'interno di un documento.
Si utilizzano semplicemente array in cui ogni elemento dell'array potrebbe essere un valore semplice o un altro oggetto complesso, anche un altro array.
Quindi una famiglia può avere più genitori e più figli e se guardi gli oggetti figlio, hanno la proprietà di un animale domestico che è essa stessa un array annidato per una relazione uno-molti tra bambini e animali domestici.
Per la proprietà location, stiamo combinando tre proprietà correlate, lo stato, la contea e la città in un oggetto.
Incorporare un oggetto in questo modo piuttosto che incorporare un array di oggetti è simile ad avere una relazione uno a uno tra due righe in tabelle separate in un database relazionale.
Incorporamento dei dati
Quando inizi a modellare i dati in un archivio di documenti, come DocumentDB, prova a trattare le tue entità come documenti autonomi rappresentati in JSON. Quando lavoriamo con database relazionali, normalizziamo sempre i dati.
La normalizzazione dei dati in genere comporta l'acquisizione di un'entità, come un cliente, e la scomposizione in parti di dati discrete, come dettagli di contatto e indirizzi.
Per leggere un cliente, con tutti i suoi dettagli di contatto e indirizzi, è necessario utilizzare JOINS per aggregare efficacemente i dati in fase di esecuzione.
Ora diamo un'occhiata a come modellare gli stessi dati di un'entità autonoma in un database di documenti.
{
"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}
]
}
Come puoi vedere, abbiamo denormalizzato il record del cliente in cui tutte le informazioni del cliente sono incorporate in un unico documento JSON.
In NoSQL abbiamo uno schema gratuito, quindi puoi aggiungere anche i dettagli di contatto e gli indirizzi in diversi formati. In NoSQL, puoi recuperare un record del cliente dal database in una singola operazione di lettura. Allo stesso modo, anche l'aggiornamento di un record è una singola operazione di scrittura.
Di seguito sono riportati i passaggi per creare documenti utilizzando .Net SDK.
Step 1- Istanziare DocumentClient. Quindi interrogheremo il database myfirstdb e interrogheremo anche la raccolta MyCollection, che memorizziamo in questa raccolta di variabili private in modo che sia accessibile in tutta la classe.
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 - Crea alcuni documenti nell'attività 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();
}
Il primo documento verrà generato da questo oggetto dinamico. Potrebbe sembrare JSON, ma ovviamente non lo è. Questo è codice C # e stiamo creando un vero oggetto .NET, ma non esiste una definizione di classe. Invece le proprietà vengono dedotte dal modo in cui l'oggetto viene inizializzato. Puoi anche notare che non abbiamo fornito una proprietà Id per questo documento.
Step 3 - Ora diamo uno sguardo al CreateDocument e sembra lo stesso modello che abbiamo visto per la creazione di database e raccolte.
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- Questa volta chiamiamo CreateDocumentAsync specificando il SelfLink della raccolta a cui vogliamo aggiungere il documento. Riceviamo una risposta con una proprietà della risorsa che, in questo caso, rappresenta il nuovo documento con le sue proprietà generate dal sistema.
Nella seguente attività CreateDocuments, abbiamo creato tre documenti.
Nel primo documento, l'oggetto Document è una classe definita nell'SDK che eredita dalla risorsa e quindi ha tutte le proprietà comuni delle risorse, ma include anche le proprietà dinamiche che definiscono il documento senza schema stesso.
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();
}
Questo secondo documento funziona solo con una stringa JSON non elaborata. Ora entriamo in un overload per CreateDocument che utilizza JavaScriptSerializer per de-serializzare la stringa in un oggetto, che poi passa allo stesso metodo CreateDocument che abbiamo usato per creare il primo documento.
Nel terzo documento abbiamo utilizzato l'oggetto C # Customer definito nella nostra applicazione.
Diamo un'occhiata a questo cliente, ha una proprietà Id e address in cui l'indirizzo è un oggetto nidificato con le sue proprietà inclusa la posizione, che è ancora un altro oggetto nidificato.
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; }
}
}
Disponiamo anche di attributi di proprietà JSON perché vogliamo mantenere le convenzioni appropriate su entrambi i lati del recinto.
Quindi creo semplicemente il mio oggetto Nuovo cliente insieme ai suoi oggetti figlio nidificati e chiamo ancora una volta CreateDocument. Sebbene il nostro oggetto cliente abbia una proprietà Id, non abbiamo fornito un valore per esso e quindi DocumentDB ne ha generato uno basato sul GUID, proprio come ha fatto per i due documenti precedenti.
Quando il codice precedente viene compilato ed eseguito, riceverai il seguente output.
**** 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