I'm working through migrating an application from Cosmos C# SDK v2 to v3. I hit a roadblock with pagination and can't seem to figure it out.
I have a request that will search a particular container and paginate through requests. My code is setup to handle the page size (maxItemCount) and continuation token. Additionally, the first few requests work great. But when I get to like the fourth page, the continuation token I get in the response from Cosmos is invalid. It looks like this:
[{\"range\":{\"min\":\"05C1DFFFFFFFFC\",\"max\":\"FF\"}}]
And, when I pass that to the next request to grab the following page, I get the following error:
Response status code does not indicate success: BadRequest (400); Substatus: 0; ActivityId: ; Reason: (Response status code does not indicate success: BadRequest (400); Substatus: 0; ActivityId: ; Reason: (Response status code does not indicate success: BadRequest (400); Substatus: 0; ActivityId: ; Reason: (Response status code does not indicate success: BadRequest (400); Substatus: 0; ActivityId: ; Reason: (Response status code does not indicate success: BadRequest (400); Substatus: 0; ActivityId: ; Reason: (Response status code does not indicate success: BadRequest (400); Substatus: 0; ActivityId: ; Reason: (Response status code does not indicate success: BadRequest (400); Substatus: 0; ActivityId: ; Reason: (Response status code does not indicate success: BadRequest (400); Substatus: 0; ActivityId: ; Reason: (CompositeContinuationToken is missing field: 'token': {"range":{"min":"05C1DFFFFFFFFC","max":"FF"}}););););););););
Now, I have found that if I modify the continuation token before sending it, I can get the request to succeed. This requires me adding a property to the JSON string of, you guessed it, token
.
Here is the code in question that processes this request:
IQueryable<LeadEntity> query;
var queryResults = new List<LeadEntity>();
var requestOptions = new QueryRequestOptions
{
MaxItemCount = input.Page?.Size ?? Core.Shared.Constants.PageSize.Default
};
if (input.Page?.ContinuationToken == "" || input.Page?.ContinuationToken == null)
{
query = Container.GetItemLinqQueryable<LeadEntity>(false, null, requestOptions);
}
else
{
query = Container.GetItemLinqQueryable<LeadEntity>(false, input.Page.ContinuationToken, requestOptions);
}
if (input.Criteria?.CallKeys.IsActive ?? false)
{
var callKeys = (input.Criteria.CallKeys?.Value ?? Enumerable.Empty<Guid>())
.Select(a => a.ToString());
query = query.Where(entity => callKeys.Contains(entity.CallKey));
}
if (input.Criteria?.AdvisorOids.IsActive ?? false)
{
var advisorOids = (input.Criteria.AdvisorOids?.Value ?? Enumerable.Empty<Guid>())
.Select(a => a.ToString());
query = query.Where(entity => advisorOids.Contains(entity.AdvisorOid));
}
if (input.Criteria?.CreatedOn.IsActive ?? false)
{
var start = input.Criteria.CreatedOn.Value.Start;
var duration = input.Criteria.CreatedOn.Value.Duration;
var end = input.Criteria.CreatedOn.Value.End;
if (start.HasValue && end.HasValue)
{
query = query.Where(entity => start.Value <= entity.Created.On && entity.Created.On <= end.Value);
}
else if (start.HasValue)
{
query = query.Where(entity => start.Value <= entity.Created.On && entity.Created.On <= start.Value.Add(+duration));
}
else if (end.HasValue)
{
query = query.Where(entity => end.Value >= entity.Created.On && entity.Created.On >= end.Value.Add(-duration));
}
else
{
query = query.Where(entity => false);
}
}
if (input.Criteria?.HasPortalKey.IsActive ?? false)
{
query = query.Where(entity => input.Criteria.HasPortalKey.Value
&& entity.PortalKey != null
&& entity.PortalKey != default(Guid).ToString()
);
}
query = query.Where(a => a.IsRemoved.IsDefined() ? !a.IsRemoved : true);
var totalCount = await query.CountAsync(ct);
query = input.Orderings?.Any() ?? false
? query.OrderBy(string.Join(",", input.Orderings))
: query;
query = input.Limit.HasValue
? query.Take(input.Limit.Value)
: query;
var feedIterator = query.ToFeedIterator();
FeedResponse<LeadEntity> feedResults = await feedIterator.ReadNextAsync(ct);
queryResults.AddRange(feedResults);
Console.WriteLine($"[LeadSearchAsync] total operation cost: {feedResults.RequestCharge} RUs");
var output = new PageOutput<LeadOutput>
{
Items = Mapper.Map<IEnumerable<LeadOutput>>(queryResults),
ContinuationToken = feedResults.ContinuationToken,
TotalCount = totalCount
};
return output;
I have been googling my butt off and have only found a few instances of this happening and it's normally on the v2 sdk, not the v3. Part of me wants to think I'm doing pagination wrong, but it appears to be working for the most part.
Any help resolving this would be greatly appreciated. I haven't found a whole lot of documentation on the v3 SDK regarding pagination and its clearly different than v2.