google-cloud-firestore 0.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. checksums.yaml +7 -0
  2. data/.yardopts +8 -0
  3. data/LICENSE +201 -0
  4. data/README.md +30 -0
  5. data/lib/google-cloud-firestore.rb +106 -0
  6. data/lib/google/cloud/firestore.rb +514 -0
  7. data/lib/google/cloud/firestore/batch.rb +462 -0
  8. data/lib/google/cloud/firestore/client.rb +449 -0
  9. data/lib/google/cloud/firestore/collection_reference.rb +249 -0
  10. data/lib/google/cloud/firestore/commit_response.rb +145 -0
  11. data/lib/google/cloud/firestore/convert.rb +561 -0
  12. data/lib/google/cloud/firestore/credentials.rb +35 -0
  13. data/lib/google/cloud/firestore/document_reference.rb +468 -0
  14. data/lib/google/cloud/firestore/document_snapshot.rb +324 -0
  15. data/lib/google/cloud/firestore/field_path.rb +216 -0
  16. data/lib/google/cloud/firestore/field_value.rb +113 -0
  17. data/lib/google/cloud/firestore/generate.rb +35 -0
  18. data/lib/google/cloud/firestore/query.rb +651 -0
  19. data/lib/google/cloud/firestore/service.rb +176 -0
  20. data/lib/google/cloud/firestore/transaction.rb +726 -0
  21. data/lib/google/cloud/firestore/v1beta1.rb +121 -0
  22. data/lib/google/cloud/firestore/v1beta1/doc/google/firestore/v1beta1/common.rb +63 -0
  23. data/lib/google/cloud/firestore/v1beta1/doc/google/firestore/v1beta1/document.rb +134 -0
  24. data/lib/google/cloud/firestore/v1beta1/doc/google/firestore/v1beta1/firestore.rb +584 -0
  25. data/lib/google/cloud/firestore/v1beta1/doc/google/firestore/v1beta1/query.rb +215 -0
  26. data/lib/google/cloud/firestore/v1beta1/doc/google/firestore/v1beta1/write.rb +167 -0
  27. data/lib/google/cloud/firestore/v1beta1/doc/google/protobuf/any.rb +124 -0
  28. data/lib/google/cloud/firestore/v1beta1/doc/google/protobuf/timestamp.rb +106 -0
  29. data/lib/google/cloud/firestore/v1beta1/doc/google/protobuf/wrappers.rb +89 -0
  30. data/lib/google/cloud/firestore/v1beta1/doc/google/rpc/status.rb +83 -0
  31. data/lib/google/cloud/firestore/v1beta1/doc/overview.rb +53 -0
  32. data/lib/google/cloud/firestore/v1beta1/firestore_client.rb +974 -0
  33. data/lib/google/cloud/firestore/v1beta1/firestore_client_config.json +100 -0
  34. data/lib/google/cloud/firestore/version.rb +22 -0
  35. data/lib/google/firestore/v1beta1/common_pb.rb +44 -0
  36. data/lib/google/firestore/v1beta1/document_pb.rb +49 -0
  37. data/lib/google/firestore/v1beta1/firestore_pb.rb +219 -0
  38. data/lib/google/firestore/v1beta1/firestore_services_pb.rb +87 -0
  39. data/lib/google/firestore/v1beta1/query_pb.rb +103 -0
  40. data/lib/google/firestore/v1beta1/write_pb.rb +73 -0
  41. metadata +251 -0
@@ -0,0 +1,449 @@
1
+ # Copyright 2017 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/v1beta1"
17
+ require "google/cloud/firestore/service"
18
+ require "google/cloud/firestore/field_path"
19
+ require "google/cloud/firestore/field_value"
20
+ require "google/cloud/firestore/collection_reference"
21
+ require "google/cloud/firestore/document_reference"
22
+ require "google/cloud/firestore/document_snapshot"
23
+ require "google/cloud/firestore/batch"
24
+ require "google/cloud/firestore/transaction"
25
+
26
+ module Google
27
+ module Cloud
28
+ module Firestore
29
+ ##
30
+ # # Client
31
+ #
32
+ # The Cloud Firestore Client used is to access and manipulate the
33
+ # collections and documents in the Firestore database.
34
+ #
35
+ # @example
36
+ # require "google/cloud/firestore"
37
+ #
38
+ # firestore = Google::Cloud::Firestore.new
39
+ #
40
+ # # Get a document reference
41
+ # nyc_ref = firestore.doc "cities/NYC"
42
+ #
43
+ # firestore.batch do |b|
44
+ # b.update(nyc_ref, { name: "New York City" })
45
+ # end
46
+ #
47
+ class Client
48
+ ##
49
+ # @private The Service object.
50
+ attr_accessor :service
51
+
52
+ ##
53
+ # @private Creates a new Firestore Database instance.
54
+ def initialize service
55
+ @service = service
56
+ end
57
+
58
+ ##
59
+ # The project identifier for the Cloud Firestore database.
60
+ #
61
+ # @return [String] project identifier.
62
+ def project_id
63
+ service.project
64
+ end
65
+
66
+ ##
67
+ # The database identifier for the Cloud Firestore database.
68
+ #
69
+ # @return [String] database identifier.
70
+ def database_id
71
+ "(default)"
72
+ end
73
+
74
+ ##
75
+ # @private The full Database path for the Cloud Firestore database.
76
+ #
77
+ # @return [String] database resource path.
78
+ def path
79
+ service.database_path
80
+ end
81
+
82
+ # @!group Access
83
+
84
+ ##
85
+ # Retrieves a list of collections.
86
+ #
87
+ # @yield [collections] The block for accessing the collections.
88
+ # @yieldparam [CollectionReference] collection A collection.
89
+ #
90
+ # @return [Enumerator<CollectionReference>] collection list.
91
+ #
92
+ # @example
93
+ # require "google/cloud/firestore"
94
+ #
95
+ # firestore = Google::Cloud::Firestore.new
96
+ #
97
+ # # Get the root collections
98
+ # firestore.cols.each do |col|
99
+ # puts col.collection_id
100
+ # end
101
+ #
102
+ def cols
103
+ ensure_service!
104
+
105
+ return enum_for(:cols) unless block_given?
106
+
107
+ collection_ids = service.list_collections "#{path}/documents"
108
+ collection_ids.each { |collection_id| yield col(collection_id) }
109
+ end
110
+ alias_method :collections, :cols
111
+
112
+ ##
113
+ # Retrieves a collection.
114
+ #
115
+ # @param [String] collection_path A string representing the path of the
116
+ # collection, relative to the document root of the database.
117
+ #
118
+ # @return [CollectionReference] A collection.
119
+ #
120
+ # @example
121
+ # require "google/cloud/firestore"
122
+ #
123
+ # firestore = Google::Cloud::Firestore.new
124
+ #
125
+ # # Get the cities collection
126
+ # cities_col = firestore.col "cities"
127
+ #
128
+ # # Get the document for NYC
129
+ # nyc_ref = cities_col.doc "NYC"
130
+ #
131
+ def col collection_path
132
+ if collection_path.to_s.split("/").count.even?
133
+ fail ArgumentError, "collection_path must refer to a collection."
134
+ end
135
+
136
+ CollectionReference.from_path \
137
+ "#{path}/documents/#{collection_path}", self
138
+ end
139
+ alias_method :collection, :col
140
+
141
+ ##
142
+ # Retrieves a document reference.
143
+ #
144
+ # @param [String] document_path A string representing the path of the
145
+ # document, relative to the document root of the database.
146
+ #
147
+ # @return [DocumentReference] A document.
148
+ #
149
+ # @example
150
+ # require "google/cloud/firestore"
151
+ #
152
+ # firestore = Google::Cloud::Firestore.new
153
+ #
154
+ # # Get a document
155
+ # nyc_ref = firestore.doc "cities/NYC"
156
+ #
157
+ # puts nyc_ref.document_id
158
+ #
159
+ def doc document_path
160
+ if document_path.to_s.split("/").count.odd?
161
+ fail ArgumentError, "document_path must refer to a document."
162
+ end
163
+
164
+ doc_path = "#{path}/documents/#{document_path}"
165
+
166
+ DocumentReference.from_path doc_path, self
167
+ end
168
+ alias_method :document, :doc
169
+
170
+ ##
171
+ # Retrieves a list of document snapshots.
172
+ #
173
+ # @param [String, DocumentReference] docs One or more strings
174
+ # representing the path of the document, or document reference
175
+ # objects.
176
+ #
177
+ # @yield [documents] The block for accessing the document snapshots.
178
+ # @yieldparam [DocumentSnapshot] document A document snapshot.
179
+ #
180
+ # @return [Enumerator<DocumentSnapshot>] document snapshots list.
181
+ #
182
+ # @example
183
+ # require "google/cloud/firestore"
184
+ #
185
+ # firestore = Google::Cloud::Firestore.new
186
+ #
187
+ # # Get and print city documents
188
+ # cities = ["cities/NYC", "cities/SF", "cities/LA"]
189
+ # firestore.get_all(cities).each do |city|
190
+ # puts "#{city.document_id} has #{city[:population]} residents."
191
+ # end
192
+ #
193
+ def get_all *docs
194
+ ensure_service!
195
+
196
+ return enum_for(:get_all, docs) unless block_given?
197
+
198
+ doc_paths = Array(docs).flatten.map do |doc_path|
199
+ coalesce_doc_path_argument doc_path
200
+ end
201
+
202
+ results = service.get_documents doc_paths
203
+ results.each do |result|
204
+ next if result.result.nil?
205
+ yield DocumentSnapshot.from_batch_result(result, self)
206
+ end
207
+ end
208
+ alias_method :get_docs, :get_all
209
+ alias_method :get_documents, :get_all
210
+ alias_method :find, :get_all
211
+
212
+ ##
213
+ # Creates a field path object representing the sentinel ID of a
214
+ # document. It can be used in queries to sort or filter by the document
215
+ # ID. See {Client#document_id}.
216
+ #
217
+ # @return [FieldPath] The field path object.
218
+ #
219
+ # @example
220
+ # require "google/cloud/firestore"
221
+ #
222
+ # firestore = Google::Cloud::Firestore.new
223
+ #
224
+ # # Get a collection reference
225
+ # cities_col = firestore.col "cities"
226
+ #
227
+ # # Create a query
228
+ # query = cities_col.start_at("NYC").order(
229
+ # Google::Cloud::Firestore::FieldPath.document_id
230
+ # )
231
+ #
232
+ # query.get do |city|
233
+ # puts "#{city.document_id} has #{city[:population]} residents."
234
+ # end
235
+ #
236
+ def document_id
237
+ FieldPath.document_id
238
+ end
239
+
240
+ ##
241
+ # Creates a field path object representing the nested fields for
242
+ # document data.
243
+ #
244
+ # @return [Array<String>] The fields.
245
+ #
246
+ # @example
247
+ # require "google/cloud/firestore"
248
+ #
249
+ # firestore = Google::Cloud::Firestore.new
250
+ #
251
+ # user_snap = firestore.doc("users/frank").get
252
+ #
253
+ # nested_field_path = firestore.field_path :favorites, :food
254
+ # user_snap.get(nested_field_path) #=> "Pizza"
255
+ #
256
+ def field_path *fields
257
+ FieldPath.new(*fields)
258
+ end
259
+
260
+ ##
261
+ # Creates a field value object representing the deletion of a field in
262
+ # document data.
263
+ #
264
+ # @return [FieldValue] The delete field value object.
265
+ #
266
+ # @example
267
+ # require "google/cloud/firestore"
268
+ #
269
+ # firestore = Google::Cloud::Firestore.new
270
+ #
271
+ # # Get a document reference
272
+ # nyc_ref = firestore.doc "cities/NYC"
273
+ #
274
+ # nyc_ref.update({ name: "New York City",
275
+ # trash: firestore.field_delete })
276
+ #
277
+ def field_delete
278
+ FieldValue.delete
279
+ end
280
+
281
+ ##
282
+ # Creates a field value object representing set a field's value to
283
+ # the server timestamp when accessing the document data.
284
+ #
285
+ # @return [FieldValue] The server time field value object.
286
+ #
287
+ # @example
288
+ # require "google/cloud/firestore"
289
+ #
290
+ # firestore = Google::Cloud::Firestore.new
291
+ #
292
+ # # Get a document reference
293
+ # nyc_ref = firestore.doc "cities/NYC"
294
+ #
295
+ # nyc_ref.update({ name: "New York City",
296
+ # updated_at: firestore.field_server_time })
297
+ #
298
+ def field_server_time
299
+ FieldValue.server_time
300
+ end
301
+
302
+ # @!endgroup
303
+
304
+ # @!group Operations
305
+
306
+ ##
307
+ # Perform multiple changes at the same time.
308
+ #
309
+ # All changes are accumulated in memory until the block completes.
310
+ # Unlike transactions, batches don't lock on document reads, should only
311
+ # fail if users provide preconditions, and are not automatically
312
+ # retried. See {Batch}.
313
+ #
314
+ # @see https://firebase.google.com/docs/firestore/manage-data/transactions
315
+ # Transactions and Batched Writes
316
+ #
317
+ # @yield [batch] The block for reading data and making changes.
318
+ # @yieldparam [Batch] batch The write batch object for making changes.
319
+ #
320
+ # @return [CommitResponse] The response from committing the changes.
321
+ #
322
+ # @example
323
+ # require "google/cloud/firestore"
324
+ #
325
+ # firestore = Google::Cloud::Firestore.new
326
+ #
327
+ # firestore.batch do |b|
328
+ # # Set the data for NYC
329
+ # b.set("cities/NYC", { name: "New York City" })
330
+ #
331
+ # # Update the population for SF
332
+ # b.update("cities/SF", { population: 1000000 })
333
+ #
334
+ # # Delete LA
335
+ # b.delete("cities/LA")
336
+ # end
337
+ #
338
+ def batch
339
+ batch = Batch.from_client self
340
+ yield batch
341
+ batch.commit
342
+ end
343
+
344
+ ##
345
+ # Create a transaction to perform multiple reads and writes that are
346
+ # executed atomically at a single logical point in time in a database.
347
+ #
348
+ # All changes are accumulated in memory until the block completes.
349
+ # Transactions will be automatically retried when documents change
350
+ # before the transaction is committed. See {Transaction}.
351
+ #
352
+ # @see https://firebase.google.com/docs/firestore/manage-data/transactions
353
+ # Transactions and Batched Writes
354
+ #
355
+ # @param [Integer] max_retries The maximum number of retries for
356
+ # transactions failed due to errors. Default is 5. Optional.
357
+ #
358
+ # @yield [transaction] The block for reading data and making changes.
359
+ # @yieldparam [Transaction] transaction The transaction object for
360
+ # making changes.
361
+ #
362
+ # @return [CommitResponse] The response from committing the changes.
363
+ #
364
+ # @example
365
+ # require "google/cloud/firestore"
366
+ #
367
+ # firestore = Google::Cloud::Firestore.new
368
+ #
369
+ # firestore.transaction do |tx|
370
+ # # Set the data for NYC
371
+ # tx.set("cities/NYC", { name: "New York City" })
372
+ #
373
+ # # Update the population for SF
374
+ # tx.update("cities/SF", { population: 1000000 })
375
+ #
376
+ # # Delete LA
377
+ # tx.delete("cities/LA")
378
+ # end
379
+ #
380
+ def transaction max_retries: nil
381
+ max_retries = 5 unless max_retries.is_a? Integer
382
+ retries = 0
383
+ backoff = 1.0
384
+
385
+ transaction = Transaction.from_client self
386
+ begin
387
+ yield transaction
388
+ transaction.commit
389
+ rescue Google::Cloud::UnavailableError => err
390
+ # Re-raise if deadline has passed
391
+ raise err if retries >= max_retries
392
+ # Sleep with incremental backoff
393
+ sleep(backoff *= 1.3)
394
+ # Create new transaction and retry
395
+ transaction = Transaction.from_client \
396
+ self, previous_transaction: transaction.transaction_id
397
+ retries += 1
398
+ retry
399
+ rescue Google::Cloud::InvalidArgumentError => err
400
+ # Return if a previous call has succeeded
401
+ return nil if retries > 0
402
+ # Re-raise error.
403
+ raise err
404
+ rescue => err
405
+ # Rollback transaction when handling unexpected error
406
+ transaction.rollback rescue nil
407
+ # Re-raise error.
408
+ raise err
409
+ end
410
+ end
411
+
412
+ # @!endgroup
413
+
414
+ protected
415
+
416
+ ##
417
+ # @private
418
+ def coalesce_get_argument obj
419
+ if obj.is_a?(String) || obj.is_a?(Symbol)
420
+ if obj.to_s.split("/").count.even?
421
+ return doc obj # Convert a DocumentReference
422
+ else
423
+ return col obj # Convert to Query
424
+ end
425
+ end
426
+
427
+ return obj.ref if obj.is_a? DocumentSnapshot
428
+
429
+ obj
430
+ end
431
+
432
+ ##
433
+ # @private
434
+ def coalesce_doc_path_argument doc_path
435
+ return doc_path.path if doc_path.respond_to? :path
436
+
437
+ doc(doc_path).path
438
+ end
439
+
440
+ ##
441
+ # @private Raise an error unless an active connection to the service is
442
+ # available.
443
+ def ensure_service!
444
+ fail "Must have active connection to service" unless service
445
+ end
446
+ end
447
+ end
448
+ end
449
+ end
@@ -0,0 +1,249 @@
1
+ # Copyright 2017 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/v1beta1"
17
+ require "google/cloud/firestore/document_reference"
18
+ require "google/cloud/firestore/document_snapshot"
19
+ require "google/cloud/firestore/query"
20
+ require "google/cloud/firestore/generate"
21
+
22
+ module Google
23
+ module Cloud
24
+ module Firestore
25
+ ##
26
+ # # CollectionReference
27
+ #
28
+ # A collection reference object ise used for adding documents, getting
29
+ # document references, and querying for documents (See {Query}).
30
+ #
31
+ # @example
32
+ # require "google/cloud/firestore"
33
+ #
34
+ # firestore = Google::Cloud::Firestore.new
35
+ #
36
+ # # Get a collection reference
37
+ # cities_col = firestore.col "cities"
38
+ #
39
+ # # Get and print all city documents
40
+ # cities_col.get do |city|
41
+ # puts "#{city.document_id} has #{city[:population]} residents."
42
+ # end
43
+ #
44
+ class CollectionReference < Query
45
+ ##
46
+ # @private The firestore client object.
47
+ attr_accessor :client
48
+
49
+ ##
50
+ # The collection identifier for the collection resource.
51
+ #
52
+ # @return [String] collection identifier.
53
+ def collection_id
54
+ path.split("/").last
55
+ end
56
+
57
+ ##
58
+ # A string representing the path of the collection, relative to the
59
+ # document root of the database.
60
+ #
61
+ # @return [String] collection path.
62
+ def collection_path
63
+ path.split("/", 6).last
64
+ end
65
+
66
+ ##
67
+ # @private A string representing the full path of the collection
68
+ # resource.
69
+ #
70
+ # @return [String] collection resource path.
71
+ def path
72
+ @path
73
+ end
74
+
75
+ ##
76
+ # @private The parent path for the collection.
77
+ def parent_path
78
+ path.split("/")[0...-1].join("/")
79
+ end
80
+
81
+ # @!group Access
82
+
83
+ ##
84
+ # Retrieves a document reference.
85
+ #
86
+ # @param [String, nil] document_path A string representing the path of
87
+ # the document, relative to the document root of the database. If a
88
+ # string is not provided, and random document identifier will be
89
+ # generated. Optional.
90
+ #
91
+ # @return [DocumentReference] A document.
92
+ #
93
+ # @example
94
+ # require "google/cloud/firestore"
95
+ #
96
+ # firestore = Google::Cloud::Firestore.new
97
+ #
98
+ # # Get a collection reference
99
+ # cities_col = firestore.col "cities"
100
+ #
101
+ # # Get a document reference
102
+ # nyc_ref = cities_col.doc "NYC"
103
+ #
104
+ # # The document ID is what was provided
105
+ # nyc_ref.document_id #=> "NYC"
106
+ #
107
+ # @example Create a document reference with a random ID:
108
+ # require "google/cloud/firestore"
109
+ #
110
+ # firestore = Google::Cloud::Firestore.new
111
+ #
112
+ # # Get a collection reference
113
+ # cities_col = firestore.col "cities"
114
+ #
115
+ # # Get a document reference without specifying path
116
+ # random_ref = cities_col.doc
117
+ #
118
+ # # The document ID is randomly generated
119
+ # random_ref.document_id #=> "RANDOMID123XYZ"
120
+ #
121
+ def doc document_path = nil
122
+ document_path ||= random_document_id
123
+
124
+ ensure_client!
125
+ client.doc "#{collection_path}/#{document_path}"
126
+ end
127
+ alias_method :document, :doc
128
+
129
+ ##
130
+ # The document reference or database the collection reference belongs
131
+ # to. If the collection is a root collection, it will return the
132
+ # database object. If the collection is nested under a document, it
133
+ # will return the document reference object.
134
+ #
135
+ # @return [Database, DocumentReference] parent object.
136
+ #
137
+ # @example Returns database object for root collections:
138
+ # require "google/cloud/firestore"
139
+ #
140
+ # firestore = Google::Cloud::Firestore.new
141
+ #
142
+ # # Get a collection reference
143
+ # cities_col = firestore.col "cities"
144
+ #
145
+ # # Get the document's parent collection
146
+ # database = cities_col.parent
147
+ #
148
+ # @example Returns document object for nested collections:
149
+ # require "google/cloud/firestore"
150
+ #
151
+ # firestore = Google::Cloud::Firestore.new
152
+ #
153
+ # # Get a collection reference
154
+ # precincts_ref = firestore.col "cities/NYC/precincts"
155
+ #
156
+ # # Get the document's parent collection
157
+ # nyc_ref = precincts_ref.parent
158
+ #
159
+ def parent
160
+ if collection_path.include? "/"
161
+ return DocumentReference.from_path parent_path, client
162
+ end
163
+ client
164
+ end
165
+
166
+ # @!endgroup
167
+
168
+ # @!group Modifications
169
+
170
+ ##
171
+ # Create a document with random document identifier.
172
+ #
173
+ # The operation will fail if the document already exists.
174
+ #
175
+ # @param [Hash] data The document's fields and values. Optional.
176
+ #
177
+ # @return [DocumentReference] A created document.
178
+ #
179
+ # @example Create a document with a random ID:
180
+ # require "google/cloud/firestore"
181
+ #
182
+ # firestore = Google::Cloud::Firestore.new
183
+ #
184
+ # # Get a collection reference
185
+ # cities_col = firestore.col "cities"
186
+ #
187
+ # # Get a document reference without data
188
+ # random_ref = cities_col.add
189
+ #
190
+ # # The document ID is randomly generated
191
+ # random_ref.document_id #=> "RANDOMID123XYZ"
192
+ #
193
+ # @example Create a document snapshot:
194
+ # require "google/cloud/firestore"
195
+ #
196
+ # firestore = Google::Cloud::Firestore.new
197
+ #
198
+ # # Get a collection reference
199
+ # cities_col = firestore.col "cities"
200
+ #
201
+ # # Get a document snapshot
202
+ # random_ref = cities_col.add({ name: "New York City" })
203
+ #
204
+ # # The document ID is randomly generated
205
+ # random_ref.document_id #=> "RANDOMID123XYZ"
206
+ #
207
+ def add data = nil
208
+ data ||= {}
209
+ doc.tap { |d| d.create data }
210
+ end
211
+
212
+ # @!endgroup
213
+
214
+ ##
215
+ # @private New Collection reference object from a path.
216
+ def self.from_path path, client
217
+ # Very important to correctly set @query on a collection object
218
+ query = StructuredQuery.new(
219
+ from: [
220
+ StructuredQuery::CollectionSelector.new(
221
+ collection_id: path.split("/").last
222
+ )
223
+ ]
224
+ )
225
+
226
+ new.tap do |c|
227
+ c.client = client
228
+ c.instance_variable_set :@path, path
229
+ c.instance_variable_set :@query, query
230
+ end
231
+ end
232
+
233
+ protected
234
+
235
+ ##
236
+ # @private
237
+ def random_document_id
238
+ Generate.unique_id
239
+ end
240
+
241
+ ##
242
+ # @private Raise an error unless an database available.
243
+ def ensure_client!
244
+ fail "Must have active connection to service" unless client
245
+ end
246
+ end
247
+ end
248
+ end
249
+ end