20
20
"""
21
21
import copy
22
22
import math
23
- import warnings
24
23
25
24
from google .protobuf import wrappers_pb2
26
25
import six
@@ -86,6 +85,8 @@ class Query(object):
86
85
The "order by" entries to use in the query.
87
86
limit (Optional[int]):
88
87
The maximum number of documents the query is allowed to return.
88
+ limit_to_last (Optional[bool]):
89
+ Denotes whether a provided limit is applied to the end of the result set.
89
90
offset (Optional[int]):
90
91
The number of results to skip.
91
92
start_at (Optional[Tuple[dict, bool]]):
@@ -134,6 +135,7 @@ def __init__(
134
135
field_filters = (),
135
136
orders = (),
136
137
limit = None ,
138
+ limit_to_last = False ,
137
139
offset = None ,
138
140
start_at = None ,
139
141
end_at = None ,
@@ -144,6 +146,7 @@ def __init__(
144
146
self ._field_filters = field_filters
145
147
self ._orders = orders
146
148
self ._limit = limit
149
+ self ._limit_to_last = limit_to_last
147
150
self ._offset = offset
148
151
self ._start_at = start_at
149
152
self ._end_at = end_at
@@ -158,6 +161,7 @@ def __eq__(self, other):
158
161
and self ._field_filters == other ._field_filters
159
162
and self ._orders == other ._orders
160
163
and self ._limit == other ._limit
164
+ and self ._limit_to_last == other ._limit_to_last
161
165
and self ._offset == other ._offset
162
166
and self ._start_at == other ._start_at
163
167
and self ._end_at == other ._end_at
@@ -212,6 +216,7 @@ def select(self, field_paths):
212
216
field_filters = self ._field_filters ,
213
217
orders = self ._orders ,
214
218
limit = self ._limit ,
219
+ limit_to_last = self ._limit_to_last ,
215
220
offset = self ._offset ,
216
221
start_at = self ._start_at ,
217
222
end_at = self ._end_at ,
@@ -281,6 +286,7 @@ def where(self, field_path, op_string, value):
281
286
field_filters = new_filters ,
282
287
orders = self ._orders ,
283
288
limit = self ._limit ,
289
+ limit_to_last = self ._limit_to_last ,
284
290
offset = self ._offset ,
285
291
start_at = self ._start_at ,
286
292
end_at = self ._end_at ,
@@ -333,16 +339,55 @@ def order_by(self, field_path, direction=ASCENDING):
333
339
field_filters = self ._field_filters ,
334
340
orders = new_orders ,
335
341
limit = self ._limit ,
342
+ limit_to_last = self ._limit_to_last ,
336
343
offset = self ._offset ,
337
344
start_at = self ._start_at ,
338
345
end_at = self ._end_at ,
339
346
all_descendants = self ._all_descendants ,
340
347
)
341
348
342
349
def limit (self , count ):
343
- """Limit a query to return a fixed number of results.
350
+ """Limit a query to return at most `count` matching results.
344
351
345
- If the current query already has a limit set, this will overwrite it.
352
+ If the current query already has a `limit` set, this will override it.
353
+
354
+ .. note::
355
+
356
+ `limit` and `limit_to_last` are mutually exclusive.
357
+ Setting `limit` will drop previously set `limit_to_last`.
358
+
359
+ Args:
360
+ count (int): Maximum number of documents to return that match
361
+ the query.
362
+
363
+ Returns:
364
+ :class:`~google.cloud.firestore_v1.query.Query`:
365
+ A limited query. Acts as a copy of the current query, modified
366
+ with the newly added "limit" filter.
367
+ """
368
+ return self .__class__ (
369
+ self ._parent ,
370
+ projection = self ._projection ,
371
+ field_filters = self ._field_filters ,
372
+ orders = self ._orders ,
373
+ limit = count ,
374
+ limit_to_last = False ,
375
+ offset = self ._offset ,
376
+ start_at = self ._start_at ,
377
+ end_at = self ._end_at ,
378
+ all_descendants = self ._all_descendants ,
379
+ )
380
+
381
+ def limit_to_last (self , count ):
382
+ """Limit a query to return the last `count` matching results.
383
+
384
+ If the current query already has a `limit_to_last`
385
+ set, this will override it.
386
+
387
+ .. note::
388
+
389
+ `limit` and `limit_to_last` are mutually exclusive.
390
+ Setting `limit_to_last` will drop previously set `limit`.
346
391
347
392
Args:
348
393
count (int): Maximum number of documents to return that match
@@ -359,6 +404,7 @@ def limit(self, count):
359
404
field_filters = self ._field_filters ,
360
405
orders = self ._orders ,
361
406
limit = count ,
407
+ limit_to_last = True ,
362
408
offset = self ._offset ,
363
409
start_at = self ._start_at ,
364
410
end_at = self ._end_at ,
@@ -386,6 +432,7 @@ def offset(self, num_to_skip):
386
432
field_filters = self ._field_filters ,
387
433
orders = self ._orders ,
388
434
limit = self ._limit ,
435
+ limit_to_last = self ._limit_to_last ,
389
436
offset = num_to_skip ,
390
437
start_at = self ._start_at ,
391
438
end_at = self ._end_at ,
@@ -729,13 +776,42 @@ def _to_protobuf(self):
729
776
return query_pb2 .StructuredQuery (** query_kwargs )
730
777
731
778
def get (self , transaction = None ):
732
- """Deprecated alias for :meth:`stream`."""
733
- warnings .warn (
734
- "'Query.get' is deprecated: please use 'Query.stream' instead." ,
735
- DeprecationWarning ,
736
- stacklevel = 2 ,
737
- )
738
- return self .stream (transaction = transaction )
779
+ """Read the documents in the collection that match this query.
780
+
781
+ This sends a ``RunQuery`` RPC and returns a list of documents
782
+ returned in the stream of ``RunQueryResponse`` messages.
783
+
784
+ Args:
785
+ transaction
786
+ (Optional[:class:`~google.cloud.firestore_v1.transaction.Transaction`]):
787
+ An existing transaction that this query will run in.
788
+
789
+ If a ``transaction`` is used and it already has write operations
790
+ added, this method cannot be used (i.e. read-after-write is not
791
+ allowed).
792
+
793
+ Returns:
794
+ list: The documents in the collection that match this query.
795
+ """
796
+ is_limited_to_last = self ._limit_to_last
797
+
798
+ if self ._limit_to_last :
799
+ # In order to fetch up to `self._limit` results from the end of the
800
+ # query flip the defined ordering on the query to start from the
801
+ # end, retrieving up to `self._limit` results from the backend.
802
+ for order in self ._orders :
803
+ order .direction = _enum_from_direction (
804
+ self .DESCENDING
805
+ if order .direction == self .ASCENDING
806
+ else self .ASCENDING
807
+ )
808
+ self ._limit_to_last = False
809
+
810
+ result = self .stream (transaction = transaction )
811
+ if is_limited_to_last :
812
+ result = reversed (list (result ))
813
+
814
+ return list (result )
739
815
740
816
def stream (self , transaction = None ):
741
817
"""Read the documents in the collection that match this query.
@@ -764,6 +840,11 @@ def stream(self, transaction=None):
764
840
:class:`~google.cloud.firestore_v1.document.DocumentSnapshot`:
765
841
The next document that fulfills the query.
766
842
"""
843
+ if self ._limit_to_last :
844
+ raise ValueError (
845
+ "Query results for queries that include limit_to_last() "
846
+ "constraints cannot be streamed. Use Query.get() instead."
847
+ )
767
848
parent_path , expected_prefix = self ._parent ._parent_info ()
768
849
response_iterator = self ._client ._firestore_api .run_query (
769
850
parent_path ,
0 commit comments