google-cloud-firestore 1.4.4 → 2.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (82) hide show
  1. checksums.yaml +4 -4
  2. data/AUTHENTICATION.md +1 -1
  3. data/CHANGELOG.md +55 -0
  4. data/lib/google-cloud-firestore.rb +15 -21
  5. data/lib/google/cloud/firestore.rb +12 -23
  6. data/lib/google/cloud/firestore/client.rb +12 -14
  7. data/lib/google/cloud/firestore/collection_reference.rb +1 -0
  8. data/lib/google/cloud/firestore/collection_reference_list.rb +137 -0
  9. data/lib/google/cloud/firestore/convert.rb +39 -39
  10. data/lib/google/cloud/firestore/credentials.rb +2 -2
  11. data/lib/google/cloud/firestore/document_listener.rb +86 -7
  12. data/lib/google/cloud/firestore/document_reference.rb +9 -10
  13. data/lib/google/cloud/firestore/document_reference/list.rb +2 -2
  14. data/lib/google/cloud/firestore/document_snapshot.rb +5 -6
  15. data/lib/google/cloud/firestore/query.rb +127 -34
  16. data/lib/google/cloud/firestore/query_listener.rb +82 -1
  17. data/lib/google/cloud/firestore/service.rb +63 -104
  18. data/lib/google/cloud/firestore/transaction.rb +2 -2
  19. data/lib/google/cloud/firestore/version.rb +1 -1
  20. data/lib/google/cloud/firestore/watch/inventory.rb +2 -0
  21. data/lib/google/cloud/firestore/watch/listener.rb +32 -25
  22. data/lib/google/cloud/firestore/watch/order.rb +1 -1
  23. metadata +9 -107
  24. data/lib/google/cloud/firestore/admin.rb +0 -144
  25. data/lib/google/cloud/firestore/admin/v1.rb +0 -151
  26. data/lib/google/cloud/firestore/admin/v1/credentials.rb +0 -44
  27. data/lib/google/cloud/firestore/admin/v1/doc/google/firestore/admin/v1/field.rb +0 -86
  28. data/lib/google/cloud/firestore/admin/v1/doc/google/firestore/admin/v1/firestore_admin.rb +0 -161
  29. data/lib/google/cloud/firestore/admin/v1/doc/google/firestore/admin/v1/index.rb +0 -142
  30. data/lib/google/cloud/firestore/admin/v1/doc/google/longrunning/operations.rb +0 -51
  31. data/lib/google/cloud/firestore/admin/v1/doc/google/protobuf/any.rb +0 -131
  32. data/lib/google/cloud/firestore/admin/v1/doc/google/protobuf/empty.rb +0 -29
  33. data/lib/google/cloud/firestore/admin/v1/doc/google/protobuf/field_mask.rb +0 -222
  34. data/lib/google/cloud/firestore/admin/v1/doc/google/rpc/status.rb +0 -39
  35. data/lib/google/cloud/firestore/admin/v1/firestore_admin_client.rb +0 -902
  36. data/lib/google/cloud/firestore/admin/v1/firestore_admin_client_config.json +0 -76
  37. data/lib/google/cloud/firestore/v1.rb +0 -152
  38. data/lib/google/cloud/firestore/v1/credentials.rb +0 -42
  39. data/lib/google/cloud/firestore/v1/doc/google/firestore/v1/common.rb +0 -64
  40. data/lib/google/cloud/firestore/v1/doc/google/firestore/v1/document.rb +0 -136
  41. data/lib/google/cloud/firestore/v1/doc/google/firestore/v1/firestore.rb +0 -566
  42. data/lib/google/cloud/firestore/v1/doc/google/firestore/v1/query.rb +0 -227
  43. data/lib/google/cloud/firestore/v1/doc/google/firestore/v1/write.rb +0 -241
  44. data/lib/google/cloud/firestore/v1/doc/google/protobuf/any.rb +0 -131
  45. data/lib/google/cloud/firestore/v1/doc/google/protobuf/empty.rb +0 -29
  46. data/lib/google/cloud/firestore/v1/doc/google/protobuf/timestamp.rb +0 -113
  47. data/lib/google/cloud/firestore/v1/doc/google/protobuf/wrappers.rb +0 -26
  48. data/lib/google/cloud/firestore/v1/doc/google/rpc/status.rb +0 -39
  49. data/lib/google/cloud/firestore/v1/firestore_client.rb +0 -1011
  50. data/lib/google/cloud/firestore/v1/firestore_client_config.json +0 -105
  51. data/lib/google/cloud/firestore/v1beta1.rb +0 -162
  52. data/lib/google/cloud/firestore/v1beta1/credentials.rb +0 -42
  53. data/lib/google/cloud/firestore/v1beta1/doc/google/firestore/v1beta1/common.rb +0 -64
  54. data/lib/google/cloud/firestore/v1beta1/doc/google/firestore/v1beta1/document.rb +0 -136
  55. data/lib/google/cloud/firestore/v1beta1/doc/google/firestore/v1beta1/firestore.rb +0 -564
  56. data/lib/google/cloud/firestore/v1beta1/doc/google/firestore/v1beta1/query.rb +0 -227
  57. data/lib/google/cloud/firestore/v1beta1/doc/google/firestore/v1beta1/write.rb +0 -237
  58. data/lib/google/cloud/firestore/v1beta1/doc/google/protobuf/any.rb +0 -131
  59. data/lib/google/cloud/firestore/v1beta1/doc/google/protobuf/empty.rb +0 -29
  60. data/lib/google/cloud/firestore/v1beta1/doc/google/protobuf/timestamp.rb +0 -113
  61. data/lib/google/cloud/firestore/v1beta1/doc/google/protobuf/wrappers.rb +0 -26
  62. data/lib/google/cloud/firestore/v1beta1/doc/google/rpc/status.rb +0 -39
  63. data/lib/google/cloud/firestore/v1beta1/firestore_client.rb +0 -1021
  64. data/lib/google/cloud/firestore/v1beta1/firestore_client_config.json +0 -100
  65. data/lib/google/firestore/admin/v1/field_pb.rb +0 -32
  66. data/lib/google/firestore/admin/v1/firestore_admin_pb.rb +0 -84
  67. data/lib/google/firestore/admin/v1/firestore_admin_services_pb.rb +0 -94
  68. data/lib/google/firestore/admin/v1/index_pb.rb +0 -58
  69. data/lib/google/firestore/admin/v1/location_pb.rb +0 -22
  70. data/lib/google/firestore/admin/v1/operation_pb.rb +0 -90
  71. data/lib/google/firestore/v1/common_pb.rb +0 -45
  72. data/lib/google/firestore/v1/document_pb.rb +0 -50
  73. data/lib/google/firestore/v1/firestore_pb.rb +0 -222
  74. data/lib/google/firestore/v1/firestore_services_pb.rb +0 -80
  75. data/lib/google/firestore/v1/query_pb.rb +0 -107
  76. data/lib/google/firestore/v1/write_pb.rb +0 -80
  77. data/lib/google/firestore/v1beta1/common_pb.rb +0 -45
  78. data/lib/google/firestore/v1beta1/document_pb.rb +0 -50
  79. data/lib/google/firestore/v1beta1/firestore_pb.rb +0 -222
  80. data/lib/google/firestore/v1beta1/firestore_services_pb.rb +0 -89
  81. data/lib/google/firestore/v1beta1/query_pb.rb +0 -107
  82. data/lib/google/firestore/v1beta1/write_pb.rb +0 -79
@@ -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
  ##
@@ -212,6 +216,9 @@ module Google
212
216
  # * greater than: `>`, `gt`
213
217
  # * greater than or equal: `>=`, `gte`
214
218
  # * equal: `=`, `==`, `eq`, `eql`, `is`
219
+ # * not equal: `!=`
220
+ # * in: `in`
221
+ # * not in: `not-in`, `not_in`
215
222
  # * array contains: `array-contains`, `array_contains`
216
223
  # @param [Object] value A value the field is compared to.
217
224
  #
@@ -246,7 +253,7 @@ module Google
246
253
  new_filter = filter field.formatted_string, operator, value
247
254
  add_filters_to_query new_query, new_filter
248
255
 
249
- Query.start new_query, parent_path, client
256
+ Query.start new_query, parent_path, client, limit_type: limit_type
250
257
  end
251
258
 
252
259
  ##
@@ -298,9 +305,8 @@ module Google
298
305
  # end
299
306
  #
300
307
  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"
308
+ if query_has_cursors? || limit_type == :last
309
+ raise "cannot call order after calling limit_to_last, start_at, start_after, end_before, or end_at"
304
310
  end
305
311
 
306
312
  new_query = @query.dup
@@ -315,7 +321,7 @@ module Google
315
321
  direction: order_direction(direction)
316
322
  )
317
323
 
318
- Query.start new_query, parent_path, client
324
+ Query.start new_query, parent_path, client, limit_type: limit_type
319
325
  end
320
326
  alias order_by order
321
327
 
@@ -348,12 +354,13 @@ module Google
348
354
 
349
355
  new_query.offset = num
350
356
 
351
- Query.start new_query, parent_path, client
357
+ Query.start new_query, parent_path, client, limit_type: limit_type
352
358
  end
353
359
 
354
360
  ##
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.
361
+ # Limits a query to return only the first matching documents.
362
+ #
363
+ # If the current query already has a limit set, this will overwrite it.
357
364
  #
358
365
  # @param [Integer] num The maximum number of results to return.
359
366
  #
@@ -368,19 +375,83 @@ module Google
368
375
  # cities_col = firestore.col "cities"
369
376
  #
370
377
  # # Create a query
371
- # query = cities_col.offset(10).limit(5)
378
+ # query = cities_col.order(:name, :desc).offset(10).limit(5)
372
379
  #
373
380
  # query.get do |city|
374
381
  # puts "#{city.document_id} has #{city[:population]} residents."
375
382
  # end
376
383
  #
377
384
  def limit num
385
+ if limit_type == :last
386
+ raise "cannot call limit after calling limit_to_last"
387
+ end
388
+
378
389
  new_query = @query.dup
379
390
  new_query ||= StructuredQuery.new
380
391
 
381
392
  new_query.limit = Google::Protobuf::Int32Value.new value: num
382
393
 
383
- Query.start new_query, parent_path, client
394
+ Query.start new_query, parent_path, client, limit_type: :first
395
+ end
396
+
397
+ ##
398
+ # Limits a query to return only the last matching documents.
399
+ #
400
+ # You must specify at least one "order by" clause for limitToLast queries.
401
+ # (See {#order}.)
402
+ #
403
+ # Results for `limit_to_last` queries are only available once all documents
404
+ # are received. Hence, `limit_to_last` queries cannot be streamed using
405
+ # {#listen}.
406
+ #
407
+ # @param [Integer] num The maximum number of results to return.
408
+ #
409
+ # @return [Query] New query with `limit_to_last` called on it.
410
+ #
411
+ # @example
412
+ # require "google/cloud/firestore"
413
+ #
414
+ # firestore = Google::Cloud::Firestore.new
415
+ #
416
+ # # Get a collection reference
417
+ # cities_col = firestore.col "cities"
418
+ #
419
+ # # Create a query
420
+ # query = cities_col.order(:name, :desc).limit_to_last(5)
421
+ #
422
+ # query.get do |city|
423
+ # puts "#{city.document_id} has #{city[:population]} residents."
424
+ # end
425
+ #
426
+ def limit_to_last num
427
+ new_query = @query.dup
428
+
429
+ if new_query.nil? || new_query.order_by.nil? || new_query.order_by.empty?
430
+ raise "specify at least one order clause before calling limit_to_last"
431
+ end
432
+
433
+ if limit_type != :last # Don't reverse order_by more than once.
434
+ # Reverse the order_by directions since we want the last results.
435
+ new_query.order_by.each do |order|
436
+ order.direction = order.direction.to_sym == :DESCENDING ? :ASCENDING : :DESCENDING
437
+ end
438
+
439
+ # Swap the cursors to match the reversed query ordering.
440
+ new_end_at = new_query.start_at.dup
441
+ new_start_at = new_query.end_at.dup
442
+ if new_end_at
443
+ new_end_at.before = !new_end_at.before
444
+ new_query.end_at = new_end_at
445
+ end
446
+ if new_start_at
447
+ new_start_at.before = !new_start_at.before
448
+ new_query.start_at = new_start_at
449
+ end
450
+ end
451
+
452
+ new_query.limit = Google::Protobuf::Int32Value.new value: num
453
+
454
+ Query.start new_query, parent_path, client, limit_type: :last
384
455
  end
385
456
 
386
457
  ##
@@ -477,6 +548,10 @@ module Google
477
548
  def start_at *values
478
549
  raise ArgumentError, "must provide values" if values.empty?
479
550
 
551
+ if limit_type == :last
552
+ raise "cannot call start_at after calling limit_to_last"
553
+ end
554
+
480
555
  new_query = @query.dup
481
556
  new_query ||= StructuredQuery.new
482
557
 
@@ -484,7 +559,7 @@ module Google
484
559
  cursor.before = true
485
560
  new_query.start_at = cursor
486
561
 
487
- Query.start new_query, parent_path, client
562
+ Query.start new_query, parent_path, client, limit_type: limit_type
488
563
  end
489
564
 
490
565
  ##
@@ -581,6 +656,11 @@ module Google
581
656
  def start_after *values
582
657
  raise ArgumentError, "must provide values" if values.empty?
583
658
 
659
+ if limit_type == :last
660
+ raise "cannot call start_after after calling limit_to_last"
661
+ end
662
+
663
+
584
664
  new_query = @query.dup
585
665
  new_query ||= StructuredQuery.new
586
666
 
@@ -588,7 +668,7 @@ module Google
588
668
  cursor.before = false
589
669
  new_query.start_at = cursor
590
670
 
591
- Query.start new_query, parent_path, client
671
+ Query.start new_query, parent_path, client, limit_type: limit_type
592
672
  end
593
673
 
594
674
  ##
@@ -685,6 +765,11 @@ module Google
685
765
  def end_before *values
686
766
  raise ArgumentError, "must provide values" if values.empty?
687
767
 
768
+ if limit_type == :last
769
+ raise "cannot call end_before after calling limit_to_last"
770
+ end
771
+
772
+
688
773
  new_query = @query.dup
689
774
  new_query ||= StructuredQuery.new
690
775
 
@@ -692,7 +777,7 @@ module Google
692
777
  cursor.before = true
693
778
  new_query.end_at = cursor
694
779
 
695
- Query.start new_query, parent_path, client
780
+ Query.start new_query, parent_path, client, limit_type: limit_type
696
781
  end
697
782
 
698
783
  ##
@@ -789,6 +874,11 @@ module Google
789
874
  def end_at *values
790
875
  raise ArgumentError, "must provide values" if values.empty?
791
876
 
877
+ if limit_type == :last
878
+ raise "cannot call end_at after calling limit_to_last"
879
+ end
880
+
881
+
792
882
  new_query = @query.dup
793
883
  new_query ||= StructuredQuery.new
794
884
 
@@ -796,7 +886,7 @@ module Google
796
886
  cursor.before = false
797
887
  new_query.end_at = cursor
798
888
 
799
- Query.start new_query, parent_path, client
889
+ Query.start new_query, parent_path, client, limit_type: limit_type
800
890
  end
801
891
 
802
892
  ##
@@ -828,6 +918,10 @@ module Google
828
918
  return enum_for :get unless block_given?
829
919
 
830
920
  results = service.run_query parent_path, @query
921
+
922
+ # Reverse the results for Query#limit_to_last queries since that method reversed the order_by directions.
923
+ results = results.to_a.reverse if limit_type == :last
924
+
831
925
  results.each do |result|
832
926
  next if result.document.nil?
833
927
  yield DocumentSnapshot.from_query_result result, client
@@ -870,11 +964,12 @@ module Google
870
964
 
871
965
  ##
872
966
  # @private Start a new Query.
873
- def self.start query, parent_path, client
967
+ def self.start query, parent_path, client, limit_type: nil
874
968
  query ||= StructuredQuery.new
875
969
  Query.new.tap do |q|
876
970
  q.instance_variable_set :@query, query
877
971
  q.instance_variable_set :@parent_path, parent_path
972
+ q.instance_variable_set :@limit_type, limit_type
878
973
  q.instance_variable_set :@client, client
879
974
  end
880
975
  end
@@ -883,7 +978,7 @@ module Google
883
978
 
884
979
  ##
885
980
  # @private
886
- StructuredQuery = Google::Firestore::V1::StructuredQuery
981
+ StructuredQuery = Google::Cloud::Firestore::V1::StructuredQuery
887
982
 
888
983
  ##
889
984
  # @private
@@ -901,23 +996,20 @@ module Google
901
996
  "eq" => :EQUAL,
902
997
  "eql" => :EQUAL,
903
998
  "is" => :EQUAL,
999
+ "!=" => :NOT_EQUAL,
904
1000
  "array_contains" => :ARRAY_CONTAINS,
905
1001
  "array-contains" => :ARRAY_CONTAINS,
906
1002
  "include" => :ARRAY_CONTAINS,
907
1003
  "include?" => :ARRAY_CONTAINS,
908
1004
  "has" => :ARRAY_CONTAINS,
909
1005
  "in" => :IN,
1006
+ "not_in" => :NOT_IN,
1007
+ "not-in" => :NOT_IN,
910
1008
  "array_contains_any" => :ARRAY_CONTAINS_ANY,
911
1009
  "array-contains-any" => :ARRAY_CONTAINS_ANY
912
1010
  }.freeze
913
1011
  ##
914
1012
  # @private
915
- EQUALITY_FILTERS = %i[
916
- EQUAL
917
- ARRAY_CONTAINS
918
- ].freeze
919
- ##
920
- # @private
921
1013
  INEQUALITY_FILTERS = %i[
922
1014
  LESS_THAN
923
1015
  LESS_THAN_OR_EQUAL
@@ -945,12 +1037,13 @@ module Google
945
1037
  raise ArgumentError, "unknown operator #{op}" if operator.nil?
946
1038
 
947
1039
  if value_unary? value
948
- if operator != :EQUAL
949
- raise ArgumentError,
950
- "can only check equality for #{value} values"
951
- end
952
-
953
- operator = value_nan?(value) ? :IS_NAN : :IS_NULL
1040
+ operator = if operator == :EQUAL
1041
+ value_nan?(value) ? :IS_NAN : :IS_NULL
1042
+ elsif operator == :NOT_EQUAL
1043
+ value_nan?(value) ? :IS_NOT_NAN : :IS_NOT_NULL
1044
+ else
1045
+ raise ArgumentError, "can only perform '==' and '!=' comparisons on #{value} values"
1046
+ end
954
1047
 
955
1048
  return StructuredQuery::Filter.new(
956
1049
  unary_filter: StructuredQuery::UnaryFilter.new(
@@ -987,7 +1080,7 @@ module Google
987
1080
  end
988
1081
 
989
1082
  def order_direction direction
990
- return :DESCENDING if direction.to_s.downcase.start_with? "d".freeze
1083
+ return :DESCENDING if direction.to_s.downcase.start_with? "d"
991
1084
  :ASCENDING
992
1085
  end
993
1086
 
@@ -1014,7 +1107,7 @@ module Google
1014
1107
  Convert.raw_to_value value
1015
1108
  end
1016
1109
 
1017
- Google::Firestore::V1::Cursor.new values: values
1110
+ Google::Cloud::Firestore::V1::Cursor.new values: values
1018
1111
  end
1019
1112
 
1020
1113
  def snapshot_to_cursor snapshot, query
@@ -41,17 +41,21 @@ module Google
41
41
  # listener.stop
42
42
  #
43
43
  class QueryListener
44
+ include MonitorMixin
44
45
  ##
45
46
  # @private
46
47
  # Creates the watch stream and listener object.
47
48
  def initialize query, &callback
49
+ super() # to init MonitorMixin
50
+
48
51
  @query = query
49
52
  raise ArgumentError if @query.nil?
50
53
 
51
54
  @callback = callback
52
55
  raise ArgumentError if @callback.nil?
56
+ @error_callbacks = []
53
57
 
54
- @listener = Watch::Listener.for_query query, &callback
58
+ @listener = Watch::Listener.for_query self, query, &callback
55
59
  end
56
60
 
57
61
  ##
@@ -112,6 +116,83 @@ module Google
112
116
  def stopped?
113
117
  @listener.stopped?
114
118
  end
119
+
120
+ ##
121
+ # Register to be notified of errors when raised.
122
+ #
123
+ # If an unhandled error has occurred the listener will attempt to
124
+ # recover from the error and resume listening.
125
+ #
126
+ # Multiple error handlers can be added.
127
+ #
128
+ # @yield [callback] The block to be called when an error is raised.
129
+ # @yieldparam [Exception] error The error raised.
130
+ #
131
+ # @example
132
+ # require "google/cloud/firestore"
133
+ #
134
+ # firestore = Google::Cloud::Firestore.new
135
+ #
136
+ # # Create a query
137
+ # query = firestore.col(:cities).order(:population, :desc)
138
+ #
139
+ # listener = query.listen do |snapshot|
140
+ # puts "The query snapshot has #{snapshot.docs.count} documents "
141
+ # puts "and has #{snapshot.changes.count} changes."
142
+ # end
143
+ #
144
+ # # Register to be notified when unhandled errors occur.
145
+ # listener.on_error do |error|
146
+ # puts error
147
+ # end
148
+ #
149
+ # # When ready, stop the listen operation and close the stream.
150
+ # listener.stop
151
+ #
152
+ def on_error &block
153
+ raise ArgumentError, "on_error must be called with a block" unless block_given?
154
+ synchronize { @error_callbacks << block }
155
+ end
156
+
157
+ ##
158
+ # The most recent unhandled error to occur while listening for changes.
159
+ #
160
+ # If an unhandled error has occurred the listener will attempt to
161
+ # recover from the error and resume listening.
162
+ #
163
+ # @return [Exception, nil] error The most recent error raised.
164
+ #
165
+ # @example
166
+ # require "google/cloud/firestore"
167
+ #
168
+ # firestore = Google::Cloud::Firestore.new
169
+ #
170
+ # # Create a query
171
+ # query = firestore.col(:cities).order(:population, :desc)
172
+ #
173
+ # listener = query.listen do |snapshot|
174
+ # puts "The query snapshot has #{snapshot.docs.count} documents "
175
+ # puts "and has #{snapshot.changes.count} changes."
176
+ # end
177
+ #
178
+ # # If an error was raised, it can be retrieved here:
179
+ # listener.last_error #=> nil
180
+ #
181
+ # # When ready, stop the listen operation and close the stream.
182
+ # listener.stop
183
+ #
184
+ def last_error
185
+ synchronize { @last_error }
186
+ end
187
+
188
+ # @private Pass the error to user-provided error callbacks.
189
+ def error! error
190
+ error_callbacks = synchronize do
191
+ @last_error = error
192
+ @error_callbacks.dup
193
+ end
194
+ error_callbacks.each { |error_callback| error_callback.call error }
195
+ end
115
196
  end
116
197
  end
117
198
  end