google-cloud-firestore 2.0.0 → 2.4.1
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +38 -0
- data/CONTRIBUTING.md +1 -1
- data/lib/google-cloud-firestore.rb +1 -0
- data/lib/google/cloud/firestore/batch.rb +4 -4
- data/lib/google/cloud/firestore/convert.rb +141 -161
- data/lib/google/cloud/firestore/document_listener.rb +86 -7
- data/lib/google/cloud/firestore/document_reference.rb +1 -2
- data/lib/google/cloud/firestore/document_snapshot.rb +1 -2
- data/lib/google/cloud/firestore/query.rb +123 -30
- data/lib/google/cloud/firestore/query_listener.rb +82 -1
- data/lib/google/cloud/firestore/transaction.rb +4 -4
- data/lib/google/cloud/firestore/version.rb +1 -1
- data/lib/google/cloud/firestore/watch/inventory.rb +2 -0
- data/lib/google/cloud/firestore/watch/listener.rb +23 -16
- metadata +3 -3
@@ -14,11 +14,14 @@
|
|
14
14
|
|
15
15
|
|
16
16
|
require "google/cloud/firestore/watch/listener"
|
17
|
+
require "monitor"
|
17
18
|
|
18
19
|
module Google
|
19
20
|
module Cloud
|
20
21
|
module Firestore
|
21
22
|
##
|
23
|
+
# # DocumentListener
|
24
|
+
#
|
22
25
|
# An ongoing listen operation on a document reference. This is returned by
|
23
26
|
# calling {DocumentReference#listen}.
|
24
27
|
#
|
@@ -31,25 +34,28 @@ module Google
|
|
31
34
|
# nyc_ref = firestore.doc "cities/NYC"
|
32
35
|
#
|
33
36
|
# listener = nyc_ref.listen do |snapshot|
|
34
|
-
# puts "The population of #{snapshot[:name]} "
|
35
|
-
# puts "is #{snapshot[:population]}."
|
37
|
+
# puts "The population of #{snapshot[:name]} is #{snapshot[:population]}."
|
36
38
|
# end
|
37
39
|
#
|
38
40
|
# # When ready, stop the listen operation and close the stream.
|
39
41
|
# listener.stop
|
40
42
|
#
|
41
43
|
class DocumentListener
|
44
|
+
include MonitorMixin
|
42
45
|
##
|
43
46
|
# @private
|
44
47
|
# Creates the watch stream and listener object.
|
45
48
|
def initialize doc_ref, &callback
|
49
|
+
super() # to init MonitorMixin
|
50
|
+
|
46
51
|
@doc_ref = doc_ref
|
47
52
|
raise ArgumentError if @doc_ref.nil?
|
48
53
|
|
49
54
|
@callback = callback
|
50
55
|
raise ArgumentError if @callback.nil?
|
56
|
+
@error_callbacks = []
|
51
57
|
|
52
|
-
@listener = Watch::Listener.for_doc_ref doc_ref do |query_snp|
|
58
|
+
@listener = Watch::Listener.for_doc_ref self, doc_ref do |query_snp|
|
53
59
|
doc_snp = query_snp.docs.find { |doc| doc.path == @doc_ref.path }
|
54
60
|
|
55
61
|
if doc_snp.nil?
|
@@ -80,8 +86,7 @@ module Google
|
|
80
86
|
# nyc_ref = firestore.doc "cities/NYC"
|
81
87
|
#
|
82
88
|
# listener = nyc_ref.listen do |snapshot|
|
83
|
-
# puts "The population of #{snapshot[:name]} "
|
84
|
-
# puts "is #{snapshot[:population]}."
|
89
|
+
# puts "The population of #{snapshot[:name]} is #{snapshot[:population]}."
|
85
90
|
# end
|
86
91
|
#
|
87
92
|
# # When ready, stop the listen operation and close the stream.
|
@@ -103,8 +108,7 @@ module Google
|
|
103
108
|
# nyc_ref = firestore.doc "cities/NYC"
|
104
109
|
#
|
105
110
|
# listener = nyc_ref.listen do |snapshot|
|
106
|
-
# puts "The population of #{snapshot[:name]} "
|
107
|
-
# puts "is #{snapshot[:population]}."
|
111
|
+
# puts "The population of #{snapshot[:name]} is #{snapshot[:population]}."
|
108
112
|
# end
|
109
113
|
#
|
110
114
|
# # Checks if the listener is stopped.
|
@@ -119,6 +123,81 @@ module Google
|
|
119
123
|
def stopped?
|
120
124
|
@listener.stopped?
|
121
125
|
end
|
126
|
+
|
127
|
+
##
|
128
|
+
# Register to be notified of errors when raised.
|
129
|
+
#
|
130
|
+
# If an unhandled error has occurred the listener will attempt to
|
131
|
+
# recover from the error and resume listening.
|
132
|
+
#
|
133
|
+
# Multiple error handlers can be added.
|
134
|
+
#
|
135
|
+
# @yield [callback] The block to be called when an error is raised.
|
136
|
+
# @yieldparam [Exception] error The error raised.
|
137
|
+
#
|
138
|
+
# @example
|
139
|
+
# require "google/cloud/firestore"
|
140
|
+
#
|
141
|
+
# firestore = Google::Cloud::Firestore.new
|
142
|
+
#
|
143
|
+
# # Get a document reference
|
144
|
+
# nyc_ref = firestore.doc "cities/NYC"
|
145
|
+
#
|
146
|
+
# listener = nyc_ref.listen do |snapshot|
|
147
|
+
# puts "The population of #{snapshot[:name]} is #{snapshot[:population]}."
|
148
|
+
# end
|
149
|
+
#
|
150
|
+
# # Register to be notified when unhandled errors occur.
|
151
|
+
# listener.on_error do |error|
|
152
|
+
# puts error
|
153
|
+
# end
|
154
|
+
#
|
155
|
+
# # When ready, stop the listen operation and close the stream.
|
156
|
+
# listener.stop
|
157
|
+
#
|
158
|
+
def on_error &block
|
159
|
+
raise ArgumentError, "on_error must be called with a block" unless block_given?
|
160
|
+
synchronize { @error_callbacks << block }
|
161
|
+
end
|
162
|
+
|
163
|
+
##
|
164
|
+
# The most recent unhandled error to occur while listening for changes.
|
165
|
+
#
|
166
|
+
# If an unhandled error has occurred the listener will attempt to
|
167
|
+
# recover from the error and resume listening.
|
168
|
+
#
|
169
|
+
# @return [Exception, nil] error The most recent error raised.
|
170
|
+
#
|
171
|
+
# @example
|
172
|
+
# require "google/cloud/firestore"
|
173
|
+
#
|
174
|
+
# firestore = Google::Cloud::Firestore.new
|
175
|
+
#
|
176
|
+
# # Get a document reference
|
177
|
+
# nyc_ref = firestore.doc "cities/NYC"
|
178
|
+
#
|
179
|
+
# listener = nyc_ref.listen do |snapshot|
|
180
|
+
# puts "The population of #{snapshot[:name]} is #{snapshot[:population]}."
|
181
|
+
# end
|
182
|
+
#
|
183
|
+
# # If an error was raised, it can be retrieved here:
|
184
|
+
# listener.last_error #=> nil
|
185
|
+
#
|
186
|
+
# # When ready, stop the listen operation and close the stream.
|
187
|
+
# listener.stop
|
188
|
+
#
|
189
|
+
def last_error
|
190
|
+
synchronize { @last_error }
|
191
|
+
end
|
192
|
+
|
193
|
+
# @private Pass the error to user-provided error callbacks.
|
194
|
+
def error! error
|
195
|
+
error_callbacks = synchronize do
|
196
|
+
@last_error = error
|
197
|
+
@error_callbacks.dup
|
198
|
+
end
|
199
|
+
error_callbacks.each { |error_callback| error_callback.call error }
|
200
|
+
end
|
122
201
|
end
|
123
202
|
end
|
124
203
|
end
|
@@ -168,8 +168,7 @@ module Google
|
|
168
168
|
# nyc_ref = firestore.doc "cities/NYC"
|
169
169
|
#
|
170
170
|
# listener = nyc_ref.listen do |snapshot|
|
171
|
-
# puts "The population of #{snapshot[:name]} "
|
172
|
-
# puts "is #{snapshot[:population]}."
|
171
|
+
# puts "The population of #{snapshot[:name]} is #{snapshot[:population]}."
|
173
172
|
# end
|
174
173
|
#
|
175
174
|
# # When ready, stop the listen operation and close the stream.
|
@@ -53,8 +53,7 @@ module Google
|
|
53
53
|
# nyc_ref = firestore.doc "cities/NYC"
|
54
54
|
#
|
55
55
|
# listener = nyc_ref.listen do |snapshot|
|
56
|
-
# puts "The population of #{snapshot[:name]} "
|
57
|
-
# puts "is #{snapshot[:population]}."
|
56
|
+
# puts "The population of #{snapshot[:name]} is #{snapshot[:population]}."
|
58
57
|
# end
|
59
58
|
#
|
60
59
|
# # When ready, stop the listen operation and close the stream.
|
@@ -62,6 +62,10 @@ module Google
|
|
62
62
|
# @private The parent path for the query.
|
63
63
|
attr_accessor :parent_path
|
64
64
|
|
65
|
+
##
|
66
|
+
# @private The type for limit queries.
|
67
|
+
attr_reader :limit_type
|
68
|
+
|
65
69
|
##
|
66
70
|
# @private The Google::Cloud::Firestore::V1::StructuredQuery object.
|
67
71
|
attr_accessor :query
|
@@ -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
|
356
|
-
#
|
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
|
@@ -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
|
949
|
-
|
950
|
-
|
951
|
-
|
952
|
-
|
953
|
-
|
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(
|