@@ -208,6 +208,9 @@ func (q *Query) ConsumedCapacity(cc *ConsumedCapacity) *Query {
208208
209209// One executes this query and retrieves a single result,
210210// unmarshaling the result to out.
211+ // This uses the DynamoDB GetItem API when possible, otherwise Query.
212+ // If the query returns more than one result, [ErrTooMany] may be returned. This is intended as a diagnostic for query mistakes.
213+ // To avoid [ErrTooMany], set the [Query.Limit] to 1.
211214func (q * Query ) One (ctx context.Context , out interface {}) error {
212215 if q .err != nil {
213216 return q .err
@@ -239,34 +242,20 @@ func (q *Query) One(ctx context.Context, out interface{}) error {
239242 }
240243
241244 // If not, try a Query.
242- req := q .queryInput ()
243-
244- var res * dynamodb.QueryOutput
245- err := q .table .db .retry (ctx , func () error {
246- var err error
247- res , err = q .table .db .client .Query (ctx , req )
248- q .cc .incRequests ()
249- if err != nil {
250- return err
251- }
252-
253- switch {
254- case len (res .Items ) == 0 :
255- return ErrNotFound
256- case len (res .Items ) > 1 && q .limit != 1 :
257- return ErrTooMany
258- case res .LastEvaluatedKey != nil && q .searchLimit != 0 :
259- return ErrTooMany
260- }
261-
262- return nil
263- })
264- if err != nil {
245+ iter := q .newIter (unmarshalItem )
246+ var item Item
247+ ok := iter .Next (ctx , & item )
248+ if err := iter .Err (); err != nil {
265249 return err
266250 }
267- q .cc .add (res .ConsumedCapacity )
268-
269- return unmarshalItem (res .Items [0 ], out )
251+ if ! ok {
252+ return ErrNotFound
253+ }
254+ // Best effort: do we have any pending unused items?
255+ if iter .hasMore () {
256+ return ErrTooMany
257+ }
258+ return unmarshalItem (item , out )
270259}
271260
272261// Count executes this request, returning the number of results.
@@ -314,6 +303,14 @@ func (q *Query) Count(ctx context.Context) (int, error) {
314303 return count , nil
315304}
316305
306+ func (q * Query ) newIter (unmarshal unmarshalFunc ) * queryIter {
307+ return & queryIter {
308+ query : q ,
309+ unmarshal : unmarshal ,
310+ err : q .err ,
311+ }
312+ }
313+
317314// queryIter is the iterator for Query operations
318315type queryIter struct {
319316 query * Query
@@ -422,6 +419,13 @@ func (itr *queryIter) Next(ctx context.Context, out interface{}) bool {
422419 return itr .err == nil
423420}
424421
422+ func (itr * queryIter ) hasMore () bool {
423+ if itr .query .limit > 0 && itr .n == itr .query .limit {
424+ return false
425+ }
426+ return itr .output != nil && itr .idx < len (itr .output .Items )
427+ }
428+
425429// Err returns the error encountered, if any.
426430// You should check this after Next is finished.
427431func (itr * queryIter ) Err () error {
@@ -458,11 +462,7 @@ func (itr *queryIter) LastEvaluatedKey(ctx context.Context) (PagingKey, error) {
458462
459463// All executes this request and unmarshals all results to out, which must be a pointer to a slice.
460464func (q * Query ) All (ctx context.Context , out interface {}) error {
461- iter := & queryIter {
462- query : q ,
463- unmarshal : unmarshalAppendTo (out ),
464- err : q .err ,
465- }
465+ iter := q .newIter (unmarshalAppendTo (out ))
466466 for iter .Next (ctx , out ) {
467467 }
468468 return iter .Err ()
@@ -471,11 +471,7 @@ func (q *Query) All(ctx context.Context, out interface{}) error {
471471// AllWithLastEvaluatedKey executes this request and unmarshals all results to out, which must be a pointer to a slice.
472472// This returns a PagingKey you can use with StartFrom to split up results.
473473func (q * Query ) AllWithLastEvaluatedKey (ctx context.Context , out interface {}) (PagingKey , error ) {
474- iter := & queryIter {
475- query : q ,
476- unmarshal : unmarshalAppendTo (out ),
477- err : q .err ,
478- }
474+ iter := q .newIter (unmarshalAppendTo (out ))
479475 for iter .Next (ctx , out ) {
480476 }
481477 lek , err := iter .LastEvaluatedKey (ctx )
@@ -484,12 +480,7 @@ func (q *Query) AllWithLastEvaluatedKey(ctx context.Context, out interface{}) (P
484480
485481// Iter returns a results iterator for this request.
486482func (q * Query ) Iter () PagingIter {
487- iter := & queryIter {
488- query : q ,
489- unmarshal : unmarshalItem ,
490- err : q .err ,
491- }
492- return iter
483+ return q .newIter (unmarshalItem )
493484}
494485
495486// can we use the get item API?
0 commit comments