Search documentationEsc

Querying

Filters, paging, continuation tokens, streaming, and the specification pattern.

The Cosmos Repository supports a rich set of methods for the queries you’ll actually run: simple Where filters, page-number / page-size paging, Cosmos DB continuation-token paging, and a specification pattern for more advanced scenarios.

Basic queries

The simplest entry point is IRepository<TItem>.GetAsync — pass an expression and the library translates it into a Cosmos SQL query.

public static class PeopleRepositoryExtensions
{
public static async Task<IEnumerable<Person>> GetPeopleOlderThan(
this IRepository<Person> repository, DateTime date)
{
return await repository.GetAsync(p => p.BirthDate > date);
}
public static async Task<IEnumerable<Person>> GetPeopleWithoutMiddleNames(
this IRepository<Person> repository)
{
IEnumerable<Person> peopleWithoutMiddleNames =
await repository.GetAsync(p => p.MiddleName == null);
return peopleWithoutMiddleNames;
}
}

Page-number paging

The simplest paging API uses page numbers and a fixed page size:

public class PagingExamples
{
public async Task BasicPageAsync(IRepository<Person> repository)
{
double totalCharge = 0;
IPageQueryResult<Person> page = await repository.PageAsync(pageNumber: 1, pageSize: 25);
while (page.HasNextPage)
{
foreach (Person person in page.Items)
{
Console.WriteLine(person);
}
totalCharge += page.Charge;
page = await repository.PageAsync(
pageNumber: page.PageNumber.Value + 1,
pageSize: 25);
Console.WriteLine($"Get page {page.PageNumber} (25 results) cost {page.Charge}");
}
Console.WriteLine($"Total Charge {totalCharge} RU's");
}
}

Continuation-token paging

A more cost-effective approach: pass the continuation token from the previous page back to PageAsync.

public class PagingExamples
{
public async Task BasicScrollingAsync(IRepository<Person> repository)
{
double totalCharge = 0;
IPage<Person> page = await repository.PageAsync(pageSize: 25, continuationToken: null);
foreach (Person person in page.Items)
Console.WriteLine(person);
totalCharge += page.Charge;
Console.WriteLine($"First 25 results cost {page.Charge}");
page = await repository.PageAsync(pageSize: 25, continuationToken: page.Continuation);
foreach (Person person in page.Items)
Console.WriteLine(person);
totalCharge += page.Charge;
Console.WriteLine($"Second 25 results cost {page.Charge}");
page = await repository.PageAsync(pageSize: 50, continuationToken: page.Continuation);
foreach (Person person in page.Items)
Console.WriteLine(person);
totalCharge += page.Charge;
Console.WriteLine($"Last 50 results cost {page.Charge}");
Console.WriteLine($"Total Charge {totalCharge} RU's");
}
}

Streaming with IAsyncEnumerable<T>

To stream large result sets, expose pages as IAsyncEnumerable<T> and let consumers pull chunks at their own pace:

public class ParcelRepository
{
private readonly IRepository<Parcel> _parcelCosmosRepository;
public ParcelRepository(IRepository<Parcel> parcelCosmosRepository) =>
_parcelCosmosRepository = parcelCosmosRepository;
public async IAsyncEnumerable<IParcel> StreamParcelsWithDeliveryRegionId(
string deliveryRegionId,
int max,
int chunkSize = 25,
[EnumeratorCancellation] CancellationToken cancellationToken = default)
{
int collected = 0;
bool hasMoreResults = true;
string? token = null;
Expression<Func<Parcel, bool>> expression = parcel =>
parcel.PartitionKey == deliveryRegionId &&
parcel.Status == ParcelStatus.Inducted;
while (hasMoreResults && collected < max)
{
var page = await _parcelCosmosRepository
.PageAsync(expression, chunkSize, token, cancellationToken);
token = page.Continuation;
hasMoreResults = page.Continuation is not null;
foreach (var item in page.Items)
{
if (collected < max)
yield return item;
collected++;
}
}
}
}

Want even more?