google-cloud-bigquery 1.11.2 → 1.12.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/CHANGELOG.md +9 -0
- data/lib/google/cloud/bigquery/convert.rb +22 -4
- data/lib/google/cloud/bigquery/dataset.rb +84 -0
- data/lib/google/cloud/bigquery/model.rb +665 -0
- data/lib/google/cloud/bigquery/model/list.rb +168 -0
- data/lib/google/cloud/bigquery/service.rb +54 -0
- data/lib/google/cloud/bigquery/standard_sql.rb +291 -0
- data/lib/google/cloud/bigquery/version.rb +1 -1
- metadata +5 -2
@@ -0,0 +1,168 @@
|
|
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 "delegate"
|
17
|
+
|
18
|
+
module Google
|
19
|
+
module Cloud
|
20
|
+
module Bigquery
|
21
|
+
class Model
|
22
|
+
##
|
23
|
+
# Model::List is a special case Array with additional values.
|
24
|
+
class List < DelegateClass(::Array)
|
25
|
+
##
|
26
|
+
# If not empty, indicates that there are more records that match
|
27
|
+
# the request and this value should be passed to continue.
|
28
|
+
attr_accessor :token
|
29
|
+
|
30
|
+
##
|
31
|
+
# @private Create a new Model::List with an array of models.
|
32
|
+
def initialize arr = []
|
33
|
+
super arr
|
34
|
+
end
|
35
|
+
|
36
|
+
##
|
37
|
+
# Whether there is a next page of models.
|
38
|
+
#
|
39
|
+
# @return [Boolean]
|
40
|
+
#
|
41
|
+
# @example
|
42
|
+
# require "google/cloud/bigquery"
|
43
|
+
#
|
44
|
+
# bigquery = Google::Cloud::Bigquery.new
|
45
|
+
# dataset = bigquery.dataset "my_dataset"
|
46
|
+
#
|
47
|
+
# models = dataset.models
|
48
|
+
# if models.next?
|
49
|
+
# next_models = models.next
|
50
|
+
# end
|
51
|
+
#
|
52
|
+
def next?
|
53
|
+
!token.nil?
|
54
|
+
end
|
55
|
+
|
56
|
+
##
|
57
|
+
# Retrieve the next page of models.
|
58
|
+
#
|
59
|
+
# @return [Model::List]
|
60
|
+
#
|
61
|
+
# @example
|
62
|
+
# require "google/cloud/bigquery"
|
63
|
+
#
|
64
|
+
# bigquery = Google::Cloud::Bigquery.new
|
65
|
+
# dataset = bigquery.dataset "my_dataset"
|
66
|
+
#
|
67
|
+
# models = dataset.models
|
68
|
+
# if models.next?
|
69
|
+
# next_models = models.next
|
70
|
+
# end
|
71
|
+
#
|
72
|
+
def next
|
73
|
+
return nil unless next?
|
74
|
+
ensure_service!
|
75
|
+
gapi = @service.list_models @dataset_id, token: token, max: @max
|
76
|
+
self.class.from_gapi gapi, @service, @dataset_id, @max
|
77
|
+
end
|
78
|
+
|
79
|
+
##
|
80
|
+
# Retrieves remaining results by repeatedly invoking {#next} until
|
81
|
+
# {#next?} returns `false`. Calls the given block once for each
|
82
|
+
# result, which is passed as the argument to the block.
|
83
|
+
#
|
84
|
+
# An Enumerator is returned if no block is given.
|
85
|
+
#
|
86
|
+
# This method will make repeated API calls until all remaining results
|
87
|
+
# are retrieved. (Unlike `#each`, for example, which merely iterates
|
88
|
+
# over the results returned by a single API call.) Use with caution.
|
89
|
+
#
|
90
|
+
# @param [Integer] request_limit The upper limit of API requests to
|
91
|
+
# make to load all models. Default is no limit.
|
92
|
+
# @yield [model] The block for accessing each model.
|
93
|
+
# @yieldparam [Model] model The model object.
|
94
|
+
#
|
95
|
+
# @return [Enumerator]
|
96
|
+
#
|
97
|
+
# @example Iterating each result by passing a block:
|
98
|
+
# require "google/cloud/bigquery"
|
99
|
+
#
|
100
|
+
# bigquery = Google::Cloud::Bigquery.new
|
101
|
+
# dataset = bigquery.dataset "my_dataset"
|
102
|
+
#
|
103
|
+
# dataset.models.all do |model|
|
104
|
+
# puts model.model_id
|
105
|
+
# end
|
106
|
+
#
|
107
|
+
# @example Using the enumerator by not passing a block:
|
108
|
+
# require "google/cloud/bigquery"
|
109
|
+
#
|
110
|
+
# bigquery = Google::Cloud::Bigquery.new
|
111
|
+
# dataset = bigquery.dataset "my_dataset"
|
112
|
+
#
|
113
|
+
# all_names = dataset.models.all.map do |model|
|
114
|
+
# model.model_id
|
115
|
+
# end
|
116
|
+
#
|
117
|
+
# @example Limit the number of API requests made:
|
118
|
+
# require "google/cloud/bigquery"
|
119
|
+
#
|
120
|
+
# bigquery = Google::Cloud::Bigquery.new
|
121
|
+
# dataset = bigquery.dataset "my_dataset"
|
122
|
+
#
|
123
|
+
# dataset.models.all(request_limit: 10) do |model|
|
124
|
+
# puts model.model_id
|
125
|
+
# end
|
126
|
+
#
|
127
|
+
def all request_limit: nil
|
128
|
+
request_limit = request_limit.to_i if request_limit
|
129
|
+
unless block_given?
|
130
|
+
return enum_for :all, request_limit: request_limit
|
131
|
+
end
|
132
|
+
results = self
|
133
|
+
loop do
|
134
|
+
results.each { |r| yield r }
|
135
|
+
if request_limit
|
136
|
+
request_limit -= 1
|
137
|
+
break if request_limit < 0
|
138
|
+
end
|
139
|
+
break unless results.next?
|
140
|
+
results = results.next
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
##
|
145
|
+
# @private New Model::List from a response object.
|
146
|
+
def self.from_gapi gapi_list, service, dataset_id = nil, max = nil
|
147
|
+
models = List.new(Array(gapi_list[:models]).map do |gapi_json|
|
148
|
+
Model.from_gapi_json gapi_json, service
|
149
|
+
end)
|
150
|
+
models.instance_variable_set :@token, gapi_list[:nextPageToken]
|
151
|
+
models.instance_variable_set :@service, service
|
152
|
+
models.instance_variable_set :@dataset_id, dataset_id
|
153
|
+
models.instance_variable_set :@max, max
|
154
|
+
models
|
155
|
+
end
|
156
|
+
|
157
|
+
protected
|
158
|
+
|
159
|
+
##
|
160
|
+
# Raise an error unless an active service is available.
|
161
|
+
def ensure_service!
|
162
|
+
raise "Must have active connection" unless @service
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
@@ -238,6 +238,60 @@ module Google
|
|
238
238
|
end
|
239
239
|
end
|
240
240
|
|
241
|
+
##
|
242
|
+
# Lists all models in the specified dataset.
|
243
|
+
# Requires the READER dataset role.
|
244
|
+
def list_models dataset_id, max: nil, token: nil
|
245
|
+
options = { skip_deserialization: true }
|
246
|
+
# The list operation is considered idempotent
|
247
|
+
execute backoff: true do
|
248
|
+
json_txt = service.list_models @project, dataset_id,
|
249
|
+
max_results: max,
|
250
|
+
page_token: token,
|
251
|
+
options: options
|
252
|
+
JSON.parse json_txt, symbolize_names: true
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
# Gets the specified model resource by model ID.
|
257
|
+
# This method does not return the data in the model,
|
258
|
+
# it only returns the model resource,
|
259
|
+
# which describes the structure of this model.
|
260
|
+
def get_model dataset_id, model_id
|
261
|
+
# The get operation is considered idempotent
|
262
|
+
execute backoff: true do
|
263
|
+
json_txt = service.get_model @project, dataset_id, model_id,
|
264
|
+
options: { skip_deserialization: true }
|
265
|
+
JSON.parse json_txt, symbolize_names: true
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
269
|
+
##
|
270
|
+
# Updates information in an existing model, replacing fields that
|
271
|
+
# are provided in the submitted model resource.
|
272
|
+
def patch_model dataset_id, model_id, patched_model_gapi, etag = nil
|
273
|
+
patch_with_backoff = false
|
274
|
+
options = { skip_deserialization: true }
|
275
|
+
if etag
|
276
|
+
options[:header] = { "If-Match" => etag }
|
277
|
+
# The patch with etag operation is considered idempotent
|
278
|
+
patch_with_backoff = true
|
279
|
+
end
|
280
|
+
execute backoff: patch_with_backoff do
|
281
|
+
json_txt = service.patch_model @project, dataset_id, model_id,
|
282
|
+
patched_model_gapi,
|
283
|
+
options: options
|
284
|
+
JSON.parse json_txt, symbolize_names: true
|
285
|
+
end
|
286
|
+
end
|
287
|
+
|
288
|
+
##
|
289
|
+
# Deletes the model specified by modelId from the dataset.
|
290
|
+
# If the model contains data, all the data will be deleted.
|
291
|
+
def delete_model dataset_id, model_id
|
292
|
+
execute { service.delete_model @project, dataset_id, model_id }
|
293
|
+
end
|
294
|
+
|
241
295
|
##
|
242
296
|
# Lists all jobs in the specified project to which you have
|
243
297
|
# been granted the READER job role.
|
@@ -0,0 +1,291 @@
|
|
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
|
+
module Google
|
17
|
+
module Cloud
|
18
|
+
module Bigquery
|
19
|
+
##
|
20
|
+
# BigQuery standard SQL is compliant with the SQL 2011 standard and has
|
21
|
+
# extensions that support querying nested and repeated data.
|
22
|
+
module StandardSql
|
23
|
+
##
|
24
|
+
# A field or a column.
|
25
|
+
class Field
|
26
|
+
##
|
27
|
+
# @private Create an empty StandardSql::Field object.
|
28
|
+
def initialize
|
29
|
+
@gapi_json = nil
|
30
|
+
end
|
31
|
+
|
32
|
+
##
|
33
|
+
# The name of the field. (Can be absent for struct fields.)
|
34
|
+
#
|
35
|
+
# @return [String, nil]
|
36
|
+
#
|
37
|
+
def name
|
38
|
+
return nil if @gapi_json[:name] == "".freeze
|
39
|
+
|
40
|
+
@gapi_json[:name]
|
41
|
+
end
|
42
|
+
|
43
|
+
##
|
44
|
+
# The type of the field.
|
45
|
+
#
|
46
|
+
# @return [DataType]
|
47
|
+
#
|
48
|
+
def type
|
49
|
+
DataType.from_gapi_json @gapi_json[:type]
|
50
|
+
end
|
51
|
+
|
52
|
+
##
|
53
|
+
# @private New StandardSql::Field from a JSON object.
|
54
|
+
def self.from_gapi_json gapi_json
|
55
|
+
new.tap do |f|
|
56
|
+
f.instance_variable_set :@gapi_json, gapi_json
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
##
|
62
|
+
# The type of a field or a column.
|
63
|
+
class DataType
|
64
|
+
##
|
65
|
+
# @private Create an empty StandardSql::DataType object.
|
66
|
+
def initialize
|
67
|
+
@gapi_json = nil
|
68
|
+
end
|
69
|
+
|
70
|
+
##
|
71
|
+
# The top level type of this field.
|
72
|
+
#
|
73
|
+
# Can be any standard SQL data type (e.g., "INT64", "DATE", "ARRAY").
|
74
|
+
#
|
75
|
+
# @see https://cloud.google.com/bigquery/docs/reference/standard-sql/data-types
|
76
|
+
# Standard SQL Data Types
|
77
|
+
#
|
78
|
+
# @return [String]
|
79
|
+
#
|
80
|
+
def type_kind
|
81
|
+
@gapi_json[:typeKind]
|
82
|
+
end
|
83
|
+
|
84
|
+
##
|
85
|
+
# The type of a fields when DataType is an Array. (See #array?)
|
86
|
+
#
|
87
|
+
# @return [DataType, nil]
|
88
|
+
#
|
89
|
+
def array_element_type
|
90
|
+
return if @gapi_json[:arrayElementType].nil?
|
91
|
+
|
92
|
+
DataType.from_gapi_json @gapi_json[:arrayElementType]
|
93
|
+
end
|
94
|
+
|
95
|
+
##
|
96
|
+
# The fields of the struct. (See #struct?)
|
97
|
+
#
|
98
|
+
# @return [StructType, nil]
|
99
|
+
#
|
100
|
+
def struct_type
|
101
|
+
return if @gapi_json[:structType].nil?
|
102
|
+
|
103
|
+
StructType.from_gapi_json @gapi_json[:structType]
|
104
|
+
end
|
105
|
+
|
106
|
+
##
|
107
|
+
# Checks if the {#type_kind} of the field is `INT64`.
|
108
|
+
#
|
109
|
+
# @return [Boolean] `true` when `INT64`, `false` otherwise.
|
110
|
+
#
|
111
|
+
# @!group Helpers
|
112
|
+
#
|
113
|
+
def int?
|
114
|
+
type_kind == "INT64".freeze
|
115
|
+
end
|
116
|
+
|
117
|
+
##
|
118
|
+
# Checks if the {#type_kind} of the field is `FLOAT64`.
|
119
|
+
#
|
120
|
+
# @return [Boolean] `true` when `FLOAT64`, `false` otherwise.
|
121
|
+
#
|
122
|
+
# @!group Helpers
|
123
|
+
#
|
124
|
+
def float?
|
125
|
+
type_kind == "FLOAT64".freeze
|
126
|
+
end
|
127
|
+
|
128
|
+
##
|
129
|
+
# Checks if the {#type_kind} of the field is `NUMERIC`.
|
130
|
+
#
|
131
|
+
# @return [Boolean] `true` when `NUMERIC`, `false` otherwise.
|
132
|
+
#
|
133
|
+
# @!group Helpers
|
134
|
+
#
|
135
|
+
def numeric?
|
136
|
+
type_kind == "NUMERIC".freeze
|
137
|
+
end
|
138
|
+
|
139
|
+
##
|
140
|
+
# Checks if the {#type_kind} of the field is `BOOL`.
|
141
|
+
#
|
142
|
+
# @return [Boolean] `true` when `BOOL`, `false` otherwise.
|
143
|
+
#
|
144
|
+
# @!group Helpers
|
145
|
+
#
|
146
|
+
def boolean?
|
147
|
+
type_kind == "BOOL".freeze
|
148
|
+
end
|
149
|
+
|
150
|
+
##
|
151
|
+
# Checks if the {#type_kind} of the field is `STRING`.
|
152
|
+
#
|
153
|
+
# @return [Boolean] `true` when `STRING`, `false` otherwise.
|
154
|
+
#
|
155
|
+
# @!group Helpers
|
156
|
+
#
|
157
|
+
def string?
|
158
|
+
type_kind == "STRING".freeze
|
159
|
+
end
|
160
|
+
|
161
|
+
##
|
162
|
+
# Checks if the {#type_kind} of the field is `BYTES`.
|
163
|
+
#
|
164
|
+
# @return [Boolean] `true` when `BYTES`, `false` otherwise.
|
165
|
+
#
|
166
|
+
# @!group Helpers
|
167
|
+
#
|
168
|
+
def bytes?
|
169
|
+
type_kind == "BYTES".freeze
|
170
|
+
end
|
171
|
+
|
172
|
+
##
|
173
|
+
# Checks if the {#type_kind} of the field is `DATE`.
|
174
|
+
#
|
175
|
+
# @return [Boolean] `true` when `DATE`, `false` otherwise.
|
176
|
+
#
|
177
|
+
# @!group Helpers
|
178
|
+
#
|
179
|
+
def date?
|
180
|
+
type_kind == "DATE".freeze
|
181
|
+
end
|
182
|
+
|
183
|
+
##
|
184
|
+
# Checks if the {#type_kind} of the field is `DATETIME`.
|
185
|
+
#
|
186
|
+
# @return [Boolean] `true` when `DATETIME`, `false` otherwise.
|
187
|
+
#
|
188
|
+
# @!group Helpers
|
189
|
+
#
|
190
|
+
def datetime?
|
191
|
+
type_kind == "DATETIME".freeze
|
192
|
+
end
|
193
|
+
|
194
|
+
##
|
195
|
+
# Checks if the {#type_kind} of the field is `GEOGRAPHY`.
|
196
|
+
#
|
197
|
+
# @return [Boolean] `true` when `GEOGRAPHY`, `false` otherwise.
|
198
|
+
#
|
199
|
+
# @!group Helpers
|
200
|
+
#
|
201
|
+
def geography?
|
202
|
+
type_kind == "GEOGRAPHY".freeze
|
203
|
+
end
|
204
|
+
|
205
|
+
##
|
206
|
+
# Checks if the {#type_kind} of the field is `TIME`.
|
207
|
+
#
|
208
|
+
# @return [Boolean] `true` when `TIME`, `false` otherwise.
|
209
|
+
#
|
210
|
+
# @!group Helpers
|
211
|
+
#
|
212
|
+
def time?
|
213
|
+
type_kind == "TIME".freeze
|
214
|
+
end
|
215
|
+
|
216
|
+
##
|
217
|
+
# Checks if the {#type_kind} of the field is `TIMESTAMP`.
|
218
|
+
#
|
219
|
+
# @return [Boolean] `true` when `TIMESTAMP`, `false` otherwise.
|
220
|
+
#
|
221
|
+
# @!group Helpers
|
222
|
+
#
|
223
|
+
def timestamp?
|
224
|
+
type_kind == "TIMESTAMP".freeze
|
225
|
+
end
|
226
|
+
|
227
|
+
##
|
228
|
+
# Checks if the {#type_kind} of the field is `ARRAY`.
|
229
|
+
#
|
230
|
+
# @return [Boolean] `true` when `ARRAY`, `false` otherwise.
|
231
|
+
#
|
232
|
+
# @!group Helpers
|
233
|
+
#
|
234
|
+
def array?
|
235
|
+
type_kind == "ARRAY".freeze
|
236
|
+
end
|
237
|
+
|
238
|
+
##
|
239
|
+
# Checks if the {#type_kind} of the field is `STRUCT`.
|
240
|
+
#
|
241
|
+
# @return [Boolean] `true` when `STRUCT`, `false` otherwise.
|
242
|
+
#
|
243
|
+
# @!group Helpers
|
244
|
+
#
|
245
|
+
def struct?
|
246
|
+
type_kind == "STRUCT".freeze
|
247
|
+
end
|
248
|
+
|
249
|
+
##
|
250
|
+
# @private New StandardSql::DataType from a JSON object.
|
251
|
+
def self.from_gapi_json gapi_json
|
252
|
+
new.tap do |dt|
|
253
|
+
dt.instance_variable_set :@gapi_json, gapi_json
|
254
|
+
end
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
258
|
+
##
|
259
|
+
# The type of a `STRUCT` field or a column.
|
260
|
+
class StructType
|
261
|
+
##
|
262
|
+
# @private Create an empty StandardSql::DataType object.
|
263
|
+
def initialize
|
264
|
+
@gapi_json = nil
|
265
|
+
end
|
266
|
+
|
267
|
+
##
|
268
|
+
# The top level type of this field.
|
269
|
+
#
|
270
|
+
# Can be any standard SQL data type (e.g., "INT64", "DATE", "ARRAY").
|
271
|
+
#
|
272
|
+
# @return [Array<Field>]
|
273
|
+
#
|
274
|
+
def fields
|
275
|
+
Array(@gapi_json[:fields]).map do |field_gapi_json|
|
276
|
+
Field.from_gapi_json field_gapi_json
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
##
|
281
|
+
# @private New StandardSql::StructType from a JSON object.
|
282
|
+
def self.from_gapi_json gapi_json
|
283
|
+
new.tap do |st|
|
284
|
+
st.instance_variable_set :@gapi_json, gapi_json
|
285
|
+
end
|
286
|
+
end
|
287
|
+
end
|
288
|
+
end
|
289
|
+
end
|
290
|
+
end
|
291
|
+
end
|