-
Notifications
You must be signed in to change notification settings - Fork 4
Querying
How to read many documents from a repository — LINQ queries, pagination, projections, streaming cursors, and how to keep large scans cheap. For single-document reads see CRUD operations.
QueryElementsAsync hands you an IQueryable<TModel> translated to a MongoDB aggregation, which you
terminate with a driver async operator (from Etherna.MongoDB.Driver.Linq):
using Etherna.MongoDB.Driver.Linq;
var cutoff = DateTime.UtcNow.AddYears(-2);
var adults = await db.Cats.QueryElementsAsync(cats => cats
.Where(c => c.Birthday <= cutoff)
.OrderBy(c => c.Name)
.ToListAsync());
var count = await db.Cats.QueryElementsAsync(cats => cats.LongCountAsync());
var oldest = await db.Cats.QueryElementsAsync(cats =>
cats.OrderBy(c => c.Birthday).FirstOrDefaultAsync());The query runs server-side; only the result is materialized. Pass AggregateOptions as the second
argument when you need them (e.g. AllowDiskUse). The provider is the driver's LINQ3 provider.
Shape the result inside the query to fetch only what you need:
var cutoff = DateTime.UtcNow.AddYears(-2);
var names = await db.Cats.QueryElementsAsync(cats => cats
.Where(c => c.Birthday <= cutoff)
.Select(c => new { c.Id, c.Name })
.ToListAsync());QueryPaginatedElementsAsync filters, orders, and pages in one call, returning a
PaginatedEnumerable<TResult>:
var cutoff = DateTime.UtcNow.AddYears(-2);
PaginatedEnumerable<Cat> page = await db.Cats.QueryPaginatedElementsAsync(
filter: cats => cats.Where(c => c.Birthday <= cutoff),
orderKeySelector: c => c.Name,
page: 0,
take: 20,
useDescendingOrder: false);
foreach (var cat in page.Elements) { /* ... */ }PaginatedEnumerable<T> |
Meaning |
|---|---|
Elements |
The page's items. |
CurrentPage |
Zero-based current page. |
MaxPage |
Index of the last page. |
PageSize |
Items per page (take). |
TotalElements |
Total matching documents. |
A negative page, a page size below 1, and a page so far in the sequence that the elements to skip
exceed int.MaxValue are refused with an ArgumentOutOfRangeException, before any query runs.
Deep pages cost. Paging by number skips the elements of every page before the requested one, and the server walks them to discard them — with the right index too. It is the right shape for a UI showing page numbers, on the first pages; for scanning a whole collection use a streaming read, and for an endless list prefer filtering from the last key you read (
Where(c => c.Name.CompareTo(lastName) > 0)) over a growing page number.
To stream rather than buffer, FindAsync returns a driver IAsyncCursor<TProjection> — wrapped so
the db execution context stays alive until you dispose it:
using var cursor = await db.Cats.FindAsync<Cat>(Builders<Cat>.Filter.Empty);
while (await cursor.MoveNextAsync())
foreach (var cat in cursor.Current) { /* ... */ }Every entity a query materializes normally enters the scope's identity map (and is change-tracked). Over a big scan that's wasted memory that grows with the result set. Wrap read-only scans in the no-cache serializer modifier so materialized models skip the identity map and change tracking:
using (db.Engine.SerializerModifierAccessor.EnableCacheSerializerModifier(noCache: true))
{
await db.Cats.QueryElementsAsync(async cats =>
{
await foreach (var cat in cats.ToAsyncEnumerable())
Process(cat);
return 0; // QueryElementsAsync requires a result
});
}You can also inject ISerializerModifierAccessor directly. The modifier applies to everything read
inside the using scope, on the current execution flow — so on a background
thread with no ambient context, open one first (see Execution contexts).
Best practice. Any job that cursors over a whole collection should read under no-cache — see Best practices and pitfalls.
The companion EnableReferenceSerializerModifier(readOnlyId: true) materializes
references with only their id loaded — the denormalized summary
members are discarded on read. Reading any other member still triggers a lazy full load, so use it
when the ids alone are what you need.
Next: Change tracking and saving for write internals, or Transactions to group writes.
Scrinium — source · issues (SCR) · GNU LGPL-3.0 · info@etherna.io
Getting started
Core concepts
Working with data
Serialization & mapping
Operations & maintenance
Advanced & reference