Documentdb 简明教程
DocumentDB - Data Modeling
虽然无模式数据库(如DocumentDB)让您非常容易地接受对数据模型的更改,但您仍然应该花一些时间考虑您的数据。
While schema-free databases, like DocumentDB, make it super easy to embrace changes to your data model, you should still spend some time thinking about your data.
-
You have a lot of options. Naturally, you can just work JSON object graphs or even raw strings of JSON text, but you can also use dynamic objects that lets you bind to properties at runtime without defining a class at compile time.
-
You can also work with real C# objects, or Entities as they are called, which might be your business domain classes.
Relationships
让我们看一下文档的层次结构。它有一些顶级属性,例如必需的id,以及lastName和isRegistered,但它还具有嵌套属性。
Let’s take a look at the document’s hierarchal structure. It has a few top-level properties like the required id, as well as lastName and isRegistered, but it also has nested properties.
{
"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
}
-
For instance, the parents property is supplied as a JSON array as denoted by the square brackets.
-
We also have another array for children, even though there’s only one child in the array in this example. So this is how you model the equivalent of one-to-many relationships within a document.
-
You simply use arrays where each element in the array could be a simple value or another complex object, even another array.
-
So one family can have multiple parents and multiple children and if you look at the child objects, they have a pet’s property that is itself a nested array for a oneto-many relationship between children and pets.
-
For the location property, we’re combining three related properties, the state, county, and city into an object.
-
Embedding an object this way rather than embedding an array of objects is similar to having a one-to-one relationship between two rows in separate tables in a relational database.
Embedding Data
当您开始在文档存储中(如DocumentDB)对数据建模时,请尝试将您的实体视为JSON中表示的自包含文档。当使用关系数据库时,我们总是对数据进行规范化。
When you start modeling data in a document store, such as DocumentDB, try to treat your entities as self-contained documents represented in JSON. When working with relational databases, we always normalize data.
-
Normalizing your data typically involves taking an entity, such as a customer, and breaking it down into discreet pieces of data, like contact details and addresses.
-
To read a customer, with all their contact details and addresses, you need to use JOINS to effectively aggregate your data at run time.
现在让我们来看看如何在文档数据库中将相同的数据建模为自包含的实体。
Now let’s take a look at how we would model the same data as a self-contained entity in a document database.
{
"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文档中。
As you can see that we have denormalized the customer record where all the information of the customer is embedded into a single JSON document.
在NoSQL中,我们有免费模式,因此您也可以以不同的格式添加联系方式和地址。在NoSQL中,您可以通过一次读取操作从数据库中检索客户记录。同样,更新记录也只是一次写入操作。
In NoSQL we have a free schema, so you can add contact details and addresses in different format as well. In NoSQL, you can retrieve a customer record from the database in a single read operation. Similarly, updating a record is also a single write operation.
以下是用.Net SDK创建文档的步骤。
Following are the steps to create documents using .Net SDK.
Step 1 − 实例化DocumentClient。然后我们将查询myfirstdb数据库,也查询MyCollection集合,我们将该集合存储在这个private变量集合中,以便整个类中都能访问它。
Step 1 − Instantiate DocumentClient. Then we will query for the myfirstdb database and also query for MyCollection collection, which we store in this private variable collection so that’s it’s accessible throughout the class.
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 任务中创建一些文档。
Step 2 − Create some documents in CreateDocuments task.
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 属性。
The first document will be generated from this dynamic object. This might look like JSON, but of course it isn’t. This is C# code and we’re creating a real .NET object, but there’s no class definition. Instead the properties are inferred from the way the object is initialized. You can notice also that we haven’t supplied an Id property for this document.
Step 3 - 现在让我们看看 CreateDocument,它看起来与我们看到的用于创建数据库和集合的模式相同。
Step 3 − Now let’s take a look at the CreateDocument and it looks like the same pattern we saw for creating databases and 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 - 这次,我们调用 CreateDocumentAsync,指定要向其中添加文档的集合的 SelfLink。我们收到响应,该响应具有 resource 属性,在此情况下,它表示具有其系统生成属性的新文档。
Step 4 − This time we call CreateDocumentAsync specifying the SelfLink of the collection we want to add the document to. We get back a response with a resource property that, in this case, represents the new document with its system-generated properties.
在以下 CreateDocuments 任务中,我们创建了三个文档。
In the following CreateDocuments task, we have created three documents.
-
In the first document, the Document object is a defined class in the SDK that inherits from resource and so it has all the common resource properties, but it also includes the dynamic properties that define the schema-free document itself.
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();
}
-
This second document just works with a raw JSON string. Now we step into an overload for CreateDocument that uses the JavaScriptSerializer to de-serialize the string into an object, which it then passes on to the same CreateDocument method that we used to create the first document.
-
In the third document, we have used the C# object Customer which is defined in our application.
让我们看看这个客户,它具有一个 Id 和 address 属性,其中 address 是一个嵌套对象,具有自己的属性,包括 location,它还是另一个嵌套对象。
Let’s take a look at this customer, it has an Id and address property where the address is a nested object with its own properties including location, which is yet another nested object.
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 属性属性,因为我们希望在双方面都保持适当的约定。
We also have JSON property attributes in place because we want to maintain proper conventions on both sides of the fence.
所以我只需创建我的 New Customer 对象及其嵌套子对象,并再次调用 CreateDocument。尽管我们的客户对象确实有一个 Id 属性,但我们没有为它提供值,因此 DocumentDB 根据 GUID 生成了一个值,就像它对前两个文档所做的那样。
So I just create my New Customer object along with its nested child objects and call into CreateDocument once more. Although our customer object does have an Id property we didn’t supply a value for it and so DocumentDB generated one based on the GUID, just like it did for the previous two documents.
当以上代码编译并执行时,您将收到以下输出。
When the above code is compiled and executed you will receive the following 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