Alternative Architecture DOJO

オルターブースのクラウドネイティブ特化型ブログです。

Azure Cosmos DBデザインパターン解説:Materialized Viewパターン

こんにちは、MLBお兄さんこと松村です。
シカゴ・カブスの今永昇太投手は、ここまで3試合の先発登板でいまだに防御率が 0.00 という、素晴らしい成績を残しています。


今回は Azure Cosmos DB for NoSQL のデザインパターン解説記事の第6弾です。
前回は「イベントソーシング (Event sourcing) パターン」を解説しました。

aadojo.alterbooth.com

今回は「マテリアライズド・ビュー (Materialized View) パターン」について解説をします。
このデザインパターンについては、Cosmos DB の GitHub リポジトリやブログで紹介されていますので、詳しく読みたい方はこちらもご覧ください。

github.com

devblogs.microsoft.com


Materialized View は、日本語訳では「具現化されたビュー」と表記されることがあります。
Materialized View パターンを雑に言えば、「複雑なクエリを頻繁に実行するなら、そのクエリのために集計したビューを作り、クエリを単純にする」方法です。
このパターンは Azure アーキテクチャーセンターでも取り上げられています。

https://learn.microsoft.com/ja-jp/azure/architecture/patterns/materialized-view?WT.mc_id=AZ-MVP-5002209

RDB では CREATE VIEW クエリでビューを作成するため、ビューで参照するデータには元のテーブルが存在します。
しかし NoSQL の場合は、元のデータの作成・更新をトリガーとして、マテリアライズド・ビューのためのテーブルのデータの準備が必要となります。

Cosmos DB でマテリアライズド・ビューを使用するケースは、分析クエリや集計されたデータへのアクセスのためです。
ただし大規模な分析や集計を行う場合、 Azure Cosmos DB 分析ストアAzure Synapse Link for Azure Cosmos DB の利用を検討しましょう。

サンプルコードがリポジトリで公開されていますので、あわせて確認してください。
マテリアライズド・ビューのためのデータを作るには、Change Feed を利用します。

元のデータクラス

public class Sales {    
    public string id { get; set; }  = Guid.NewGuid().ToString();
    public int CustomerId { get; set; }
    public int OrderId { get; set; }
    public DateTime OrderDate {get; set;} = DateTime.UtcNow;
    public int Qty { get; set;}
    public string Product { get; set; }
    public double Total { get; set; }
}

マテリアライズド・ビューのためのデータクラス
データの詰替えはコンストラクタで行っています。

public class SalesByProduct {
    public string id { get; set; }  = Guid.NewGuid().ToString();
    public DateTime OrderDate { get; set; }
    public int Qty { get; set;}
    public double Total { get; set; }
    public string Product { get; set; }

    public SalesByProduct(){}
    public SalesByProduct(Sales salesItem){
            this.OrderDate = salesItem.OrderDate;
            this.Product = salesItem.Product;
            this.Qty = salesItem.Qty;
            this.Total = salesItem.Total;                
    }
}

Change Feed 処理

[FunctionName("MaterializedViewProcessor")]
public static async Task Run([CosmosDBTrigger(
    databaseName: "Sales",
    containerName: "Sales",
    Connection = "CosmosDBConnection",
    LeaseContainerName = "leases", CreateLeaseContainerIfNotExists=true)]IReadOnlyList<Sales> input,
    [CosmosDB(databaseName: "Sales",
                containerName: "SalesByProduct", 
                Connection="CosmosDBConnection", CreateIfNotExists=true, PartitionKey="/Product")] IAsyncCollector<SalesByProduct> salesByProduct,
    ILogger log)
{           
    if (input != null && input.Count > 0)
    {
        log.LogInformation("Document count: " + input.Count);
        foreach (Sales document in input){
            await salesByProduct.AddAsync(new SalesByProduct(document));
        }
    }
}

コード自体はシンプルですね。
Materialized View パターンで大事なのは、都度集計するのか、集計のためのテーブルを準備するのか、というデータ設計のほうだと思いますね。


サービス一覧 www.alterbooth.com cloudpointer.tech www.alterbooth.com