google-cloud-bigquery 1.21.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +7 -0
  2. data/.yardopts +16 -0
  3. data/AUTHENTICATION.md +158 -0
  4. data/CHANGELOG.md +397 -0
  5. data/CODE_OF_CONDUCT.md +40 -0
  6. data/CONTRIBUTING.md +188 -0
  7. data/LICENSE +201 -0
  8. data/LOGGING.md +27 -0
  9. data/OVERVIEW.md +463 -0
  10. data/TROUBLESHOOTING.md +31 -0
  11. data/lib/google-cloud-bigquery.rb +139 -0
  12. data/lib/google/cloud/bigquery.rb +145 -0
  13. data/lib/google/cloud/bigquery/argument.rb +197 -0
  14. data/lib/google/cloud/bigquery/convert.rb +383 -0
  15. data/lib/google/cloud/bigquery/copy_job.rb +316 -0
  16. data/lib/google/cloud/bigquery/credentials.rb +50 -0
  17. data/lib/google/cloud/bigquery/data.rb +526 -0
  18. data/lib/google/cloud/bigquery/dataset.rb +2845 -0
  19. data/lib/google/cloud/bigquery/dataset/access.rb +1021 -0
  20. data/lib/google/cloud/bigquery/dataset/list.rb +162 -0
  21. data/lib/google/cloud/bigquery/encryption_configuration.rb +123 -0
  22. data/lib/google/cloud/bigquery/external.rb +2432 -0
  23. data/lib/google/cloud/bigquery/extract_job.rb +368 -0
  24. data/lib/google/cloud/bigquery/insert_response.rb +180 -0
  25. data/lib/google/cloud/bigquery/job.rb +657 -0
  26. data/lib/google/cloud/bigquery/job/list.rb +162 -0
  27. data/lib/google/cloud/bigquery/load_job.rb +1704 -0
  28. data/lib/google/cloud/bigquery/model.rb +740 -0
  29. data/lib/google/cloud/bigquery/model/list.rb +164 -0
  30. data/lib/google/cloud/bigquery/project.rb +1655 -0
  31. data/lib/google/cloud/bigquery/project/list.rb +161 -0
  32. data/lib/google/cloud/bigquery/query_job.rb +1695 -0
  33. data/lib/google/cloud/bigquery/routine.rb +1108 -0
  34. data/lib/google/cloud/bigquery/routine/list.rb +165 -0
  35. data/lib/google/cloud/bigquery/schema.rb +564 -0
  36. data/lib/google/cloud/bigquery/schema/field.rb +668 -0
  37. data/lib/google/cloud/bigquery/service.rb +589 -0
  38. data/lib/google/cloud/bigquery/standard_sql.rb +495 -0
  39. data/lib/google/cloud/bigquery/table.rb +3340 -0
  40. data/lib/google/cloud/bigquery/table/async_inserter.rb +520 -0
  41. data/lib/google/cloud/bigquery/table/list.rb +172 -0
  42. data/lib/google/cloud/bigquery/time.rb +65 -0
  43. data/lib/google/cloud/bigquery/version.rb +22 -0
  44. metadata +297 -0
@@ -0,0 +1,740 @@
1
+ # Copyright 2019 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/errors"
17
+ require "google/cloud/bigquery/service"
18
+ require "google/cloud/bigquery/model/list"
19
+ require "google/cloud/bigquery/standard_sql"
20
+ require "google/cloud/bigquery/convert"
21
+ require "google/apis/bigquery_v2"
22
+
23
+ module Google
24
+ module Cloud
25
+ module Bigquery
26
+ ##
27
+ # # Model
28
+ #
29
+ # A model in BigQuery ML represents what an ML system has learned from the
30
+ # training data.
31
+ #
32
+ # The following types of models are supported by BigQuery ML:
33
+ #
34
+ # * Linear regression for forecasting; for example, the sales of an item
35
+ # on a given day. Labels are real-valued (they cannot be +/- infinity or
36
+ # NaN).
37
+ # * Binary logistic regression for classification; for example,
38
+ # determining whether a customer will make a purchase. Labels must only
39
+ # have two possible values.
40
+ # * Multiclass logistic regression for classification. These models can be
41
+ # used to predict multiple possible values such as whether an input is
42
+ # "low-value," "medium-value," or "high-value." Labels can have up to 50
43
+ # unique values. In BigQuery ML, multiclass logistic regression training
44
+ # uses a multinomial classifier with a cross entropy loss function.
45
+ # * K-means clustering for data segmentation (beta); for example,
46
+ # identifying customer segments. K-means is an unsupervised learning
47
+ # technique, so model training does not require labels nor split data
48
+ # for training or evaluation.
49
+ #
50
+ # In BigQuery ML, a model can be used with data from multiple BigQuery
51
+ # datasets for training and for prediction.
52
+ #
53
+ # @see https://cloud.google.com/bigquery-ml/docs/bigqueryml-intro
54
+ # Introduction to BigQuery ML
55
+ # @see https://cloud.google.com/bigquery-ml/docs/getting-model-metadata
56
+ # Getting model metadata
57
+ #
58
+ # @example
59
+ # require "google/cloud/bigquery"
60
+ #
61
+ # bigquery = Google::Cloud::Bigquery.new
62
+ # dataset = bigquery.dataset "my_dataset"
63
+ #
64
+ # model = dataset.model "my_model"
65
+ #
66
+ class Model
67
+ ##
68
+ # @private The Service object.
69
+ attr_accessor :service
70
+
71
+ ##
72
+ # @private The Google API Client JSON Hash.
73
+ attr_accessor :gapi_json
74
+
75
+ ##
76
+ # @private A Google API Client Model Reference object.
77
+ attr_reader :reference
78
+
79
+ ##
80
+ # @private Create an empty Model object.
81
+ def initialize
82
+ @service = nil
83
+ @gapi_json = nil
84
+ @reference = nil
85
+ end
86
+
87
+ ##
88
+ # A unique ID for this model.
89
+ #
90
+ # @return [String] The ID must contain only letters (a-z, A-Z), numbers
91
+ # (0-9), or underscores (_). The maximum length is 1,024 characters.
92
+ #
93
+ # @!group Attributes
94
+ #
95
+ def model_id
96
+ return @reference.model_id if reference?
97
+ @gapi_json[:modelReference][:modelId]
98
+ end
99
+
100
+ ##
101
+ # The ID of the `Dataset` containing this model.
102
+ #
103
+ # @return [String] The ID must contain only letters (a-z, A-Z), numbers
104
+ # (0-9), or underscores (_). The maximum length is 1,024 characters.
105
+ #
106
+ # @!group Attributes
107
+ #
108
+ def dataset_id
109
+ return @reference.dataset_id if reference?
110
+ @gapi_json[:modelReference][:datasetId]
111
+ end
112
+
113
+ ##
114
+ # The ID of the `Project` containing this model.
115
+ #
116
+ # @return [String] The project ID.
117
+ #
118
+ # @!group Attributes
119
+ #
120
+ def project_id
121
+ return @reference.project_id if reference?
122
+ @gapi_json[:modelReference][:projectId]
123
+ end
124
+
125
+ ##
126
+ # @private The gapi_json fragment containing the Project ID, Dataset ID,
127
+ # and Model ID.
128
+ #
129
+ # @return [Google::Apis::BigqueryV2::ModelReference]
130
+ #
131
+ def model_ref
132
+ return @reference if reference?
133
+ Google::Apis::BigqueryV2::ModelReference.new(
134
+ project_id: project_id,
135
+ dataset_id: dataset_id,
136
+ model_id: model_id
137
+ )
138
+ end
139
+
140
+ ##
141
+ # Type of the model resource. Expected to be one of the following:
142
+ #
143
+ # * LINEAR_REGRESSION - Linear regression model.
144
+ # * LOGISTIC_REGRESSION - Logistic regression based classification
145
+ # model.
146
+ # * KMEANS - K-means clustering model (beta).
147
+ # * TENSORFLOW - An imported TensorFlow model (beta).
148
+ #
149
+ # @return [String, nil] The model type, or `nil` if the object is a
150
+ # reference (see {#reference?}).
151
+ #
152
+ # @!group Attributes
153
+ #
154
+ def model_type
155
+ return nil if reference?
156
+ @gapi_json[:modelType]
157
+ end
158
+
159
+ ##
160
+ # The name of the model.
161
+ #
162
+ # @return [String, nil] The friendly name, or `nil` if the object is a
163
+ # reference (see {#reference?}).
164
+ #
165
+ # @!group Attributes
166
+ #
167
+ def name
168
+ return nil if reference?
169
+ ensure_full_data!
170
+ @gapi_json[:friendlyName]
171
+ end
172
+
173
+ ##
174
+ # Updates the name of the model.
175
+ #
176
+ # If the model is not a full resource representation (see
177
+ # {#resource_full?}), the full representation will be retrieved before
178
+ # the update to comply with ETag-based optimistic concurrency control.
179
+ #
180
+ # @param [String] new_name The new friendly name.
181
+ #
182
+ # @!group Attributes
183
+ #
184
+ def name= new_name
185
+ ensure_full_data!
186
+ patch_gapi! friendlyName: new_name
187
+ end
188
+
189
+ ##
190
+ # The ETag hash of the model.
191
+ #
192
+ # @return [String, nil] The ETag hash, or `nil` if the object is a
193
+ # reference (see {#reference?}).
194
+ #
195
+ # @!group Attributes
196
+ #
197
+ def etag
198
+ return nil if reference?
199
+ ensure_full_data!
200
+ @gapi_json[:etag]
201
+ end
202
+
203
+ ##
204
+ # A user-friendly description of the model.
205
+ #
206
+ # @return [String, nil] The description, or `nil` if the object is a
207
+ # reference (see {#reference?}).
208
+ #
209
+ # @!group Attributes
210
+ #
211
+ def description
212
+ return nil if reference?
213
+ ensure_full_data!
214
+ @gapi_json[:description]
215
+ end
216
+
217
+ ##
218
+ # Updates the user-friendly description of the model.
219
+ #
220
+ # If the model is not a full resource representation (see
221
+ # {#resource_full?}), the full representation will be retrieved before
222
+ # the update to comply with ETag-based optimistic concurrency control.
223
+ #
224
+ # @param [String] new_description The new user-friendly description.
225
+ #
226
+ # @!group Attributes
227
+ #
228
+ def description= new_description
229
+ ensure_full_data!
230
+ patch_gapi! description: new_description
231
+ end
232
+
233
+ ##
234
+ # The time when this model was created.
235
+ #
236
+ # @return [Time, nil] The creation time, or `nil` if the object is a
237
+ # reference (see {#reference?}).
238
+ #
239
+ # @!group Attributes
240
+ #
241
+ def created_at
242
+ return nil if reference?
243
+ Convert.millis_to_time @gapi_json[:creationTime]
244
+ end
245
+
246
+ ##
247
+ # The date when this model was last modified.
248
+ #
249
+ # @return [Time, nil] The last modified time, or `nil` if not present or
250
+ # the object is a reference (see {#reference?}).
251
+ #
252
+ # @!group Attributes
253
+ #
254
+ def modified_at
255
+ return nil if reference?
256
+ Convert.millis_to_time @gapi_json[:lastModifiedTime]
257
+ end
258
+
259
+ ##
260
+ # The time when this model expires.
261
+ # If not present, the model will persist indefinitely.
262
+ # Expired models will be deleted and their storage reclaimed.
263
+ #
264
+ # @return [Time, nil] The expiration time, or `nil` if not present or
265
+ # the object is a reference (see {#reference?}).
266
+ #
267
+ # @!group Attributes
268
+ #
269
+ def expires_at
270
+ return nil if reference?
271
+ ensure_full_data!
272
+ Convert.millis_to_time @gapi_json[:expirationTime]
273
+ end
274
+
275
+ ##
276
+ # Updates time when this model expires.
277
+ #
278
+ # If the model is not a full resource representation (see
279
+ # {#resource_full?}), the full representation will be retrieved before
280
+ # the update to comply with ETag-based optimistic concurrency control.
281
+ #
282
+ # @param [Integer] new_expires_at The new time when this model expires.
283
+ #
284
+ # @!group Attributes
285
+ #
286
+ def expires_at= new_expires_at
287
+ ensure_full_data!
288
+ new_expires_millis = Convert.time_to_millis new_expires_at
289
+ patch_gapi! expirationTime: new_expires_millis
290
+ end
291
+
292
+ ##
293
+ # The geographic location where the model should reside. Possible
294
+ # values include `EU` and `US`. The default value is `US`.
295
+ #
296
+ # @return [String, nil] The location code.
297
+ #
298
+ # @!group Attributes
299
+ #
300
+ def location
301
+ return nil if reference?
302
+ ensure_full_data!
303
+ @gapi_json[:location]
304
+ end
305
+
306
+ ##
307
+ # A hash of user-provided labels associated with this model. Labels
308
+ # are used to organize and group models. See [Using
309
+ # Labels](https://cloud.google.com/bigquery/docs/labels).
310
+ #
311
+ # The returned hash is frozen and changes are not allowed. Use
312
+ # {#labels=} to replace the entire hash.
313
+ #
314
+ # @return [Hash<String, String>, nil] A hash containing key/value pairs.
315
+ #
316
+ # @example
317
+ # require "google/cloud/bigquery"
318
+ #
319
+ # bigquery = Google::Cloud::Bigquery.new
320
+ # dataset = bigquery.dataset "my_dataset"
321
+ # model = dataset.model "my_model"
322
+ #
323
+ # labels = model.labels
324
+ #
325
+ # @!group Attributes
326
+ #
327
+ def labels
328
+ return nil if reference?
329
+ m = @gapi_json[:labels]
330
+ m = m.to_h if m.respond_to? :to_h
331
+ m.dup.freeze
332
+ end
333
+
334
+ ##
335
+ # Updates the hash of user-provided labels associated with this model.
336
+ # Labels are used to organize and group models. See [Using
337
+ # Labels](https://cloud.google.com/bigquery/docs/labels).
338
+ #
339
+ # If the model is not a full resource representation (see
340
+ # {#resource_full?}), the full representation will be retrieved before
341
+ # the update to comply with ETag-based optimistic concurrency control.
342
+ #
343
+ # @param [Hash<String, String>] new_labels A hash containing key/value
344
+ # pairs.
345
+ #
346
+ # * Label keys and values can be no longer than 63 characters.
347
+ # * Label keys and values can contain only lowercase letters, numbers,
348
+ # underscores, hyphens, and international characters.
349
+ # * Label keys and values cannot exceed 128 bytes in size.
350
+ # * Label keys must begin with a letter.
351
+ # * Label keys must be unique within a model.
352
+ #
353
+ # @example
354
+ # require "google/cloud/bigquery"
355
+ #
356
+ # bigquery = Google::Cloud::Bigquery.new
357
+ # dataset = bigquery.dataset "my_dataset"
358
+ # model = dataset.model "my_model"
359
+ #
360
+ # model.labels = { "env" => "production" }
361
+ #
362
+ # @!group Attributes
363
+ #
364
+ def labels= new_labels
365
+ ensure_full_data!
366
+ patch_gapi! labels: new_labels
367
+ end
368
+
369
+ ##
370
+ # The {EncryptionConfiguration} object that represents the custom
371
+ # encryption method used to protect this model. If not set,
372
+ # {Dataset#default_encryption} is used.
373
+ #
374
+ # Present only if this model is using custom encryption.
375
+ #
376
+ # @see https://cloud.google.com/bigquery/docs/customer-managed-encryption
377
+ # Protecting Data with Cloud KMS Keys
378
+ #
379
+ # @return [EncryptionConfiguration, nil] The encryption configuration.
380
+ #
381
+ # @!group Attributes
382
+ #
383
+ # @example
384
+ # require "google/cloud/bigquery"
385
+ #
386
+ # bigquery = Google::Cloud::Bigquery.new
387
+ # dataset = bigquery.dataset "my_dataset"
388
+ # model = dataset.model "my_model"
389
+ #
390
+ # encrypt_config = model.encryption
391
+ #
392
+ # @!group Attributes
393
+ #
394
+ def encryption
395
+ return nil if reference?
396
+ return nil if @gapi_json[:encryptionConfiguration].nil?
397
+ # We have to create a gapic object from the hash because that is what
398
+ # EncryptionConfiguration is expecing.
399
+ json_cmek = @gapi_json[:encryptionConfiguration].to_json
400
+ gapi_cmek = Google::Apis::BigqueryV2::EncryptionConfiguration.from_json json_cmek
401
+ EncryptionConfiguration.from_gapi(gapi_cmek).freeze
402
+ end
403
+
404
+ ##
405
+ # Set the {EncryptionConfiguration} object that represents the custom
406
+ # encryption method used to protect this model. If not set,
407
+ # {Dataset#default_encryption} is used.
408
+ #
409
+ # Present only if this model is using custom encryption.
410
+ #
411
+ # If the model is not a full resource representation (see
412
+ # {#resource_full?}), the full representation will be retrieved before
413
+ # the update to comply with ETag-based optimistic concurrency control.
414
+ #
415
+ # @see https://cloud.google.com/bigquery/docs/customer-managed-encryption
416
+ # Protecting Data with Cloud KMS Keys
417
+ #
418
+ # @param [EncryptionConfiguration] value The new encryption config.
419
+ #
420
+ # @example
421
+ # require "google/cloud/bigquery"
422
+ #
423
+ # bigquery = Google::Cloud::Bigquery.new
424
+ # dataset = bigquery.dataset "my_dataset"
425
+ # model = dataset.model "my_model"
426
+ #
427
+ # key_name = "projects/a/locations/b/keyRings/c/cryptoKeys/d"
428
+ # encrypt_config = bigquery.encryption kms_key: key_name
429
+ #
430
+ # model.encryption = encrypt_config
431
+ #
432
+ # @!group Attributes
433
+ #
434
+ def encryption= value
435
+ ensure_full_data!
436
+ # We have to create a hash from the gapic object's JSON because that
437
+ # is what Model is expecing.
438
+ json_cmek = JSON.parse value.to_gapi.to_json, symbolize_names: true
439
+ patch_gapi! encryptionConfiguration: json_cmek
440
+ end
441
+
442
+ ##
443
+ # The input feature columns that were used to train this model.
444
+ #
445
+ # @return [Array<StandardSql::Field>]
446
+ #
447
+ # @!group Attributes
448
+ #
449
+ def feature_columns
450
+ ensure_full_data!
451
+ Array(@gapi_json[:featureColumns]).map do |field_gapi_json|
452
+ field_gapi = Google::Apis::BigqueryV2::StandardSqlField.from_json field_gapi_json.to_json
453
+ StandardSql::Field.from_gapi field_gapi
454
+ end
455
+ end
456
+
457
+ ##
458
+ # The label columns that were used to train this model. The output of
459
+ # the model will have a "predicted_" prefix to these columns.
460
+ #
461
+ # @return [Array<StandardSql::Field>]
462
+ #
463
+ # @!group Attributes
464
+ #
465
+ def label_columns
466
+ ensure_full_data!
467
+ Array(@gapi_json[:labelColumns]).map do |field_gapi_json|
468
+ field_gapi = Google::Apis::BigqueryV2::StandardSqlField.from_json field_gapi_json.to_json
469
+ StandardSql::Field.from_gapi field_gapi
470
+ end
471
+ end
472
+
473
+ ##
474
+ # Information for all training runs in increasing order of startTime.
475
+ #
476
+ # @return [Array<Google::Cloud::Bigquery::Model::TrainingRun>]
477
+ #
478
+ # @!group Attributes
479
+ #
480
+ def training_runs
481
+ ensure_full_data!
482
+ Array @gapi_json[:trainingRuns]
483
+ end
484
+
485
+ ##
486
+ # Permanently deletes the model.
487
+ #
488
+ # @return [Boolean] Returns `true` if the model was deleted.
489
+ #
490
+ # @example
491
+ # require "google/cloud/bigquery"
492
+ #
493
+ # bigquery = Google::Cloud::Bigquery.new
494
+ # dataset = bigquery.dataset "my_dataset"
495
+ # model = dataset.model "my_model"
496
+ #
497
+ # model.delete
498
+ #
499
+ # @!group Lifecycle
500
+ #
501
+ def delete
502
+ ensure_service!
503
+ service.delete_model dataset_id, model_id
504
+ # Set flag for #exists?
505
+ @exists = false
506
+ true
507
+ end
508
+
509
+ ##
510
+ # Reloads the model with current data from the BigQuery service.
511
+ #
512
+ # @return [Google::Cloud::Bigquery::Model] Returns the reloaded
513
+ # model.
514
+ #
515
+ # @example Skip retrieving the model from the service, then load it:
516
+ # require "google/cloud/bigquery"
517
+ #
518
+ # bigquery = Google::Cloud::Bigquery.new
519
+ #
520
+ # dataset = bigquery.dataset "my_dataset"
521
+ # model = dataset.model "my_model", skip_lookup: true
522
+ #
523
+ # model.reference? #=> true
524
+ # model.reload!
525
+ # model.resource? #=> true
526
+ #
527
+ # @!group Lifecycle
528
+ #
529
+ def reload!
530
+ ensure_service!
531
+ @gapi_json = service.get_model dataset_id, model_id
532
+ @reference = nil
533
+ @exists = nil
534
+ self
535
+ end
536
+ alias refresh! reload!
537
+
538
+ ##
539
+ # Determines whether the model exists in the BigQuery service. The
540
+ # result is cached locally. To refresh state, set `force` to `true`.
541
+ #
542
+ # @param [Boolean] force Force the latest resource representation to be
543
+ # retrieved from the BigQuery service when `true`. Otherwise the
544
+ # return value of this method will be memoized to reduce the number of
545
+ # API calls made to the BigQuery service. The default is `false`.
546
+ #
547
+ # @return [Boolean] `true` when the model exists in the BigQuery
548
+ # service, `false` otherwise.
549
+ #
550
+ # @example
551
+ # require "google/cloud/bigquery"
552
+ #
553
+ # bigquery = Google::Cloud::Bigquery.new
554
+ #
555
+ # dataset = bigquery.dataset "my_dataset"
556
+ # model = dataset.model "my_model", skip_lookup: true
557
+ # model.exists? #=> true
558
+ #
559
+ def exists? force: false
560
+ return resource_exists? if force
561
+ # If we have a value, return it
562
+ return @exists unless @exists.nil?
563
+ # Always true if we have a gapi_json object
564
+ return true if resource?
565
+ resource_exists?
566
+ end
567
+
568
+ ##
569
+ # Whether the model was created without retrieving the resource
570
+ # representation from the BigQuery service.
571
+ #
572
+ # @return [Boolean] `true` when the model is just a local reference
573
+ # object, `false` otherwise.
574
+ #
575
+ # @example
576
+ # require "google/cloud/bigquery"
577
+ #
578
+ # bigquery = Google::Cloud::Bigquery.new
579
+ #
580
+ # dataset = bigquery.dataset "my_dataset"
581
+ # model = dataset.model "my_model", skip_lookup: true
582
+ #
583
+ # model.reference? #=> true
584
+ # model.reload!
585
+ # model.reference? #=> false
586
+ #
587
+ def reference?
588
+ @gapi_json.nil?
589
+ end
590
+
591
+ ##
592
+ # Whether the model was created with a resource representation from
593
+ # the BigQuery service.
594
+ #
595
+ # @return [Boolean] `true` when the model was created with a resource
596
+ # representation, `false` otherwise.
597
+ #
598
+ # @example
599
+ # require "google/cloud/bigquery"
600
+ #
601
+ # bigquery = Google::Cloud::Bigquery.new
602
+ #
603
+ # dataset = bigquery.dataset "my_dataset"
604
+ # model = dataset.model "my_model", skip_lookup: true
605
+ #
606
+ # model.resource? #=> false
607
+ # model.reload!
608
+ # model.resource? #=> true
609
+ #
610
+ def resource?
611
+ !@gapi_json.nil?
612
+ end
613
+
614
+ ##
615
+ # Whether the model was created with a partial resource representation
616
+ # from the BigQuery service by retrieval through {Dataset#models}.
617
+ # See [Models: list
618
+ # response](https://cloud.google.com/bigquery/docs/reference/rest/v2/models/list#response)
619
+ # for the contents of the partial representation. Accessing any
620
+ # attribute outside of the partial representation will result in loading
621
+ # the full representation.
622
+ #
623
+ # @return [Boolean] `true` when the model was created with a partial
624
+ # resource representation, `false` otherwise.
625
+ #
626
+ # @example
627
+ # require "google/cloud/bigquery"
628
+ #
629
+ # bigquery = Google::Cloud::Bigquery.new
630
+ #
631
+ # dataset = bigquery.dataset "my_dataset"
632
+ # model = dataset.models.first
633
+ #
634
+ # model.resource_partial? #=> true
635
+ # model.description # Loads the full resource.
636
+ # model.resource_partial? #=> false
637
+ #
638
+ def resource_partial?
639
+ resource? && !resource_full?
640
+ end
641
+
642
+ ##
643
+ # Whether the model was created with a full resource representation
644
+ # from the BigQuery service.
645
+ #
646
+ # @return [Boolean] `true` when the model was created with a full
647
+ # resource representation, `false` otherwise.
648
+ #
649
+ # @example
650
+ # require "google/cloud/bigquery"
651
+ #
652
+ # bigquery = Google::Cloud::Bigquery.new
653
+ #
654
+ # dataset = bigquery.dataset "my_dataset"
655
+ # model = dataset.model "my_model"
656
+ #
657
+ # model.resource_full? #=> true
658
+ #
659
+ def resource_full?
660
+ resource? && @gapi_json.key?(:friendlyName)
661
+ end
662
+
663
+ ##
664
+ # @private New Model from a Google API Client object.
665
+ def self.from_gapi_json gapi_json, service
666
+ new.tap do |m|
667
+ m.instance_variable_set :@gapi_json, gapi_json
668
+ m.instance_variable_set :@service, service
669
+ end
670
+ end
671
+
672
+ ##
673
+ # @private New lazy Model object without making an HTTP request, for use with the skip_lookup option.
674
+ def self.new_reference project_id, dataset_id, model_id, service
675
+ raise ArgumentError, "project_id is required" unless project_id
676
+ raise ArgumentError, "dataset_id is required" unless dataset_id
677
+ raise ArgumentError, "model_id is required" unless model_id
678
+ raise ArgumentError, "service is required" unless service
679
+
680
+ new.tap do |m|
681
+ reference_gapi_json = Google::Apis::BigqueryV2::ModelReference.new(
682
+ project_id: project_id,
683
+ dataset_id: dataset_id,
684
+ model_id: model_id
685
+ )
686
+ m.instance_variable_set :@reference, reference_gapi_json
687
+ m.instance_variable_set :@service, service
688
+ end
689
+ end
690
+
691
+ protected
692
+
693
+ ##
694
+ # Raise an error unless an active service is available.
695
+ def ensure_service!
696
+ raise "Must have active connection" unless service
697
+ end
698
+
699
+ ##
700
+ # Ensures the Google::Apis::BigqueryV2::Model object has been loaded
701
+ # from the service.
702
+ def ensure_gapi_json!
703
+ ensure_service!
704
+ return unless reference?
705
+ reload!
706
+ end
707
+
708
+ ##
709
+ # Fetch gapi_json and memoize whether resource exists.
710
+ def resource_exists?
711
+ reload!
712
+ @exists = true
713
+ rescue Google::Cloud::NotFoundError
714
+ @exists = false
715
+ end
716
+
717
+ def patch_gapi! **changes
718
+ return if changes.empty?
719
+ ensure_service!
720
+ patch_gapi = Google::Apis::BigqueryV2::Model.from_json changes.to_json
721
+ patch_gapi.model_reference = model_ref
722
+ @gapi_json = service.patch_model \
723
+ dataset_id, model_id, patch_gapi, etag
724
+ @reference = nil
725
+
726
+ # TODO: restore original impl after acceptance test indicates that
727
+ # service etag bug is fixed
728
+ reload!
729
+ end
730
+
731
+ ##
732
+ # Load the complete representation of the model if it has been
733
+ # only partially loaded by a request to the API list method.
734
+ def ensure_full_data!
735
+ reload! unless resource_full?
736
+ end
737
+ end
738
+ end
739
+ end
740
+ end