「データを一覧してページング表示する」という要件は エンプラ系でも コンシューマー系でも 常にある要件です。
ということで、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