google-cloud-firestore 0.22.0 → 0.23.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.
- checksums.yaml +4 -4
- data/.yardopts +1 -0
- data/README.md +8 -8
- data/lib/google-cloud-firestore.rb +1 -1
- data/lib/google/cloud/firestore.rb +46 -0
- data/lib/google/cloud/firestore/batch.rb +1 -1
- data/lib/google/cloud/firestore/client.rb +18 -13
- data/lib/google/cloud/firestore/convert.rb +78 -35
- data/lib/google/cloud/firestore/credentials.rb +2 -12
- data/lib/google/cloud/firestore/document_change.rb +124 -0
- data/lib/google/cloud/firestore/document_listener.rb +125 -0
- data/lib/google/cloud/firestore/document_reference.rb +35 -0
- data/lib/google/cloud/firestore/document_snapshot.rb +91 -9
- data/lib/google/cloud/firestore/field_path.rb +23 -13
- data/lib/google/cloud/firestore/query.rb +513 -69
- data/lib/google/cloud/firestore/query_listener.rb +118 -0
- data/lib/google/cloud/firestore/query_snapshot.rb +121 -0
- data/lib/google/cloud/firestore/service.rb +8 -0
- data/lib/google/cloud/firestore/transaction.rb +2 -2
- data/lib/google/cloud/firestore/v1beta1.rb +62 -37
- data/lib/google/cloud/firestore/v1beta1/credentials.rb +41 -0
- data/lib/google/cloud/firestore/v1beta1/doc/google/firestore/v1beta1/common.rb +1 -1
- data/lib/google/cloud/firestore/v1beta1/doc/google/firestore/v1beta1/document.rb +5 -4
- data/lib/google/cloud/firestore/v1beta1/doc/google/firestore/v1beta1/firestore.rb +1 -12
- data/lib/google/cloud/firestore/v1beta1/doc/google/firestore/v1beta1/query.rb +4 -1
- data/lib/google/cloud/firestore/v1beta1/doc/google/firestore/v1beta1/write.rb +37 -8
- data/lib/google/cloud/firestore/v1beta1/doc/google/protobuf/any.rb +1 -1
- data/lib/google/cloud/firestore/v1beta1/doc/google/protobuf/empty.rb +28 -0
- data/lib/google/cloud/firestore/v1beta1/doc/google/protobuf/timestamp.rb +1 -1
- data/lib/google/cloud/firestore/v1beta1/doc/google/protobuf/wrappers.rb +1 -1
- data/lib/google/cloud/firestore/v1beta1/doc/google/rpc/status.rb +1 -1
- data/lib/google/cloud/firestore/v1beta1/firestore_client.rb +124 -56
- data/lib/google/cloud/firestore/v1beta1/firestore_client_config.json +2 -2
- data/lib/google/cloud/firestore/version.rb +1 -1
- data/lib/google/cloud/firestore/watch/enumerator_queue.rb +47 -0
- data/lib/google/cloud/firestore/watch/inventory.rb +280 -0
- data/lib/google/cloud/firestore/watch/listener.rb +298 -0
- data/lib/google/cloud/firestore/watch/order.rb +98 -0
- data/lib/google/firestore/v1beta1/firestore_services_pb.rb +2 -4
- data/lib/google/firestore/v1beta1/query_pb.rb +1 -0
- data/lib/google/firestore/v1beta1/write_pb.rb +2 -0
- metadata +40 -3
- data/lib/google/cloud/firestore/v1beta1/doc/overview.rb +0 -53
@@ -0,0 +1,125 @@
|
|
1
|
+
# Copyright 2018 Google LLC
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# https://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
|
16
|
+
require "google/cloud/firestore/watch/listener"
|
17
|
+
|
18
|
+
module Google
|
19
|
+
module Cloud
|
20
|
+
module Firestore
|
21
|
+
##
|
22
|
+
# An ongoing listen operation on a document reference. This is returned by
|
23
|
+
# calling {DocumentReference#listen}.
|
24
|
+
#
|
25
|
+
# @example
|
26
|
+
# require "google/cloud/firestore"
|
27
|
+
#
|
28
|
+
# firestore = Google::Cloud::Firestore.new
|
29
|
+
#
|
30
|
+
# # Get a document reference
|
31
|
+
# nyc_ref = firestore.doc "cities/NYC"
|
32
|
+
#
|
33
|
+
# listener = nyc_ref.listen do |snapshot|
|
34
|
+
# puts "The population of #{snapshot[:name]} "
|
35
|
+
# puts "is #{snapshot[:population]}."
|
36
|
+
# end
|
37
|
+
#
|
38
|
+
# # When ready, stop the listen operation and close the stream.
|
39
|
+
# listener.stop
|
40
|
+
#
|
41
|
+
class DocumentListener
|
42
|
+
##
|
43
|
+
# @private
|
44
|
+
# Creates the watch stream and listener object.
|
45
|
+
def initialize doc_ref, &callback
|
46
|
+
@doc_ref = doc_ref
|
47
|
+
raise ArgumentError if @doc_ref.nil?
|
48
|
+
|
49
|
+
@callback = callback
|
50
|
+
raise ArgumentError if @callback.nil?
|
51
|
+
|
52
|
+
@listener = Watch::Listener.for_doc_ref doc_ref do |query_snp|
|
53
|
+
doc_snp = query_snp.docs.find { |doc| doc.path == @doc_ref.path }
|
54
|
+
|
55
|
+
if doc_snp.nil?
|
56
|
+
doc_snp = DocumentSnapshot.missing \
|
57
|
+
@doc_ref, read_at: query_snp.read_at
|
58
|
+
end
|
59
|
+
|
60
|
+
@callback.call doc_snp
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
##
|
65
|
+
# @private
|
66
|
+
def start
|
67
|
+
@listener.start
|
68
|
+
self
|
69
|
+
end
|
70
|
+
|
71
|
+
##
|
72
|
+
# Stops the client listening for changes.
|
73
|
+
#
|
74
|
+
# @example
|
75
|
+
# require "google/cloud/firestore"
|
76
|
+
#
|
77
|
+
# firestore = Google::Cloud::Firestore.new
|
78
|
+
#
|
79
|
+
# # Get a document reference
|
80
|
+
# nyc_ref = firestore.doc "cities/NYC"
|
81
|
+
#
|
82
|
+
# listener = nyc_ref.listen do |snapshot|
|
83
|
+
# puts "The population of #{snapshot[:name]} "
|
84
|
+
# puts "is #{snapshot[:population]}."
|
85
|
+
# end
|
86
|
+
#
|
87
|
+
# # When ready, stop the listen operation and close the stream.
|
88
|
+
# listener.stop
|
89
|
+
#
|
90
|
+
def stop
|
91
|
+
@listener.stop
|
92
|
+
end
|
93
|
+
|
94
|
+
##
|
95
|
+
# Whether the client has stopped listening for changes.
|
96
|
+
#
|
97
|
+
# @example
|
98
|
+
# require "google/cloud/firestore"
|
99
|
+
#
|
100
|
+
# firestore = Google::Cloud::Firestore.new
|
101
|
+
#
|
102
|
+
# # Get a document reference
|
103
|
+
# nyc_ref = firestore.doc "cities/NYC"
|
104
|
+
#
|
105
|
+
# listener = nyc_ref.listen do |snapshot|
|
106
|
+
# puts "The population of #{snapshot[:name]} "
|
107
|
+
# puts "is #{snapshot[:population]}."
|
108
|
+
# end
|
109
|
+
#
|
110
|
+
# # Checks if the listener is stopped.
|
111
|
+
# listener.stopped? #=> false
|
112
|
+
#
|
113
|
+
# # When ready, stop the listen operation and close the stream.
|
114
|
+
# listener.stop
|
115
|
+
#
|
116
|
+
# # Checks if the listener is stopped.
|
117
|
+
# listener.stopped? #=> true
|
118
|
+
#
|
119
|
+
def stopped?
|
120
|
+
@listener.stopped?
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
@@ -16,6 +16,7 @@
|
|
16
16
|
require "google/cloud/firestore/v1beta1"
|
17
17
|
require "google/cloud/firestore/document_snapshot"
|
18
18
|
require "google/cloud/firestore/collection_reference"
|
19
|
+
require "google/cloud/firestore/document_listener"
|
19
20
|
|
20
21
|
module Google
|
21
22
|
module Cloud
|
@@ -147,6 +148,40 @@ module Google
|
|
147
148
|
client.get_all([self]).first
|
148
149
|
end
|
149
150
|
|
151
|
+
##
|
152
|
+
# Listen to this document reference for changes.
|
153
|
+
#
|
154
|
+
# @yield [callback] The block for accessing the document snapshot.
|
155
|
+
# @yieldparam [DocumentSnapshot] snapshot A document snapshot.
|
156
|
+
#
|
157
|
+
# @return [DocumentListener] The ongoing listen operation on the
|
158
|
+
# document reference.
|
159
|
+
#
|
160
|
+
# @example
|
161
|
+
# require "google/cloud/firestore"
|
162
|
+
#
|
163
|
+
# firestore = Google::Cloud::Firestore.new
|
164
|
+
#
|
165
|
+
# # Get a document reference
|
166
|
+
# nyc_ref = firestore.doc "cities/NYC"
|
167
|
+
#
|
168
|
+
# listener = nyc_ref.listen do |snapshot|
|
169
|
+
# puts "The population of #{snapshot[:name]} "
|
170
|
+
# puts "is #{snapshot[:population]}."
|
171
|
+
# end
|
172
|
+
#
|
173
|
+
# # When ready, stop the listen operation and close the stream.
|
174
|
+
# listener.stop
|
175
|
+
#
|
176
|
+
def listen &callback
|
177
|
+
raise ArgumentError, "callback required" if callback.nil?
|
178
|
+
|
179
|
+
ensure_client!
|
180
|
+
|
181
|
+
DocumentListener.new(self, &callback).start
|
182
|
+
end
|
183
|
+
alias on_snapshot listen
|
184
|
+
|
150
185
|
##
|
151
186
|
# The collection the document reference belongs to.
|
152
187
|
#
|
@@ -17,6 +17,7 @@ require "google/cloud/firestore/v1beta1"
|
|
17
17
|
require "google/cloud/firestore/document_reference"
|
18
18
|
require "google/cloud/firestore/collection_reference"
|
19
19
|
require "google/cloud/firestore/convert"
|
20
|
+
require "google/cloud/firestore/watch/order"
|
20
21
|
|
21
22
|
module Google
|
22
23
|
module Cloud
|
@@ -29,6 +30,9 @@ module Google
|
|
29
30
|
#
|
30
31
|
# The snapshot can reference a non-existing document.
|
31
32
|
#
|
33
|
+
# See {DocumentReference#get}, {DocumentReference#listen},
|
34
|
+
# {Query#get}, {Query#listen}, and {QuerySnapshot#docs}.
|
35
|
+
#
|
32
36
|
# @example
|
33
37
|
# require "google/cloud/firestore"
|
34
38
|
#
|
@@ -40,6 +44,22 @@ module Google
|
|
40
44
|
# # Get the document data
|
41
45
|
# nyc_snap[:population] #=> 1000000
|
42
46
|
#
|
47
|
+
# @example Listen to a document reference for changes:
|
48
|
+
# require "google/cloud/firestore"
|
49
|
+
#
|
50
|
+
# firestore = Google::Cloud::Firestore.new
|
51
|
+
#
|
52
|
+
# # Get a document reference
|
53
|
+
# nyc_ref = firestore.doc "cities/NYC"
|
54
|
+
#
|
55
|
+
# listener = nyc_ref.listen do |snapshot|
|
56
|
+
# puts "The population of #{snapshot[:name]} "
|
57
|
+
# puts "is #{snapshot[:population]}."
|
58
|
+
# end
|
59
|
+
#
|
60
|
+
# # When ready, stop the listen operation and close the stream.
|
61
|
+
# listener.stop
|
62
|
+
#
|
43
63
|
class DocumentSnapshot
|
44
64
|
##
|
45
65
|
# @private The Google::Firestore::V1beta1::Document object.
|
@@ -118,9 +138,11 @@ module Google
|
|
118
138
|
# @!group Data
|
119
139
|
|
120
140
|
##
|
121
|
-
# Retrieves the document data.
|
141
|
+
# Retrieves the document data. When the document exists the data hash is
|
142
|
+
# frozen and will not allow any changes. When the document does not
|
143
|
+
# exist `nil` will be returned.
|
122
144
|
#
|
123
|
-
# @return [Hash] The document data.
|
145
|
+
# @return [Hash, nil] The document data.
|
124
146
|
#
|
125
147
|
# @example
|
126
148
|
# require "google/cloud/firestore"
|
@@ -134,7 +156,7 @@ module Google
|
|
134
156
|
#
|
135
157
|
def data
|
136
158
|
return nil if missing?
|
137
|
-
Convert.fields_to_hash
|
159
|
+
@data ||= Convert.fields_to_hash(grpc.fields, ref.client).freeze
|
138
160
|
end
|
139
161
|
alias fields data
|
140
162
|
|
@@ -192,8 +214,9 @@ module Google
|
|
192
214
|
end
|
193
215
|
|
194
216
|
nodes = field_path.fields.map(&:to_sym)
|
195
|
-
|
217
|
+
return ref if nodes == [:__name__]
|
196
218
|
|
219
|
+
selected_data = data
|
197
220
|
nodes.each do |node|
|
198
221
|
unless selected_data.is_a? Hash
|
199
222
|
err_msg = "#{field_path.formatted_string} is not " \
|
@@ -285,11 +308,46 @@ module Google
|
|
285
308
|
grpc.nil?
|
286
309
|
end
|
287
310
|
|
311
|
+
##
|
312
|
+
# @private
|
313
|
+
def <=> other
|
314
|
+
return nil unless other.is_a? DocumentSnapshot
|
315
|
+
return data <=> other.data if path == other.path
|
316
|
+
path <=> other.path
|
317
|
+
end
|
318
|
+
|
319
|
+
##
|
320
|
+
# @private
|
321
|
+
def eql? other
|
322
|
+
return nil unless other.is_a? DocumentSnapshot
|
323
|
+
return data.eql? other.data if path == other.path
|
324
|
+
path.eql? other.path
|
325
|
+
end
|
326
|
+
|
327
|
+
##
|
328
|
+
# @private
|
329
|
+
def hash
|
330
|
+
@hash ||= [path, data].hash
|
331
|
+
end
|
332
|
+
|
333
|
+
##
|
334
|
+
# @private
|
335
|
+
def query_comparisons_for query_grpc
|
336
|
+
@memoized_comps ||= {}
|
337
|
+
if @memoized_comps.key? query_grpc.hash
|
338
|
+
return @memoized_comps[query_grpc.hash]
|
339
|
+
end
|
340
|
+
|
341
|
+
@memoized_comps[query_grpc.hash] = query_grpc.order_by.map do |order|
|
342
|
+
Watch::Order.field_comparison get(order.field.field_path)
|
343
|
+
end
|
344
|
+
end
|
345
|
+
|
288
346
|
##
|
289
347
|
# @private New DocumentSnapshot from a
|
290
348
|
# Google::Firestore::V1beta1::RunQueryResponse object.
|
291
|
-
def self.from_query_result result,
|
292
|
-
ref = DocumentReference.from_path result.document.name,
|
349
|
+
def self.from_query_result result, client
|
350
|
+
ref = DocumentReference.from_path result.document.name, client
|
293
351
|
read_at = Convert.timestamp_to_time result.read_time
|
294
352
|
|
295
353
|
new.tap do |s|
|
@@ -299,17 +357,30 @@ module Google
|
|
299
357
|
end
|
300
358
|
end
|
301
359
|
|
360
|
+
##
|
361
|
+
# @private New DocumentSnapshot from a
|
362
|
+
# Google::Firestore::V1beta1::DocumentChange object.
|
363
|
+
def self.from_document document, client, read_at: nil
|
364
|
+
ref = DocumentReference.from_path document.name, client
|
365
|
+
|
366
|
+
new.tap do |s|
|
367
|
+
s.grpc = document
|
368
|
+
s.instance_variable_set :@ref, ref
|
369
|
+
s.instance_variable_set :@read_at, read_at
|
370
|
+
end
|
371
|
+
end
|
372
|
+
|
302
373
|
##
|
303
374
|
# @private New DocumentSnapshot from a
|
304
375
|
# Google::Firestore::V1beta1::BatchGetDocumentsResponse object.
|
305
|
-
def self.from_batch_result result,
|
376
|
+
def self.from_batch_result result, client
|
306
377
|
ref = nil
|
307
378
|
grpc = nil
|
308
379
|
if result.result == :found
|
309
380
|
grpc = result.found
|
310
|
-
ref = DocumentReference.from_path grpc.name,
|
381
|
+
ref = DocumentReference.from_path grpc.name, client
|
311
382
|
else
|
312
|
-
ref = DocumentReference.from_path result.missing,
|
383
|
+
ref = DocumentReference.from_path result.missing, client
|
313
384
|
end
|
314
385
|
read_at = Convert.timestamp_to_time result.read_time
|
315
386
|
|
@@ -319,6 +390,17 @@ module Google
|
|
319
390
|
s.instance_variable_set :@read_at, read_at
|
320
391
|
end
|
321
392
|
end
|
393
|
+
|
394
|
+
##
|
395
|
+
# @private New non-existant DocumentSnapshot from a
|
396
|
+
# DocumentReference object.
|
397
|
+
def self.missing doc_ref, read_at: nil
|
398
|
+
new.tap do |s|
|
399
|
+
s.grpc = nil
|
400
|
+
s.instance_variable_set :@ref, doc_ref
|
401
|
+
s.instance_variable_set :@read_at, read_at
|
402
|
+
end
|
403
|
+
end
|
322
404
|
end
|
323
405
|
end
|
324
406
|
end
|
@@ -61,15 +61,15 @@ module Google
|
|
61
61
|
# user_snap.get(nested_field_path) #=> "Pizza"
|
62
62
|
#
|
63
63
|
def initialize *fields
|
64
|
-
@fields = fields.flatten.map(&:to_s)
|
65
|
-
|
66
|
-
|
67
|
-
|
64
|
+
@fields = fields.flatten.map(&:to_s).freeze
|
65
|
+
|
66
|
+
invalid_fields = @fields.detect(&:empty?)
|
67
|
+
raise ArgumentError, "empty paths not allowed" if invalid_fields
|
68
68
|
end
|
69
69
|
|
70
70
|
##
|
71
71
|
# @private The individual fields representing the nested field path for
|
72
|
-
# document data.
|
72
|
+
# document data. The fields are frozen.
|
73
73
|
#
|
74
74
|
# @return [Array<String>] The fields.
|
75
75
|
#
|
@@ -129,9 +129,9 @@ module Google
|
|
129
129
|
# cities_col = firestore.col "cities"
|
130
130
|
#
|
131
131
|
# # Create a query
|
132
|
-
# query = cities_col.
|
132
|
+
# query = cities_col.order(
|
133
133
|
# Google::Cloud::Firestore::FieldPath.document_id
|
134
|
-
# )
|
134
|
+
# ).start_at("NYC")
|
135
135
|
#
|
136
136
|
# query.get do |city|
|
137
137
|
# puts "#{city.document_id} has #{city[:population]} residents."
|
@@ -145,6 +145,8 @@ module Google
|
|
145
145
|
# @private Creates a field path object representing the nested fields
|
146
146
|
# for document data.
|
147
147
|
#
|
148
|
+
# The values are memoized to increase performance.
|
149
|
+
#
|
148
150
|
# @param [String] dotted_string A string representing the path of the
|
149
151
|
# document data. The string can represent as a string of individual
|
150
152
|
# fields joined by ".". Fields containing `~`, `*`, `/`, `[`, `]`, and
|
@@ -166,15 +168,23 @@ module Google
|
|
166
168
|
# field_path.fields #=> ["favorites", "food"]
|
167
169
|
#
|
168
170
|
def self.parse dotted_string
|
169
|
-
|
171
|
+
# Memoize parsed field paths
|
172
|
+
@memoized_field_paths ||= {}
|
173
|
+
if @memoized_field_paths.key? dotted_string
|
174
|
+
return @memoized_field_paths[dotted_string]
|
175
|
+
end
|
176
|
+
|
177
|
+
if dotted_string.is_a? Array
|
178
|
+
return @memoized_field_paths[dotted_string] = new(dotted_string)
|
179
|
+
end
|
170
180
|
|
171
181
|
fields = String(dotted_string).split(".")
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
end
|
182
|
+
|
183
|
+
if fields.grep(INVALID_FIELD_PATH_CHARS).any?
|
184
|
+
raise ArgumentError, "invalid character, use FieldPath instead"
|
176
185
|
end
|
177
|
-
|
186
|
+
|
187
|
+
@memoized_field_paths[dotted_string] = new(fields)
|
178
188
|
end
|
179
189
|
|
180
190
|
##
|
@@ -15,6 +15,7 @@
|
|
15
15
|
|
16
16
|
require "google/cloud/firestore/v1beta1"
|
17
17
|
require "google/cloud/firestore/document_snapshot"
|
18
|
+
require "google/cloud/firestore/query_listener"
|
18
19
|
require "google/cloud/firestore/convert"
|
19
20
|
|
20
21
|
module Google
|
@@ -40,6 +41,22 @@ module Google
|
|
40
41
|
# puts "#{city.document_id} has #{city[:population]} residents."
|
41
42
|
# end
|
42
43
|
#
|
44
|
+
# @example Listen to a query for changes:
|
45
|
+
# require "google/cloud/firestore"
|
46
|
+
#
|
47
|
+
# firestore = Google::Cloud::Firestore.new
|
48
|
+
#
|
49
|
+
# # Create a query
|
50
|
+
# query = firestore.col(:cities).order(:population, :desc)
|
51
|
+
#
|
52
|
+
# listener = query.listen do |snapshot|
|
53
|
+
# puts "The query snapshot has #{snapshot.docs.count} documents "
|
54
|
+
# puts "and has #{snapshot.changes.count} changes."
|
55
|
+
# end
|
56
|
+
#
|
57
|
+
# # When ready, stop the listen operation and close the stream.
|
58
|
+
# listener.stop
|
59
|
+
#
|
43
60
|
class Query
|
44
61
|
##
|
45
62
|
# @private The parent path for the query.
|
@@ -88,13 +105,15 @@ module Google
|
|
88
105
|
new_query = @query.dup
|
89
106
|
new_query ||= StructuredQuery.new
|
90
107
|
|
108
|
+
fields = Array(fields).flatten.compact
|
109
|
+
fields = [FieldPath.document_id] if fields.empty?
|
91
110
|
field_refs = fields.flatten.compact.map do |field|
|
92
111
|
field = FieldPath.parse field unless field.is_a? FieldPath
|
93
112
|
StructuredQuery::FieldReference.new \
|
94
113
|
field_path: field.formatted_string
|
95
114
|
end
|
96
115
|
|
97
|
-
new_query.select
|
116
|
+
new_query.select = StructuredQuery::Projection.new
|
98
117
|
field_refs.each do |field_ref|
|
99
118
|
new_query.select.fields << field_ref
|
100
119
|
end
|
@@ -130,7 +149,7 @@ module Google
|
|
130
149
|
new_query ||= StructuredQuery.new
|
131
150
|
|
132
151
|
if new_query.from.empty?
|
133
|
-
raise "missing collection_id to specify descendants
|
152
|
+
raise "missing collection_id to specify descendants"
|
134
153
|
end
|
135
154
|
|
136
155
|
new_query.from.last.all_descendants = true
|
@@ -166,7 +185,7 @@ module Google
|
|
166
185
|
new_query ||= StructuredQuery.new
|
167
186
|
|
168
187
|
if new_query.from.empty?
|
169
|
-
raise "missing collection_id to specify descendants
|
188
|
+
raise "missing collection_id to specify descendants"
|
170
189
|
end
|
171
190
|
|
172
191
|
new_query.from.last.all_descendants = false
|
@@ -213,14 +232,18 @@ module Google
|
|
213
232
|
# end
|
214
233
|
#
|
215
234
|
def where field, operator, value
|
235
|
+
if query_has_cursors?
|
236
|
+
raise "cannot call where after calling " \
|
237
|
+
"start_at, start_after, end_before, or end_at"
|
238
|
+
end
|
239
|
+
|
216
240
|
new_query = @query.dup
|
217
241
|
new_query ||= StructuredQuery.new
|
218
242
|
|
219
243
|
field = FieldPath.parse field unless field.is_a? FieldPath
|
220
244
|
|
221
|
-
|
222
|
-
new_query
|
223
|
-
filter(field.formatted_string, operator, value)
|
245
|
+
new_filter = filter field.formatted_string, operator, value
|
246
|
+
add_filters_to_query new_query, new_filter
|
224
247
|
|
225
248
|
Query.start new_query, parent_path, client
|
226
249
|
end
|
@@ -274,6 +297,11 @@ module Google
|
|
274
297
|
# end
|
275
298
|
#
|
276
299
|
def order field, direction = :asc
|
300
|
+
if query_has_cursors?
|
301
|
+
raise "cannot call order after calling " \
|
302
|
+
"start_at, start_after, end_before, or end_at"
|
303
|
+
end
|
304
|
+
|
277
305
|
new_query = @query.dup
|
278
306
|
new_query ||= StructuredQuery.new
|
279
307
|
|
@@ -355,108 +383,285 @@ module Google
|
|
355
383
|
end
|
356
384
|
|
357
385
|
##
|
358
|
-
# Starts query results at a set of field values. The
|
386
|
+
# Starts query results at a set of field values. The field values can be
|
387
|
+
# specified explicitly as arguments, or can be specified implicitly by
|
388
|
+
# providing a {DocumentSnapshot} object instead. The result set will
|
359
389
|
# include the document specified by `values`.
|
360
390
|
#
|
361
391
|
# If the current query already has specified `start_at` or
|
362
392
|
# `start_after`, this will overwrite it.
|
363
393
|
#
|
364
|
-
# The values
|
365
|
-
#
|
366
|
-
#
|
394
|
+
# The values are associated with the field paths that have been provided
|
395
|
+
# to `order`, and must match the same sort order. An ArgumentError will
|
396
|
+
# be raised if more explicit values are given than are present in
|
397
|
+
# `order`.
|
367
398
|
#
|
368
|
-
# @param [Object, Array<Object>] values The field
|
369
|
-
# query at.
|
399
|
+
# @param [DocumentSnapshot, Object, Array<Object>] values The field
|
400
|
+
# values to start the query at.
|
370
401
|
#
|
371
402
|
# @return [Query] New query with `start_at` called on it.
|
372
403
|
#
|
373
|
-
# @example
|
404
|
+
# @example Starting a query at a document reference id
|
374
405
|
# require "google/cloud/firestore"
|
375
406
|
#
|
376
407
|
# firestore = Google::Cloud::Firestore.new
|
377
408
|
#
|
378
409
|
# # Get a collection reference
|
379
410
|
# cities_col = firestore.col "cities"
|
411
|
+
# nyc_doc_id = "NYC"
|
380
412
|
#
|
381
413
|
# # Create a query
|
382
|
-
# query = cities_col.
|
414
|
+
# query = cities_col.order(firestore.document_id)
|
415
|
+
# .start_at(nyc_doc_id)
|
416
|
+
#
|
417
|
+
# query.get do |city|
|
418
|
+
# puts "#{city.document_id} has #{city[:population]} residents."
|
419
|
+
# end
|
420
|
+
#
|
421
|
+
# @example Starting a query at a document reference object
|
422
|
+
# require "google/cloud/firestore"
|
423
|
+
#
|
424
|
+
# firestore = Google::Cloud::Firestore.new
|
425
|
+
#
|
426
|
+
# # Get a collection reference
|
427
|
+
# cities_col = firestore.col "cities"
|
428
|
+
# nyc_doc_id = "NYC"
|
429
|
+
# nyc_ref = cities_col.doc nyc_doc_id
|
430
|
+
#
|
431
|
+
# # Create a query
|
432
|
+
# query = cities_col.order(firestore.document_id)
|
433
|
+
# .start_at(nyc_ref)
|
434
|
+
#
|
435
|
+
# query.get do |city|
|
436
|
+
# puts "#{city.document_id} has #{city[:population]} residents."
|
437
|
+
# end
|
438
|
+
#
|
439
|
+
# @example Starting a query at multiple explicit values
|
440
|
+
# require "google/cloud/firestore"
|
441
|
+
#
|
442
|
+
# firestore = Google::Cloud::Firestore.new
|
443
|
+
#
|
444
|
+
# # Get a collection reference
|
445
|
+
# cities_col = firestore.col "cities"
|
446
|
+
#
|
447
|
+
# # Create a query
|
448
|
+
# query = cities_col.order(:population, :desc)
|
449
|
+
# .order(:name)
|
450
|
+
# .start_at(1000000, "New York City")
|
451
|
+
#
|
452
|
+
# query.get do |city|
|
453
|
+
# puts "#{city.document_id} has #{city[:population]} residents."
|
454
|
+
# end
|
455
|
+
#
|
456
|
+
# @example Starting a query at a DocumentSnapshot
|
457
|
+
# require "google/cloud/firestore"
|
458
|
+
#
|
459
|
+
# firestore = Google::Cloud::Firestore.new
|
460
|
+
#
|
461
|
+
# # Get a collection reference
|
462
|
+
# cities_col = firestore.col "cities"
|
463
|
+
#
|
464
|
+
# # Get a document snapshot
|
465
|
+
# nyc_snap = firestore.doc("cities/NYC").get
|
466
|
+
#
|
467
|
+
# # Create a query
|
468
|
+
# query = cities_col.order(:population, :desc)
|
469
|
+
# .order(:name)
|
470
|
+
# .start_at(nyc_snap)
|
383
471
|
#
|
384
472
|
# query.get do |city|
|
385
473
|
# puts "#{city.document_id} has #{city[:population]} residents."
|
386
474
|
# end
|
387
475
|
#
|
388
476
|
def start_at *values
|
477
|
+
raise ArgumentError, "must provide values" if values.empty?
|
478
|
+
|
389
479
|
new_query = @query.dup
|
390
480
|
new_query ||= StructuredQuery.new
|
391
481
|
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
)
|
482
|
+
cursor = values_to_cursor values, new_query
|
483
|
+
cursor.before = true
|
484
|
+
new_query.start_at = cursor
|
396
485
|
|
397
486
|
Query.start new_query, parent_path, client
|
398
487
|
end
|
399
488
|
|
400
489
|
##
|
401
|
-
# Starts query results after a set of field values. The
|
490
|
+
# Starts query results after a set of field values. The field values can
|
491
|
+
# be specified explicitly as arguments, or can be specified implicitly
|
492
|
+
# by providing a {DocumentSnapshot} object instead. The result set will
|
402
493
|
# not include the document specified by `values`.
|
403
494
|
#
|
404
495
|
# If the current query already has specified `start_at` or
|
405
496
|
# `start_after`, this will overwrite it.
|
406
497
|
#
|
407
|
-
# The values
|
408
|
-
#
|
409
|
-
#
|
498
|
+
# The values are associated with the field paths that have been provided
|
499
|
+
# to `order`, and must match the same sort order. An ArgumentError will
|
500
|
+
# be raised if more explicit values are given than are present in
|
501
|
+
# `order`.
|
410
502
|
#
|
411
|
-
# @param [Object, Array<Object>] values The field
|
412
|
-
# query after.
|
503
|
+
# @param [DocumentSnapshot, Object, Array<Object>] values The field
|
504
|
+
# values to start the query after.
|
413
505
|
#
|
414
506
|
# @return [Query] New query with `start_after` called on it.
|
415
507
|
#
|
416
|
-
# @example
|
508
|
+
# @example Starting a query after a document reference id
|
509
|
+
# require "google/cloud/firestore"
|
510
|
+
#
|
511
|
+
# firestore = Google::Cloud::Firestore.new
|
512
|
+
#
|
513
|
+
# # Get a collection reference
|
514
|
+
# cities_col = firestore.col "cities"
|
515
|
+
# nyc_doc_id = "NYC"
|
516
|
+
#
|
517
|
+
# # Create a query
|
518
|
+
# query = cities_col.order(firestore.document_id)
|
519
|
+
# .start_after(nyc_doc_id)
|
520
|
+
#
|
521
|
+
# query.get do |city|
|
522
|
+
# puts "#{city.document_id} has #{city[:population]} residents."
|
523
|
+
# end
|
524
|
+
#
|
525
|
+
# @example Starting a query after a document reference object
|
417
526
|
# require "google/cloud/firestore"
|
418
527
|
#
|
419
528
|
# firestore = Google::Cloud::Firestore.new
|
420
529
|
#
|
421
530
|
# # Get a collection reference
|
422
531
|
# cities_col = firestore.col "cities"
|
532
|
+
# nyc_doc_id = "NYC"
|
533
|
+
# nyc_ref = cities_col.doc nyc_doc_id
|
423
534
|
#
|
424
535
|
# # Create a query
|
425
|
-
# query = cities_col.
|
536
|
+
# query = cities_col.order(firestore.document_id)
|
537
|
+
# .start_after(nyc_ref)
|
538
|
+
#
|
539
|
+
# query.get do |city|
|
540
|
+
# puts "#{city.document_id} has #{city[:population]} residents."
|
541
|
+
# end
|
542
|
+
#
|
543
|
+
# @example Starting a query after multiple explicit values
|
544
|
+
# require "google/cloud/firestore"
|
545
|
+
#
|
546
|
+
# firestore = Google::Cloud::Firestore.new
|
547
|
+
#
|
548
|
+
# # Get a collection reference
|
549
|
+
# cities_col = firestore.col "cities"
|
550
|
+
#
|
551
|
+
# # Create a query
|
552
|
+
# query = cities_col.order(:population, :desc)
|
553
|
+
# .order(:name)
|
554
|
+
# .start_after(1000000, "New York City")
|
555
|
+
#
|
556
|
+
# query.get do |city|
|
557
|
+
# puts "#{city.document_id} has #{city[:population]} residents."
|
558
|
+
# end
|
559
|
+
#
|
560
|
+
# @example Starting a query after a DocumentSnapshot
|
561
|
+
# require "google/cloud/firestore"
|
562
|
+
#
|
563
|
+
# firestore = Google::Cloud::Firestore.new
|
564
|
+
#
|
565
|
+
# # Get a collection reference
|
566
|
+
# cities_col = firestore.col "cities"
|
567
|
+
#
|
568
|
+
# # Get a document snapshot
|
569
|
+
# nyc_snap = firestore.doc("cities/NYC").get
|
570
|
+
#
|
571
|
+
# # Create a query
|
572
|
+
# query = cities_col.order(:population, :desc)
|
573
|
+
# .order(:name)
|
574
|
+
# .start_after(nyc_snap)
|
426
575
|
#
|
427
576
|
# query.get do |city|
|
428
577
|
# puts "#{city.document_id} has #{city[:population]} residents."
|
429
578
|
# end
|
430
579
|
#
|
431
580
|
def start_after *values
|
581
|
+
raise ArgumentError, "must provide values" if values.empty?
|
582
|
+
|
432
583
|
new_query = @query.dup
|
433
584
|
new_query ||= StructuredQuery.new
|
434
585
|
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
)
|
586
|
+
cursor = values_to_cursor values, new_query
|
587
|
+
cursor.before = false
|
588
|
+
new_query.start_at = cursor
|
439
589
|
|
440
590
|
Query.start new_query, parent_path, client
|
441
591
|
end
|
442
592
|
|
443
593
|
##
|
444
|
-
# Ends query results before a set of field values. The
|
594
|
+
# Ends query results before a set of field values. The field values can
|
595
|
+
# be specified explicitly as arguments, or can be specified implicitly
|
596
|
+
# by providing a {DocumentSnapshot} object instead. The result set will
|
445
597
|
# not include the document specified by `values`.
|
446
598
|
#
|
447
599
|
# If the current query already has specified `end_before` or
|
448
600
|
# `end_at`, this will overwrite it.
|
449
601
|
#
|
450
|
-
# The values
|
451
|
-
#
|
452
|
-
#
|
602
|
+
# The values are associated with the field paths that have been provided
|
603
|
+
# to `order`, and must match the same sort order. An ArgumentError will
|
604
|
+
# be raised if more explicit values are given than are present in
|
605
|
+
# `order`.
|
453
606
|
#
|
454
|
-
# @param [Object, Array<Object>] values The field
|
455
|
-
# before.
|
607
|
+
# @param [DocumentSnapshot, Object, Array<Object>] values The field
|
608
|
+
# values to end the query before.
|
456
609
|
#
|
457
610
|
# @return [Query] New query with `end_before` called on it.
|
458
611
|
#
|
459
|
-
# @example
|
612
|
+
# @example Ending a query before a document reference id
|
613
|
+
# require "google/cloud/firestore"
|
614
|
+
#
|
615
|
+
# firestore = Google::Cloud::Firestore.new
|
616
|
+
#
|
617
|
+
# # Get a collection reference
|
618
|
+
# cities_col = firestore.col "cities"
|
619
|
+
# nyc_doc_id = "NYC"
|
620
|
+
#
|
621
|
+
# # Create a query
|
622
|
+
# query = cities_col.order(firestore.document_id)
|
623
|
+
# .end_before(nyc_doc_id)
|
624
|
+
#
|
625
|
+
# query.get do |city|
|
626
|
+
# puts "#{city.document_id} has #{city[:population]} residents."
|
627
|
+
# end
|
628
|
+
#
|
629
|
+
# @example Ending a query before a document reference object
|
630
|
+
# require "google/cloud/firestore"
|
631
|
+
#
|
632
|
+
# firestore = Google::Cloud::Firestore.new
|
633
|
+
#
|
634
|
+
# # Get a collection reference
|
635
|
+
# cities_col = firestore.col "cities"
|
636
|
+
# nyc_doc_id = "NYC"
|
637
|
+
# nyc_ref = cities_col.doc nyc_doc_id
|
638
|
+
#
|
639
|
+
# # Create a query
|
640
|
+
# query = cities_col.order(firestore.document_id)
|
641
|
+
# .end_before(nyc_ref)
|
642
|
+
#
|
643
|
+
# query.get do |city|
|
644
|
+
# puts "#{city.document_id} has #{city[:population]} residents."
|
645
|
+
# end
|
646
|
+
#
|
647
|
+
# @example Ending a query before multiple explicit values
|
648
|
+
# require "google/cloud/firestore"
|
649
|
+
#
|
650
|
+
# firestore = Google::Cloud::Firestore.new
|
651
|
+
#
|
652
|
+
# # Get a collection reference
|
653
|
+
# cities_col = firestore.col "cities"
|
654
|
+
#
|
655
|
+
# # Create a query
|
656
|
+
# query = cities_col.order(:population, :desc)
|
657
|
+
# .order(:name)
|
658
|
+
# .end_before(1000000, "New York City")
|
659
|
+
#
|
660
|
+
# query.get do |city|
|
661
|
+
# puts "#{city.document_id} has #{city[:population]} residents."
|
662
|
+
# end
|
663
|
+
#
|
664
|
+
# @example Ending a query before a DocumentSnapshot
|
460
665
|
# require "google/cloud/firestore"
|
461
666
|
#
|
462
667
|
# firestore = Google::Cloud::Firestore.new
|
@@ -464,64 +669,131 @@ module Google
|
|
464
669
|
# # Get a collection reference
|
465
670
|
# cities_col = firestore.col "cities"
|
466
671
|
#
|
672
|
+
# # Get a document snapshot
|
673
|
+
# nyc_snap = firestore.doc("cities/NYC").get
|
674
|
+
#
|
467
675
|
# # Create a query
|
468
|
-
# query = cities_col.
|
676
|
+
# query = cities_col.order(:population, :desc)
|
677
|
+
# .order(:name)
|
678
|
+
# .end_before(nyc_snap)
|
469
679
|
#
|
470
680
|
# query.get do |city|
|
471
681
|
# puts "#{city.document_id} has #{city[:population]} residents."
|
472
682
|
# end
|
473
683
|
#
|
474
684
|
def end_before *values
|
685
|
+
raise ArgumentError, "must provide values" if values.empty?
|
686
|
+
|
475
687
|
new_query = @query.dup
|
476
688
|
new_query ||= StructuredQuery.new
|
477
689
|
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
)
|
690
|
+
cursor = values_to_cursor values, new_query
|
691
|
+
cursor.before = true
|
692
|
+
new_query.end_at = cursor
|
482
693
|
|
483
694
|
Query.start new_query, parent_path, client
|
484
695
|
end
|
485
696
|
|
486
697
|
##
|
487
|
-
# Ends query results at a set of field values. The
|
698
|
+
# Ends query results at a set of field values. The field values can
|
699
|
+
# be specified explicitly as arguments, or can be specified implicitly
|
700
|
+
# by providing a {DocumentSnapshot} object instead. The result set will
|
488
701
|
# include the document specified by `values`.
|
489
702
|
#
|
490
703
|
# If the current query already has specified `end_before` or
|
491
704
|
# `end_at`, this will overwrite it.
|
492
705
|
#
|
493
|
-
# The values
|
494
|
-
#
|
495
|
-
#
|
706
|
+
# The values are associated with the field paths that have been provided
|
707
|
+
# to `order`, and must match the same sort order. An ArgumentError will
|
708
|
+
# be raised if more explicit values are given than are present in
|
709
|
+
# `order`.
|
496
710
|
#
|
497
|
-
# @param [Object, Array<Object>] values The field
|
498
|
-
# at.
|
711
|
+
# @param [DocumentSnapshot, Object, Array<Object>] values The field
|
712
|
+
# values to end the query at.
|
499
713
|
#
|
500
714
|
# @return [Query] New query with `end_at` called on it.
|
501
715
|
#
|
502
|
-
# @example
|
716
|
+
# @example Ending a query at a document reference id
|
503
717
|
# require "google/cloud/firestore"
|
504
718
|
#
|
505
719
|
# firestore = Google::Cloud::Firestore.new
|
506
720
|
#
|
507
721
|
# # Get a collection reference
|
508
722
|
# cities_col = firestore.col "cities"
|
723
|
+
# nyc_doc_id = "NYC"
|
509
724
|
#
|
510
725
|
# # Create a query
|
511
|
-
# query = cities_col.
|
726
|
+
# query = cities_col.order(firestore.document_id)
|
727
|
+
# .end_at(nyc_doc_id)
|
728
|
+
#
|
729
|
+
# query.get do |city|
|
730
|
+
# puts "#{city.document_id} has #{city[:population]} residents."
|
731
|
+
# end
|
732
|
+
#
|
733
|
+
# @example Ending a query at a document reference object
|
734
|
+
# require "google/cloud/firestore"
|
735
|
+
#
|
736
|
+
# firestore = Google::Cloud::Firestore.new
|
737
|
+
#
|
738
|
+
# # Get a collection reference
|
739
|
+
# cities_col = firestore.col "cities"
|
740
|
+
# nyc_doc_id = "NYC"
|
741
|
+
# nyc_ref = cities_col.doc nyc_doc_id
|
742
|
+
#
|
743
|
+
# # Create a query
|
744
|
+
# query = cities_col.order(firestore.document_id)
|
745
|
+
# .end_at(nyc_ref)
|
746
|
+
#
|
747
|
+
# query.get do |city|
|
748
|
+
# puts "#{city.document_id} has #{city[:population]} residents."
|
749
|
+
# end
|
750
|
+
#
|
751
|
+
# @example Ending a query at multiple explicit values
|
752
|
+
# require "google/cloud/firestore"
|
753
|
+
#
|
754
|
+
# firestore = Google::Cloud::Firestore.new
|
755
|
+
#
|
756
|
+
# # Get a collection reference
|
757
|
+
# cities_col = firestore.col "cities"
|
758
|
+
#
|
759
|
+
# # Create a query
|
760
|
+
# query = cities_col.order(:population, :desc)
|
761
|
+
# .order(:name)
|
762
|
+
# .end_at(1000000, "New York City")
|
763
|
+
#
|
764
|
+
# query.get do |city|
|
765
|
+
# puts "#{city.document_id} has #{city[:population]} residents."
|
766
|
+
# end
|
767
|
+
#
|
768
|
+
# @example Ending a query at a DocumentSnapshot
|
769
|
+
# require "google/cloud/firestore"
|
770
|
+
#
|
771
|
+
# firestore = Google::Cloud::Firestore.new
|
772
|
+
#
|
773
|
+
# # Get a collection reference
|
774
|
+
# cities_col = firestore.col "cities"
|
775
|
+
#
|
776
|
+
# # Get a document snapshot
|
777
|
+
# nyc_snap = firestore.doc("cities/NYC").get
|
778
|
+
#
|
779
|
+
# # Create a query
|
780
|
+
# query = cities_col.order(:population, :desc)
|
781
|
+
# .order(:name)
|
782
|
+
# .end_at(nyc_snap)
|
512
783
|
#
|
513
784
|
# query.get do |city|
|
514
785
|
# puts "#{city.document_id} has #{city[:population]} residents."
|
515
786
|
# end
|
516
787
|
#
|
517
788
|
def end_at *values
|
789
|
+
raise ArgumentError, "must provide values" if values.empty?
|
790
|
+
|
518
791
|
new_query = @query.dup
|
519
792
|
new_query ||= StructuredQuery.new
|
520
793
|
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
)
|
794
|
+
cursor = values_to_cursor values, new_query
|
795
|
+
cursor.before = false
|
796
|
+
new_query.end_at = cursor
|
525
797
|
|
526
798
|
Query.start new_query, parent_path, client
|
527
799
|
end
|
@@ -552,7 +824,7 @@ module Google
|
|
552
824
|
def get
|
553
825
|
ensure_service!
|
554
826
|
|
555
|
-
return enum_for(:
|
827
|
+
return enum_for(:get) unless block_given?
|
556
828
|
|
557
829
|
results = service.run_query parent_path, @query
|
558
830
|
results.each do |result|
|
@@ -562,6 +834,39 @@ module Google
|
|
562
834
|
end
|
563
835
|
alias run get
|
564
836
|
|
837
|
+
##
|
838
|
+
# Listen to this query for changes.
|
839
|
+
#
|
840
|
+
# @yield [callback] The block for accessing the query snapshot.
|
841
|
+
# @yieldparam [QuerySnapshot] snapshot A query snapshot.
|
842
|
+
#
|
843
|
+
# @return [QueryListener] The ongoing listen operation on the query.
|
844
|
+
#
|
845
|
+
# @example
|
846
|
+
# require "google/cloud/firestore"
|
847
|
+
#
|
848
|
+
# firestore = Google::Cloud::Firestore.new
|
849
|
+
#
|
850
|
+
# # Create a query
|
851
|
+
# query = firestore.col(:cities).order(:population, :desc)
|
852
|
+
#
|
853
|
+
# listener = query.listen do |snapshot|
|
854
|
+
# puts "The query snapshot has #{snapshot.docs.count} documents "
|
855
|
+
# puts "and has #{snapshot.changes.count} changes."
|
856
|
+
# end
|
857
|
+
#
|
858
|
+
# # When ready, stop the listen operation and close the stream.
|
859
|
+
# listener.stop
|
860
|
+
#
|
861
|
+
def listen &callback
|
862
|
+
raise ArgumentError, "callback required" if callback.nil?
|
863
|
+
|
864
|
+
ensure_service!
|
865
|
+
|
866
|
+
QueryListener.new(self, &callback).start
|
867
|
+
end
|
868
|
+
alias on_snapshot listen
|
869
|
+
|
565
870
|
##
|
566
871
|
# @private Start a new Query.
|
567
872
|
def self.start query, parent_path, client
|
@@ -608,41 +913,180 @@ module Google
|
|
608
913
|
|
609
914
|
def filter name, op, value
|
610
915
|
field = StructuredQuery::FieldReference.new field_path: name.to_s
|
611
|
-
|
916
|
+
operator = FILTER_OPS[op.to_s.downcase]
|
917
|
+
raise ArgumentError, "unknown operator #{op}" if operator.nil?
|
612
918
|
|
613
919
|
is_value_nan = value.respond_to?(:nan?) && value.nan?
|
614
920
|
if UNARY_VALUES.include?(value) || is_value_nan
|
615
|
-
if
|
921
|
+
if operator != :EQUAL
|
616
922
|
raise ArgumentError,
|
617
|
-
"can only check equality for #{value} values
|
923
|
+
"can only check equality for #{value} values"
|
618
924
|
end
|
619
925
|
|
620
|
-
|
621
|
-
|
926
|
+
operator = :IS_NULL
|
927
|
+
if UNARY_NAN_VALUES.include?(value) || is_value_nan
|
928
|
+
operator = :IS_NAN
|
929
|
+
end
|
622
930
|
|
623
931
|
return StructuredQuery::Filter.new(unary_filter:
|
624
|
-
StructuredQuery::UnaryFilter.new(field: field, op:
|
932
|
+
StructuredQuery::UnaryFilter.new(field: field, op: operator))
|
625
933
|
end
|
626
934
|
|
627
935
|
value = Convert.raw_to_value value
|
628
936
|
StructuredQuery::Filter.new(field_filter:
|
629
|
-
StructuredQuery::FieldFilter.new(field: field, op:
|
937
|
+
StructuredQuery::FieldFilter.new(field: field, op: operator,
|
630
938
|
value: value))
|
631
939
|
end
|
632
940
|
|
633
|
-
def
|
941
|
+
def composite_filter
|
634
942
|
StructuredQuery::Filter.new(composite_filter:
|
635
943
|
StructuredQuery::CompositeFilter.new(op: :AND))
|
636
944
|
end
|
637
945
|
|
638
|
-
def
|
639
|
-
if
|
640
|
-
|
641
|
-
elsif
|
642
|
-
|
946
|
+
def add_filters_to_query query, filter
|
947
|
+
if query.where.nil?
|
948
|
+
query.where = filter
|
949
|
+
elsif query.where.filter_type == :composite_filter
|
950
|
+
query.where.composite_filter.filters << filter
|
643
951
|
else
|
644
|
-
|
952
|
+
old_filter = query.where
|
953
|
+
query.where = composite_filter
|
954
|
+
query.where.composite_filter.filters << old_filter
|
955
|
+
query.where.composite_filter.filters << filter
|
956
|
+
end
|
957
|
+
end
|
958
|
+
|
959
|
+
def order_direction direction
|
960
|
+
return :DESCENDING if direction.to_s.downcase.start_with? "d".freeze
|
961
|
+
:ASCENDING
|
962
|
+
end
|
963
|
+
|
964
|
+
def query_has_cursors?
|
965
|
+
query.start_at || query.end_at
|
966
|
+
end
|
967
|
+
|
968
|
+
def values_to_cursor values, query
|
969
|
+
if values.count == 1 && values.first.is_a?(DocumentSnapshot)
|
970
|
+
return snapshot_to_cursor(values.first, query)
|
971
|
+
end
|
972
|
+
|
973
|
+
# pair values with their field_paths to ensure correct formatting
|
974
|
+
order_field_paths = order_by_field_paths query
|
975
|
+
if values.count > order_field_paths.count
|
976
|
+
# raise if too many values provided for the cursor
|
977
|
+
raise ArgumentError, "too many values"
|
978
|
+
end
|
979
|
+
|
980
|
+
values = values.zip(order_field_paths).map do |value, field_path|
|
981
|
+
if field_path == doc_id_path && !value.is_a?(DocumentReference)
|
982
|
+
value = document_reference value
|
983
|
+
end
|
984
|
+
Convert.raw_to_value value
|
985
|
+
end
|
986
|
+
|
987
|
+
Google::Firestore::V1beta1::Cursor.new values: values
|
988
|
+
end
|
989
|
+
|
990
|
+
def snapshot_to_cursor snapshot, query
|
991
|
+
if snapshot.parent.path != query_collection_path
|
992
|
+
raise ArgumentError, "cursor snapshot must belong to collection"
|
993
|
+
end
|
994
|
+
|
995
|
+
# first, add any inequality filters missing from existing order_by
|
996
|
+
ensure_inequality_field_paths_in_order_by! query
|
997
|
+
|
998
|
+
# second, make sure __name__ is present in order_by
|
999
|
+
ensure_document_id_in_order_by! query
|
1000
|
+
|
1001
|
+
# lastly, create cursor for all field_paths in order_by
|
1002
|
+
values = order_by_field_paths(query).map do |field_path|
|
1003
|
+
if field_path == doc_id_path
|
1004
|
+
snapshot.ref
|
1005
|
+
else
|
1006
|
+
snapshot[field_path]
|
1007
|
+
end
|
645
1008
|
end
|
1009
|
+
|
1010
|
+
values_to_cursor values, query
|
1011
|
+
end
|
1012
|
+
|
1013
|
+
def ensure_inequality_field_paths_in_order_by! query
|
1014
|
+
inequality_paths = inequality_filter_field_paths query
|
1015
|
+
orig_order = order_by_field_paths query
|
1016
|
+
|
1017
|
+
inequality_paths.reverse.each do |field_path|
|
1018
|
+
next if orig_order.include? field_path
|
1019
|
+
|
1020
|
+
query.order_by.unshift StructuredQuery::Order.new(
|
1021
|
+
field: StructuredQuery::FieldReference.new(
|
1022
|
+
field_path: field_path
|
1023
|
+
),
|
1024
|
+
direction: :ASCENDING
|
1025
|
+
)
|
1026
|
+
end
|
1027
|
+
end
|
1028
|
+
|
1029
|
+
def ensure_document_id_in_order_by! query
|
1030
|
+
return if order_by_field_paths(query).include? doc_id_path
|
1031
|
+
|
1032
|
+
query.order_by.push StructuredQuery::Order.new(
|
1033
|
+
field: StructuredQuery::FieldReference.new(
|
1034
|
+
field_path: doc_id_path
|
1035
|
+
),
|
1036
|
+
direction: last_order_direction(query)
|
1037
|
+
)
|
1038
|
+
end
|
1039
|
+
|
1040
|
+
def inequality_filter_field_paths query
|
1041
|
+
return [] if query.where.nil?
|
1042
|
+
|
1043
|
+
# The way we construct a query, where is always a CompositeFilter
|
1044
|
+
filters = if query.where.filter_type == :composite_filter
|
1045
|
+
query.where.composite_filter.filters
|
1046
|
+
else
|
1047
|
+
[query.where]
|
1048
|
+
end
|
1049
|
+
ineq_filters = filters.select do |filter|
|
1050
|
+
if filter.filter_type == :field_filter
|
1051
|
+
filter.field_filter.op != :EQUAL
|
1052
|
+
end
|
1053
|
+
end
|
1054
|
+
ineq_filters.map { |filter| filter.field_filter.field.field_path }
|
1055
|
+
end
|
1056
|
+
|
1057
|
+
def order_by_field_paths query
|
1058
|
+
query.order_by.map { |order_by| order_by.field.field_path }
|
1059
|
+
end
|
1060
|
+
|
1061
|
+
def last_order_direction query
|
1062
|
+
last_order_by = query.order_by.last
|
1063
|
+
return :ASCENDING if last_order_by.nil?
|
1064
|
+
last_order_by.direction
|
1065
|
+
end
|
1066
|
+
|
1067
|
+
def document_reference document_path
|
1068
|
+
if document_path.to_s.split("/").count.even?
|
1069
|
+
raise ArgumentError, "document_path must refer to a document"
|
1070
|
+
end
|
1071
|
+
|
1072
|
+
DocumentReference.from_path(
|
1073
|
+
"#{query_collection_path}/#{document_path}", client
|
1074
|
+
)
|
1075
|
+
end
|
1076
|
+
|
1077
|
+
def query_collection_path
|
1078
|
+
"#{parent_path}/#{query_collection_id}"
|
1079
|
+
end
|
1080
|
+
|
1081
|
+
def query_collection_id
|
1082
|
+
# We trust that query.from is always set, since Query cannot be
|
1083
|
+
# created without it.
|
1084
|
+
return nil if query.from.empty?
|
1085
|
+
query.from.first.collection_id
|
1086
|
+
end
|
1087
|
+
|
1088
|
+
def doc_id_path
|
1089
|
+
"__name__".freeze
|
646
1090
|
end
|
647
1091
|
|
648
1092
|
##
|