如何从 Cosmos DB 中过滤的 FeedIterator 获取总数

How to get total count from a filtered FeedIterator in Cosmos DB

我有以下代码来过滤 Cosmos DB 中的一些数据:

Container container = await service.GetContainer(containerName, partitionKeyA);
using (FeedIterator<T> resultSet = container.GetItemQueryIterator<T>(
    queryDefinition: GetComplexSQLQueryDefinition(),
    paginationInfo.Token,
    requestOptions: new QueryRequestOptions()
    {
      PartitionKey = new PartitionKey(partitionKey),
      MaxItemCount = 10
    }
    ))
{ 
  FeedResponse<T> response = await resultSet.ReadNextAsync();
  //get total count
  int totalCount = -1;
}

查询可以产生很多记录,因此我需要分页

不幸的是,我需要项目的 总数 - 它可能会因过滤而异。

根据这个answer,我只有两个选择:

  1. 我创建了第二个 SQL 查询,它使用 SQL select 命令来计算记录 - 然后再次查询数据库 到select实际记录:

    var query = new QueryDefinition("SELECT value count(1) FROM c WHERE c.tenantId = @type");
    query.WithParameter("@type", '5d484526d76e9653e6226aa2');
    var container = client.GetContainer("DatabaseName", "CollectionName");
    var iterator = container.GetItemQueryIterator<int>(query);
    var count = 0;
    while (iterator.HasMoreResults)
    {
        var currentResultSet = await iterator.ReadNextAsync();
        foreach (var res in currentResultSet)
        {
            count += res;
        }
    }
    Console.WriterLine($"The first count is: {count}");
    
  2. 我将我的 复杂 SQL 查询转换为 LINQ 并使用它的 Count 方法:

    //应该格式化为code

    var 计数 = container.GetItemLinqQueryable(true) .Count(item => item.tenantId.Equals('5d484526d76e9653e6226aa2'));

对于这样一个简单的任务来说,两者似乎都很麻烦,所以我想知道是否有更好或更有效的方法。

我还能尝试什么?

下面是我使用 .NET SDK v3 基于任意 QueryDefinition 执行计数操作的大致代码。有一些方面没有显示,例如使用 System.Text.Json 反序列化的完整逻辑,但希望能找到要点。这个想法是提供一个带有 base:

的查询
SELECT VALUE COUNT(1) FROM c

获取包含结果计数的单个文档作为结果;

public async Task<int?> CountAsync(QueryDefinition query, string partitionKey)
{
    var options = new QueryRequestOptions() { PartitionKey = new(partitionKey), MaxItemCount = 1 };
    int? count = null;
    using var resultSet = cosmosService.DataContainer.GetItemQueryStreamIterator(query, requestOptions: options);
    while (resultSet.HasMoreResults)
    {
        using var response = await resultSet.ReadNextAsync();
        if (response.IsSuccessStatusCode)
        {
            // Deserialize response into CosmosResponse<int>
            var deserializeResult = await FromJsonStream<CosmosResponse<int>>(response.Content);
            if (!deserializeResult.HasSuccessValue(out CosmosResponse<int>? responseContent))
            {
                return null; // Or some failure
            }

            if (responseContent.Documents.Any())
            {
                count = responseContent.Documents[0];
                break;
            }
            else
            {
                return null;// Or some failure
            }
        }
        else // Unexpected status. Abort processing.
        {
            return null;// Or some failure
        }
    }

    return count;
}

帮助 class 反序列化响应:

public class CosmosResponse<T>
{
    [JsonPropertyName("Documents")]
    public IReadOnlyList<T> Documents { get; set; } = Array.Empty<T>();

    [JsonPropertyName("_count")]
    public int Count { get; set; }
}