google-cloud-firestore 1.4.2 → 2.2.0

Sign up to get free protection for your applications and to get access to all the features.
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