【问题标题】:Pagination in Cosmos DB using Page Size and Page Number在 Cosmos DB 中使用页面大小和页码进行分页
【发布时间】:2018-07-04 00:34:22
【问题描述】:

我正在尝试使用 PageSizePageNumber 从 cosmosDB 返回项目。我知道我们可以在MaxItemCount 中设置页面大小,但是我们如何在这个函数中输入页码呢?

这是我目前得到的:

  public async Task<IEnumerable<T>> RunSQLQueryAsync(string queryString, int pageSize, int pageNumber)
        {
            var feedOptions = new FeedOptions { MaxItemCount = pageSize, EnableCrossPartitionQuery = true };
            IQueryable<T> filter = _client.CreateDocumentQuery<T>(_collectionUri, queryString, feedOptions);
            IDocumentQuery<T> query = filter.AsDocumentQuery();
            var currentPageNumber = 0;
            var documentNumber = 0;
            List<T> results = new List<T>();
            while (query.HasMoreResults)
            {
                foreach (T t in await query.ExecuteNextAsync())
                {
                    results.Add(t);
                    documentNumber++;
                }
                currentPageNumber++;
                return results;

            }
            return null;
        }

【问题讨论】:

    标签: azure asp.net-core pagination azure-cosmosdb paging


    【解决方案1】:

    目前,分页支持仅基于延续标记

    在下面找到一些关于此限制的有趣讨论和功能请求:


    --- 延续令牌示例 ---

    以下示例说明了一种基于所需页码、页面大小和继续标记查询文档的方法(与您的方法非常相似):

        private static async Task<KeyValuePair<string, IEnumerable<CeleryTask>>> QueryDocumentsByPage(int pageNumber, int pageSize, string continuationToken)
        {
            DocumentClient documentClient = new DocumentClient(new Uri("https://{CosmosDB/SQL Account Name}.documents.azure.com:443/"), "{CosmosDB/SQL Account Key}");
    
            var feedOptions = new FeedOptions {
                MaxItemCount = pageSize,
                EnableCrossPartitionQuery = true,
    
                // IMPORTANT: Set the continuation token (NULL for the first ever request/page)
                RequestContinuation = continuationToken 
            };
    
            IQueryable<CeleryTask> filter = documentClient.CreateDocumentQuery<CeleryTask>("dbs/{Database Name}/colls/{Collection Name}", feedOptions);
            IDocumentQuery<CeleryTask> query = filter.AsDocumentQuery();
    
            FeedResponse<CeleryTask> feedRespose = await query.ExecuteNextAsync<CeleryTask>();
    
            List<CeleryTask> documents = new List<CeleryTask>();
            foreach (CeleryTask t in feedRespose)
            {
                documents.Add(t);
            }
    
            // IMPORTANT: Ensure the continuation token is kept for the next requests
            return new KeyValuePair<string, IEnumerable<CeleryTask>>(feedRespose.ResponseContinuation, documents);
        }
    

    现在,以下示例说明如何通过调用先前的方法来检索给定页面的文档:

        private static async Task QueryPageByPage()
        {
            // Number of documents per page
            const int PAGE_SIZE = 3;
    
            int currentPageNumber = 1;
            int documentNumber = 1;
    
            // Continuation token for subsequent queries (NULL for the very first request/page)
            string continuationToken = null;
    
            do
            {
                Console.WriteLine($"----- PAGE {currentPageNumber} -----");
    
                // Loads ALL documents for the current page
                KeyValuePair<string, IEnumerable<CeleryTask>> currentPage = await QueryDocumentsByPage(currentPageNumber, PAGE_SIZE, continuationToken);
    
                foreach (CeleryTask celeryTask in currentPage.Value)
                {
                    Console.WriteLine($"[{documentNumber}] {celeryTask.Id}");
                    documentNumber++;
                }
    
                // Ensure the continuation token is kept for the next page query execution
                continuationToken = currentPage.Key;
                currentPageNumber++;
            } while (continuationToken != null);
    
            Console.WriteLine("\n--- END: Finished Querying ALL Dcuments ---");
        }
    

    【讨论】:

    • 感谢您的示例代码。我不知道为什么,但是当我将页面大小设置为 2 并且 cosmosDB 中的项目总数为 5 时。对于第一页,它返回 2 个项目 - 这很好;然后下一页,它出于某种奇怪的原因返回 1 个项目; 3d 页面,它再次返回 1 个项目。知道为什么吗?再次感谢!
    • 当继续标记为null时,表示根据查询没有更多可读取的文档。我将更新示例代码以反映这一点。
    • 如果不在正确的页面上不应该跳过吗?
    • 如何上一页?
    • pageNumberQueryDocumentsByPage 中的作用在哪里?
    【解决方案2】:

    现在可以通过新的 OFFSET LIMIT 子句在 Cosmos DB 中使用 Skip & take: https://docs.microsoft.com/en-us/azure/cosmos-db/sql-query-offset-limit

    【讨论】:

    • 这似乎不在链接的文档中...try here instead
    • 谢谢,@Matt。微软显然移动了文档。
    • 偏移限制在应用分页之前加载所有内容,这违背了分页的目的。我们通过不一次加载所有内容来分页以减少加载时间。
    【解决方案3】:

    公共静态列表分页(int pageNo=1,int pageSize=20) { List ArticlesList = new List(); var collection = UriFactory.CreateDocumentCollectionUri(databaseName, loginCollectionId);

        using (client = new DocumentClient(new Uri(endpointUrl), primaryKey))
        {
    
            var optionss = new FeedOptions
            {
                MaxItemCount = (pageNo!=1)?((pageNo-1)*pageSize): ((pageNo) * pageSize)
            };
    
            var query1 = client.CreateDocumentQuery<ArticlesListDetails>(collection, optionss).OrderByDescending(x => x._ts).AsDocumentQuery();
    
            var res = query1.ExecuteNextAsync<ArticlesListDetails>().Result;
            if (pageNo == 1)
            {
                return   ArticlesList = res.ToList();
            }
            else
            {
                var options = new FeedOptions
                {
                    MaxItemCount = pageSize,
                    RequestContinuation = res.ResponseContinuation
                };
    
                var query = client.CreateDocumentQuery<ArticlesListDetails>(collection, options).OrderByDescending(x => x._ts).AsDocumentQuery();
    
                while (query.HasMoreResults)
                {
                    return  ArticlesList = query.ExecuteNextAsync<ArticlesListDetails>().Result.ToList();
                }
            }
    
            return ArticlesList;
        }
    
    }
    

    【讨论】:

      【解决方案4】:

      有一种解决方法,但不是最佳的:

      ...
      int pageNumber = 1;
      int pageSize = 10;
      ...
          var query = Client.CreateDocumentQuery<T>(
                          UriFactory.CreateDocumentCollectionUri(DatabaseId, CollectionId),
                          new FeedOptions { MaxItemCount = pageNumber * pageSize }) // Limit count or returned items
                          .Where(predicate)
                          .AsDocumentQuery();
      
                      var results = new List<T>();
                      int resultsToSkip = (pageNumber - 1) * pageSize;
      
                      while (query.HasMoreResults)
                      {
                          var result = await query.ExecuteNextAsync<T>();
      
                          // Skip pages, not optimal way, the best solution is to use RequestContinuation token.
                          if (resultsToSkip > 0)
                          {
                              resultsToSkip--;
                              continue;
                          }
      
                          results.AddRange(result);
                      }
      
                      return results;
          ...
      

      【讨论】:

        猜你喜欢
        • 2020-09-14
        • 1970-01-01
        • 1970-01-01
        • 2021-10-27
        • 1970-01-01
        • 1970-01-01
        • 2020-03-05
        • 1970-01-01
        • 2021-03-10
        相关资源
        最近更新 更多