[DB] Azure Cosmos DB 基礎編

今回ははAzure Cosmos DB, C# Core 2x からのアクセスと管理をまとめます。

# Cosmos DB?

Cosmos DBはAzureファミリーが提供するNoSQL型データベースです。詳しくはこちらをご参照ください。https://docs.microsoft.com/ja-jp/azure/cosmos-db/introduction

開発側としてはSQLのようにデータベースのスキーマを更新する必要がなく手軽で便利であります。NoSQLの良さや特徴はまた別の機会にまとめたいと思います。

## Cosmos DBの構成

Cosmos DBには基本構成概念として、
`Database` > `Collection` > `Document`
というものがあります。値段はCollectionのグレードによって設定されています。

# 事前準備
まずはAzure のアカウントが必要です。持っているAzureアカウントからCosmos DBを検索してCosmos DBアカウントを開設してください（ここの手順は一旦スキップ）。その他に事前にNuGetパッケージをプロジェクトにインストールする必要があります。　

https://www.nuget.org/packages/Microsoft.Azure.DocumentDB/

Cosmos DBにアクセスするためのキーを `appsettings.json`に保存します。


# Databaseの管理

ではさっそく最高層にいる`Database`の管理から。

まずは`Database`の作成

```csharp
/// <summary>
/// Create Database
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
public async Task<Database> CreateDatabase(string id)
{
　　client = new DocumentClient(new Uri(_config.Value.CosmoDbSettings.endpoint),
               _config.Value.CosmoDbSettings.key);

　　var database = await client.CreateDatabaseAsync(new Database { Id = id });

　　return database;
}
```

作成した`Database`の取得

```csharp
/// <summary>
/// Get specific Database
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
public Database GetDatabase(string id)
{
　　　client = new DocumentClient(new Uri(_config.Value.CosmoDbSettings.endpoint), 
                _config.Value.CosmoDbSettings.key);

　　　Database database = client.CreateDatabaseQuery().Where(db => db.Id == id).AsEnumerable().FirstOrDefault();

　　　return database;
}
```
毎度`client`を作るのは面倒なので、`client`生成を`class`化して、次は`Database`の一覧を取得。

```csharp
/// <summary>
/// Get list of Databases
/// </summary>
/// <returns></returns>
public List<Database> GetListOfDatabases()
{
   return GetClient().ReadDatabaseFeedAsync().Result.ToList();
}
```

最後に`Database`の削除

```csharp
/// <summary>
/// Delete Databae
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
public async Task DeleteDatabase(string id)
{
   await GetClient().DeleteDatabaseAsync(UriFactory.CreateDatabaseUri(id));
}
```

# Collectionの管理

次は`Collection`の管理です。Cosmos DBの価格の`Collection`の`Tier`に連動しています。通常`Collection`を作成すると`S1`に設定されています。現在の`Tier`が何か確認するメソッドと、アップグレードダウングレードできるメソッドも書いてみます。

まずは、Collectionの作成

```csharp
/// <summary>
/// Create collection
/// </summary>
/// <param name="databaseId"></param>
/// <param name="collectionId"></param>
/// <returns></returns>
public async Task<DocumentCollection> CreateCollection(string databaseId, string collectionId)
{
   var database = GetDatabase(databaseId);

   // Default is S1 tier
   return await GetClient().CreateDocumentCollectionAsync(database.SelfLink, new DocumentCollection { Id = collectionId });
}
```

作成したCollectionのTierを確認

```csharp
/// <summary>
/// Get Collectoin tier 
/// </summary>
/// <param name="databaseId"></param>
/// <param name="collectionId"></param>
/// <returns></returns>
public Offer GetCurrentCollectionTier(string databaseId, string collectionId)
{
   var database = GetDatabase(databaseId);
   var collection = GetClient().CreateDocumentCollectionQuery(database.SelfLink).Where(c => c.Id == collectionId).ToArray().FirstOrDefault();

   return GetClient().CreateOfferQuery().Where(o => o.ResourceLink == collection.SelfLink).AsEnumerable().Single();
}
```

次は指定のCollection + Tierに変更。

```csharp
/// <summary>
/// Set Collection tier
/// </summary>
/// <param name="databaseId"></param>
/// <param name="collectionId"></param>
/// <param name="tier"></param>
/// <returns></returns>
public async Task<Offer> SetCollectionTier(string databaseId, string collectionId, string tier)
{
   // Get Collection
   var collection = GetCollection(databaseId, collectionId);

   // Get Tier of Collection
   var offer = GetCurrentCollectionTier(collection);

   // Set New OfferType
   offer.OfferType = tier;

   // Replace
   var replaced = await GetClient().ReplaceOfferAsync(offer);

   // Get Collection Again
   collection = GetCollection(databaseId, collectionId);

   // Get new Offer and return
   return GetClient().CreateOfferQuery().Where(o => o.ResourceLink == collection.SelfLink).AsEnumerable().Single();
}
```

次にDatabase内にあるすべてのCollectionを表示するメソッド

```csharp
/// <summary>
/// Get list of Collections in Database
/// </summary>
/// <param name="databaseId"></param>
/// <returns></returns>
public async Task<List<DocumentCollection>> GetCollectionResource(string databaseId)
{
   var collections = await GetClient().ReadDocumentCollectionFeedAsync(UriFactory.CreateDatabaseUri(databaseId));
   return collections.ToList();
}
```

では、最後にCollectionを削除

```csharp
/// <summary>
/// Delete collection
/// </summary>
/// <param name="databaseId"></param>
/// <param name="collectionId"></param>
/// <returns></returns>
public async Task DeleteCollection(string databaseId, string collectionId)
{
　　　// Get Collection
     var collection = GetCollection(databaseId, collectionId);

     await GetClient().DeleteDocumentCollectionAsync(collection.SelfLink);
}
```

# Documentの管理

最後にDocumentの作成、編集、検索、削除を見てみます。

Documentの作成

```csharp
/// <summary>
/// Write Document
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="databaseId"></param>
/// <param name="collectionId"></param>
/// <param name="document"></param>
/// <returns></returns>
public async Task<ResourceResponse<Document>> WriteDocumentAsync<T>(string databaseId, string collectionId, T document)
{
   var createdDocument = await GetClient().CreateDocumentAsync(UriFactory.CreateDocumentCollectionUri(databaseId, collectionId).ToString(), collection.SelfLink, document);

   return createdDocument;
}
```

次にDocumentの検索

```csharp
/// <summary>
/// Get T
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="databaseId"></param>
/// <param name="collectionId"></param>
/// <param name="query"></param>
/// <returns></returns>
public List<T> GetDocuments<T>(string databaseId, string collectionId, string query)
{
   var document = GetClient().CreateDocumentQuery<T>(UriFactory.CreateDocumentCollectionUri(databaseId,collectionId), query);

   return document.ToList();
}
```

ここにある`query`はSQL文をそのまま使うことができます。

# 最後に

今日は以上です。このコラムは継続的にアップデートしていきたいと思います。

参照：https://github.com/Azure/azure-cosmos-dotnet-v2/tree/530c8d9cf7c99df7300246da05206c57ce654233/samples/code-samples