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



使用Cosmos SDK V3。

在下面的例子中,Cosmos支持LINQ跳过和Take进行服务器端分页吗?

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

我为什么这么说:

我尝试使用fiddler并在while循环的开头放置断点,以查看cosmos数据库是用skip和take调用的。然而,没有服务器端调用,似乎所有数据都是在调用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确实通过在查询中使用OFFSETLIMIT子句来支持skiptake,但由于以下原因,使用这种方法并不可取:

  • 就RU消耗而言,这会导致昂贵的操作
  • 它仍然不提供服务器端分页,因为当您使用OFFSETLIMIT执行查询时,您获得的文档数基于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();
}
}
}

它是受支持的,可以在queryable上使用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"}

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

最新更新