在CosmosDB中使用CosmosClient分页

时间:2019-12-12 15:22:19

标签: c# azure-cosmosdb asp.net-core-3.0

我正在尝试使用SDK v3 CosmosClient而不是旧的DocumentClient来实现分页。

这样做的原因是看来DocumentClient不能很好地转换包含空间函数的LINQ查询(即:当使用Within()时,我将从DocumentClient中收到一条错误消息,指出该方法未实现)。

DocumentClient.CreateDocumentQuery<T>上,分页效果很好:

var query = DocumentClient.CreateDocumentQuery<T>(UriFactory.CreateDocumentCollectionUri("master", "features"), feedOptions)
                .Where(t => t.Type == typeof(T).Name)
                .Where(pred)
                .AsDocumentQuery();

string queryContinuationToken = null;
var page = await query.ExecuteNextAsync<T>();
if (query.HasMoreResults)
    queryContinuationToken = page.ResponseContinuation;

对于使用CosmosClient及其Container类收集延续令牌的地方,我有些困惑:

QueryRequestOptions options = new QueryRequestOptions();
options.MaxItemCount = maxRecords;

FeedIterator<T> feed;

if (continuationToken == "")
    feed = Container.GetItemLinqQueryable<T>(true, null, options).Where(x => x.Type == typeof(T).Name).Where(pred).ToFeedIterator();
else
    feed = Container.GetItemLinqQueryable<T>(true, continuationToken, options).Where(x => x.Type == typeof(T).Name).Where(pred).ToFeedIterator();

FeedIterator似乎有一些成员IDocumentQuery(例如HasMoreResults),但是我在任何地方都找不到延续令牌。

我想念什么?

3 个答案:

答案 0 :(得分:2)

好的,这是我实现的Where方法。乍一看似乎有用。

如果您进行var f = feed.ReadNextAsync(),将不会得到类型为FeedResponse的对象,从而阻止您访问令牌。您需要显式声明f

类型的FeedResponse<T>
public async Task<(IEnumerable<T> Results, string ContinuationToken)> Where<T>(Expression<Func<T, bool>> pred, int maxRecords = 0, string partitionKey = "", string continuationToken = "") where T : IDocumentModel
{

    QueryRequestOptions options = new QueryRequestOptions();

    if (partitionKey != "")
        options.PartitionKey = new PartitionKey(partitionKey);


    if (maxRecords == 0)
    {
        return (Container.GetItemLinqQueryable<T>(true, null, options).Where(x => x.Type == typeof(T).Name).Where(pred), "");
    }
    else
    {
        options.MaxItemCount = maxRecords;
        string token = "";
        FeedIterator<T> feed;
        List<T> res = new List<T>();

        if (continuationToken == "")
            feed = Container.GetItemLinqQueryable<T>(true, null, options).Where(x => x.Type == typeof(T).Name).Where(pred).ToFeedIterator();
        else
            feed = Container.GetItemLinqQueryable<T>(true, continuationToken, options).Where(x => x.Type == typeof(T).Name).Where(pred).ToFeedIterator();

        Microsoft.Azure.Cosmos.FeedResponse<T> f = await feed.ReadNextAsync();
        token = f.ContinuationToken;

        foreach (var item in f)
        {
            res.Add(item);
        }

        return (res, token);
    }

}

答案 1 :(得分:0)

在Cosmos SDK 3.12.0版本中,以下功能可以按预期工作,可以替代旧的DocumentQuery。

用于比较的原始DocumentClient方法:

[
   "2016" : ["09/2016", "10/2016", "11/2016", "12/2016"],
   "2017":  ["01/2017", "02/2017"...],
   "2018":  [....]
]

使用CosmosClient,它将变为:

IDocumentQuery<ToDoItem> query = client.CreateDocumentQuery<ToDoItem>(collectionUri)
            .Where(t => t.Description.Contains(searchterm))
            .AsDocumentQuery();

while (query.HasMoreResults)
{
  foreach (ToDoItem result in await query.ExecuteNextAsync())
  {
    log.LogInformation(result.Description);
  }
}

因此您的查询现在是var database = client.GetDatabase("ToDoItems"); var container = database.GetContainer("Items"); var query = container.GetItemLinqQueryable<ToDoItem>() .Where(t => t.Description.Contains(searchTerm)) .ToFeedIterator(); while (query.HasMoreResults) { foreach (ToDoItem result in await query.ReadNextAsync()) { log.LogInformation(result.Description); } } ,可以在其上调用FeedIteratorHasMoreResults

诚然,这无法让您访问FeedIterator附带的诊断信息,要求付费等,但是它将整洁地浏览结果。

答案 2 :(得分:0)

IQueryable<returnVModel> query;
var requestOptions = new QueryRequestOptions
{
 MaxItemCount = 20
};
if (Token == "" || Token == null)
{
      query = Container.GetItemLinqQueryable<returnVModel>(false, null, requestOptions).Where(x => x.id == id);
 }
 else
 {
      query = Container.GetItemLinqQueryable<returnVModel>(false, Token, requestOptions).Where(x => x.id == id);
 }
 var ct = new CancellationTokenSource();
 var totalCount = await query.CountAsync(ct.Token); //Total Count
 var feedIterator = query.ToFeedIterator();
 var queryResults = new List<returnVModel>();
 FeedResponse<returnVModel> feedResults = await feedIterator.ReadNextAsync(ct.Token);
 queryResults.AddRange(feedResults); // Output
 var PaginationToken = feedResults.ContinuationToken //Token

第一次我们需要将令牌传递为空,从下一页开始传递我们在之前输出中收到的令牌。

分页在 v3 中运行良好。