DocumentDB - Modélisation des données

Bien que les bases de données sans schéma, telles que DocumentDB, facilitent la prise en compte des modifications de votre modèle de données, vous devriez quand même passer un peu de temps à réfléchir à vos données.

  • Tu as beaucoup d'options. Naturellement, vous pouvez simplement travailler des graphiques d'objets JSON ou même des chaînes brutes de texte JSON, mais vous pouvez également utiliser des objets dynamiques qui vous permettent de vous lier à des propriétés au moment de l'exécution sans définir de classe au moment de la compilation.

  • Vous pouvez également travailler avec de vrais objets C #, ou Entités comme ils sont appelés, qui peuvent être vos classes de domaine métier.

Des relations

Jetons un coup d'œil à la structure hiérarchique du document. Il a quelques propriétés de premier niveau comme l'id requis, ainsi que lastName et isRegistered, mais il a également des propriétés imbriquées.

{ 
   "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 
}
  • Par exemple, la propriété parents est fournie sous forme de tableau JSON comme indiqué par les crochets.

  • Nous avons également un autre tableau pour les enfants, même s'il n'y a qu'un seul enfant dans le tableau dans cet exemple. C'est ainsi que vous modélisez l'équivalent des relations un-à-plusieurs dans un document.

  • Vous utilisez simplement des tableaux où chaque élément du tableau peut être une valeur simple ou un autre objet complexe, voire un autre tableau.

  • Ainsi, une famille peut avoir plusieurs parents et plusieurs enfants et si vous regardez les objets enfants, ils ont la propriété d'un animal qui est lui-même un tableau imbriqué pour une relation un à plusieurs entre les enfants et les animaux domestiques.

  • Pour la propriété location, nous combinons trois propriétés associées, l'état, le comté et la ville dans un objet.

  • Incorporer un objet de cette façon plutôt que d'incorporer un tableau d'objets est similaire à avoir une relation un-à-un entre deux lignes dans des tables séparées dans une base de données relationnelle.

Incorporer des données

Lorsque vous commencez à modéliser des données dans un magasin de documents, tel que DocumentDB, essayez de traiter vos entités comme des documents autonomes représentés dans JSON. Lorsque nous travaillons avec des bases de données relationnelles, nous normalisons toujours les données.

  • Normaliser vos données implique généralement de prendre une entité, telle qu'un client, et de la décomposer en éléments de données discrets, comme les coordonnées et les adresses.

  • Pour lire un client, avec toutes ses coordonnées et adresses, vous devez utiliser JOINS pour agréger efficacement vos données au moment de l'exécution.

Voyons maintenant comment nous modéliserons les mêmes données qu'une entité autonome dans une base de données de documents.

{
   "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} 
   ]
}

Comme vous pouvez le constater, nous avons dénormalisé l'enregistrement client où toutes les informations du client sont intégrées dans un seul document JSON.

Dans NoSQL, nous avons un schéma gratuit, vous pouvez donc également ajouter des coordonnées et des adresses dans un format différent. Dans NoSQL, vous pouvez récupérer un enregistrement client de la base de données en une seule opération de lecture. De même, la mise à jour d'un enregistrement est également une opération d'écriture unique.

Voici les étapes de création de documents à l'aide du SDK .Net.

Step 1- Instanciez DocumentClient. Ensuite, nous interrogerons la base de données myfirstdb et également la collection MyCollection, que nous stockons dans cette collection de variables privées afin qu'elle soit accessible dans toute 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 - Créez des documents dans la tâche 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(); 
}

Le premier document sera généré à partir de cet objet dynamique. Cela peut ressembler à JSON, mais bien sûr ce n'est pas le cas. C'est du code C # et nous créons un véritable objet .NET, mais il n'y a pas de définition de classe. Au lieu de cela, les propriétés sont déduites de la façon dont l'objet est initialisé. Vous pouvez également remarquer que nous n'avons pas fourni de propriété Id pour ce document.

Step 3 - Jetons maintenant un coup d'œil au CreateDocument et il ressemble au même modèle que nous avons vu pour la création de bases de données et de collections.

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- Cette fois, nous appelons CreateDocumentAsync en spécifiant le SelfLink de la collection à laquelle nous voulons ajouter le document. Nous obtenons une réponse avec une propriété de ressource qui, dans ce cas, représente le nouveau document avec ses propriétés générées par le système.

Dans la tâche CreateDocuments suivante, nous avons créé trois documents.

  • Dans le premier document, l'objet Document est une classe définie dans le SDK qui hérite de la ressource et possède donc toutes les propriétés de ressource communes, mais il inclut également les propriétés dynamiques qui définissent le document sans schéma lui-même.

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(); 
}
  • Ce deuxième document fonctionne uniquement avec une chaîne JSON brute. Nous entrons maintenant dans une surcharge pour CreateDocument qui utilise JavaScriptSerializer pour dé-sérialiser la chaîne en un objet, qu'il transmet ensuite à la même méthode CreateDocument que nous avons utilisée pour créer le premier document.

  • Dans le troisième document, nous avons utilisé l'objet C # Customer qui est défini dans notre application.

Jetons un coup d'œil à ce client, il a une propriété Id et adresse où l'adresse est un objet imbriqué avec ses propres propriétés, y compris l'emplacement, qui est encore un autre objet imbriqué.

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

Nous avons également des attributs de propriété JSON en place, car nous souhaitons maintenir les conventions appropriées des deux côtés de la clôture.

Je crée donc simplement mon objet Nouveau client avec ses objets enfants imbriqués et j'appelle à nouveau CreateDocument. Bien que notre objet client ait une propriété Id, nous n'avons pas fourni de valeur pour lui et DocumentDB en a donc généré une basée sur le GUID, tout comme il l'a fait pour les deux documents précédents.

Lorsque le code ci-dessus est compilé et exécuté, vous recevrez la sortie suivante.

**** 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