DocumentDB でページング処理を行う
「データを一覧してページング表示する」という要件は エンプラ系でも コンシューマー系でも 常にある要件です。
ということで、DocumentDBでデータのページング取得を行う方法を書いておこうと思います。
1. 前提
DocumentDBアカウントには、既に以下のデータが保存されている状態を前提とします。
データベース名: ExampleDB1
コレクション名: ExampleCollection1
ドキュメント: ProductItemドキュメントが1,000件保存されている
ProductItemは以下のようなデータクラスです。
// 保存するドキュメント using Newtonsoft.Json; namespace PagingExample { public class ProductItem { [JsonProperty(PropertyName = "id")] public string Id { get; set; } public string Name { get; set; } public int Price { get; set; } public int StockNumber { get; set; } } }
以下のような感じで、適当なデータが入っています。
2. DocumentDB操作管理クラスを用意
DocumentDBを操作する管理クラス(DocumentManager)の実装は以下の通りです。
using System; using System.Collections.Generic; using System.Linq; using Microsoft.Azure.Documents; using Microsoft.Azure.Documents.Linq; using Microsoft.Azure.Documents.Client; using System.IO; using System.Threading.Tasks; namespace PagingExample { public class DocumentManager { private const string EndpointUrl = "https://rddocdb.documents.azure.com:443/"; private const string PrimaryKey = "[マスターキー]"; private const string DatabaseID = "ExampleDB1"; private const string CollectionID = "ExampleCollection1"; private DocumentClient client; // DocumentClientオブジェクトを初期化 public async Task<bool> InitializeDocumentClient() { this.client = new DocumentClient(new Uri(DocumentManager.EndpointUrl), DocumentManager.PrimaryKey, new ConnectionPolicy { ConnectionMode = ConnectionMode.Gateway, ConnectionProtocol = Protocol.Https }); return true; } // ProductItemを 10件 ずつ取得 public async Task<(List<ProductItem>, string)> QueryProductItems(string continuration) { List<ProductItem> result = null; var feedOption = new FeedOptions() { MaxItemCount = 10, RequestContinuation = continuration }; Uri documentCollectionUri = UriFactory.CreateDocumentCollectionUri( DocumentManager.DatabaseID, DocumentManager.CollectionID); var query = this.client.CreateDocumentQuery<ProductItem>( documentCollectionUri, feedOption) .Where( p => p.Price > 700 ) .AsDocumentQuery(); var ret = await query.ExecuteNextAsync<ProductItem>(); result = ret.ToList(); string retContinuration = ret.ResponseContinuation; return (result, retContinuration); } } }
InitializeDocumentClient()メソッド
DocumentClientオブジェクトを new してメンバーフィールドに保持します。
QueryProductItems()メソッド
引数 continuration は、DocumentDBに「どのページ(というかどの位置から継続抽出するか)」の状態文字列です。1ページ目の取得時には「空文字列」を設定します。
CreateDocumentQuery()を呼び出す際の引数「FeedOptions」で、「1度に取得する件数(MaxItemCount)」と「どのページ(どこから継続取得するかを表す状態文字列(RequestContinuation )」を指定します。
AsDocumentQuery()呼び出して、ExecuteNextAsync()呼び出した結果から、10件の抽出データ(ret.ToList()) と
次のページを読み込む為の継続取得状態文字列(ret.ResponseContinuation)が得られます。
これら2つのデータをタプルで戻り値として呼び出し元に返却しています。
また、サンプルなので Price > 700 という固定的なWhere条件を付加しています。
3. DocumentManagerを呼び出してページングデータ取得
では、前述で用意した DocumentManager を利用してProductItemデータを10件ずつページング取得したいと思います。
以下が実装になります。
var manager = new DocumentManager(); var initResult = await manager.InitializeDocumentClient(); // コンソール出力用ページ番号 int pageNo = 0; // DocumentDBに投げるcontinuration値(1ページ目は空文字列) string continuration = ""; do { (List<ProductItem> items, string continuration) queryResult = await manager.QueryProductItems(continuration); // 次のページを取得するためのcontinuration文字列取得 continuration = queryResult.continuration; // 取得したデータをコンソール出力 Console.WriteLine(string.Format("page {0}", pageNo)); Console.WriteLine(string.Format("continuration {0}", continuration)); foreach (var item in queryResult.items) { Console.WriteLine(string.Format("{0} {1}", item.Name, item.Price)); } Console.WriteLine("-----"); pageNo++; } while (!string.IsNullOrEmpty(continuration)); // continurationが空だと終端ページに達したということ
データ抽出で得られた(Responseされた)Continuation値を、次のデータ抽出(Request)で利用する形です。
4. 実行結果
実行結果は以下の通りです。
page 0 continuration {"token":"+RID:mi01AOyHIgA7AAAAAAAAAA==#RT:1#TRC:10#FPC:AgEAAAB8AD8AABhwwAEHHHDAAQcccMABBxxwwAEHHHDAAQcccMABLcAHHHDAAQcccMABBxxwwAEHHHDAAQcccMABBxxwwAEHHHDAAQcccMABBxxwwAEHHHDAAQcccMABBxxwwAEHHHDAAQcccMABBxxwwAEHHHDAAQcccMABBxxwwAE=","range":{"min":"","max":"FF"}} Product_8 800 Product_9 900 Product_18 800 Product_19 900 Product_28 800 Product_29 900 Product_38 800 Product_39 900 Product_48 800 Product_49 900 ----- page 1 continuration {"token":"+RID:mi01AOyHIgBtAAAAAAAAAA==#RT:2#TRC:20#FPC:AgEAAAB2AG8AAGDAAQcccMABBxxwwAEHHHDAAQcccMABBxxwwAEHKsAccMABBxxwwAEHHHDAAQcccMABBxxwwAEHHHDAAQcccMABBxxwwAEHHHDAAQcccMABBxxwwAEHHHDAAQcccMABBxxwwAEHHHDAAQcccMABBxxwwAE=","range":{"min":"","max":"FF"}} Product_58 800 Product_59 900 Product_68 800 Product_69 900 Product_78 800 Product_79 900 Product_88 800 Product_89 900 Product_98 800 Product_99 900 ----- page 2 continuration {"token":"+RID:mi01AOyHIgCfAAAAAAAAAA==#RT:3#TRC:30#FPC:AgEAAABuAJ+ANcABBxxwwAEHHHDAAQcccMABBxxwwAEHHHDAAQcccMABBxxwwAEHHHDAAQcccMABBxxwwAEHHHDAAQcccMABBxxwwAEHHHDAAQcccMABBxxwwAEHHHDAAQcccMABBxxwwAEHHHDAAQcccMAB","range":{"min":"","max":"FF"}} Product_108 800 Product_109 900 Product_118 800 Product_119 900 Product_128 800 Product_129 900 Product_138 800 Product_139 900 Product_148 800 Product_149 900 ----- page 3 continuration {"token":"+RID:mi01AOyHIgDRAAAAAAAAAA==#RT:4#TRC:40#FPC:AgEAAABoAN8ABhxwwAEHHHDAAQcccMABBxxwwAEHHHDAAQcccMABI8AHHHDAAQcccMABBxxwwAEHHHDAAQcccMABBxxwwAEHHHDAAQcccMABBxxwwAEHHHDAAQcccMABBxxwwAEHHHDAAQcccMAB","range":{"min":"","max":"FF"}} Product_158 800 Product_159 900 Product_168 800 Product_169 900 Product_178 800 Product_179 900 Product_188 800 Product_189 900 Product_198 800 Product_199 900 ----- page 4 continuration {"token":"+RID:mi01AOyHIgADAQAAAAAAAA==#RT:5#TRC:50#FPC:AgEAAABiAA8BGHDAAQcccMABBxxwwAEHHHDAAQcccMABBxxwwAEHIMAccMABBxxwwAEHHHDAAQcccMABBxxwwAEHHHDAAQcccMABBxxwwAEHHHDAAQcccMABBxxwwAEHHHDAAQcccMAB","range":{"min":"","max":"FF"}} Product_208 800 Product_209 900 Product_218 800 Product_219 900 Product_228 800 Product_229 900 Product_238 800 Product_239 900 Product_248 800 Product_249 900 ----- ...省略... ----- page 18 continuration {"token":"+RID:mi01AOyHIgC-AwAAAAAAAA==#RT:19#TRC:190#FPC:AgEAAAAKAL+DA8ABBxxwwAE=","range":{"min":"","max":"FF"}} Product_908 800 Product_909 900 Product_918 800 Product_919 900 Product_928 800 Product_929 900 Product_938 800 Product_939 900 Product_948 800 Product_949 900 ----- page 19 continuration Product_958 800 Product_959 900 Product_968 800 Product_969 900 Product_978 800 Product_979 900 Product_988 800 Product_989 900 Product_998 800 Product_999 900 ----- 続行するには何かキーを押してください . . .
5. HTTP通信内容
DocumentDBライブラリにより、裏側で具体的にどのようなREST API リクエストとレスポンスが行われたのかをFiddlerで確認します。
上記Fiddler画面から以下のことが分かります。
- 「/dbs/ExampleDB1/colls/ExampleCollection1/docs」へのページごとのリクエストが行われた
- FeedOptions.MaxItemCount / FeedOptions.RequestContinuation の値がHTTPリクエストヘッダでそれぞれ「x-ms-max-item-count」「x-ms-continuation」して指定されている
- HTTPレスポンスヘッダ「x-ms-continuation」で、次のページを読み込むための継続状態文字列が返されている
資料
公式ドキュメントでは以下を見ればいいのかな。
Common DocumentDB REST request headers | Microsoft Docs
Common Azure Cosmos DB REST response headers | Microsoft Docs