{"id":3892,"date":"2022-01-13T06:00:10","date_gmt":"2022-01-13T14:00:10","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/cosmosdb\/?p=3892"},"modified":"2022-01-13T14:11:17","modified_gmt":"2022-01-13T22:11:17","slug":"tips-tricks-query-pagination","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/cosmosdb\/tips-tricks-query-pagination\/","title":{"rendered":"Tips &#038; Tricks for Query Pagination in Azure Cosmos DB"},"content":{"rendered":"<p>In this post, we&#8217;ll take a look at tips for query pagination in Azure Cosmos DB. Pagination of query results is done to divide a large volume of data across many small pages or handle a long-running query. While it\u2019s important to understand how query pagination works in Azure Cosmos DB, it\u2019s not a concept that\u2019s unique to databases.<\/p>\n<p>&nbsp;<\/p>\n<p>Let&#8217;s start by taking a look an example of pagination outside of databases. Here&#8217;s an Internet search that returns 61.8 million results:<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/cosmosdb\/wp-content\/uploads\/sites\/52\/2022\/01\/paginationCosmosDB.png\"><img decoding=\"async\" class=\"alignnone size-large wp-image-3896\" src=\"https:\/\/devblogs.microsoft.com\/cosmosdb\/wp-content\/uploads\/sites\/52\/2022\/01\/paginationCosmosDB-1024x182.png\" alt=\"Image paginationCosmosDB\" width=\"640\" height=\"114\" srcset=\"https:\/\/devblogs.microsoft.com\/cosmosdb\/wp-content\/uploads\/sites\/52\/2022\/01\/paginationCosmosDB-1024x182.png 1024w, https:\/\/devblogs.microsoft.com\/cosmosdb\/wp-content\/uploads\/sites\/52\/2022\/01\/paginationCosmosDB-300x53.png 300w, https:\/\/devblogs.microsoft.com\/cosmosdb\/wp-content\/uploads\/sites\/52\/2022\/01\/paginationCosmosDB-768x137.png 768w, https:\/\/devblogs.microsoft.com\/cosmosdb\/wp-content\/uploads\/sites\/52\/2022\/01\/paginationCosmosDB.png 1248w\" sizes=\"(max-width: 640px) 100vw, 640px\" \/><\/a><\/p>\n<p>Only 10 results are shown on the first page, but you can select to go to a later page if you want more results:<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/cosmosdb\/wp-content\/uploads\/sites\/52\/2022\/01\/TabPagination.png\"><img decoding=\"async\" class=\"alignnone size-full wp-image-3897\" src=\"https:\/\/devblogs.microsoft.com\/cosmosdb\/wp-content\/uploads\/sites\/52\/2022\/01\/TabPagination.png\" alt=\"Image TabPagination\" width=\"698\" height=\"138\" srcset=\"https:\/\/devblogs.microsoft.com\/cosmosdb\/wp-content\/uploads\/sites\/52\/2022\/01\/TabPagination.png 698w, https:\/\/devblogs.microsoft.com\/cosmosdb\/wp-content\/uploads\/sites\/52\/2022\/01\/TabPagination-300x59.png 300w\" sizes=\"(max-width: 698px) 100vw, 698px\" \/><\/a><\/p>\n<p>Why is it beneficial to break up the results into multiple pages? There are a few reasons:<\/p>\n<ul>\n<li><strong>Data volume &#8211;<\/strong> There\u2019s a finite amount of data that the server can return back to my web browser. Returning the full 61.8 million results would take too many resources.<\/li>\n<li><strong>Execution time<\/strong> &#8211; Users are only willing to wait a short amount of time for data. In general, most users expect a web page to return data within a second or less.<\/li>\n<li><strong>Usability &#8211;<\/strong> It\u2019s easiest for the user to read through a small set of results at a time. Even if we could return all 61.8 million results at once, the user would have a more difficult time digesting it.<\/li>\n<\/ul>\n<p>In Azure Cosmos DB, there are similar reasons why queries can be split up into multiple pages of results:<\/p>\n<ul>\n<li><strong>Data volume &#8211;<\/strong>\u00a0Each page of query results is capped at <strong>4 MB<\/strong>.<\/li>\n<li><strong>Execution<\/strong> <strong>time &#8211; <\/strong>Every query execution is capped at <strong>5 seconds<\/strong>. Even if you run a query and results aren\u2019t available yet, you will always get a response back from Azure Cosmos DB after 5 seconds.<\/li>\n<li><strong>Usability &#8211;<\/strong>\u00a0Many applications can only handle a certain number of results back at once from Azure Cosmos DB. This is configurable with the <strong>MaxItemCount<\/strong>.<\/li>\n<\/ul>\n<p>There are also cases where the query engine returns results in multiple pages because it\u2019s better for query performance. This is common for cross-partition queries, where you\u2019ll occasionally get an empty page of results returned if there aren\u2019t results from all physical partitions. The query engine may also split up results due to throttling.<\/p>\n<p>&nbsp;<\/p>\n<p><strong>Pagination tips<\/strong><\/p>\n<p>Your code should always be able to handle the case where query results span multiple pages. <a href=\"https:\/\/github.com\/Azure\/azure-cosmos-dotnet-v3\/blob\/master\/Microsoft.Azure.Cosmos.Samples\/Usage\/Queries\/Program.cs#L294\">Here\u2019s a query pagination example using the .NET SDK. <\/a>The Azure Cosmos DB SDK does most of the work for you. Including a while loop is essential here, allowing the query to keep running until there are no more results.<\/p>\n<p>If you want to stop your query and resume it later, you can extract a continuation token. A continuation token is a string (up to 16 KB) that the query engine can use to continue the query without having to repeat previous executions leading up to that point. You can limit continuation token size if needed, but we recommend keeping it as high as your application allows. <a href=\"https:\/\/github.com\/Azure\/azure-cosmos-dotnet-v3\/blob\/master\/Microsoft.Azure.Cosmos.Samples\/Usage\/Queries\/Program.cs#L230\">Here\u2019s how to do pagination with continuation tokens.<\/a><\/p>\n<p><strong>Getting query results as quickly as possible<\/strong><\/p>\n<p>In some cases, you might want your query to return as much data as it can, as quickly as possible. The best way to do this is by minimizing the number of round trips that your application needs to make to Azure Cosmos DB.<\/p>\n<p>In this case, you should set <strong>MaxItemCount<\/strong> to <strong>-1<\/strong> in your QueryOptions. In most Azure Cosmos DB SDK versions, MaxItemCount defaults to 100. With that default setting, queries are limited to returning 100 items per query page, regardless of whether the query is close to hitting the 4 MB limit per page limit or 5 second query execution limit. If the query engine can return more results in each page, the query will require fewer round trips.<\/p>\n<p>Here\u2019s an example:<\/p>\n<pre>string sqlQuery = \"SELECT TOP 20 * FROM c WHERE c.Item = 'shirt' AND c.Price &gt; 75\";\r\n\r\nQueryDefinition query = new QueryDefinition(sqlQuery);\r\n\r\nFeedIterator&lt;Item&gt; resultSetIterator = exampleApp.container.GetItemQueryIterator&lt;Item&gt;(\r\nquery, requestOptions: new QueryRequestOptions\r\n    {\r\n        MaxItemCount = -1\r\n    }\r\n);<\/pre>\n<p><strong>Retrieving an exact number of query results<\/strong><\/p>\n<p>Many applications want to obtain an exact number of query results. In the earlier Bing search example, the server returned 10 results per page. In that case, while it was important to have a maximum of 10 results per page, it was also totally fine if there were fewer than 10 results. However, there are other instances where you might want an exact number of results returned.<\/p>\n<p>Here\u2019s one approach to get an exact number of query results, assuming exactly 10 results are desired:<\/p>\n<ol>\n<li>Run the query with MaxItemCount = 10<\/li>\n<li>If n results are returned from the query and n &lt; 10, repeat the query with a new MaxItemCount value of 10 \u2013 n<\/li>\n<li>Repeat step 2, subtracting the total number of query results so far from the new MaxItemCount value<\/li>\n<\/ol>\n<p><strong>Cancelling expensive queries<\/strong><\/p>\n<p>An expensive query can quickly consume a significant amount of your provisioned throughput. Each time a page of query results is returned, you can check the query\u2019s RU charge and, if necessary, stop progressing to the next page. Because a query will return results after 5 seconds, this limits the damage from an unexpectedly expensive query. When a query is executing, you cannot stop execution until the query engine returns a response for that page.<\/p>\n<p>Here\u2019s an example for you to adjust your code to support stopping expensive queries:<\/p>\n<pre>double totalRUsConsumed = 0;\r\ndouble budgetRUs = 100;\r\n\r\nFeedIterator&lt;Item&gt; resultSetIterator = exampleApp.container.GetItemQueryIterator&lt;Item&gt;(\r\nquery, requestOptions: new QueryRequestOptions\r\n    {\r\n        MaxItemCount = -1\r\n    });\r\n\r\nFeedResponse&lt;Item&gt; response;\r\n\r\nwhile (resultSetIterator.HasMoreResults)\r\n{\r\n    response = await resultSetIterator.ReadNextAsync();\r\n    totalRUsConsumed = totalRUsConsumed + response.RequestCharge;\r\n\r\n    if (totalRUsConsumed &gt; budgetRUs)\r\n    {\r\n        throw new Exception(\"RUs budget exceeded!\");\r\n    }\r\n}\r\n<\/pre>\n<p><strong>Learn more about query pagination in Azure Cosmos DB: <\/strong><\/p>\n<ul>\n<li><a href=\"https:\/\/docs.microsoft.com\/azure\/cosmos-db\/sql\/sql-query-pagination\">Query pagination documentation<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/Azure\/azure-cosmos-dotnet-v3\/blob\/master\/Microsoft.Azure.Cosmos.Samples\/Usage\/Queries\/Program.cs\">Query pagination code samples (.NET)<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/Azure-Samples\/azure-cosmos-java-sql-api-samples\/blob\/main\/src\/main\/java\/com\/azure\/cosmos\/examples\/queries\/sync\/QueriesQuickstart.java\">Query pagination code samples (Java)<\/a><\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>In this blog, we&#8217;ll take a look at tips for query pagination in Azure Cosmos DB. Pagination is the process of dividing a large volume of data across many small pages.<\/p>\n","protected":false},"author":12128,"featured_media":3896,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[14,818,19],"tags":[],"class_list":["post-3892","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-core-sql-api","category-query","category-tips-and-tricks"],"acf":[],"blog_post_summary":"<p>In this blog, we&#8217;ll take a look at tips for query pagination in Azure Cosmos DB. Pagination is the process of dividing a large volume of data across many small pages.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/cosmosdb\/wp-json\/wp\/v2\/posts\/3892","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/cosmosdb\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/cosmosdb\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/cosmosdb\/wp-json\/wp\/v2\/users\/12128"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/cosmosdb\/wp-json\/wp\/v2\/comments?post=3892"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/cosmosdb\/wp-json\/wp\/v2\/posts\/3892\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/cosmosdb\/wp-json\/wp\/v2\/media\/3896"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/cosmosdb\/wp-json\/wp\/v2\/media?parent=3892"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/cosmosdb\/wp-json\/wp\/v2\/categories?post=3892"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/cosmosdb\/wp-json\/wp\/v2\/tags?post=3892"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}