Struct QueryCommand<T>
- Namespace
- DynaSharp
- Assembly
- DynaSharp.dll
A Query under construction: the items of one partition, in the order of the sort key. Chain the options and end with ItemsAsync(CancellationToken), ItemAsync(CancellationToken),
PagesAsync(CancellationToken), PageAsync(ContinuationToken, CancellationToken) or CountAsync(CancellationToken) in one expression.
public readonly ref struct QueryCommand<T> : IFilterable<QueryCommand<T>>, IRetryable<QueryCommand<T>> where T : IDynamoDbDocument<T>
Type Parameters
THow the items are read.
- Implements
- Inherited Members
Examples
var bigOrders = orders.Query(Order.Keys.Pk.Of("alice"), Order.Keys.Sk.BeginsWith())
.Filter(Order.Attrs.Total.Gt(100m))
.Descending()
.ItemsAsync(ct);
await foreach (var order in bigOrders)
{
Console.WriteLine(order.Number);
}
Remarks
DynamoDB answers in pages of at most 1 MB of data read, before any filter. ItemsAsync(CancellationToken) and PagesAsync(CancellationToken) follow them until the end, which makes a filtered page without items no reason to stop. Each page is one call, with its own span, retries and metrics. See GetCommand<T> for what a command is and what it cannot do. Reads are eventually consistent unless Consistent() says otherwise.
Other entities in the partition. A read of an entity with a discriminator filters on it (#_type = :t), so that a table shared by several types gives back its own items, and that
filter is in the request for everyone to see. DynamoDB applies it after reading, so the items of other entities under the same partition key are still read: they count against
Limit(int) and the capacity, and are in ScannedCount but not in Count or Items.
orders.Query(Order.Keys.Pk.Of("dana")).Limit(10) gives 9 orders in its first page when the profile of the customer shares the partition. A condition on the sort key that only
this entity matches, orders.Query(Order.Keys.Pk.Of("dana"), Order.Keys.Sk.BeginsWith()), reads its items alone. To read several types at once, read the table as Item.
Methods
Consistent()
Asks for a strongly consistent read, which sees every write that succeeded before it and costs twice the capacity. A read is eventually consistent otherwise.
public QueryCommand<T> Consistent()
Returns
- QueryCommand<T>
The command, which replaces this one.
Remarks
A global secondary index is always eventually consistent, so this throws on one that is known to be global, which is an index opened through its entity or one with another partition key than the table, and DynamoDB answers with a ValidationException for any other.
Exceptions
- InvalidOperationException
The index is known to be global.
CountAsync(CancellationToken)
Counts the items of all pages with Select: COUNT, so no item travels. It costs the capacity of reading them all, as a count of a filtered read costs
the items scanned, and it takes as many calls as the read would. Limit(int) sets the size of each page and not the count.
public ValueTask<int> CountAsync(CancellationToken cancellationToken = default)
Parameters
cancellationTokenCancellationTokenCancels the read.
Returns
Exceptions
- InvalidOperationException
The command was already sent, extended or rendered, or it has a projection, which is no use for a count and which DynamoDB rejects.
- ObjectDisposedException
The client is disposed.
- ValidationException
DynamoDB rejects the request; the exception shows the expressions.
- ResourceNotFoundException
The table does not exist.
- DynamoDbException
DynamoDB returns any other error; a throttling or server error only after the last attempt.
- OverflowException
There are more items than an int holds.
Descending()
Reads the sort key downwards, so that the newest of a time-ordered partition come first (ScanIndexForward: false). It costs the same.
public QueryCommand<T> Descending()
Returns
- QueryCommand<T>
The command, which replaces this one.
Filter(ExpressionHandler)
Adds a filter in the grammar of DynamoDB, for what the conditions of handles do not cover: .Filter($"size({Attr("tags")}) < {10}"). DynamoDB applies it after reading, so
capacity is charged for every item read, not for those returned.
public QueryCommand<T> Filter(ExpressionHandler condition)
Parameters
conditionExpressionHandlerThe condition.
Returns
- QueryCommand<T>
The command, which replaces this one.
Remarks
An interpolated Attr stands for an attribute name and any other interpolated value for a value. A page can come back empty. A filter cannot name a key of the table or index that is read; that is what the key condition is for. A second filter means both must hold.
Filter<TCondition>(TCondition)
Adds a filter. DynamoDB applies it after reading, so it shortens the result, not the read: capacity is charged for every item scanned, and a page can come back empty. A second filter means both must hold.
public QueryCommand<T> Filter<TCondition>(TCondition condition) where TCondition : ICondition
Parameters
conditionTConditionThe condition.
Returns
- QueryCommand<T>
The command, which replaces this one.
Type Parameters
TConditionThe type of the condition.
ItemAsync(CancellationToken)
Reads the first item that matches, in the order of the query, and stops there. It goes on through pages that a filter left empty, and sends no page after the one that holds the item.
public ValueTask<T?> ItemAsync(CancellationToken cancellationToken = default)
Parameters
cancellationTokenCancellationTokenCancels the read. It is looked at per page.
Returns
Remarks
It adds no Limit(int), because a limit counts items before the filter, so a limit of 1 on a filtered read would look at one item and could miss the match, and because what goes out
stays what ToRequestJson() shows. So the first page comes back whole, up to 1 MB, and costs the capacity of all it read. A read of a partition without a filter that wants one
item says so: orders.Query(pk).Descending().Limit(1).ItemAsync(ct) reads the newest and pays for one. A read of an entity filters on its discriminator, so in a partition that holds
several types that limit counts the items of the other types too, and ItemAsync(CancellationToken) goes on to the next page.
An entity that is a struct reads as its default value when no item matches, so a miss looks like an item of default values there. PageAsync(ContinuationToken, CancellationToken) tells them apart.
Exceptions
- InvalidOperationException
The command was already sent, extended or rendered.
- ObjectDisposedException
The client is disposed.
- ValidationException
DynamoDB rejects the request; the exception shows the expressions.
- ResourceNotFoundException
The table does not exist.
- DynamoDbMappingException
An item of the page cannot be read as
T, which counts for all items of that page, not only for the first.- DynamoDbException
DynamoDB returns any other error; a throttling or server error only after the last attempt.
ItemsAsync(CancellationToken)
Reads all items of all pages as they come, following pages that a filter left empty. Nothing is sent until the enumeration starts, and only what the enumeration reaches is read, so breaking out early saves the remaining pages. Capacity is charged for the items scanned, which with a filter can be far more than those returned; it is not reported here, only on the spans and the metric, see PagesAsync(CancellationToken).
public IAsyncEnumerable<T> ItemsAsync(CancellationToken cancellationToken = default)
Parameters
cancellationTokenCancellationTokenCancels the read, as does
WithCancellationon the result. It is looked at per page.
Returns
- IAsyncEnumerable<T>
The items, which can be enumerated once; a second enumeration throws InvalidOperationException, so build the command again to read again.
Exceptions
- InvalidOperationException
The command was already sent, extended or rendered.
Limit(int)
Limits the items DynamoDB reads per page, before the filter. It does not limit the result: ItemsAsync(CancellationToken) reads on until the end, in
pages of this size, and with a filter a page holds fewer items than this. A cap on items is .ItemsAsync(ct).Take(n), which reads until it has them, across empty
filtered pages, and stops. To read one page, use PageAsync(ContinuationToken, CancellationToken).
public QueryCommand<T> Limit(int items)
Parameters
itemsintThe most items per page.
Returns
- QueryCommand<T>
The command, which replaces this one.
Remarks
Other entities in the partition. A read of an entity with a discriminator filters on it (#_type = :t), so that a table shared by several types gives back its own items, and that
filter is in the request for everyone to see. DynamoDB applies it after reading, so the items of other entities under the same partition key are still read: they count against
Limit(int) and the capacity, and are in ScannedCount but not in Count or Items.
orders.Query(Order.Keys.Pk.Of("dana")).Limit(10) gives 9 orders in its first page when the profile of the customer shares the partition. A condition on the sort key that only
this entity matches, orders.Query(Order.Keys.Pk.Of("dana"), Order.Keys.Sk.BeginsWith()), reads its items alone. To read several types at once, read the table as Item.
Exceptions
- ArgumentOutOfRangeException
itemsis below 1.
MaxAttempts(int)
Sets the number of attempts of this call including the first, in place of MaxAttempts. A retry after a
lost response repeats a write that may have been applied, so 1 makes an ADD count at most once, and a lost response then reports a failure after the write was applied.
public QueryCommand<T> MaxAttempts(int attempts)
Parameters
attemptsintThe number of attempts.
Returns
- QueryCommand<T>
The command, which replaces this one.
Exceptions
- ArgumentOutOfRangeException
attemptsis below 1.
OnMappingFailure(Action<DynamoDbMappingException>)
Skips an item that cannot be read as T and hands its DynamoDbMappingException to onFailure, where the read would otherwise end with it:
.OnMappingFailure(failure => bad.Add(failure.Key)), or .OnMappingFailure(static _ => { }) to skip and count only. Ending the read stays the default, because an item that does not fit
is a bug of the model or of a writer, which a read that drops items would hide.
public QueryCommand<T> OnMappingFailure(Action<DynamoDbMappingException> onFailure)
Parameters
onFailureAction<DynamoDbMappingException>What to do with the failure of a skipped item.
Returns
- QueryCommand<T>
The command, which replaces this one.
Remarks
The exception says which item it was and why: EntityType, AttributeName, the message, and
Key, the key attributes of the table as they are stored, such as USER#alice, also on an index. The key holds values, so nothing logs it: the callback decides where it goes,
and the message holds none. After a Select the response holds no key, so Key is null there.
It runs once per skipped item, when the page that holds it has arrived and before its items are given out, outside the call: it is no part of a retry, and an exception it throws ends the read as it is.
The pages of a parallel scan and of a fan-out arrive on several threads at once, so there it can run concurrently. Each item it returns from counts in the metric dynasharp.client.operation.skipped_items.
What a skipped item does to the rest of the read: it is not in Items, and Count still counts it, as DynamoDB does, so Count - Items.Count is what a page skipped.
Limit(int) counts it, because DynamoDB read it, so a page can hold fewer items. ItemsAsync(ct).Take(n) counts what it gets, so it reads on past a skipped item, and
ItemAsync(CancellationToken) gives the first item that can be read. Next continues after the page, so a skipped item is not read again. CountAsync(CancellationToken) reads no item and counts it.
Only a DynamoDbMappingException is skipped: an item of another type in a read keyed by the discriminator, a value a converter cannot read, a missing required attribute,
a stored key that does not fit its template. It covers every way of sending the read that gives items, also those of a Select after it. A second call replaces the first.
Exceptions
- ArgumentNullException
onFailureis null.
PageAsync(ContinuationToken, CancellationToken)
Reads one page, for paging that a client drives across requests: build the same query each time and pass the position of the page before. The page can be empty and still have a next one, so a client that wants items keeps going while HasMore holds.
public ValueTask<Page<T>> PageAsync(ContinuationToken after = default, CancellationToken cancellationToken = default)
Parameters
afterContinuationTokenThe position to continue from, or
defaultfor the first page. It must come from the same query. AContinuationToken?passes asafter ?? default.cancellationTokenCancellationTokenCancels the call.
Returns
Remarks
Other entities in the partition. A read of an entity with a discriminator filters on it (#_type = :t), so that a table shared by several types gives back its own items, and that
filter is in the request for everyone to see. DynamoDB applies it after reading, so the items of other entities under the same partition key are still read: they count against
Limit(int) and the capacity, and are in ScannedCount but not in Count or Items.
orders.Query(Order.Keys.Pk.Of("dana")).Limit(10) gives 9 orders in its first page when the profile of the customer shares the partition. A condition on the sort key that only
this entity matches, orders.Query(Order.Keys.Pk.Of("dana"), Order.Keys.Sk.BeginsWith()), reads its items alone. To read several types at once, read the table as Item.
Exceptions
- InvalidOperationException
The command was already sent, extended or rendered.
- ObjectDisposedException
The client is disposed.
- FormatException
afterdoes not fit what TokenSigner says: a client that signs gets a plain token that came as text, or one whose signature does not hold for this read; a client that does not sign gets a signed one. The message quotes nothing of the token, and no request goes out.- ValidationException
DynamoDB rejects the request, for example a position of another query; the exception shows the expressions.
- ResourceNotFoundException
The table does not exist.
- DynamoDbMappingException
An item cannot be read as
T.- DynamoDbException
DynamoDB returns any other error; a throttling or server error only after the last attempt.
PageAsync(CancellationToken)
Reads the first page, as PageAsync(ContinuationToken, CancellationToken) does at the default position, for a caller that has a token and no position yet.
public ValueTask<Page<T>> PageAsync(CancellationToken cancellationToken)
Parameters
cancellationTokenCancellationTokenCancels the call. It has no default, because
PageAsync()belongs to the overload with the position.
Returns
Exceptions
- InvalidOperationException
The command was already sent, extended or rendered.
- ObjectDisposedException
The client is disposed.
- ValidationException
DynamoDB rejects the request; the exception shows the expressions.
- ResourceNotFoundException
The table does not exist.
- DynamoDbMappingException
An item cannot be read as
T.- DynamoDbException
DynamoDB returns any other error; a throttling or server error only after the last attempt.
PagesAsync(CancellationToken)
Reads all pages as they come, for code that wants ConsumedCapacity or works page by page. It gives every response, also one that a filter left empty. Otherwise as ItemsAsync(CancellationToken).
public IAsyncEnumerable<Page<T>> PagesAsync(CancellationToken cancellationToken = default)
Parameters
cancellationTokenCancellationTokenCancels the read, as does
WithCancellationon the result.
Returns
- IAsyncEnumerable<Page<T>>
The pages, which can be enumerated once.
Exceptions
- InvalidOperationException
The command was already sent, extended or rendered.
Project(params ReadOnlySpan<Attr>)
Reads only some attributes, given as handles: Project(Order.Attrs.Status, Order.Attrs.Total). See Project(params ReadOnlySpan<string>).
public QueryCommand<T> Project(params ReadOnlySpan<Attr> attributes)
Parameters
attributesReadOnlySpan<Attr>The attributes. A second call adds to the first.
Returns
- QueryCommand<T>
The command, which replaces this one.
Exceptions
- ArgumentException
A path is not valid.
Project(params ReadOnlySpan<string>)
Reads only some attributes. It saves bandwidth, not capacity: DynamoDB charges for whole items. A read of an entity needs its discriminator and its keys, so they join the projection on their own, and it needs whatever else the entity requires, so a projection is mostly for Item. A global secondary index gives only what it projects.
public QueryCommand<T> Project(params ReadOnlySpan<string> paths)
Parameters
pathsReadOnlySpan<string>The names or document paths. Reserved words need no care, and a second call adds to the first.
Returns
- QueryCommand<T>
The command, which replaces this one.
Exceptions
- ArgumentException
A path is not valid.
ReturnCapacity(ReturnConsumedCapacity)
Asks for the consumed capacity of each page, which goes to ConsumedCapacity, the span of the page and the capacity metric. ItemsAsync(CancellationToken) has no result to put it on, so there it reaches telemetry only.
public QueryCommand<T> ReturnCapacity(ReturnConsumedCapacity detail = ReturnConsumedCapacity.Total)
Parameters
detailReturnConsumedCapacityHow much detail to return.
Returns
- QueryCommand<T>
The command, which replaces this one.
Select<TValue, TConverter>(Attr<TValue, NullableHandleConverter<TValue, TConverter>>)
Reads one nullable attribute instead of the entity, which gives null for an item without it. See Select<TValue, TConverter>(Attr<TValue, TConverter>).
public QuerySelectCommand<TValue?, NullableConverter<TValue, TConverter>> Select<TValue, TConverter>(Attr<TValue, NullableHandleConverter<TValue, TConverter>> attribute) where TValue : struct where TConverter : IDynamoDbConverter<TValue>
Parameters
attributeAttr<TValue, NullableHandleConverter<TValue, TConverter>>The attribute, a name or a path through maps.
Returns
- QuerySelectCommand<TValue?, NullableConverter<TValue, TConverter>>
The command that is left.
Type Parameters
TValueThe type of the attribute.
TConverterThe converter of the attribute.
Exceptions
- ArgumentException
The handle is a default value, its path has a list index, or it is not valid.
- InvalidOperationException
The read has a
Project, which has nothing to add here.
Select<TValue, TConverter>(Attr<TValue, NullableReferenceHandleConverter<TValue, TConverter>>)
Reads one nullable attribute instead of the entity, which gives null for an item without it. See Select<TValue, TConverter>(Attr<TValue, TConverter>).
public QuerySelectCommand<TValue?, NullableReferenceConverter<TValue, TConverter>> Select<TValue, TConverter>(Attr<TValue, NullableReferenceHandleConverter<TValue, TConverter>> attribute) where TValue : class where TConverter : IDynamoDbConverter<TValue>
Parameters
attributeAttr<TValue, NullableReferenceHandleConverter<TValue, TConverter>>The attribute, a name or a path through maps.
Returns
- QuerySelectCommand<TValue, NullableReferenceConverter<TValue, TConverter>>
The command that is left.
Type Parameters
TValueThe type of the attribute.
TConverterThe converter of the attribute.
Exceptions
- ArgumentException
The handle is a default value, its path has a list index, or it is not valid.
- InvalidOperationException
The read has a
Project, which has nothing to add here.
Select<TValue, TConverter>(Attr<TValue, TConverter>)
Reads one attribute of each item instead of the whole entity: await foreach (var total in orders.Query(pk).Select(Order.Attrs.Total).ValuesAsync(ct)). It saves bandwidth, not capacity: DynamoDB charges for whole items.
public QuerySelectCommand<TValue, TConverter> Select<TValue, TConverter>(Attr<TValue, TConverter> attribute) where TConverter : IDynamoDbConverter<TValue>
Parameters
attributeAttr<TValue, TConverter>The attribute, a name or a path through maps. A list element cannot be read: DynamoDB returns a shortened list, in which its position is not known.
Returns
- QuerySelectCommand<TValue, TConverter>
The command that is left.
Type Parameters
TValueThe type of the attribute.
TConverterThe converter of the attribute.
Remarks
Over Project. The result is the value and no entity, so a required member cannot be in the way. The attribute is projected and nothing else: the filter on the discriminator keeps
items of other types away, and only a key condition that stands in for that filter brings the discriminator along.
Last clause. After Select only the send is left, as after Return: Filter<TCondition>(TCondition), Limit(int), Descending(), Consistent(), ReturnCapacity(ReturnConsumedCapacity) and MaxAttempts(int) come before this, a second Select and a Project after it do not compile, and a Project before it throws.
Last clause. After Select only the send is left, as after Return: Consistent(), ReturnCapacity(ReturnConsumedCapacity) and MaxAttempts(int) come before this, a second Select and a Project after it do not compile, and a Project before it throws.
A missing attribute follows the type of the handle: null for one of a nullable property, an empty set for a set, because DynamoDB has none, and otherwise a DynamoDbMappingException. Never a default, and that is where this differs from a read of the entity,
which leaves a property that is not required at its initial value: an int without its attribute is 0 there and throws here, and so does a [Version] on an item without one, which code of another kind writes, with a message that points at VersionMissing.Allow.
In a nested path the last handle decides, so ShipTo.At(Address.Attrs.City) throws for an item without ShipTo when City cannot be null, which reading ShipTo itself would not.
A key input. A [KeyOnly] property has no attribute and no handle. A stored property that a key of the table holds has both, and an item that an update created has only the key: a read of the entity restores
the property from it, and this throws, which DYNA209 reports.
Exceptions
- ArgumentException
The handle is a default value, its path has a list index, or it is not valid.
- InvalidOperationException
The read has a
Project, which has nothing to add here.
ToRequestJson()
Renders the body of the first page, as DynamoDB JSON, without sending it. It ends the command, and is meant for tests and debugging. The text contains the key and the values.
public string ToRequestJson()
Returns
- string
The request body.
Exceptions
- InvalidOperationException
The command was already sent, extended or rendered.