在 Cosmos 数据库上使用 LINQ Skip Take 是否支持服务器端分页?

问题描述 投票:0回答:3

使用Cosmos SDK V3。

在以下示例中,Cosmos 是否支持 LINQ 跳过和 Take 进行服务器端分页?

根据我的分析,虽然我能够检索数据,但似乎查询没有进行服务器端分页。

我为什么这么说:

我尝试使用fiddler并在while循环的开头放置断点,以查看使用skip和take调用cosmos db。但是没有服务器端调用,似乎所有数据都是在调用 Count 本身时获取的。

private static async Task ExportAsync<T>(Database database, string paritionKeyName, string partitionKeyPath)
{
    IOrderedQueryable<T> query = database
                                .GetContainer(SourceContainerName)
                                .GetItemLinqQueryable<T>(allowSynchronousQueryExecution: true);
    var totalCount = query.Count();

    int skip = 0;
    int take = MAX_BATCH_SIZE;
    int taken = 0;

    while (taken < totalCount)
    {
        //breakpoint
        var itemsToInsert = query.Skip(skip).Take(take).ToList().AsReadOnly();

        await ProcessBatchAsync(database, paritionKeyName, partitionKeyPath, itemsToInsert);

        taken += take;
        skip++;
    }
}
c# linq azure-cosmosdb
3个回答
2
投票

除了 @404 在答案中提到的内容之外,Cosmos DB 确实通过在查询中使用

skip
take
子句来支持
OFFSET
LIMIT
,但实际上不建议使用它,原因如下:

  • 这会导致 RU 消耗方面的昂贵操作。
  • 它仍然不提供服务器端分页,因为当您使用
    OFFSET
    LIMIT
    执行查询时,您根据
    LIMIT
    的值获取的文档数量,并且它不会告诉您是否还有更多文档可用。

有关 OFFSET 和 LIMIT 子句的更多信息可以在此处找到:https://learn.microsoft.com/en-us/azure/cosmos-db/sql-query-offset-limit

在您的场景中,建议使用延续令牌(如 @mjwills 所建议)。使用延续令牌,您可以在请求一定数量的项目时实现服务器端分页(使用

QueryRequestOptions
指定)。当查询执行时,您会得到两件事:

  1. 与您的查询相匹配的文档以及
  2. 如果有更多文档与您的查询匹配,则为继续标记。

您可以处理收到的文件。如果您收到延续令牌,则向 Cosmos DB 服务发送另一个查询(但这次包含延续令牌),该服务将返回下一组文档。

请参阅示例代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Azure.Cosmos;
using Microsoft.Azure.Cosmos.Linq;

    namespace SO67263501
    {
        class Program
        {
            static string connectionString = "connection-string";
            static string databaseName = "database-name";
            static string containerName = "container-name";
            static async Task Main(string[] args)
            {
                string continuationToken = null;
                int pageSize = 100;//Let's fetch 100 items at a time
                CosmosClient cosmosClient = new CosmosClient(connectionString);
                Container container = cosmosClient.GetContainer(databaseName, containerName);
                QueryRequestOptions requestOptions = new QueryRequestOptions()
                {
                    MaxItemCount = pageSize
                };
                do
                {
                    FeedIterator<dynamic> queryResult = container.GetItemLinqQueryable<dynamic>(true, continuationToken, requestOptions).ToFeedIterator();
                    FeedResponse<dynamic> feedResponse = await queryResult.ReadNextAsync();
                    List<dynamic> documents = feedResponse.Resource.ToList();
                    continuationToken = feedResponse.ContinuationToken;
                    //Do something with the documents...
                } while (continuationToken != null);
                Console.WriteLine("All done...");
                Console.WriteLine("Press any key to terminate the application.");
                Console.ReadKey();
            }
        }
    }

2
投票

它受支持,可以使用

ToString()
在可查询上进行测试,以查看发送到数据库的查询。

var query = container.GetItemLinqQueryable<Dictionary<string, object>>()
    .OrderBy(x => x["_ts"])
    .Skip(50)
    .Take(10)
    .ToString();
//result:
//{"query":"SELECT VALUE root FROM root ORDER BY root[\"_ts\"] ASC OFFSET 50 LIMIT 10"}

使用

OFFSET
会以线性方式增加 RU 使用量。当您有很多页面时,在后面的页面中使用这种类型的查询会变得非常昂贵。如果可能,您最好使用延续标记或
WHERE
子句来过滤结果。


0
投票

我很确定有两个问题

  1. 正在执行skip++;而不是执行skip += take;
  2. 使用 FeedIterator .ToFeedIterator() 循环结果并将它们放入列表中。
© www.soinside.com 2019 - 2024. All rights reserved.