google-cloud-bigquery 0.23.0 → 0.24.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/lib/google/cloud/bigquery.rb +33 -32
- data/lib/google/cloud/bigquery/convert.rb +258 -0
- data/lib/google/cloud/bigquery/data.rb +20 -44
- data/lib/google/cloud/bigquery/dataset.rb +49 -65
- data/lib/google/cloud/bigquery/project.rb +42 -44
- data/lib/google/cloud/bigquery/query_data.rb +9 -8
- data/lib/google/cloud/bigquery/schema.rb +151 -179
- data/lib/google/cloud/bigquery/schema/field.rb +498 -0
- data/lib/google/cloud/bigquery/service.rb +11 -105
- data/lib/google/cloud/bigquery/table.rb +130 -6
- data/lib/google/cloud/bigquery/time.rb +2 -2
- data/lib/google/cloud/bigquery/version.rb +1 -1
- data/lib/google/cloud/bigquery/view.rb +36 -7
- metadata +11 -9
@@ -13,6 +13,8 @@
|
|
13
13
|
# limitations under the License.
|
14
14
|
|
15
15
|
|
16
|
+
require "google/cloud/bigquery/schema/field"
|
17
|
+
|
16
18
|
module Google
|
17
19
|
module Cloud
|
18
20
|
module Bigquery
|
@@ -42,69 +44,35 @@ module Google
|
|
42
44
|
# end
|
43
45
|
#
|
44
46
|
class Schema
|
45
|
-
|
46
|
-
|
47
|
-
end
|
48
|
-
|
47
|
+
##
|
48
|
+
# The fields of the table schema.
|
49
49
|
def fields
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
@fields = @gapi.fields.map { |f| Field.from_gapi f }
|
56
|
-
end
|
57
|
-
|
58
|
-
def empty?
|
59
|
-
fields.empty?
|
60
|
-
end
|
61
|
-
|
62
|
-
# @private
|
63
|
-
def changed?
|
64
|
-
return false if frozen?
|
65
|
-
check_for_mutated_schema!
|
66
|
-
@original_json != @gapi.to_json
|
67
|
-
end
|
68
|
-
|
69
|
-
# @private
|
70
|
-
def freeze
|
71
|
-
@gapi = @gapi.dup.freeze
|
72
|
-
@gapi.fields.freeze
|
73
|
-
@fields = @gapi.fields.map { |f| Field.from_gapi(f).freeze }
|
74
|
-
@fields.freeze
|
75
|
-
super
|
50
|
+
if frozen?
|
51
|
+
Array(@gapi.fields).map { |f| Field.from_gapi(f).freeze }.freeze
|
52
|
+
else
|
53
|
+
Array(@gapi.fields).map { |f| Field.from_gapi f }
|
54
|
+
end
|
76
55
|
end
|
77
56
|
|
78
57
|
##
|
79
|
-
#
|
80
|
-
def
|
81
|
-
|
82
|
-
return if @gapi.frozen?
|
83
|
-
return if @fields.nil?
|
84
|
-
gapi_fields = Array(@fields).map(&:to_gapi)
|
85
|
-
@gapi.update! fields: gapi_fields
|
86
|
-
end
|
87
|
-
|
88
|
-
# @private
|
89
|
-
def self.from_gapi gapi
|
90
|
-
gapi ||= Google::Apis::BigqueryV2::TableSchema.new fields: []
|
91
|
-
gapi.fields ||= []
|
92
|
-
new.tap do |s|
|
93
|
-
s.instance_variable_set :@gapi, gapi
|
94
|
-
s.instance_variable_set :@original_json, gapi.to_json
|
95
|
-
end
|
58
|
+
# The names of the fields as symbols.
|
59
|
+
def headers
|
60
|
+
fields.map(&:name).map(&:to_sym)
|
96
61
|
end
|
97
62
|
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
63
|
+
##
|
64
|
+
# Retreive a fields by name.
|
65
|
+
def field name
|
66
|
+
f = fields.find { |fld| fld.name == name.to_s }
|
67
|
+
return nil if f.nil?
|
68
|
+
yield f if block_given?
|
69
|
+
f
|
102
70
|
end
|
103
71
|
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
72
|
+
##
|
73
|
+
# Whether the schema has no fields defined.
|
74
|
+
def empty?
|
75
|
+
fields.empty?
|
108
76
|
end
|
109
77
|
|
110
78
|
##
|
@@ -119,7 +87,7 @@ module Google
|
|
119
87
|
# `:nullable`, `:required`, and `:repeated`. The default value is
|
120
88
|
# `:nullable`.
|
121
89
|
def string name, description: nil, mode: :nullable
|
122
|
-
add_field name, :string,
|
90
|
+
add_field name, :string, description: description, mode: mode
|
123
91
|
end
|
124
92
|
|
125
93
|
##
|
@@ -134,7 +102,7 @@ module Google
|
|
134
102
|
# `:nullable`, `:required`, and `:repeated`. The default value is
|
135
103
|
# `:nullable`.
|
136
104
|
def integer name, description: nil, mode: :nullable
|
137
|
-
add_field name, :integer,
|
105
|
+
add_field name, :integer, description: description, mode: mode
|
138
106
|
end
|
139
107
|
|
140
108
|
##
|
@@ -149,7 +117,7 @@ module Google
|
|
149
117
|
# `:nullable`, `:required`, and `:repeated`. The default value is
|
150
118
|
# `:nullable`.
|
151
119
|
def float name, description: nil, mode: :nullable
|
152
|
-
add_field name, :float,
|
120
|
+
add_field name, :float, description: description, mode: mode
|
153
121
|
end
|
154
122
|
|
155
123
|
##
|
@@ -164,7 +132,22 @@ module Google
|
|
164
132
|
# `:nullable`, `:required`, and `:repeated`. The default value is
|
165
133
|
# `:nullable`.
|
166
134
|
def boolean name, description: nil, mode: :nullable
|
167
|
-
add_field name, :boolean,
|
135
|
+
add_field name, :boolean, description: description, mode: mode
|
136
|
+
end
|
137
|
+
|
138
|
+
##
|
139
|
+
# Adds a bytes field to the schema.
|
140
|
+
#
|
141
|
+
# @param [String] name The field name. The name must contain only
|
142
|
+
# letters (a-z, A-Z), numbers (0-9), or underscores (_), and must
|
143
|
+
# start with a letter or underscore. The maximum length is 128
|
144
|
+
# characters.
|
145
|
+
# @param [String] description A description of the field.
|
146
|
+
# @param [Symbol] mode The field's mode. The possible values are
|
147
|
+
# `:nullable`, `:required`, and `:repeated`. The default value is
|
148
|
+
# `:nullable`.
|
149
|
+
def bytes name, description: nil, mode: :nullable
|
150
|
+
add_field name, :bytes, description: description, mode: mode
|
168
151
|
end
|
169
152
|
|
170
153
|
##
|
@@ -179,7 +162,52 @@ module Google
|
|
179
162
|
# `:nullable`, `:required`, and `:repeated`. The default value is
|
180
163
|
# `:nullable`.
|
181
164
|
def timestamp name, description: nil, mode: :nullable
|
182
|
-
add_field name, :timestamp,
|
165
|
+
add_field name, :timestamp, description: description, mode: mode
|
166
|
+
end
|
167
|
+
|
168
|
+
##
|
169
|
+
# Adds a time field to the schema.
|
170
|
+
#
|
171
|
+
# @param [String] name The field name. The name must contain only
|
172
|
+
# letters (a-z, A-Z), numbers (0-9), or underscores (_), and must
|
173
|
+
# start with a letter or underscore. The maximum length is 128
|
174
|
+
# characters.
|
175
|
+
# @param [String] description A description of the field.
|
176
|
+
# @param [Symbol] mode The field's mode. The possible values are
|
177
|
+
# `:nullable`, `:required`, and `:repeated`. The default value is
|
178
|
+
# `:nullable`.
|
179
|
+
def time name, description: nil, mode: :nullable
|
180
|
+
add_field name, :time, description: description, mode: mode
|
181
|
+
end
|
182
|
+
|
183
|
+
##
|
184
|
+
# Adds a datetime field to the schema.
|
185
|
+
#
|
186
|
+
# @param [String] name The field name. The name must contain only
|
187
|
+
# letters (a-z, A-Z), numbers (0-9), or underscores (_), and must
|
188
|
+
# start with a letter or underscore. The maximum length is 128
|
189
|
+
# characters.
|
190
|
+
# @param [String] description A description of the field.
|
191
|
+
# @param [Symbol] mode The field's mode. The possible values are
|
192
|
+
# `:nullable`, `:required`, and `:repeated`. The default value is
|
193
|
+
# `:nullable`.
|
194
|
+
def datetime name, description: nil, mode: :nullable
|
195
|
+
add_field name, :datetime, description: description, mode: mode
|
196
|
+
end
|
197
|
+
|
198
|
+
##
|
199
|
+
# Adds a date field to the schema.
|
200
|
+
#
|
201
|
+
# @param [String] name The field name. The name must contain only
|
202
|
+
# letters (a-z, A-Z), numbers (0-9), or underscores (_), and must
|
203
|
+
# start with a letter or underscore. The maximum length is 128
|
204
|
+
# characters.
|
205
|
+
# @param [String] description A description of the field.
|
206
|
+
# @param [Symbol] mode The field's mode. The possible values are
|
207
|
+
# `:nullable`, `:required`, and `:repeated`. The default value is
|
208
|
+
# `:nullable`.
|
209
|
+
def date name, description: nil, mode: :nullable
|
210
|
+
add_field name, :date, description: description, mode: mode
|
183
211
|
end
|
184
212
|
|
185
213
|
##
|
@@ -196,8 +224,8 @@ module Google
|
|
196
224
|
# @param [Symbol] mode The field's mode. The possible values are
|
197
225
|
# `:nullable`, `:required`, and `:repeated`. The default value is
|
198
226
|
# `:nullable`.
|
199
|
-
# @yield [
|
200
|
-
# @yieldparam [
|
227
|
+
# @yield [field] a block for setting the nested record's schema
|
228
|
+
# @yieldparam [Field] field the object accepting the
|
201
229
|
# nested schema
|
202
230
|
#
|
203
231
|
# @example
|
@@ -216,142 +244,86 @@ module Google
|
|
216
244
|
# end
|
217
245
|
#
|
218
246
|
def record name, description: nil, mode: nil
|
219
|
-
|
247
|
+
# TODO: do we need to fail if no block was given?
|
220
248
|
fail ArgumentError, "a block is required" unless block_given?
|
221
|
-
empty_schema = Google::Apis::BigqueryV2::TableSchema.new fields: []
|
222
|
-
nested_schema = self.class.from_gapi(empty_schema).tap do |s|
|
223
|
-
s.instance_variable_set :@nested, true
|
224
|
-
end
|
225
|
-
yield nested_schema
|
226
|
-
add_field name, :record, nested_schema.fields,
|
227
|
-
description: description, mode: mode
|
228
|
-
end
|
229
|
-
|
230
|
-
protected
|
231
249
|
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
fields << Field.new(name, type, description: description,
|
237
|
-
mode: mode, fields: nested_fields)
|
250
|
+
nested_field = add_field name, :record, description: description,
|
251
|
+
mode: mode
|
252
|
+
yield nested_field
|
253
|
+
nested_field
|
238
254
|
end
|
239
255
|
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
TYPES = %w( STRING INTEGER FLOAT BOOLEAN TIMESTAMP RECORD )
|
246
|
-
|
247
|
-
def initialize name, type, description: nil,
|
248
|
-
mode: :nullable, fields: nil
|
249
|
-
@gapi = Google::Apis::BigqueryV2::TableFieldSchema.new
|
250
|
-
@gapi.update! name: name
|
251
|
-
@gapi.update! type: verify_type(type)
|
252
|
-
@gapi.update! description: description if description
|
253
|
-
@gapi.update! mode: verify_mode(mode) if mode
|
254
|
-
if fields
|
255
|
-
@fields = fields
|
256
|
-
check_for_changed_fields!
|
257
|
-
end
|
258
|
-
@original_json = @gapi.to_json
|
259
|
-
end
|
260
|
-
|
261
|
-
def name
|
262
|
-
@gapi.name
|
263
|
-
end
|
264
|
-
|
265
|
-
def name= new_name
|
266
|
-
@gapi.update! name: new_name
|
267
|
-
end
|
268
|
-
|
269
|
-
def type
|
270
|
-
@gapi.type
|
271
|
-
end
|
272
|
-
|
273
|
-
def type= new_type
|
274
|
-
@gapi.update! type: verify_type(new_type)
|
275
|
-
end
|
276
|
-
|
277
|
-
def description
|
278
|
-
@gapi.description
|
279
|
-
end
|
280
|
-
|
281
|
-
def description= new_description
|
282
|
-
@gapi.update! description: new_description
|
283
|
-
end
|
256
|
+
# @private
|
257
|
+
def changed?
|
258
|
+
return false if frozen?
|
259
|
+
@original_json != @gapi.to_json
|
260
|
+
end
|
284
261
|
|
285
|
-
|
286
|
-
|
262
|
+
# @private
|
263
|
+
def self.from_gapi gapi
|
264
|
+
gapi ||= Google::Apis::BigqueryV2::TableSchema.new fields: []
|
265
|
+
gapi.fields ||= []
|
266
|
+
new.tap do |s|
|
267
|
+
s.instance_variable_set :@gapi, gapi
|
268
|
+
s.instance_variable_set :@original_json, gapi.to_json
|
287
269
|
end
|
270
|
+
end
|
288
271
|
|
289
|
-
|
290
|
-
|
291
|
-
|
272
|
+
# @private
|
273
|
+
def to_gapi
|
274
|
+
@gapi
|
275
|
+
end
|
292
276
|
|
293
|
-
|
294
|
-
|
295
|
-
|
277
|
+
# @private
|
278
|
+
def == other
|
279
|
+
return false unless other.is_a? Schema
|
280
|
+
to_gapi.to_json == other.to_gapi.to_json
|
281
|
+
end
|
296
282
|
|
297
|
-
|
298
|
-
@fields = new_fields
|
299
|
-
end
|
283
|
+
protected
|
300
284
|
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
fields.each(&:check_for_changed_fields!)
|
306
|
-
gapi_fields = Array(fields).map(&:to_gapi)
|
307
|
-
gapi_fields = nil if gapi_fields.empty?
|
308
|
-
@gapi.update! fields: gapi_fields
|
309
|
-
end
|
285
|
+
def frozen_check!
|
286
|
+
return unless frozen?
|
287
|
+
fail ArgumentError, "Cannot modify a frozen schema"
|
288
|
+
end
|
310
289
|
|
311
|
-
|
312
|
-
|
313
|
-
@original_json == to_gapi.to_json
|
314
|
-
end
|
290
|
+
def add_field name, type, description: nil, mode: :nullable
|
291
|
+
frozen_check!
|
315
292
|
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
end
|
293
|
+
new_gapi = Google::Apis::BigqueryV2::TableFieldSchema.new(
|
294
|
+
name: String(name),
|
295
|
+
type: verify_type(type),
|
296
|
+
description: description,
|
297
|
+
mode: verify_mode(mode),
|
298
|
+
fields: [])
|
323
299
|
|
324
|
-
#
|
325
|
-
|
326
|
-
|
327
|
-
check_for_changed_fields!
|
328
|
-
@gapi
|
329
|
-
end
|
300
|
+
# Remove any existing field of this name
|
301
|
+
@gapi.fields ||= []
|
302
|
+
@gapi.fields.reject! { |f| f.name == new_gapi.name }
|
330
303
|
|
331
|
-
#
|
332
|
-
|
333
|
-
return false unless other.is_a? Field
|
334
|
-
to_gapi.to_h == other.to_gapi.to_h
|
335
|
-
end
|
304
|
+
# Add to the nested fields
|
305
|
+
@gapi.fields << new_gapi
|
336
306
|
|
337
|
-
|
307
|
+
# return the public API object
|
308
|
+
Field.from_gapi new_gapi
|
309
|
+
end
|
338
310
|
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
end
|
345
|
-
upcase_type
|
311
|
+
def verify_type type
|
312
|
+
type = type.to_s.upcase
|
313
|
+
unless Field::TYPES.include? type
|
314
|
+
fail ArgumentError,
|
315
|
+
"Type '#{type}' not found in #{TYPES.inspect}"
|
346
316
|
end
|
317
|
+
type
|
318
|
+
end
|
347
319
|
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
upcase_mode
|
320
|
+
def verify_mode mode
|
321
|
+
mode = :nullable if mode.nil?
|
322
|
+
mode = mode.to_s.upcase
|
323
|
+
unless Field::MODES.include? mode
|
324
|
+
fail ArgumentError "Unable to determine mode for '#{mode}'"
|
354
325
|
end
|
326
|
+
mode
|
355
327
|
end
|
356
328
|
end
|
357
329
|
end
|
@@ -0,0 +1,498 @@
|
|
1
|
+
# Copyright 2017 Google Inc. All rights reserved.
|
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
|
+
# http://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
|
+
class Schema
|
20
|
+
##
|
21
|
+
# # Schema Field
|
22
|
+
#
|
23
|
+
# The fields of a table schema.
|
24
|
+
#
|
25
|
+
# @see https://cloud.google.com/bigquery/preparing-data-for-bigquery
|
26
|
+
# Preparing Data for BigQuery
|
27
|
+
#
|
28
|
+
# @example
|
29
|
+
# require "google/cloud/bigquery"
|
30
|
+
#
|
31
|
+
# bigquery = Google::Cloud::Bigquery.new
|
32
|
+
# dataset = bigquery.dataset "my_dataset"
|
33
|
+
# table = dataset.table "my_table"
|
34
|
+
#
|
35
|
+
# name_field = table.schema.field "name"
|
36
|
+
# name_field.required? #=> true
|
37
|
+
class Field
|
38
|
+
# @private
|
39
|
+
MODES = %w( NULLABLE REQUIRED REPEATED )
|
40
|
+
|
41
|
+
# @private
|
42
|
+
TYPES = %w( STRING INTEGER FLOAT BOOLEAN BYTES TIMESTAMP TIME DATETIME
|
43
|
+
DATE RECORD )
|
44
|
+
|
45
|
+
##
|
46
|
+
# The name of the field.
|
47
|
+
#
|
48
|
+
def name
|
49
|
+
@gapi.name
|
50
|
+
end
|
51
|
+
|
52
|
+
##
|
53
|
+
# Updates the name of the field.
|
54
|
+
#
|
55
|
+
def name= new_name
|
56
|
+
@gapi.update! name: String(new_name)
|
57
|
+
end
|
58
|
+
|
59
|
+
##
|
60
|
+
# The type of the field.
|
61
|
+
#
|
62
|
+
def type
|
63
|
+
@gapi.type
|
64
|
+
end
|
65
|
+
|
66
|
+
##
|
67
|
+
# Updates the type of the field.
|
68
|
+
#
|
69
|
+
def type= new_type
|
70
|
+
@gapi.update! type: verify_type(new_type)
|
71
|
+
end
|
72
|
+
|
73
|
+
##
|
74
|
+
# Checks if the type of the field is `NULLABLE`.
|
75
|
+
def nullable?
|
76
|
+
mode == "NULLABLE"
|
77
|
+
end
|
78
|
+
|
79
|
+
##
|
80
|
+
# Checks if the type of the field is `REQUIRED`.
|
81
|
+
def required?
|
82
|
+
mode == "REQUIRED"
|
83
|
+
end
|
84
|
+
|
85
|
+
##
|
86
|
+
# Checks if the type of the field is `REPEATED`.
|
87
|
+
def repeated?
|
88
|
+
mode == "REPEATED"
|
89
|
+
end
|
90
|
+
|
91
|
+
##
|
92
|
+
# The description of the field.
|
93
|
+
#
|
94
|
+
def description
|
95
|
+
@gapi.description
|
96
|
+
end
|
97
|
+
|
98
|
+
##
|
99
|
+
# Updates the description of the field.
|
100
|
+
#
|
101
|
+
def description= new_description
|
102
|
+
@gapi.update! description: new_description
|
103
|
+
end
|
104
|
+
|
105
|
+
##
|
106
|
+
# The mode of the field.
|
107
|
+
#
|
108
|
+
def mode
|
109
|
+
@gapi.mode
|
110
|
+
end
|
111
|
+
|
112
|
+
##
|
113
|
+
# Updates the mode of the field.
|
114
|
+
#
|
115
|
+
def mode= new_mode
|
116
|
+
@gapi.update! mode: verify_mode(new_mode)
|
117
|
+
end
|
118
|
+
|
119
|
+
##
|
120
|
+
# Checks if the mode of the field is `STRING`.
|
121
|
+
def string?
|
122
|
+
mode == "STRING"
|
123
|
+
end
|
124
|
+
|
125
|
+
##
|
126
|
+
# Checks if the mode of the field is `INTEGER`.
|
127
|
+
def integer?
|
128
|
+
mode == "INTEGER"
|
129
|
+
end
|
130
|
+
|
131
|
+
##
|
132
|
+
# Checks if the mode of the field is `FLOAT`.
|
133
|
+
def float?
|
134
|
+
mode == "FLOAT"
|
135
|
+
end
|
136
|
+
|
137
|
+
##
|
138
|
+
# Checks if the mode of the field is `BOOLEAN`.
|
139
|
+
def boolean?
|
140
|
+
mode == "BOOLEAN"
|
141
|
+
end
|
142
|
+
|
143
|
+
##
|
144
|
+
# Checks if the mode of the field is `BYTES`.
|
145
|
+
def bytes?
|
146
|
+
mode == "BYTES"
|
147
|
+
end
|
148
|
+
|
149
|
+
##
|
150
|
+
# Checks if the mode of the field is `TIMESTAMP`.
|
151
|
+
def timestamp?
|
152
|
+
mode == "TIMESTAMP"
|
153
|
+
end
|
154
|
+
|
155
|
+
##
|
156
|
+
# Checks if the mode of the field is `TIME`.
|
157
|
+
def time?
|
158
|
+
mode == "TIME"
|
159
|
+
end
|
160
|
+
|
161
|
+
##
|
162
|
+
# Checks if the mode of the field is `DATETIME`.
|
163
|
+
def datetime?
|
164
|
+
mode == "DATETIME"
|
165
|
+
end
|
166
|
+
|
167
|
+
##
|
168
|
+
# Checks if the mode of the field is `DATE`.
|
169
|
+
def date?
|
170
|
+
mode == "DATE"
|
171
|
+
end
|
172
|
+
|
173
|
+
##
|
174
|
+
# Checks if the mode of the field is `RECORD`.
|
175
|
+
def record?
|
176
|
+
mode == "RECORD"
|
177
|
+
end
|
178
|
+
|
179
|
+
##
|
180
|
+
# The nested fields if the type property is set to `RECORD`. Will be
|
181
|
+
# empty otherwise.
|
182
|
+
def fields
|
183
|
+
if frozen?
|
184
|
+
Array(@gapi.fields).map { |f| Field.from_gapi(f).freeze }.freeze
|
185
|
+
else
|
186
|
+
Array(@gapi.fields).map { |f| Field.from_gapi f }
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
##
|
191
|
+
# The names of the nested fields as symbols if the type property is
|
192
|
+
# set to `RECORD`. Will be empty otherwise.
|
193
|
+
def headers
|
194
|
+
fields.map(&:name).map(&:to_sym)
|
195
|
+
end
|
196
|
+
|
197
|
+
##
|
198
|
+
# Retreive a nested fields by name, if the type property is
|
199
|
+
# set to `RECORD`. Will return `nil` otherwise.
|
200
|
+
def field name
|
201
|
+
f = fields.find { |fld| fld.name == name.to_s }
|
202
|
+
return nil if f.nil?
|
203
|
+
yield f if block_given?
|
204
|
+
f
|
205
|
+
end
|
206
|
+
|
207
|
+
##
|
208
|
+
# Adds a string field to the schema.
|
209
|
+
#
|
210
|
+
# This can only be called on fields that are of type `RECORD`.
|
211
|
+
#
|
212
|
+
# @param [String] name The field name. The name must contain only
|
213
|
+
# letters (a-z, A-Z), numbers (0-9), or underscores (_), and must
|
214
|
+
# start with a letter or underscore. The maximum length is 128
|
215
|
+
# characters.
|
216
|
+
# @param [String] description A description of the field.
|
217
|
+
# @param [Symbol] mode The field's mode. The possible values are
|
218
|
+
# `:nullable`, `:required`, and `:repeated`. The default value is
|
219
|
+
# `:nullable`.
|
220
|
+
def string name, description: nil, mode: :nullable
|
221
|
+
record_check!
|
222
|
+
|
223
|
+
add_field name, :string, description: description, mode: mode
|
224
|
+
end
|
225
|
+
|
226
|
+
##
|
227
|
+
# Adds an integer field to the schema.
|
228
|
+
#
|
229
|
+
# This can only be called on fields that are of type `RECORD`.
|
230
|
+
#
|
231
|
+
# @param [String] name The field name. The name must contain only
|
232
|
+
# letters (a-z, A-Z), numbers (0-9), or underscores (_), and must
|
233
|
+
# start with a letter or underscore. The maximum length is 128
|
234
|
+
# characters.
|
235
|
+
# @param [String] description A description of the field.
|
236
|
+
# @param [Symbol] mode The field's mode. The possible values are
|
237
|
+
# `:nullable`, `:required`, and `:repeated`. The default value is
|
238
|
+
# `:nullable`.
|
239
|
+
def integer name, description: nil, mode: :nullable
|
240
|
+
record_check!
|
241
|
+
|
242
|
+
add_field name, :integer, description: description, mode: mode
|
243
|
+
end
|
244
|
+
|
245
|
+
##
|
246
|
+
# Adds a floating-point number field to the schema.
|
247
|
+
#
|
248
|
+
# This can only be called on fields that are of type `RECORD`.
|
249
|
+
#
|
250
|
+
# @param [String] name The field name. The name must contain only
|
251
|
+
# letters (a-z, A-Z), numbers (0-9), or underscores (_), and must
|
252
|
+
# start with a letter or underscore. The maximum length is 128
|
253
|
+
# characters.
|
254
|
+
# @param [String] description A description of the field.
|
255
|
+
# @param [Symbol] mode The field's mode. The possible values are
|
256
|
+
# `:nullable`, `:required`, and `:repeated`. The default value is
|
257
|
+
# `:nullable`.
|
258
|
+
def float name, description: nil, mode: :nullable
|
259
|
+
record_check!
|
260
|
+
|
261
|
+
add_field name, :float, description: description, mode: mode
|
262
|
+
end
|
263
|
+
|
264
|
+
##
|
265
|
+
# Adds a boolean field to the schema.
|
266
|
+
#
|
267
|
+
# This can only be called on fields that are of type `RECORD`.
|
268
|
+
#
|
269
|
+
# @param [String] name The field name. The name must contain only
|
270
|
+
# letters (a-z, A-Z), numbers (0-9), or underscores (_), and must
|
271
|
+
# start with a letter or underscore. The maximum length is 128
|
272
|
+
# characters.
|
273
|
+
# @param [String] description A description of the field.
|
274
|
+
# @param [Symbol] mode The field's mode. The possible values are
|
275
|
+
# `:nullable`, `:required`, and `:repeated`. The default value is
|
276
|
+
# `:nullable`.
|
277
|
+
def boolean name, description: nil, mode: :nullable
|
278
|
+
record_check!
|
279
|
+
|
280
|
+
add_field name, :boolean, description: description, mode: mode
|
281
|
+
end
|
282
|
+
|
283
|
+
##
|
284
|
+
# Adds a bytes field to the schema.
|
285
|
+
#
|
286
|
+
# This can only be called on fields that are of type `RECORD`.
|
287
|
+
#
|
288
|
+
# @param [String] name The field name. The name must contain only
|
289
|
+
# letters (a-z, A-Z), numbers (0-9), or underscores (_), and must
|
290
|
+
# start with a letter or underscore. The maximum length is 128
|
291
|
+
# characters.
|
292
|
+
# @param [String] description A description of the field.
|
293
|
+
# @param [Symbol] mode The field's mode. The possible values are
|
294
|
+
# `:nullable`, `:required`, and `:repeated`. The default value is
|
295
|
+
# `:nullable`.
|
296
|
+
def bytes name, description: nil, mode: :nullable
|
297
|
+
record_check!
|
298
|
+
|
299
|
+
add_field name, :bytes, description: description, mode: mode
|
300
|
+
end
|
301
|
+
|
302
|
+
##
|
303
|
+
# Adds a timestamp field to the schema.
|
304
|
+
#
|
305
|
+
# This can only be called on fields that are of type `RECORD`.
|
306
|
+
#
|
307
|
+
# @param [String] name The field name. The name must contain only
|
308
|
+
# letters (a-z, A-Z), numbers (0-9), or underscores (_), and must
|
309
|
+
# start with a letter or underscore. The maximum length is 128
|
310
|
+
# characters.
|
311
|
+
# @param [String] description A description of the field.
|
312
|
+
# @param [Symbol] mode The field's mode. The possible values are
|
313
|
+
# `:nullable`, `:required`, and `:repeated`. The default value is
|
314
|
+
# `:nullable`.
|
315
|
+
def timestamp name, description: nil, mode: :nullable
|
316
|
+
record_check!
|
317
|
+
|
318
|
+
add_field name, :timestamp, description: description, mode: mode
|
319
|
+
end
|
320
|
+
|
321
|
+
##
|
322
|
+
# Adds a time field to the schema.
|
323
|
+
#
|
324
|
+
# This can only be called on fields that are of type `RECORD`.
|
325
|
+
#
|
326
|
+
# @param [String] name The field name. The name must contain only
|
327
|
+
# letters (a-z, A-Z), numbers (0-9), or underscores (_), and must
|
328
|
+
# start with a letter or underscore. The maximum length is 128
|
329
|
+
# characters.
|
330
|
+
# @param [String] description A description of the field.
|
331
|
+
# @param [Symbol] mode The field's mode. The possible values are
|
332
|
+
# `:nullable`, `:required`, and `:repeated`. The default value is
|
333
|
+
# `:nullable`.
|
334
|
+
def time name, description: nil, mode: :nullable
|
335
|
+
record_check!
|
336
|
+
|
337
|
+
add_field name, :time, description: description, mode: mode
|
338
|
+
end
|
339
|
+
|
340
|
+
##
|
341
|
+
# Adds a datetime field to the schema.
|
342
|
+
#
|
343
|
+
# This can only be called on fields that are of type `RECORD`.
|
344
|
+
#
|
345
|
+
# @param [String] name The field name. The name must contain only
|
346
|
+
# letters (a-z, A-Z), numbers (0-9), or underscores (_), and must
|
347
|
+
# start with a letter or underscore. The maximum length is 128
|
348
|
+
# characters.
|
349
|
+
# @param [String] description A description of the field.
|
350
|
+
# @param [Symbol] mode The field's mode. The possible values are
|
351
|
+
# `:nullable`, `:required`, and `:repeated`. The default value is
|
352
|
+
# `:nullable`.
|
353
|
+
def datetime name, description: nil, mode: :nullable
|
354
|
+
record_check!
|
355
|
+
|
356
|
+
add_field name, :datetime, description: description, mode: mode
|
357
|
+
end
|
358
|
+
|
359
|
+
##
|
360
|
+
# Adds a date field to the schema.
|
361
|
+
#
|
362
|
+
# This can only be called on fields that are of type `RECORD`.
|
363
|
+
#
|
364
|
+
# @param [String] name The field name. The name must contain only
|
365
|
+
# letters (a-z, A-Z), numbers (0-9), or underscores (_), and must
|
366
|
+
# start with a letter or underscore. The maximum length is 128
|
367
|
+
# characters.
|
368
|
+
# @param [String] description A description of the field.
|
369
|
+
# @param [Symbol] mode The field's mode. The possible values are
|
370
|
+
# `:nullable`, `:required`, and `:repeated`. The default value is
|
371
|
+
# `:nullable`.
|
372
|
+
def date name, description: nil, mode: :nullable
|
373
|
+
record_check!
|
374
|
+
|
375
|
+
add_field name, :date, description: description, mode: mode
|
376
|
+
end
|
377
|
+
|
378
|
+
##
|
379
|
+
# Adds a record field to the schema. A block must be passed describing
|
380
|
+
# the nested fields of the record. For more information about nested
|
381
|
+
# and repeated records, see [Preparing Data for BigQuery
|
382
|
+
# ](https://cloud.google.com/bigquery/preparing-data-for-bigquery).
|
383
|
+
#
|
384
|
+
# This can only be called on fields that are of type `RECORD`.
|
385
|
+
#
|
386
|
+
# @param [String] name The field name. The name must contain only
|
387
|
+
# letters (a-z, A-Z), numbers (0-9), or underscores (_), and must
|
388
|
+
# start with a letter or underscore. The maximum length is 128
|
389
|
+
# characters.
|
390
|
+
# @param [String] description A description of the field.
|
391
|
+
# @param [Symbol] mode The field's mode. The possible values are
|
392
|
+
# `:nullable`, `:required`, and `:repeated`. The default value is
|
393
|
+
# `:nullable`.
|
394
|
+
# @yield [nested_schema] a block for setting the nested schema
|
395
|
+
# @yieldparam [Schema] nested_schema the object accepting the
|
396
|
+
# nested schema
|
397
|
+
#
|
398
|
+
# @example
|
399
|
+
# require "google/cloud/bigquery"
|
400
|
+
#
|
401
|
+
# bigquery = Google::Cloud::Bigquery.new
|
402
|
+
# dataset = bigquery.dataset "my_dataset"
|
403
|
+
# table = dataset.create_table "my_table"
|
404
|
+
#
|
405
|
+
# table.schema do |schema|
|
406
|
+
# schema.string "first_name", mode: :required
|
407
|
+
# schema.record "cities_lived", mode: :repeated do |cities_lived|
|
408
|
+
# cities_lived.string "place", mode: :required
|
409
|
+
# cities_lived.integer "number_of_years", mode: :required
|
410
|
+
# end
|
411
|
+
# end
|
412
|
+
#
|
413
|
+
def record name, description: nil, mode: nil
|
414
|
+
record_check!
|
415
|
+
|
416
|
+
# TODO: do we need to fail if no block was given?
|
417
|
+
fail ArgumentError, "a block is required" unless block_given?
|
418
|
+
|
419
|
+
nested_field = add_field name, :record, description: description,
|
420
|
+
mode: mode
|
421
|
+
yield nested_field
|
422
|
+
nested_field
|
423
|
+
end
|
424
|
+
|
425
|
+
# @private
|
426
|
+
def self.from_gapi gapi
|
427
|
+
new.tap do |f|
|
428
|
+
f.instance_variable_set :@gapi, gapi
|
429
|
+
f.instance_variable_set :@original_json, gapi.to_json
|
430
|
+
end
|
431
|
+
end
|
432
|
+
|
433
|
+
# @private
|
434
|
+
def to_gapi
|
435
|
+
@gapi
|
436
|
+
end
|
437
|
+
|
438
|
+
# @private
|
439
|
+
def == other
|
440
|
+
return false unless other.is_a? Field
|
441
|
+
to_gapi.to_h == other.to_gapi.to_h
|
442
|
+
end
|
443
|
+
|
444
|
+
protected
|
445
|
+
|
446
|
+
def frozen_check!
|
447
|
+
return unless frozen?
|
448
|
+
fail ArgumentError, "Cannot modify a frozen field"
|
449
|
+
end
|
450
|
+
|
451
|
+
def record_check!
|
452
|
+
return unless type != "RECORD"
|
453
|
+
fail ArgumentError,
|
454
|
+
"Cannot add fields to a non-RECORD field (#{type})"
|
455
|
+
end
|
456
|
+
|
457
|
+
def add_field name, type, description: nil, mode: :nullable
|
458
|
+
frozen_check!
|
459
|
+
|
460
|
+
new_gapi = Google::Apis::BigqueryV2::TableFieldSchema.new(
|
461
|
+
name: String(name),
|
462
|
+
type: verify_type(type),
|
463
|
+
description: description,
|
464
|
+
mode: verify_mode(mode),
|
465
|
+
fields: [])
|
466
|
+
|
467
|
+
# Remove any existing field of this name
|
468
|
+
@gapi.fields ||= []
|
469
|
+
@gapi.fields.reject! { |f| f.name == new_gapi.name }
|
470
|
+
# Add to the nested fields
|
471
|
+
@gapi.fields << new_gapi
|
472
|
+
|
473
|
+
# return the public API object
|
474
|
+
Field.from_gapi new_gapi
|
475
|
+
end
|
476
|
+
|
477
|
+
def verify_type type
|
478
|
+
type = type.to_s.upcase
|
479
|
+
unless TYPES.include? type
|
480
|
+
fail ArgumentError,
|
481
|
+
"Type '#{type}' not found in #{TYPES.inspect}"
|
482
|
+
end
|
483
|
+
type
|
484
|
+
end
|
485
|
+
|
486
|
+
def verify_mode mode
|
487
|
+
mode = :nullable if mode.nil?
|
488
|
+
mode = mode.to_s.upcase
|
489
|
+
unless MODES.include? mode
|
490
|
+
fail ArgumentError "Unable to determine mode for '#{mode}'"
|
491
|
+
end
|
492
|
+
mode
|
493
|
+
end
|
494
|
+
end
|
495
|
+
end
|
496
|
+
end
|
497
|
+
end
|
498
|
+
end
|