google-cloud-firestore 1.4.2 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. checksums.yaml +4 -4
  2. data/AUTHENTICATION.md +1 -1
  3. data/CHANGELOG.md +51 -0
  4. data/TROUBLESHOOTING.md +2 -8
  5. data/lib/google-cloud-firestore.rb +15 -21
  6. data/lib/google/cloud/firestore.rb +12 -23
  7. data/lib/google/cloud/firestore/client.rb +12 -14
  8. data/lib/google/cloud/firestore/collection_reference.rb +1 -0
  9. data/lib/google/cloud/firestore/collection_reference_list.rb +137 -0
  10. data/lib/google/cloud/firestore/convert.rb +39 -39
  11. data/lib/google/cloud/firestore/credentials.rb +2 -2
  12. data/lib/google/cloud/firestore/document_reference.rb +8 -8
  13. data/lib/google/cloud/firestore/document_reference/list.rb +2 -2
  14. data/lib/google/cloud/firestore/document_snapshot.rb +4 -4
  15. data/lib/google/cloud/firestore/query.rb +114 -22
  16. data/lib/google/cloud/firestore/service.rb +63 -104
  17. data/lib/google/cloud/firestore/transaction.rb +2 -2
  18. data/lib/google/cloud/firestore/version.rb +1 -1
  19. data/lib/google/cloud/firestore/watch/inventory.rb +2 -0
  20. data/lib/google/cloud/firestore/watch/listener.rb +9 -9
  21. data/lib/google/cloud/firestore/watch/order.rb +1 -1
  22. metadata +9 -107
  23. data/lib/google/cloud/firestore/admin.rb +0 -144
  24. data/lib/google/cloud/firestore/admin/v1.rb +0 -151
  25. data/lib/google/cloud/firestore/admin/v1/credentials.rb +0 -44
  26. data/lib/google/cloud/firestore/admin/v1/doc/google/firestore/admin/v1/field.rb +0 -86
  27. data/lib/google/cloud/firestore/admin/v1/doc/google/firestore/admin/v1/firestore_admin.rb +0 -161
  28. data/lib/google/cloud/firestore/admin/v1/doc/google/firestore/admin/v1/index.rb +0 -142
  29. data/lib/google/cloud/firestore/admin/v1/doc/google/longrunning/operations.rb +0 -51
  30. data/lib/google/cloud/firestore/admin/v1/doc/google/protobuf/any.rb +0 -131
  31. data/lib/google/cloud/firestore/admin/v1/doc/google/protobuf/empty.rb +0 -29
  32. data/lib/google/cloud/firestore/admin/v1/doc/google/protobuf/field_mask.rb +0 -222
  33. data/lib/google/cloud/firestore/admin/v1/doc/google/rpc/status.rb +0 -39
  34. data/lib/google/cloud/firestore/admin/v1/firestore_admin_client.rb +0 -902
  35. data/lib/google/cloud/firestore/admin/v1/firestore_admin_client_config.json +0 -71
  36. data/lib/google/cloud/firestore/v1.rb +0 -152
  37. data/lib/google/cloud/firestore/v1/credentials.rb +0 -42
  38. data/lib/google/cloud/firestore/v1/doc/google/firestore/v1/common.rb +0 -64
  39. data/lib/google/cloud/firestore/v1/doc/google/firestore/v1/document.rb +0 -136
  40. data/lib/google/cloud/firestore/v1/doc/google/firestore/v1/firestore.rb +0 -566
  41. data/lib/google/cloud/firestore/v1/doc/google/firestore/v1/query.rb +0 -227
  42. data/lib/google/cloud/firestore/v1/doc/google/firestore/v1/write.rb +0 -241
  43. data/lib/google/cloud/firestore/v1/doc/google/protobuf/any.rb +0 -131
  44. data/lib/google/cloud/firestore/v1/doc/google/protobuf/empty.rb +0 -29
  45. data/lib/google/cloud/firestore/v1/doc/google/protobuf/timestamp.rb +0 -113
  46. data/lib/google/cloud/firestore/v1/doc/google/protobuf/wrappers.rb +0 -26
  47. data/lib/google/cloud/firestore/v1/doc/google/rpc/status.rb +0 -39
  48. data/lib/google/cloud/firestore/v1/firestore_client.rb +0 -1011
  49. data/lib/google/cloud/firestore/v1/firestore_client_config.json +0 -100
  50. data/lib/google/cloud/firestore/v1beta1.rb +0 -162
  51. data/lib/google/cloud/firestore/v1beta1/credentials.rb +0 -42
  52. data/lib/google/cloud/firestore/v1beta1/doc/google/firestore/v1beta1/common.rb +0 -64
  53. data/lib/google/cloud/firestore/v1beta1/doc/google/firestore/v1beta1/document.rb +0 -136
  54. data/lib/google/cloud/firestore/v1beta1/doc/google/firestore/v1beta1/firestore.rb +0 -564
  55. data/lib/google/cloud/firestore/v1beta1/doc/google/firestore/v1beta1/query.rb +0 -227
  56. data/lib/google/cloud/firestore/v1beta1/doc/google/firestore/v1beta1/write.rb +0 -237
  57. data/lib/google/cloud/firestore/v1beta1/doc/google/protobuf/any.rb +0 -131
  58. data/lib/google/cloud/firestore/v1beta1/doc/google/protobuf/empty.rb +0 -29
  59. data/lib/google/cloud/firestore/v1beta1/doc/google/protobuf/timestamp.rb +0 -113
  60. data/lib/google/cloud/firestore/v1beta1/doc/google/protobuf/wrappers.rb +0 -26
  61. data/lib/google/cloud/firestore/v1beta1/doc/google/rpc/status.rb +0 -39
  62. data/lib/google/cloud/firestore/v1beta1/firestore_client.rb +0 -1021
  63. data/lib/google/cloud/firestore/v1beta1/firestore_client_config.json +0 -100
  64. data/lib/google/firestore/admin/v1/field_pb.rb +0 -32
  65. data/lib/google/firestore/admin/v1/firestore_admin_pb.rb +0 -84
  66. data/lib/google/firestore/admin/v1/firestore_admin_services_pb.rb +0 -94
  67. data/lib/google/firestore/admin/v1/index_pb.rb +0 -58
  68. data/lib/google/firestore/admin/v1/location_pb.rb +0 -22
  69. data/lib/google/firestore/admin/v1/operation_pb.rb +0 -90
  70. data/lib/google/firestore/v1/common_pb.rb +0 -45
  71. data/lib/google/firestore/v1/document_pb.rb +0 -50
  72. data/lib/google/firestore/v1/firestore_pb.rb +0 -222
  73. data/lib/google/firestore/v1/firestore_services_pb.rb +0 -80
  74. data/lib/google/firestore/v1/query_pb.rb +0 -107
  75. data/lib/google/firestore/v1/write_pb.rb +0 -80
  76. data/lib/google/firestore/v1beta1/common_pb.rb +0 -45
  77. data/lib/google/firestore/v1beta1/document_pb.rb +0 -50
  78. data/lib/google/firestore/v1beta1/firestore_pb.rb +0 -222
  79. data/lib/google/firestore/v1beta1/firestore_services_pb.rb +0 -89
  80. data/lib/google/firestore/v1beta1/query_pb.rb +0 -107
  81. data/lib/google/firestore/v1beta1/write_pb.rb +0 -79
@@ -85,40 +85,40 @@ module Google
85
85
 
86
86
  def raw_to_value obj
87
87
  if NilClass === obj
88
- Google::Firestore::V1::Value.new null_value: :NULL_VALUE
88
+ Google::Cloud::Firestore::V1::Value.new null_value: :NULL_VALUE
89
89
  elsif TrueClass === obj || FalseClass === obj
90
- Google::Firestore::V1::Value.new boolean_value: obj
90
+ Google::Cloud::Firestore::V1::Value.new boolean_value: obj
91
91
  elsif Integer === obj
92
- Google::Firestore::V1::Value.new integer_value: obj
92
+ Google::Cloud::Firestore::V1::Value.new integer_value: obj
93
93
  elsif Numeric === obj # Any number not an integer is a double
94
- Google::Firestore::V1::Value.new double_value: obj.to_f
94
+ Google::Cloud::Firestore::V1::Value.new double_value: obj.to_f
95
95
  elsif Time === obj || DateTime === obj || Date === obj
96
- Google::Firestore::V1::Value.new \
96
+ Google::Cloud::Firestore::V1::Value.new \
97
97
  timestamp_value: time_to_timestamp(obj.to_time)
98
98
  elsif String === obj || Symbol === obj
99
- Google::Firestore::V1::Value.new string_value: obj.to_s
99
+ Google::Cloud::Firestore::V1::Value.new string_value: obj.to_s
100
100
  elsif Google::Cloud::Firestore::DocumentReference === obj
101
- Google::Firestore::V1::Value.new reference_value: obj.path
101
+ Google::Cloud::Firestore::V1::Value.new reference_value: obj.path
102
102
  elsif Array === obj
103
103
  values = obj.map { |o| raw_to_value(o) }
104
- Google::Firestore::V1::Value.new(array_value:
105
- Google::Firestore::V1::ArrayValue.new(values: values))
104
+ Google::Cloud::Firestore::V1::Value.new(array_value:
105
+ Google::Cloud::Firestore::V1::ArrayValue.new(values: values))
106
106
  elsif Hash === obj
107
107
  # keys have been changed to strings before the hash gets here
108
108
  geo_pairs = hash_is_geo_point? obj
109
109
  if geo_pairs
110
- Google::Firestore::V1::Value.new(
110
+ Google::Cloud::Firestore::V1::Value.new(
111
111
  geo_point_value: hash_to_geo_point(obj, geo_pairs)
112
112
  )
113
113
  else
114
114
  fields = hash_to_fields obj
115
- Google::Firestore::V1::Value.new(map_value:
116
- Google::Firestore::V1::MapValue.new(fields: fields))
115
+ Google::Cloud::Firestore::V1::Value.new(map_value:
116
+ Google::Cloud::Firestore::V1::MapValue.new(fields: fields))
117
117
  end
118
118
  elsif obj.respond_to?(:read) && obj.respond_to?(:rewind)
119
119
  obj.rewind
120
120
  content = obj.read.force_encoding "ASCII-8BIT"
121
- Google::Firestore::V1::Value.new bytes_value: content
121
+ Google::Cloud::Firestore::V1::Value.new bytes_value: content
122
122
  else
123
123
  raise ArgumentError,
124
124
  "A value of type #{obj.class} is not supported."
@@ -129,7 +129,7 @@ module Google
129
129
  return false unless hash.keys.count == 2
130
130
 
131
131
  pairs = hash.map { |k, v| [String(k), v] }.sort
132
- if pairs.map(&:first) == ["latitude".freeze, "longitude".freeze]
132
+ if pairs.map(&:first) == ["latitude", "longitude"]
133
133
  pairs
134
134
  end
135
135
  end
@@ -156,11 +156,11 @@ module Google
156
156
  data, field_paths_and_values = remove_field_value_from data
157
157
 
158
158
  if data.any? || field_paths_and_values.empty?
159
- write = Google::Firestore::V1::Write.new(
160
- update: Google::Firestore::V1::Document.new(
159
+ write = Google::Cloud::Firestore::V1::Write.new(
160
+ update: Google::Cloud::Firestore::V1::Document.new(
161
161
  name: doc_path,
162
162
  fields: hash_to_fields(data)),
163
- current_document: Google::Firestore::V1::Precondition.new(
163
+ current_document: Google::Cloud::Firestore::V1::Precondition.new(
164
164
  exists: false)
165
165
  )
166
166
  writes << write
@@ -171,7 +171,7 @@ module Google
171
171
 
172
172
  if data.empty?
173
173
  transform_write.current_document = \
174
- Google::Firestore::V1::Precondition.new(exists: false)
174
+ Google::Cloud::Firestore::V1::Precondition.new(exists: false)
175
175
  end
176
176
 
177
177
  writes << transform_write
@@ -207,8 +207,8 @@ module Google
207
207
 
208
208
  data, field_paths_and_values = remove_field_value_from data
209
209
 
210
- writes << Google::Firestore::V1::Write.new(
211
- update: Google::Firestore::V1::Document.new(
210
+ writes << Google::Cloud::Firestore::V1::Write.new(
211
+ update: Google::Cloud::Firestore::V1::Document.new(
212
212
  name: doc_path,
213
213
  fields: hash_to_fields(data))
214
214
  )
@@ -278,11 +278,11 @@ module Google
278
278
  end
279
279
 
280
280
  if data.any? || field_paths.any? || (allow_empty && field_paths_and_values.empty?)
281
- writes << Google::Firestore::V1::Write.new(
282
- update: Google::Firestore::V1::Document.new(
281
+ writes << Google::Cloud::Firestore::V1::Write.new(
282
+ update: Google::Cloud::Firestore::V1::Document.new(
283
283
  name: doc_path,
284
284
  fields: hash_to_fields(data)),
285
- update_mask: Google::Firestore::V1::DocumentMask.new(
285
+ update_mask: Google::Cloud::Firestore::V1::DocumentMask.new(
286
286
  field_paths: field_paths.map(&:formatted_string).sort)
287
287
  )
288
288
  end
@@ -339,18 +339,18 @@ module Google
339
339
  end
340
340
 
341
341
  if data.any? || delete_paths.any?
342
- write = Google::Firestore::V1::Write.new(
343
- update: Google::Firestore::V1::Document.new(
342
+ write = Google::Cloud::Firestore::V1::Write.new(
343
+ update: Google::Cloud::Firestore::V1::Document.new(
344
344
  name: doc_path,
345
345
  fields: hash_to_fields(data)),
346
- update_mask: Google::Firestore::V1::DocumentMask.new(
346
+ update_mask: Google::Cloud::Firestore::V1::DocumentMask.new(
347
347
  field_paths: (field_paths).map(&:formatted_string).sort),
348
- current_document: Google::Firestore::V1::Precondition.new(
348
+ current_document: Google::Cloud::Firestore::V1::Precondition.new(
349
349
  exists: true)
350
350
  )
351
351
  if update_time
352
352
  write.current_document = \
353
- Google::Firestore::V1::Precondition.new(
353
+ Google::Cloud::Firestore::V1::Precondition.new(
354
354
  update_time: time_to_timestamp(update_time))
355
355
  end
356
356
  writes << write
@@ -360,7 +360,7 @@ module Google
360
360
  transform_write = transform_write doc_path, field_paths_and_values
361
361
  if data.empty?
362
362
  transform_write.current_document = \
363
- Google::Firestore::V1::Precondition.new(exists: true)
363
+ Google::Cloud::Firestore::V1::Precondition.new(exists: true)
364
364
  end
365
365
  writes << transform_write
366
366
  end
@@ -373,13 +373,13 @@ module Google
373
373
  raise ArgumentError, "cannot specify both exists and update_time"
374
374
  end
375
375
 
376
- write = Google::Firestore::V1::Write.new(
376
+ write = Google::Cloud::Firestore::V1::Write.new(
377
377
  delete: doc_path
378
378
  )
379
379
 
380
380
  unless exists.nil? && update_time.nil?
381
381
  write.current_document = \
382
- Google::Firestore::V1::Precondition.new({
382
+ Google::Cloud::Firestore::V1::Precondition.new({
383
383
  exists: exists, update_time: time_to_timestamp(update_time)
384
384
  }.delete_if { |_, v| v.nil? })
385
385
  end
@@ -584,8 +584,8 @@ module Google
584
584
  to_field_transform field_path, field_value
585
585
  end
586
586
 
587
- Google::Firestore::V1::Write.new(
588
- transform: Google::Firestore::V1::DocumentTransform.new(
587
+ Google::Cloud::Firestore::V1::Write.new(
588
+ transform: Google::Cloud::Firestore::V1::DocumentTransform.new(
589
589
  document: doc_path,
590
590
  field_transforms: field_transforms
591
591
  )
@@ -594,32 +594,32 @@ module Google
594
594
 
595
595
  def to_field_transform field_path, field_value
596
596
  if field_value.type == :server_time
597
- Google::Firestore::V1::DocumentTransform::FieldTransform.new(
597
+ Google::Cloud::Firestore::V1::DocumentTransform::FieldTransform.new(
598
598
  field_path: field_path.formatted_string,
599
599
  set_to_server_value: :REQUEST_TIME
600
600
  )
601
601
  elsif field_value.type == :array_union
602
- Google::Firestore::V1::DocumentTransform::FieldTransform.new(
602
+ Google::Cloud::Firestore::V1::DocumentTransform::FieldTransform.new(
603
603
  field_path: field_path.formatted_string,
604
604
  append_missing_elements: raw_to_value(Array(field_value.value)).array_value
605
605
  )
606
606
  elsif field_value.type == :array_delete
607
- Google::Firestore::V1::DocumentTransform::FieldTransform.new(
607
+ Google::Cloud::Firestore::V1::DocumentTransform::FieldTransform.new(
608
608
  field_path: field_path.formatted_string,
609
609
  remove_all_from_array: raw_to_value(Array(field_value.value)).array_value
610
610
  )
611
611
  elsif field_value.type == :increment
612
- Google::Firestore::V1::DocumentTransform::FieldTransform.new(
612
+ Google::Cloud::Firestore::V1::DocumentTransform::FieldTransform.new(
613
613
  field_path: field_path.formatted_string,
614
614
  increment: raw_to_value(field_value.value)
615
615
  )
616
616
  elsif field_value.type == :maximum
617
- Google::Firestore::V1::DocumentTransform::FieldTransform.new(
617
+ Google::Cloud::Firestore::V1::DocumentTransform::FieldTransform.new(
618
618
  field_path: field_path.formatted_string,
619
619
  maximum: raw_to_value(field_value.value)
620
620
  )
621
621
  elsif field_value.type == :minimum
622
- Google::Firestore::V1::DocumentTransform::FieldTransform.new(
622
+ Google::Cloud::Firestore::V1::DocumentTransform::FieldTransform.new(
623
623
  field_path: field_path.formatted_string,
624
624
  minimum: raw_to_value(field_value.value)
625
625
  )
@@ -13,7 +13,7 @@
13
13
  # limitations under the License.
14
14
 
15
15
 
16
- require "google/cloud/firestore/v1/credentials"
16
+ require "google/cloud/firestore/v1/firestore/credentials"
17
17
 
18
18
  module Google
19
19
  module Cloud
@@ -37,7 +37,7 @@ module Google
37
37
  #
38
38
  # firestore.project_id #=> "my-project"
39
39
  #
40
- class Credentials < Google::Cloud::Firestore::V1::Credentials
40
+ class Credentials < Google::Cloud::Firestore::V1::Firestore::Credentials
41
41
  end
42
42
  end
43
43
  end
@@ -70,12 +70,13 @@ module Google
70
70
  # @!group Access
71
71
 
72
72
  ##
73
- # Retrieves a list of collections nested under the document snapshot.
73
+ # Retrieves an enumerator for the collections nested under the document snapshot.
74
74
  #
75
75
  # @yield [collections] The block for accessing the collections.
76
- # @yieldparam [CollectionReference] collection A collection.
76
+ # @yieldparam [CollectionReference] collection A collection reference object.
77
77
  #
78
- # @return [Enumerator<CollectionReference>] collection list.
78
+ # @return [Enumerator<CollectionReference>] An enumerator of collection references. If a block is provided, this
79
+ # is the same enumerator that is accessed through the block.
79
80
  #
80
81
  # @example
81
82
  # require "google/cloud/firestore"
@@ -91,11 +92,10 @@ module Google
91
92
  #
92
93
  def cols
93
94
  ensure_service!
94
-
95
- return enum_for :cols unless block_given?
96
-
97
- collection_ids = service.list_collections path
98
- collection_ids.each { |collection_id| yield col collection_id }
95
+ grpc = service.list_collections path
96
+ cols_enum = CollectionReferenceList.from_grpc(grpc, client, path).all
97
+ cols_enum.each { |c| yield c } if block_given?
98
+ cols_enum
99
99
  end
100
100
  alias collections cols
101
101
  alias list_collections cols
@@ -162,7 +162,7 @@ module Google
162
162
 
163
163
  ##
164
164
  # @private New DocumentReference::List from a
165
- # Google::Firestore::V1::ListDocumentsResponse object.
165
+ # Google::Cloud::Firestore::V1::ListDocumentsResponse object.
166
166
  def self.from_grpc grpc, client, parent, collection_id, max = nil
167
167
  documents = List.new(Array(grpc.documents).map do |document|
168
168
  DocumentReference.from_path document.name, client
@@ -170,7 +170,7 @@ module Google
170
170
  documents.instance_variable_set :@parent, parent
171
171
  documents.instance_variable_set :@collection_id, collection_id
172
172
  token = grpc.next_page_token
173
- token = nil if token == "".freeze
173
+ token = nil if token == ""
174
174
  documents.instance_variable_set :@token, token
175
175
  documents.instance_variable_set :@client, client
176
176
  documents.instance_variable_set :@max, max
@@ -62,7 +62,7 @@ module Google
62
62
  #
63
63
  class DocumentSnapshot
64
64
  ##
65
- # @private The Google::Firestore::V1::Document object.
65
+ # @private The Google::Cloud::Firestore::V1::Document object.
66
66
  attr_accessor :grpc
67
67
 
68
68
  ##
@@ -345,7 +345,7 @@ module Google
345
345
 
346
346
  ##
347
347
  # @private New DocumentSnapshot from a
348
- # Google::Firestore::V1::RunQueryResponse object.
348
+ # Google::Cloud::Firestore::V1::RunQueryResponse object.
349
349
  def self.from_query_result result, client
350
350
  ref = DocumentReference.from_path result.document.name, client
351
351
  read_at = Convert.timestamp_to_time result.read_time
@@ -359,7 +359,7 @@ module Google
359
359
 
360
360
  ##
361
361
  # @private New DocumentSnapshot from a
362
- # Google::Firestore::V1::DocumentChange object.
362
+ # Google::Cloud::Firestore::V1::DocumentChange object.
363
363
  def self.from_document document, client, read_at: nil
364
364
  ref = DocumentReference.from_path document.name, client
365
365
 
@@ -372,7 +372,7 @@ module Google
372
372
 
373
373
  ##
374
374
  # @private New DocumentSnapshot from a
375
- # Google::Firestore::V1::BatchGetDocumentsResponse object.
375
+ # Google::Cloud::Firestore::V1::BatchGetDocumentsResponse object.
376
376
  def self.from_batch_result result, client
377
377
  ref = nil
378
378
  grpc = nil
@@ -63,7 +63,11 @@ module Google
63
63
  attr_accessor :parent_path
64
64
 
65
65
  ##
66
- # @private The Google::Firestore::V1::StructuredQuery object.
66
+ # @private The type for limit queries.
67
+ attr_reader :limit_type
68
+
69
+ ##
70
+ # @private The Google::Cloud::Firestore::V1::StructuredQuery object.
67
71
  attr_accessor :query
68
72
 
69
73
  ##
@@ -118,7 +122,7 @@ module Google
118
122
  new_query.select.fields << field_ref
119
123
  end
120
124
 
121
- Query.start new_query, parent_path, client
125
+ Query.start new_query, parent_path, client, limit_type: limit_type
122
126
  end
123
127
 
124
128
  ##
@@ -154,7 +158,7 @@ module Google
154
158
 
155
159
  new_query.from.last.all_descendants = true
156
160
 
157
- Query.start new_query, parent_path, client
161
+ Query.start new_query, parent_path, client, limit_type: limit_type
158
162
  end
159
163
 
160
164
  ##
@@ -190,7 +194,7 @@ module Google
190
194
 
191
195
  new_query.from.last.all_descendants = false
192
196
 
193
- Query.start new_query, parent_path, client
197
+ Query.start new_query, parent_path, client, limit_type: limit_type
194
198
  end
195
199
 
196
200
  ##
@@ -246,7 +250,7 @@ module Google
246
250
  new_filter = filter field.formatted_string, operator, value
247
251
  add_filters_to_query new_query, new_filter
248
252
 
249
- Query.start new_query, parent_path, client
253
+ Query.start new_query, parent_path, client, limit_type: limit_type
250
254
  end
251
255
 
252
256
  ##
@@ -298,9 +302,8 @@ module Google
298
302
  # end
299
303
  #
300
304
  def order field, direction = :asc
301
- if query_has_cursors?
302
- raise "cannot call order after calling " \
303
- "start_at, start_after, end_before, or end_at"
305
+ if query_has_cursors? || limit_type == :last
306
+ raise "cannot call order after calling limit_to_last, start_at, start_after, end_before, or end_at"
304
307
  end
305
308
 
306
309
  new_query = @query.dup
@@ -315,7 +318,7 @@ module Google
315
318
  direction: order_direction(direction)
316
319
  )
317
320
 
318
- Query.start new_query, parent_path, client
321
+ Query.start new_query, parent_path, client, limit_type: limit_type
319
322
  end
320
323
  alias order_by order
321
324
 
@@ -348,12 +351,13 @@ module Google
348
351
 
349
352
  new_query.offset = num
350
353
 
351
- Query.start new_query, parent_path, client
354
+ Query.start new_query, parent_path, client, limit_type: limit_type
352
355
  end
353
356
 
354
357
  ##
355
- # Limits a query to return a fixed number of results. If the current
356
- # query already has a limit set, this will overwrite it.
358
+ # Limits a query to return only the first matching documents.
359
+ #
360
+ # If the current query already has a limit set, this will overwrite it.
357
361
  #
358
362
  # @param [Integer] num The maximum number of results to return.
359
363
  #
@@ -368,19 +372,83 @@ module Google
368
372
  # cities_col = firestore.col "cities"
369
373
  #
370
374
  # # Create a query
371
- # query = cities_col.offset(10).limit(5)
375
+ # query = cities_col.order(:name, :desc).offset(10).limit(5)
372
376
  #
373
377
  # query.get do |city|
374
378
  # puts "#{city.document_id} has #{city[:population]} residents."
375
379
  # end
376
380
  #
377
381
  def limit num
382
+ if limit_type == :last
383
+ raise "cannot call limit after calling limit_to_last"
384
+ end
385
+
378
386
  new_query = @query.dup
379
387
  new_query ||= StructuredQuery.new
380
388
 
381
389
  new_query.limit = Google::Protobuf::Int32Value.new value: num
382
390
 
383
- Query.start new_query, parent_path, client
391
+ Query.start new_query, parent_path, client, limit_type: :first
392
+ end
393
+
394
+ ##
395
+ # Limits a query to return only the last matching documents.
396
+ #
397
+ # You must specify at least one "order by" clause for limitToLast queries.
398
+ # (See {#order}.)
399
+ #
400
+ # Results for `limit_to_last` queries are only available once all documents
401
+ # are received. Hence, `limit_to_last` queries cannot be streamed using
402
+ # {#listen}.
403
+ #
404
+ # @param [Integer] num The maximum number of results to return.
405
+ #
406
+ # @return [Query] New query with `limit_to_last` called on it.
407
+ #
408
+ # @example
409
+ # require "google/cloud/firestore"
410
+ #
411
+ # firestore = Google::Cloud::Firestore.new
412
+ #
413
+ # # Get a collection reference
414
+ # cities_col = firestore.col "cities"
415
+ #
416
+ # # Create a query
417
+ # query = cities_col.order(:name, :desc).limit_to_last(5)
418
+ #
419
+ # query.get do |city|
420
+ # puts "#{city.document_id} has #{city[:population]} residents."
421
+ # end
422
+ #
423
+ def limit_to_last num
424
+ new_query = @query.dup
425
+
426
+ if new_query.nil? || new_query.order_by.nil? || new_query.order_by.empty?
427
+ raise "specify at least one order clause before calling limit_to_last"
428
+ end
429
+
430
+ if limit_type != :last # Don't reverse order_by more than once.
431
+ # Reverse the order_by directions since we want the last results.
432
+ new_query.order_by.each do |order|
433
+ order.direction = order.direction.to_sym == :DESCENDING ? :ASCENDING : :DESCENDING
434
+ end
435
+
436
+ # Swap the cursors to match the reversed query ordering.
437
+ new_end_at = new_query.start_at.dup
438
+ new_start_at = new_query.end_at.dup
439
+ if new_end_at
440
+ new_end_at.before = !new_end_at.before
441
+ new_query.end_at = new_end_at
442
+ end
443
+ if new_start_at
444
+ new_start_at.before = !new_start_at.before
445
+ new_query.start_at = new_start_at
446
+ end
447
+ end
448
+
449
+ new_query.limit = Google::Protobuf::Int32Value.new value: num
450
+
451
+ Query.start new_query, parent_path, client, limit_type: :last
384
452
  end
385
453
 
386
454
  ##
@@ -477,6 +545,10 @@ module Google
477
545
  def start_at *values
478
546
  raise ArgumentError, "must provide values" if values.empty?
479
547
 
548
+ if limit_type == :last
549
+ raise "cannot call start_at after calling limit_to_last"
550
+ end
551
+
480
552
  new_query = @query.dup
481
553
  new_query ||= StructuredQuery.new
482
554
 
@@ -484,7 +556,7 @@ module Google
484
556
  cursor.before = true
485
557
  new_query.start_at = cursor
486
558
 
487
- Query.start new_query, parent_path, client
559
+ Query.start new_query, parent_path, client, limit_type: limit_type
488
560
  end
489
561
 
490
562
  ##
@@ -581,6 +653,11 @@ module Google
581
653
  def start_after *values
582
654
  raise ArgumentError, "must provide values" if values.empty?
583
655
 
656
+ if limit_type == :last
657
+ raise "cannot call start_after after calling limit_to_last"
658
+ end
659
+
660
+
584
661
  new_query = @query.dup
585
662
  new_query ||= StructuredQuery.new
586
663
 
@@ -588,7 +665,7 @@ module Google
588
665
  cursor.before = false
589
666
  new_query.start_at = cursor
590
667
 
591
- Query.start new_query, parent_path, client
668
+ Query.start new_query, parent_path, client, limit_type: limit_type
592
669
  end
593
670
 
594
671
  ##
@@ -685,6 +762,11 @@ module Google
685
762
  def end_before *values
686
763
  raise ArgumentError, "must provide values" if values.empty?
687
764
 
765
+ if limit_type == :last
766
+ raise "cannot call end_before after calling limit_to_last"
767
+ end
768
+
769
+
688
770
  new_query = @query.dup
689
771
  new_query ||= StructuredQuery.new
690
772
 
@@ -692,7 +774,7 @@ module Google
692
774
  cursor.before = true
693
775
  new_query.end_at = cursor
694
776
 
695
- Query.start new_query, parent_path, client
777
+ Query.start new_query, parent_path, client, limit_type: limit_type
696
778
  end
697
779
 
698
780
  ##
@@ -789,6 +871,11 @@ module Google
789
871
  def end_at *values
790
872
  raise ArgumentError, "must provide values" if values.empty?
791
873
 
874
+ if limit_type == :last
875
+ raise "cannot call end_at after calling limit_to_last"
876
+ end
877
+
878
+
792
879
  new_query = @query.dup
793
880
  new_query ||= StructuredQuery.new
794
881
 
@@ -796,7 +883,7 @@ module Google
796
883
  cursor.before = false
797
884
  new_query.end_at = cursor
798
885
 
799
- Query.start new_query, parent_path, client
886
+ Query.start new_query, parent_path, client, limit_type: limit_type
800
887
  end
801
888
 
802
889
  ##
@@ -828,6 +915,10 @@ module Google
828
915
  return enum_for :get unless block_given?
829
916
 
830
917
  results = service.run_query parent_path, @query
918
+
919
+ # Reverse the results for Query#limit_to_last queries since that method reversed the order_by directions.
920
+ results = results.to_a.reverse if limit_type == :last
921
+
831
922
  results.each do |result|
832
923
  next if result.document.nil?
833
924
  yield DocumentSnapshot.from_query_result result, client
@@ -870,11 +961,12 @@ module Google
870
961
 
871
962
  ##
872
963
  # @private Start a new Query.
873
- def self.start query, parent_path, client
964
+ def self.start query, parent_path, client, limit_type: nil
874
965
  query ||= StructuredQuery.new
875
966
  Query.new.tap do |q|
876
967
  q.instance_variable_set :@query, query
877
968
  q.instance_variable_set :@parent_path, parent_path
969
+ q.instance_variable_set :@limit_type, limit_type
878
970
  q.instance_variable_set :@client, client
879
971
  end
880
972
  end
@@ -883,7 +975,7 @@ module Google
883
975
 
884
976
  ##
885
977
  # @private
886
- StructuredQuery = Google::Firestore::V1::StructuredQuery
978
+ StructuredQuery = Google::Cloud::Firestore::V1::StructuredQuery
887
979
 
888
980
  ##
889
981
  # @private
@@ -987,7 +1079,7 @@ module Google
987
1079
  end
988
1080
 
989
1081
  def order_direction direction
990
- return :DESCENDING if direction.to_s.downcase.start_with? "d".freeze
1082
+ return :DESCENDING if direction.to_s.downcase.start_with? "d"
991
1083
  :ASCENDING
992
1084
  end
993
1085
 
@@ -1014,7 +1106,7 @@ module Google
1014
1106
  Convert.raw_to_value value
1015
1107
  end
1016
1108
 
1017
- Google::Firestore::V1::Cursor.new values: values
1109
+ Google::Cloud::Firestore::V1::Cursor.new values: values
1018
1110
  end
1019
1111
 
1020
1112
  def snapshot_to_cursor snapshot, query