使用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++;
}
}
除了 @404 在答案中提到的内容之外,Cosmos DB 确实通过在查询中使用
skip
和 take
子句来支持 OFFSET
和 LIMIT
,但实际上不建议使用它,原因如下:
OFFSET
和 LIMIT
执行查询时,您根据 LIMIT
的值获取的文档数量,并且它不会告诉您是否还有更多文档可用。有关 OFFSET 和 LIMIT 子句的更多信息可以在此处找到:https://learn.microsoft.com/en-us/azure/cosmos-db/sql-query-offset-limit。
在您的场景中,建议使用延续令牌(如 @mjwills 所建议)。使用延续令牌,您可以在请求一定数量的项目时实现服务器端分页(使用
QueryRequestOptions
指定)。当查询执行时,您会得到两件事:
您可以处理收到的文件。如果您收到延续令牌,则向 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();
}
}
}
它受支持,可以使用
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
子句来过滤结果。
我很确定有两个问题