DocumentDB-データモデリング
DocumentDBのようなスキーマフリーのデータベースを使用すると、データモデルへの変更を非常に簡単に受け入れることができますが、それでもデータについて考えるのに時間をかける必要があります。
あなたにはたくさんの選択肢があります。当然、JSONオブジェクトグラフやJSONテキストの生の文字列を操作することもできますが、コンパイル時にクラスを定義せずに実行時にプロパティにバインドできる動的オブジェクトを使用することもできます。
また、実際のC#オブジェクト、または呼び出されたエンティティ(ビジネスドメインクラスの場合もあります)を操作することもできます。
関係
ドキュメントの階層構造を見てみましょう。必要なID、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
}
たとえば、parentsプロパティは、角括弧で示されているようにJSON配列として提供されます。
この例では配列に子が1つしかない場合でも、子用の別の配列もあります。したがって、これは、ドキュメント内の1対多の関係に相当するものをモデル化する方法です。
配列の各要素が単純な値または別の複雑なオブジェクト、さらには別の配列である可能性がある配列を使用するだけです。
したがって、1つのファミリに複数の親と複数の子を含めることができ、子オブジェクトを見ると、子とペットの間の1対多の関係のためのネストされた配列であるペットのプロパティがあります。
locationプロパティでは、州、郡、市の3つの関連プロパティを1つのオブジェクトに結合しています。
オブジェクトの配列を埋め込むのではなく、この方法でオブジェクトを埋め込むことは、リレーショナルデータベースの別々のテーブルにある2つの行の間に1対1の関係を持つことに似ています。
データの埋め込み
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では、1回の読み取り操作でデータベースから顧客レコードを取得できます。同様に、レコードの更新も1回の書き込み操作です。
以下は、.NetSDKを使用してドキュメントを作成する手順です。
Step 1−DocumentClientをインスタンス化します。次に、myfirstdbデータベースをクエリし、MyCollectionコレクションもクエリします。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−今回は、ドキュメントを追加するコレクションのSelfLinkを指定してCreateDocumentAsyncを呼び出します。この場合、システムで生成されたプロパティを持つ新しいドキュメントを表すリソースプロパティを持つ応答が返されます。
次のCreateDocumentsタスクでは、3つのドキュメントを作成しました。
最初のドキュメントでは、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();
}
この2番目のドキュメントは、生のJSON文字列でのみ機能します。次に、JavaScriptSerializerを使用して文字列をオブジェクトに逆シリアル化するCreateDocumentのオーバーロードにステップインし、最初のドキュメントの作成に使用したのと同じCreateDocumentメソッドに渡します。
3番目のドキュメントでは、アプリケーションで定義されているC#オブジェクトCustomerを使用しました。
この顧客を見てみましょう。Idとaddressプロパティがあり、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をもう一度呼び出します。顧客オブジェクトにはIdプロパティがありますが、その値を指定しなかったため、DocumentDBは、前の2つのドキュメントの場合と同様に、GUIDに基づいてIDプロパティを生成しました。
上記のコードをコンパイルして実行すると、次の出力が返されます。
**** 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