はじめに
Azure Cosmos DB の学習中にパーティション キーに日付項目を使用するケースがあり、これが一筋縄ではいかず、調査・検討した結果をメモとして記載しました。
見解に誤りやより良い方法があればコメント・アドバス願います。
Cosmos DBへの入出力操作の参考
- Azure Functions 2.x 以降に対する Azure Cosmos DB の入力バインド
- Azure Functions 2.x 以降に対する Azure Cosmos DB の出力バインド
事前知識
パーティション キーの役割
Azure Cosmos DB は、データベースの個別のコンテナーをパーティション分割を使用した水平スケーリングすることで、数ミリ秒 (1桁台) の応答時間と、自動および即時のスケーラビリティを実現しています。
そして、パーティション キーはコンテナーをパーティション分割する際のキーであり、パーティション キーに指定した項目の値が同じデータは同じパーティションに格納されます。
なお、パーティション キーはコンテナー作成時のみ指定可能で、後から変更することはできません。
パーティション キーのルール(「Microsof Docs」より)
- パーティション キーを選択した後は、その場で変更することはできません。 パーティション キーを変更する必要がある場合は、新しい必要なパーティション キーを使用して、新しいコンテナーにデータを移動する必要があります。
- すべての コンテナーで、パーティション キーが次のようになっている必要があります。
- 値が変更されないプロパティであること。 プロパティがパーティション キーの場合、そのプロパティの値を更新することはできません。
- 高いカーディナリティがあること。 言い換えると、プロパティには、有効な値が広範囲に及ぶことが必要です。
- 要求ユニット (RU) の消費量とデータ ストレージをすべての論理パーティションに均等に分散すること。 これにより、物理パーティション全体でも RU の消費とストレージの分散が保証されます。
パーティション キーのパス (例: "/userId")のルール
- パーティション キーのパスには、英数字とアンダースコア (_) 文字を使用できます。 また、標準パス表記 (/) を使用して、入れ子になったオブジェクトを使用することもできます。
パーティション キーの値 (例:"Andrew")のルール
- パーティション キーの値には、文字列型または数値型を使用できます。
- パーティション キー値の最大長は2048 バイトです。
参考
- Azure Cosmos DB でのパーティション分割と水平スケーリング
- Azure Cosmos DB サービスのクォータ
Azure Cosmos DB での日付の扱い
Azure Cosmos DB はJSON ドキュメントとしてモデル化されて保存しており、少数の基本的な型 (String、Number、Boolean、Array、Object、Null) のみサポートし、DateTime 型は直接サポートされません。
そのため、DateTime を文字列として格納する必要があります。
また、現在、Azure Cosmos DB では、日付のローカリゼーションはサポートされていないため、Microsof は日付は UTC の DateTime 値を ISO 8601 UTC 標準に準拠した yyyy-MM-ddTHH:mm:ss.fffffffZ
形式の文字列で保存することを推奨しています。
なお、フィルターとして DateTime 文字列を使用する範囲クエリは、DateTime 文字列がすべて UTC であり、同じ長さである場合にのみサポートされます。
参考
- Azure Cosmos DB で日付を扱う
日付項目をパーティション キーに使用したい場合
方法1(王道パターンだと思いきやNG)
UTC の DateTime 値に変換した日付を ISO 8601 UTC 標準に準拠した yyyy-MM-ddTHH:mm:ss.fffffffZ
形式の文字列で保存する。
概要
DateTime 型の日付項目を以下の書式で文字列に変換
-
書式:
"yyyy-MM-dd'T'HH:mm:ss'Z'"
- 実装イメージ
[JsonProperty("datePartitionKey")] public string DatePartitionKey => {DateTime 型の項目}.ToUniversalTime().ToString("yyyy-MM-dd'T'HH:mm:ss'Z'");
-
書式:
"O"
- 実装イメージ
[JsonProperty("datePartitionKey")] public string DatePartitionKey => {DateTime 型の項目}.ToUniversalTime().ToString("O");
課題・問題点など
- 「PartitionKey extracted from document doesn't match the one specified in the header.」のエラーで保存できず。
参考
- 標準の日時書式指定文字列
- Azure Cosmos DB での無効な要求例外の診断とトラブルシューティング
方法2
UTC の DateTime 値に変換した日付を Unix タイムスタンプとして保存する。
※Azure Cosmos DB の内部 Timestamp(_ts)プロパティが採用
概要
UnixDateTimeConverter クラスを使用して、DateTime を数値としてシリアル化
- 実装イメージ
[JsonProperty("datePartitionKey")]
[JsonConverter(typeof(Newtonsoft.Json.Converters.UnixDateTimeConverter))]
public string DatePartitionKey => {DateTime 型の項目}.ToUniversalTime();
参考
- UnixDateTimeConverter Class
課題・問題点など
- 日付項目に対する検索条件も正常に機能し、特に問題点は見当たらない。
方法3
日付を yyyy-MM-dd
形式の文字列で保存する。
※国際化が不要な簡易なシステムであれば、UTC 変換も不要でJSONを参照した際も分かりやすい。
概要
DateTime 型の日付項目を日付のみの書式で文字列に変換
- 実装イメージ
[JsonProperty("datePartitionKey")]
public string DatePartitionKey => {DateTime 型の項目}.ToString("yyyy-MM-dd");
参考
- 標準の日時書式指定文字列
課題・問題点など
- 日付項目に対する検索条件用に別途、DateTime 型の項目が必要。