Azure Cosmos DB入門(5)

本コンテンツは「Azure Cosmos DB入門」の(5)です

ryuichi111std.hatenablog.com

5 Cosmos DBプログラミング ~ MongoDB編

前回・前々回のDocumentDB編に続き、今回は「MongoDB編」となります。

5.1 はじめに

MongoDBは、DocumentDBと同じく、保持するデータモデル形式が「ドキュメントデータモデル」となります。
つまり JSON形式 のドキュメントデータを保持・検索・更新するNoSQLデータベースとなります。
そもそも、MongoDBは 2009年にリリースされました。
当時、先発NoSQLプロダクトであった「Redis」や「Cassandra」よりも高機能なドキュメント指向データベースとして注目を集めました。(Apache CouchDBなんかも2005年からあるドキュメント指向データベースです・・・私はこちらには明るくないのですが・・・)

Cosmos DBでAPI/データモデルとしてMongoDBを選択するメリットには、以下のようなポイントがあげられます。

* 既存の MongoDBベースシステム のCosmos DBへの移行が容易
既にMongoDBベースのシステムがあり、これをクラウド化する場合、データアクセス部に関して既存資産を生かしたままCosmos DBへの移行が行えます。
※厳密には、Cosmos DB(MongoDB API/データモデル)とオンプレMongoDBとの互換性レベルについては、私も未確認なので要検証の部分があると思っています。
例えば、Cosmos DBでは 単一要素に対するHashedシャーディング のみをサポートしています。

* MongoDBの知識を有するエンジニアによるCosmos DBシステムの構築が容易
Cosmos DBの他のAPIモデルである DocumentDB や Tableに比べて、MongoDBスキルを有するエンジニアの方が世の中には多いでしょう。
その為、エンジニアの学習コストを抑えたうえでCosmos DBベースのシステムの構築が可能になります。

* Azureクラウド および オンプレ 両対応のシステムの構築が可能
API・データモデルに DocumentDB や Table を選択した場合、その動作環境はAzureクラウドに限定されてしまいます。
システムによってはクラウドおよびオンプレでサービス展開を行いたいケースもあるでしょう。そのようなケースでは、MongoDBモデルが有効になります。

5.1.1 Cosmos DB(MongoDBデータモデル)とMongoDB

マルチデータモデルである Cosmos DB のサポートする1つのデータモデルとしてMongoDBがサポートされています。
MongoDBは、DocumentDB と同じく「JSONフォーマットベースのドキュメント」をデータとして保持します(正確にはDocumentDBよりも以前からMongoDBはドキュメン指向NoSQLデータベースとして君臨していました)。
Cosmos DBによるMongoDB対応を図にすると以下のようなイメージになります。

f:id:daigo-knowlbo:20170608232114p:plain

Cosmos DBはMongoDBのプロトコルに準拠したI/Fを提供します。
その為、従来から存在するMongoDB用のクラスライブラリを用いた開発が行えますし、「mongoコマンド」や「Studio 3T for MongoDB(旧MongoChef)」から接続する事が可能です。

5.2 準備

5.2.1 Cosmos DB(MongoDB)データベースアカウントの作成

Azureポータルを利用して「Cosmos DB(DocumentDB)」を作成します。

Azureポータルをブラウザで表示し「新規」を選択。検索テキストボックスに「Azure Cosmos DB」と入力します。

f:id:daigo-knowlbo:20170608232706p:plain f:id:daigo-knowlbo:20170608232709p:plain

「Azure Cosmos DB」を選択します。

f:id:daigo-knowlbo:20170608232754p:plain

「作成」をクリックします。

f:id:daigo-knowlbo:20170608232819p:plain

アカウント情報を入力して「作成」をクリックします。
ここでは以下の設定を行いました。
ID: cosmosmongo111
API: MongoDB
リソースグループ: cosmosmongo111
場所: 西日本

f:id:daigo-knowlbo:20170608233110p:plain

以下のCosmos DBアカウントが作成されます。

f:id:daigo-knowlbo:20170608233200p:plain

5.2.2 Visual Studio 2017プロジェクトの作成

Visual Studio 2017を起動し、メニュー「ファイル → 新規作成 → プロジェクト」を選択します。
ここでは、プロジェクトテンプレートは「コンソールアプリケーション」、名前は「CosmosMongoDBExample」としました。

f:id:daigo-knowlbo:20170608233633p:plain

次に、Cosmos DB(DocumentDB)にアクセスするために、NuGetパッケージライブラリをプロジェクトに追加します。
ソリューションエクスプローラからプロジェクトをマウス右ボタンクリックし、表示されたメニューから「NuGetパッケージの管理を選択します。

f:id:daigo-knowlbo:20170608233810p:plain

表示されたNuGetパッケージ管理画面から、左上の「参照タブ」を選択し、検索テキストボックスに「MongoDB.Driver」と入力します。
一覧に MongoDB.Driver が表示されるので、選択して「インストール」ボタンをクリックします。

f:id:daigo-knowlbo:20170609011907p:plain

「MongoDB.Driver」と、その依存関係である「MongoDB.Driver.Core」がインストールされます。
「MongoDB.Driver」は、「MongoDB, Inc.」により提供される「Official .NET driver for MongoDB.」です。
つまり、Azure Cosmos DB(MongoDB)専用のものではなく、Cosmos DBとは無関係な 一般的なMongoDB に接続するための公式ドライバーとなります。

最後にCosmos DB(MongoDB)に保存するドキュメントモデルクラスとして以下のRoomReservationInfo.csをプロジェクトに追加します。これは前回のDocumentDBの解説で使用したのと同じドキュメントモデルクラスになります。
1点だけ、「Idプロパティに対する JsonProperty属性 指定」に相違があります。
モデルクラスをJSON形式に変換した際のプロパティ名の変更マップの指定ですが、DocumentDB時には「id」としていました。今回のMongoDBでは「_id」としています。
データモデルの方言の違いで、DocumentDBでは「ドキュメント識別子=id」であるのに対し、MongoDBでは「ドキュメント識別子=_id」と決められているためです。パーティションキー(シャーディングキー)の指定がある場合は、id / _idに加えてパーティションキー値(シャーディングキー値)との複合キーが、コレクション内におけるドキュメント識別子となります。

// RoomReservationInfo.cs

using System;
using System.Collections.Generic;
using Newtonsoft.Json;

namespace CosmosMongoDBExample
{
  public class RoomReservationInfo
  {
    [JsonProperty(PropertyName = "_id")]
    public string Id { get; set; }

    /// <summary>
    /// 会議室名を取得または設定します。
    /// </summary>
    public string Room { get; set; }

    /// <summary>
    /// 会議名を取得または設定します。
    /// </summary>
    public string Title { get; set; }

    /// <summary>
    /// 予約者IDを取得または設定します。
    /// </summary>
    public string ReservedUserId { get; set; }

    /// <summary>
    /// 予約者名を取得または設定します。
    /// </summary>
    public string ReservedUserName { get; set; }

    /// <summary>
    /// 開始日時を取得または設定します。
    /// </summary>
    public DateTime Start { get; set; }

    /// <summary>
    /// 終了日時を取得または設定します。
    /// </summary>
    public DateTime End { get; set; }

    /// <summary>
    /// 参加メンバーを取得または設定します。
    /// </summary>
    public List<AssignMember> AssignMembers { get; set; }
  }

  public class AssignMember
  {
    public string UserId { get; set; }

    public string UserName { get; set; }
  }
}

以上で、プロジェクトの下準備が完了しました。

5.3 データベースとコレクションの作成

今回のCosmos DB(MongoDB)においても、DocumentDBの場合と同様に「データベースアカウント → データベース → コレクション → ドキュメント(JSONデータ)」というデータモデル構造となります。
まず、データベースとコレクションを作成したいと思います。
作成方法はいくつかありますが、その中でも以下の3つの方法についてここでは取り上げることとします。

  • Azureポータルで作成
  • プログラムで作成
  • mongoコマンドで作成

5.3.1 Azureポータルで作成

Azureポータルで、作成した cosmosmongo111 を選択し、サイドバーから「データエクスプローラ」メニューを選択します。
続いて「New Collection」ボタンをクリックします。

f:id:daigo-knowlbo:20170608234724p:plain

「Add Collection」画面が表示されるので、必要事項を入力することでデータベースおよびコレクションを作成することができます。

f:id:daigo-knowlbo:20170608234748p:plain

作成されたデータベース・コレクションは以下の通りです。

f:id:daigo-knowlbo:20170609010712p:plain

この後の手順を進めるために、一旦、このGroupwareDBデータベースは削除しましょう。
「GroupwareDB」の右側にマウスカーソルを持っていくと「…」が表示され、これをクリックすると以下のようなポップアップメニューが表示されます。 「Delete Database」を選択します。

f:id:daigo-knowlbo:20170609010904p:plain

削除の確認のためにデータベースIDを入力し、「OK」ボタンをクリックします。

f:id:daigo-knowlbo:20170609011001p:plain

データベースの削除が完了しました。

5.3.2 プログラムで作成

本稿では、Cosmos DB(MongoDB)操作を管理するクラスとして MongoDbManagerクラス を用意することとします。
プロジェクトに MongoDbManagercs を追加します。

f:id:daigo-knowlbo:20170609010544p:plain

(1)接続情報の定義

「接続情報」及びこれから「作成するデータベース名・コレクション名」等をMongoDbManagerクラスに定数として定義します(リスト 1)。

リスト1 MongoDbManager.cs
private const string host = "【ホスト名】";
private const string userName = "【ユーザー名】";
private const string password = "【パスワード】";
private const string database = "GroupwareDB";
private const string collection = "RoomReservations";
private const int port = 10255;

// 上記は各値をコードに埋め込んでいますが、実運用コードでは構成から読み取る等の工夫が必要

【ホスト名】【ユーザー名】【パスワード】は各データベースアカウントごとに適切な値を設定します。
Azureポータルで「接続文字列」タブを選択することで確認することができます。

f:id:daigo-knowlbo:20170609012658p:plain

データベースIDは「GroupwareDB」、コレクションIDは「RoomReservations」としました。これはサンプルとして、グループウェアにおける会議室予約のデータベースを想定します。

(2)基本名前空間のusing定義

MongoDB接続・操作を行う上での基本的な名前空間をusing定義しておきます(リスト 2)。

リスト2 MongoDbManager.cs
// 一般的に利用する名前空間
using System;
using System.Collections.Generic;
using System.Security.Authentication;
using System.Linq;
using System.Threading.Tasks;

// MongoDB関連の名前空間
using MongoDB.Driver;
using MongoDB.Driver.Core;

(3)接続クライアントオブジェクトの定義

CosmosDB(MongoDB)に接続する為の接続クライアント「MongoDB.Driver.MongoClient」をMongoDbManagerクラスのフィールド変数「client」として定義します(リスト 3)。
また、clientはコンストラクタで初期化することとします。

リスト3 MongoDbManager.cs
public class MongoDbManager {
  private MongoClient client = null;
  
  public MongoDbManager()
  {
    // MongoClientの初期化
    var settings = new MongoClientSettings
    {
      Server = new MongoServerAddress(MongoDbManager.host, MongoDbManager.port),
      ServerSelectionTimeout = TimeSpan.FromSeconds(5)
    };
    settings.SslSettings = new SslSettings();
    settings.UseSsl = true;
    settings.SslSettings.EnabledSslProtocols = SslProtocols.Tls12;

    MongoIdentity identity = new MongoInternalIdentity(MongoDbManager.database, MongoDbManager.userName);
    MongoIdentityEvidence evidence = new PasswordEvidence(MongoDbManager.password);

    settings.Credentials = new List<MongoCredential>()
    {
      new MongoCredential("SCRAM-SHA-1", identity, evidence)
    };
    this.client = new MongoClient(settings);
  }
}

MongoClientSettingsオブジェクトにより接続の為の情報をいくらか設定する必要があります。
MongoClientSettingsのコンストラクタでは、接続先ホスト名とポート番号、タイムアウト時間(5秒)により初期化しています。
また、Azure Cosmos DBはSSL(TLS)による接続をサポートするため、MongoClientSettings.SslSettingsの設定も行っています。

(4)コレクション作成メソッドの追加

MongoDbManagerクラスにデータベースを作成するメソッド「CreateCollection()」を定義します(リスト 4)。

リスト4 MongoDbManager.cs
public async Task<bool> CreateCollection()
{
  IMongoDatabase mongoDatabase = 
    this.client.GetDatabase(MongoDbManager.database);

  await mongoDatabase.CreateCollectionAsync(MongoDbManager.collection);

  return true;
}

MongoClient.GetDatabase()メソッド呼び出しにより「データベース」オブジェクト(IMongoDatabaseインターフェイス型)を取得することができます。未作成のデータベースでもGETすることができます。
続けて、取得したIMongoDatabaseオブジェクトのCreateCollectionAsync()を呼び出すことで、データベースおよびコレクションが作成されます(データベースもこのタイミングで同時に作成されます)。

以下が MongoDbManager.CreateCollection() 呼び出しコードです(リスト5)。

リスト5 Program.cs
MongoDbManager manager = new MongoDbManager();
bool result = manager.CreateCollection().Result;

上記コードによるデータベース・コレクションの作成を行った結果をAzureポータルで確認したものが以下です。
シングルパーティション構成、予約RUは1000での作成となります。

f:id:daigo-knowlbo:20170609014952p:plain

先程と同様、一旦 GroupwareDB データベースは削除しておきましょう。

5.3.3 mongoコマンドで作成

mongoコマンドを使用して、Cosmos DB(MongoDB)に接続することが可能です。
mongoコマンドは、データベース・コレクションの作成、ドキュメントの作成・更新・削除・抽出、また、シャーディング設定といった多くの操作を行うことが可能です。

mongoコマンド利用の準備

mongoコマンドはMongoDBをインストールすることで利用可能になります。
MongoDBは以下のURLからダウンロードすることができます。

MongoDB Download Center | MongoDB

以下のダウンロード画面では、ドロップダウンによる Version の選択があります。Windows10のようなクライアントOSでも「Windows Server 2008 R2 64-bit and later, with SSL support x64」を選択すればOKです。

f:id:daigo-knowlbo:20170609022709p:plain

ダウンロードしたセットアップEXEを起動し、インストーラの指示に従うのみでインストール可能です。
しかし、MongoDBサーバーは不要、Cosmos DB(MongoDB)との接続クライアントであるmongoコマンドのみを必要とする場合は、以下の画面に示すように Custom インストールモードにて「Client」のみをセットアップすることができます。

f:id:daigo-knowlbo:20170609023251p:plain

インストール完了後は「環境変数」の設定により、「C:\Program Files\MongoDB\Server\3.4\bin」にPathを通しておくと便利です。

f:id:daigo-knowlbo:20170609023507p:plain

f:id:daigo-knowlbo:20170609023638p:plain

環境変数設定後は、Windowsを再起動します。

mongoコマンドを利用

コマンドプロンプトを起動します。
以下のコマンドを実行し、Azure Cosmos DB(MongoDB)と接続します。
※【ホスト名】【ユーザーID】【パスワード】は、Azureポータル→cosmosmongo111→接続文字列から確認。

mongo --ssl --host 【ホスト名】 --port 10255 -u 【ユーザーID】 -p 【パスワード】

f:id:daigo-knowlbo:20170609024341p:plain

「use 【データベース名】」コマンドを実行します。
ここでは use GroupwareDB としました。

f:id:daigo-knowlbo:20170609025430p:plain

GroupwareDBデータベースは以下に、シャーディングを有効にしたコレクションを作成することにします。
シャーディングとはMongoDBの機能で「水平スケーリング」を行うための機能です。Cosmos DBでは「パーティショニング」として実装されています。
以下のコマンドを実行します。

db.runCommand( { shardCollection: "GroupwareDB.RoomReservations", key: { Room: "hashed" } } )

このコマンドを実行したタイミングで、データベースおよびシャーディング有効コレクションが作成されます。
シャーディングキー(=パーティションキー)は「Room」としました。対象コレクションに保存する予定の RoomReservationInfoクラスのRoomプロパティ を表します。

f:id:daigo-knowlbo:20170609233433p:plain

Azureポータルで確認した画面が以下になります。シャーディング(パーティショニング)が有効になったため、設定可能な最低RUが2500となっています。そして、実際に作成されたコレクションのRUは2600となります(なぜ2500ではなく2600なのかは不明・・・)。

f:id:daigo-knowlbo:20170609032024p:plain
f:id:daigo-knowlbo:20170609233527p:plain

5.4 ドキュメントの作成

MongoDbManagerクラスにドキュメントを作成するメソッド「CreateRoomReservationInfo()」を定義します(リスト 6)。

リスト6 MongoDbManager.cs
public async Task<bool> CreateRoomReservationInfo(RoomReservationInfo roomReservationInfo)
{
  var mongoDatabase = this.client.GetDatabase(MongoDbManager.database);
  var mongoCollection = mongoDatabase.GetCollection<RoomReservationInfo>(MongoDbManager.collection);
  await mongoCollection.InsertOneAsync(roomReservationInfo);

  return true;
}

MongoClientオブジェクトから IMongoDatabaseオブジェクト を取得します(MongoClient.GetDatabase()メソッド呼び出し)。
さらに IMongoCollectionオブジェクトを取得します(IMongoDatabase.GetCollection())。
IMongoDatabase.GetCollection()では扱うモデルクラス型を設定します。ここではRoomReservationInfo型としました。 IMongoCollection.InsertOneAsync()メソッドを呼び出すことで新規のドキュメントを作成することができます。

5.4.1 ドキュメントの一括投入 ~ Studio 3T for MongoDB(旧Mongo chef)

以降の検索の説明等では、もっと大量のドキュメントが登録されている状態が好ましいです。
その為に、ここでデータ量を増やしておきたいと思います。
ここでは「Studio 3T for MongoDB(旧Mongo chef) 」というツールを利用します。
SQL Serverでいうところの「SQL Server Management Studio」のようなツールです。
以下からダウンロード可能です。

studio3t.com

インストールを行い起動します。起動したら左上の「Connect」をクリックします。

f:id:daigo-knowlbo:20170610000919p:plain

表示された「Connection Manager」ウィンドウで「New Connection」をクリックします。

f:id:daigo-knowlbo:20170610001026p:plain

表示された「New Connection」ウィンドウに接続情報を入力します。
入力する情報は Azureポータル → 対象のCosmosDB → 接続文字列 で確認することができます。

「Server」タブの設定は以下の通りです。

f:id:daigo-knowlbo:20170610001205p:plain

「Authentication」タブの設定は以下の通りです。

f:id:daigo-knowlbo:20170610001410p:plain

SSL」タブの設定は以下の通りです。

f:id:daigo-knowlbo:20170610001450p:plain

接続した画面が以下になります。

f:id:daigo-knowlbo:20170610001525p:plain

左のツリーから RoomReservationsコレクションを選択し、マウス右ボタンクリックでポップアップメニューを開きます。
ポップアップメニューから「Import Data…」を選択します。

f:id:daigo-knowlbo:20170610001621p:plain

JSON形式を選択しNextボタンをクリックします。

f:id:daigo-knowlbo:20170610001809p:plain

「+」ボタンをクリックし、インポートソースとなるjsonファイル(exampledata.json)を選択します。

f:id:daigo-knowlbo:20170610002809p:plain

インポート内容の確認を行い、「Start Import」ボタンをクリックします。

f:id:daigo-knowlbo:20170610002906p:plain

jsonファイルからAzure Cosmos DB(MongoDB)にデータが投入されました。

f:id:daigo-knowlbo:20170610002948p:plain

5.5 ドキュメントの検索

RoomReservationsコレクションに登録されたドキュメントをいくつかのパターンで検索してみます。

5.5.1 Mongoクエリー言語による検索

Mongoクエリー言語によるクエリーを行います。
Mongoクエリー言語はJSON形式で記述が可能なMongoDB用クエリー言語です。

(1) Roomによる検索

パーティションキー(シャーディングキー)であるRoom(RoomReservationInfo.Room)による検索を実装します。
MongoDbManagerクラスにFindByRoom(stirng room)メソッドを追加します。

リスト7 MongoDbManager.cs
public async Task<List<RoomReservationInfo>> FindByRoom(string room)
{
  var mongoDatabase = this.client.GetDatabase(MongoDbManager.database);
  var mongoCollection = mongoDatabase.GetCollection<RoomReservationInfo>(MongoDbManager.collection);

  var find = await mongoCollection.FindAsync<RoomReservationInfo>("{ Room: \"" + room + "\"}");
  return find.ToList();
}

コレクションオブジェクトに対して、FindAsync()メソッドを呼び出します。
は取得するドキュメントクラスである RoomReservationInfo を指定します。
引数の文字列がMongoクエリー言語による抽出条件式です。FindByRoom()メソッド引数を使用して文字列連結していますが、展開すると例えば以下のようなJSON文字列になります。

{ Room: "第1会議室" }

「Room要素の値が第1会議室である」という条件式になります。
SQL風に表すと以下のようになります。

WHERE Room = '第1会議室'

MongoDbManager.FindByRoom()の呼び出し側の実装は以下の通りです(リスト8)。

リスト8 MongoDbManager.cs
MongoDbManager manager = new MongoDbManager();

var roomReservationInfos = manager.FindByRoom("第1会議室").Result;

Console.WriteLine(string.Format("{0}件", roomReservationInfos.Count));
foreach (var info in roomReservationInfos)
{
  Console.WriteLine(
    string.Format("{0} / {1} / {2} ~ {3}", info.Room, info.Title, info.Start, info.End));
}

(2) Roomによる検索(その2)

作成したFindByRoom()メソッドは以下のように書き換えることができます(リスト9)。
動作上の意味はイコールになります。

リスト9 MongoDbManager.cs
public async Task<List<RoomReservationInfo>> FindByRoomEx(string room)
{
  var mongoDatabase = this.client.GetDatabase(MongoDbManager.database);
  var mongoCollection = mongoDatabase.GetCollection<RoomReservationInfo>(MongoDbManager.collection);

  FilterDefinition<RoomReservationInfo> query = 
      Builders<RoomReservationInfo>.Filter.Eq(r => r.Room, room);
  var find = await mongoCollection.FindAsync<RoomReservationInfo>(query);
  return find.ToList();
}

リスト8とリスト9の違いは、FindAsync()メソッドへの引数にあります。
リスト8では「{ Room: “第1会議室” }」のようなJSON文字列条件式を渡していました。
一方、リスト9では「FilterDefinition型」を引数として渡しています。
FindAsync()メソッドの定義を見ると、本来 JSON文字列(stirng型) を引数として受け取る実装はありません。
なぜJSON文字列(string)を引数として渡すことができているのか?それはC#のimplicitキーワード機能に起因します。

implicit (C# リファレンス) | Microsoft Docs

「MongoDB.Driver.FilterDefinitionクラス」の実装は、以下のgithubで確認することができます。

mongo-csharp-driver/FilterDefinition.cs at master · mongodb/mongo-csharp-driver · GitHub

その中で、以下のリスト10の実装が行われています。
「implicit operator FilterDefinition(string json)」、つまり、string json型からFilterDefinition型への暗黙的変換を行うオペレータを定義しています。
リスト8が実行される際、FindAsync()の「引数string」は、FilterDefinitionのimplicit operatorにより、FilterDefinitionオブジェクトに型変換されています。

リスト10 FilterDefinition.cs
// Mongo C# Driver実装より引用

public abstract class FilterDefinition<TDocument>
{
  ... 省略
  
  /// <summary>
  /// Performs an implicit conversion from <see cref="System.String"/> to <see cref="FilterDefinition{TDocument}"/>.
  /// </summary>
  /// <param name="json">The JSON string.</param>
  /// <returns>
  /// The result of the conversion.
  /// </returns>
  public static implicit operator FilterDefinition<TDocument>(string json)
  {
    if (json == null)
    {
      return null;
    }

    return new JsonFilterDefinition<TDocument>(json);
  }
  
  ... 省略
}

(3) _id + パーティションキーによる検索

次に _id と パーティションキーRoom による検索を行います。
今回のサンプルコレクションでは、パーティショニング(シャーディング)設定を行っているので、「_id + パーティションキーRoom」がドキュメントをユニークに識別するキーになります(リスト11)。

リスト11 MongodbManager.cs
// Roomと_idから、単一のRoomReservationInfoを取得します。
public async Task<RoomReservationInfo> FindByRoomAndId(string room, string id)
{
  var mongoDatabase = this.client.GetDatabase(MongoDbManager.database);
  var mongoCollection = mongoDatabase.GetCollection<RoomReservationInfo>(MongoDbManager.collection);

  var find = await mongoCollection.FindAsync<RoomReservationInfo>(
    "{ Room: \"" + room + "\", _id: \"" + id + "\"}");

  return find.FirstOrDefault();
}

上記リスト11で実行されるMongoクエリーJSONは、以下のような形式になります。

{ Room: "第1会議室", _id: "000000001" }

(4) 複合条件による検索

次に「Room=【Room】 AND AssignMembers.UserID = 【UserID】」という2つの値のAND条件の実装を行います(リスト12)。

リスト12 MongoDbManager.cs
public async Task<List<RoomReservationInfo>> FindByRoomAndAssignMember(string room, string assignMemberId)
{
  var mongoDatabase = this.client.GetDatabase(MongoDbManager.database);
  var mongoCollection = mongoDatabase.GetCollection<RoomReservationInfo>(MongoDbManager.collection);

  var find = await mongoCollection.FindAsync<RoomReservationInfo>(
    "{ Room: \"" + room + "\" , AssignMembers: { $elemMatch: { UserId: \"" + assignMemberId + "\"}}}");
  return find.ToList();
}

5.5.2 LINQによる検索

JSONによるMongoクエリー言語以外にLINQクエリーを利用することができます。
リスト8をLINQベースで書き直したFindByRoomLinq()メソッドの実装はリスト13です。

リスト13 MongoDbManager.cs
public async Task<List<RoomReservationInfo>> FindByRoomLinq(string room)
{
  var mongoDatabase = this.client.GetDatabase(MongoDbManager.database);
  var mongoCollection = mongoDatabase.GetCollection<RoomReservationInfo>(MongoDbManager.collection);

  var find = await mongoCollection.FindAsync<RoomReservationInfo>(r => r.Room == room);

  return find.ToList();
}

5.6 ドキュメントの更新

ドキュメントを更新する処理を実装します(リスト14)。
RoomReservationInfoの Titleプロパティ のみを変更する処理とします。

リスト14 MongoDbManager.cs
public async Task<bool> UpdateTitle(RoomReservationInfo roomReservationInfo)
{
  var mongoDatabase = this.client.GetDatabase(MongoDbManager.database);
  var mongoCollection = mongoDatabase.GetCollection<RoomReservationInfo>(MongoDbManager.collection);

  // パーティショニング(シャーディング)構成なので Room + _id でユニークなキー
  var result = await mongoCollection.UpdateOneAsync<RoomReservationInfo>(
    r => r.Id == roomReservationInfo.Id && r.Room == roomReservationInfo.Room, 
    new UpdateDefinitionBuilder<RoomReservationInfo>().Set(i => i.Title, roomReservationInfo.Title));

  return true;
}

ドキュメントの更新には、UpdateOneAsync()メソッドを呼び出します。

第1引数で、更新対象のドキュメントを特定します。
第2引数で、更新個所を設定します。今回は Title のみの更新になります。

リスト14のドキュメント更新実装を呼び出す処理は以下の通りです(リスト15)。

リスト15 
MongoDbManager manager = new MongoDbManager();

// 1件取得
var roomReservationInfo = manager.FindByRoomAndId("第1会議室", "0000000000").Result;

// タイトルプロパティを変更
roomReservationInfo.Title = "タイトル変更!!!!";

// 更新処理呼び出し
var result = manager.UpdateTitle(roomReservationInfo).Result;

5.7 ドキュメントの削除

ドキュメントを削除する処理を実装します(リスト16)。

リスト16 MongoDbManager.cs
public async Task<bool> Delete(RoomReservationInfo roomReservationInfo)
{
  var mongoDatabase = this.client.GetDatabase(MongoDbManager.database);
  var mongoCollection = mongoDatabase.GetCollection<RoomReservationInfo>(MongoDbManager.collection);

  var result = await mongoCollection.DeleteOneAsync<RoomReservationInfo>(
    r => r.Id == roomReservationInfo.Id && r.Room == roomReservationInfo.Room);

  return true;
}

DeleteOneAsync()を呼び出します。
引数でドキュメントをユニークに識別するための「Room(パーティションキー)」+「_id」を設定しています。

リスト16のドキュメント削除実装を呼び出す処理は以下の通りです(リスト17)。

リスト17
MongoDbManager manager = new MongoDbManager();

var roomReservationInfo = manager.FindByRoomAndId("第1会議室", "00001").Result;

var result = manager.Delete(roomReservationInfo).Result;

5.8 資料

本記事のサンプルは以下からダウンロード可能です。

github.com

5.9 つづく・・・

次回はCosmos DBによる Graph DB、Gremlinについての説明になります。ではー。

<< Azure Cosmos DB入門(4)へ | Azure Cosmos DB入門(6)へ >>