gcloud 0.2.0 → 0.3.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 +8 -8
- data/AUTHENTICATION.md +3 -3
- data/CHANGELOG.md +12 -0
- data/OVERVIEW.md +30 -0
- data/lib/gcloud.rb +126 -9
- data/lib/gcloud/bigquery.rb +399 -0
- data/lib/gcloud/bigquery/connection.rb +592 -0
- data/lib/gcloud/bigquery/copy_job.rb +98 -0
- data/lib/gcloud/bigquery/credentials.rb +29 -0
- data/lib/gcloud/bigquery/data.rb +134 -0
- data/lib/gcloud/bigquery/dataset.rb +662 -0
- data/lib/gcloud/bigquery/dataset/list.rb +51 -0
- data/lib/gcloud/bigquery/errors.rb +62 -0
- data/lib/gcloud/bigquery/extract_job.rb +117 -0
- data/lib/gcloud/bigquery/insert_response.rb +80 -0
- data/lib/gcloud/bigquery/job.rb +283 -0
- data/lib/gcloud/bigquery/job/list.rb +55 -0
- data/lib/gcloud/bigquery/load_job.rb +199 -0
- data/lib/gcloud/bigquery/project.rb +512 -0
- data/lib/gcloud/bigquery/query_data.rb +135 -0
- data/lib/gcloud/bigquery/query_job.rb +151 -0
- data/lib/gcloud/bigquery/table.rb +827 -0
- data/lib/gcloud/bigquery/table/list.rb +55 -0
- data/lib/gcloud/bigquery/view.rb +419 -0
- data/lib/gcloud/credentials.rb +3 -3
- data/lib/gcloud/datastore.rb +15 -3
- data/lib/gcloud/datastore/credentials.rb +3 -2
- data/lib/gcloud/datastore/dataset.rb +5 -1
- data/lib/gcloud/datastore/transaction.rb +1 -1
- data/lib/gcloud/pubsub.rb +14 -3
- data/lib/gcloud/pubsub/credentials.rb +4 -4
- data/lib/gcloud/pubsub/project.rb +5 -1
- data/lib/gcloud/pubsub/topic.rb +5 -0
- data/lib/gcloud/storage.rb +14 -24
- data/lib/gcloud/storage/bucket.rb +10 -4
- data/lib/gcloud/storage/credentials.rb +3 -2
- data/lib/gcloud/storage/file.rb +8 -1
- data/lib/gcloud/storage/project.rb +5 -1
- data/lib/gcloud/upload.rb +54 -0
- data/lib/gcloud/version.rb +1 -1
- metadata +78 -2
@@ -0,0 +1,98 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright 2015 Google Inc. All rights reserved.
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
|
16
|
+
module Gcloud
|
17
|
+
module Bigquery
|
18
|
+
##
|
19
|
+
# = CopyJob
|
20
|
+
#
|
21
|
+
# A Job subclass representing a copy operation that may be performed on a
|
22
|
+
# Table. A CopyJob instance is created when you call Table#copy.
|
23
|
+
#
|
24
|
+
# See {Copying an Existing
|
25
|
+
# Table}[https://cloud.google.com/bigquery/docs/tables#copyingtable]
|
26
|
+
# and the {Jobs API
|
27
|
+
# reference}[https://cloud.google.com/bigquery/docs/reference/v2/jobs]
|
28
|
+
# for details.
|
29
|
+
#
|
30
|
+
class CopyJob < Job
|
31
|
+
##
|
32
|
+
# The table from which data is copied. This is the table on
|
33
|
+
# which Table#copy was called. Returns a Table instance.
|
34
|
+
def source
|
35
|
+
table = config["copy"]["sourceTable"]
|
36
|
+
return nil unless table
|
37
|
+
retrieve_table table["projectId"],
|
38
|
+
table["datasetId"],
|
39
|
+
table["tableId"]
|
40
|
+
end
|
41
|
+
|
42
|
+
##
|
43
|
+
# The table to which data is copied. Returns a Table instance.
|
44
|
+
def destination
|
45
|
+
table = config["copy"]["destinationTable"]
|
46
|
+
return nil unless table
|
47
|
+
retrieve_table table["projectId"],
|
48
|
+
table["datasetId"],
|
49
|
+
table["tableId"]
|
50
|
+
end
|
51
|
+
|
52
|
+
##
|
53
|
+
# Checks if the create disposition for the job is +CREATE_IF_NEEDED+,
|
54
|
+
# which provides the following behavior: If the table does not exist,
|
55
|
+
# the copy operation creates the table. This is the default.
|
56
|
+
def create_if_needed?
|
57
|
+
disp = config["copy"]["createDisposition"]
|
58
|
+
disp == "CREATE_IF_NEEDED"
|
59
|
+
end
|
60
|
+
|
61
|
+
##
|
62
|
+
# Checks if the create disposition for the job is +CREATE_NEVER+, which
|
63
|
+
# provides the following behavior: The table must already exist; if it
|
64
|
+
# does not, an error is returned in the job result.
|
65
|
+
def create_never?
|
66
|
+
disp = config["copy"]["createDisposition"]
|
67
|
+
disp == "CREATE_NEVER"
|
68
|
+
end
|
69
|
+
|
70
|
+
##
|
71
|
+
# Checks if the write disposition for the job is +WRITE_TRUNCATE+, which
|
72
|
+
# provides the following behavior: If the table already exists, the copy
|
73
|
+
# operation overwrites the table data.
|
74
|
+
def write_truncate?
|
75
|
+
disp = config["copy"]["writeDisposition"]
|
76
|
+
disp == "WRITE_TRUNCATE"
|
77
|
+
end
|
78
|
+
|
79
|
+
##
|
80
|
+
# Checks if the write disposition for the job is +WRITE_APPEND+, which
|
81
|
+
# provides the following behavior: If the table already exists, the copy
|
82
|
+
# operation appends the data to the table.
|
83
|
+
def write_append?
|
84
|
+
disp = config["copy"]["writeDisposition"]
|
85
|
+
disp == "WRITE_APPEND"
|
86
|
+
end
|
87
|
+
|
88
|
+
##
|
89
|
+
# Checks if the write disposition for the job is +WRITE_EMPTY+, which
|
90
|
+
# provides the following behavior: If the table already exists and
|
91
|
+
# contains data, the job will have an error. This is the default.
|
92
|
+
def write_empty?
|
93
|
+
disp = config["copy"]["writeDisposition"]
|
94
|
+
disp == "WRITE_EMPTY"
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright 2015 Google Inc. All rights reserved.
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
|
16
|
+
require "gcloud/credentials"
|
17
|
+
|
18
|
+
module Gcloud
|
19
|
+
module Bigquery
|
20
|
+
##
|
21
|
+
# Represents the Oauth2 signing logic for Bigquery.
|
22
|
+
class Credentials < Gcloud::Credentials #:nodoc:
|
23
|
+
SCOPE = ["https://www.googleapis.com/auth/bigquery"]
|
24
|
+
PATH_ENV_VARS = %w(BIGQUERY_KEYFILE GCLOUD_KEYFILE GOOGLE_CLOUD_KEYFILE)
|
25
|
+
JSON_ENV_VARS = %w(BIGQUERY_KEYFILE_JSON GCLOUD_KEYFILE_JSON
|
26
|
+
GOOGLE_CLOUD_KEYFILE_JSON)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,134 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright 2015 Google Inc. All rights reserved.
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
|
16
|
+
module Gcloud
|
17
|
+
module Bigquery
|
18
|
+
##
|
19
|
+
# = Data
|
20
|
+
#
|
21
|
+
# Represents Table Data as a list of name/value pairs.
|
22
|
+
# Also contains metadata such as +etag+ and +total+.
|
23
|
+
class Data < DelegateClass(::Array)
|
24
|
+
##
|
25
|
+
# The Table object the data belongs to.
|
26
|
+
attr_accessor :table #:nodoc:
|
27
|
+
|
28
|
+
##
|
29
|
+
# The Google API Client object.
|
30
|
+
attr_accessor :gapi #:nodoc:
|
31
|
+
|
32
|
+
def initialize arr = [] #:nodoc:
|
33
|
+
@table = nil
|
34
|
+
@gapi = {}
|
35
|
+
super arr
|
36
|
+
end
|
37
|
+
|
38
|
+
##
|
39
|
+
# The resource type of the API response.
|
40
|
+
def kind
|
41
|
+
@gapi["kind"]
|
42
|
+
end
|
43
|
+
|
44
|
+
##
|
45
|
+
# A token used for paging results.
|
46
|
+
def token
|
47
|
+
@gapi["pageToken"]
|
48
|
+
end
|
49
|
+
|
50
|
+
# A hash of this page of results.
|
51
|
+
def etag
|
52
|
+
@gapi["etag"]
|
53
|
+
end
|
54
|
+
|
55
|
+
# The total number of rows in the complete table.
|
56
|
+
def total
|
57
|
+
@gapi["totalRows"]
|
58
|
+
end
|
59
|
+
|
60
|
+
##
|
61
|
+
# Is there a next page of data?
|
62
|
+
def next?
|
63
|
+
!token.nil?
|
64
|
+
end
|
65
|
+
|
66
|
+
def next
|
67
|
+
return nil unless next?
|
68
|
+
ensure_table!
|
69
|
+
table.data token: token
|
70
|
+
end
|
71
|
+
|
72
|
+
##
|
73
|
+
# Represents Table Data as a list of positional values (array of arrays).
|
74
|
+
# No type conversion is made, e.g. numbers are formatted as strings.
|
75
|
+
def raw
|
76
|
+
Array(gapi["rows"]).map { |row| row["f"].map { |f| f["v"] } }
|
77
|
+
end
|
78
|
+
|
79
|
+
##
|
80
|
+
# New Data from a response object.
|
81
|
+
def self.from_response resp, table #:nodoc:
|
82
|
+
formatted_rows = format_rows resp.data["rows"], table.fields
|
83
|
+
|
84
|
+
data = new formatted_rows
|
85
|
+
data.table = table
|
86
|
+
data.gapi = resp.data
|
87
|
+
data
|
88
|
+
end
|
89
|
+
|
90
|
+
# rubocop:disable all
|
91
|
+
# Disabled rubocop because this implementation will not last.
|
92
|
+
|
93
|
+
def self.format_rows rows, fields
|
94
|
+
headers = fields.map { |f| f["name"] }
|
95
|
+
field_types = fields.map { |f| f["type"] }
|
96
|
+
|
97
|
+
Array(rows).map do |row|
|
98
|
+
values = row["f"].map { |f| f["v"] }
|
99
|
+
formatted_values = format_values field_types, values
|
100
|
+
Hash[headers.zip formatted_values]
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def self.format_values field_types, values
|
105
|
+
field_types.zip(values).map do |type, value|
|
106
|
+
begin
|
107
|
+
if value.nil?
|
108
|
+
nil
|
109
|
+
elsif type == "INTEGER"
|
110
|
+
Integer value
|
111
|
+
elsif type == "FLOAT"
|
112
|
+
Float value
|
113
|
+
elsif type == "BOOLEAN"
|
114
|
+
(value == "true" ? true : (value == "false" ? false : nil))
|
115
|
+
else
|
116
|
+
value
|
117
|
+
end
|
118
|
+
rescue
|
119
|
+
value
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
# rubocop:enable all
|
124
|
+
|
125
|
+
protected
|
126
|
+
|
127
|
+
##
|
128
|
+
# Raise an error unless an active connection is available.
|
129
|
+
def ensure_table!
|
130
|
+
fail "Must have active connection" unless table
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
@@ -0,0 +1,662 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright 2015 Google Inc. All rights reserved.
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
|
16
|
+
require "json"
|
17
|
+
require "gcloud/bigquery/errors"
|
18
|
+
require "gcloud/bigquery/table"
|
19
|
+
require "gcloud/bigquery/dataset/list"
|
20
|
+
|
21
|
+
module Gcloud
|
22
|
+
module Bigquery
|
23
|
+
##
|
24
|
+
# = Dataset
|
25
|
+
#
|
26
|
+
# Represents a Dataset. A dataset is a grouping mechanism that holds zero or
|
27
|
+
# more tables. Datasets are the lowest level unit of access control; you
|
28
|
+
# cannot control access at the table level. A dataset is contained within a
|
29
|
+
# specific project.
|
30
|
+
#
|
31
|
+
# require "gcloud"
|
32
|
+
#
|
33
|
+
# gcloud = Gcloud.new
|
34
|
+
# bigquery = gcloud.bigquery
|
35
|
+
#
|
36
|
+
# dataset = bigquery.create_dataset "my_dataset",
|
37
|
+
# name: "My Dataset"
|
38
|
+
# description: "This is my Dataset"
|
39
|
+
#
|
40
|
+
class Dataset
|
41
|
+
##
|
42
|
+
# The Connection object.
|
43
|
+
attr_accessor :connection #:nodoc:
|
44
|
+
|
45
|
+
##
|
46
|
+
# The Google API Client object.
|
47
|
+
attr_accessor :gapi #:nodoc:
|
48
|
+
|
49
|
+
##
|
50
|
+
# Create an empty Dataset object.
|
51
|
+
def initialize #:nodoc:
|
52
|
+
@connection = nil
|
53
|
+
@gapi = {}
|
54
|
+
end
|
55
|
+
|
56
|
+
##
|
57
|
+
# A unique ID for this dataset, without the project name.
|
58
|
+
# The ID must contain only letters (a-z, A-Z), numbers (0-9),
|
59
|
+
# or underscores (_). The maximum length is 1,024 characters.
|
60
|
+
#
|
61
|
+
# :category: Attributes
|
62
|
+
#
|
63
|
+
def dataset_id
|
64
|
+
@gapi["datasetReference"]["datasetId"]
|
65
|
+
end
|
66
|
+
|
67
|
+
##
|
68
|
+
# The ID of the project containing this dataset.
|
69
|
+
#
|
70
|
+
# :category: Attributes
|
71
|
+
#
|
72
|
+
def project_id
|
73
|
+
@gapi["datasetReference"]["projectId"]
|
74
|
+
end
|
75
|
+
|
76
|
+
##
|
77
|
+
# A descriptive name for the dataset.
|
78
|
+
#
|
79
|
+
# :category: Attributes
|
80
|
+
#
|
81
|
+
def name
|
82
|
+
@gapi["friendlyName"]
|
83
|
+
end
|
84
|
+
|
85
|
+
##
|
86
|
+
# Updates the descriptive name for the dataset.
|
87
|
+
#
|
88
|
+
# :category: Attributes
|
89
|
+
#
|
90
|
+
def name= new_name
|
91
|
+
patch_gapi! name: new_name
|
92
|
+
end
|
93
|
+
|
94
|
+
##
|
95
|
+
# A string hash of the dataset.
|
96
|
+
#
|
97
|
+
# :category: Attributes
|
98
|
+
#
|
99
|
+
def etag
|
100
|
+
ensure_full_data!
|
101
|
+
@gapi["etag"]
|
102
|
+
end
|
103
|
+
|
104
|
+
##
|
105
|
+
# A URL that can be used to access the dataset using the REST API.
|
106
|
+
#
|
107
|
+
# :category: Attributes
|
108
|
+
#
|
109
|
+
def url
|
110
|
+
ensure_full_data!
|
111
|
+
@gapi["selfLink"]
|
112
|
+
end
|
113
|
+
|
114
|
+
##
|
115
|
+
# A user-friendly description of the dataset.
|
116
|
+
#
|
117
|
+
# :category: Attributes
|
118
|
+
#
|
119
|
+
def description
|
120
|
+
ensure_full_data!
|
121
|
+
@gapi["description"]
|
122
|
+
end
|
123
|
+
|
124
|
+
##
|
125
|
+
# Updates the user-friendly description of the dataset.
|
126
|
+
#
|
127
|
+
# :category: Attributes
|
128
|
+
#
|
129
|
+
def description= new_description
|
130
|
+
patch_gapi! description: new_description
|
131
|
+
end
|
132
|
+
|
133
|
+
##
|
134
|
+
# The default lifetime of all tables in the dataset, in milliseconds.
|
135
|
+
#
|
136
|
+
# :category: Attributes
|
137
|
+
#
|
138
|
+
def default_expiration
|
139
|
+
ensure_full_data!
|
140
|
+
@gapi["defaultTableExpirationMs"]
|
141
|
+
end
|
142
|
+
|
143
|
+
##
|
144
|
+
# Updates the default lifetime of all tables in the dataset, in
|
145
|
+
# milliseconds.
|
146
|
+
#
|
147
|
+
# :category: Attributes
|
148
|
+
#
|
149
|
+
def default_expiration= new_default_expiration
|
150
|
+
patch_gapi! default_expiration: new_default_expiration
|
151
|
+
end
|
152
|
+
|
153
|
+
##
|
154
|
+
# The time when this dataset was created.
|
155
|
+
#
|
156
|
+
# :category: Attributes
|
157
|
+
#
|
158
|
+
def created_at
|
159
|
+
ensure_full_data!
|
160
|
+
Time.at(@gapi["creationTime"] / 1000.0)
|
161
|
+
end
|
162
|
+
|
163
|
+
##
|
164
|
+
# The date when this dataset or any of its tables was last modified.
|
165
|
+
#
|
166
|
+
# :category: Attributes
|
167
|
+
#
|
168
|
+
def modified_at
|
169
|
+
ensure_full_data!
|
170
|
+
Time.at(@gapi["lastModifiedTime"] / 1000.0)
|
171
|
+
end
|
172
|
+
|
173
|
+
##
|
174
|
+
# The geographic location where the dataset should reside. Possible
|
175
|
+
# values include EU and US. The default value is US.
|
176
|
+
#
|
177
|
+
# :category: Attributes
|
178
|
+
#
|
179
|
+
def location
|
180
|
+
ensure_full_data!
|
181
|
+
@gapi["location"]
|
182
|
+
end
|
183
|
+
|
184
|
+
##
|
185
|
+
# Permanently deletes the dataset. The dataset must be empty before it can
|
186
|
+
# be deleted unless the +force+ option is set to +true+.
|
187
|
+
#
|
188
|
+
# === Parameters
|
189
|
+
#
|
190
|
+
# +options+::
|
191
|
+
# An optional Hash for controlling additional behavior. (+Hash+)
|
192
|
+
# <code>options[:force]</code>::
|
193
|
+
# If +true+, delete all the tables in the dataset. If +false+ and the
|
194
|
+
# dataset contains tables, the request will fail. Default is +false+.
|
195
|
+
# (+Boolean+)
|
196
|
+
#
|
197
|
+
# === Returns
|
198
|
+
#
|
199
|
+
# +true+ if the dataset was deleted.
|
200
|
+
#
|
201
|
+
# === Example
|
202
|
+
#
|
203
|
+
# require "gcloud"
|
204
|
+
#
|
205
|
+
# gcloud = Gcloud.new
|
206
|
+
# bigquery = gcloud.bigquery
|
207
|
+
#
|
208
|
+
# dataset = bigquery.dataset "my_dataset"
|
209
|
+
# dataset.delete
|
210
|
+
#
|
211
|
+
# :category: Lifecycle
|
212
|
+
#
|
213
|
+
def delete options = {}
|
214
|
+
ensure_connection!
|
215
|
+
resp = connection.delete_dataset dataset_id, options
|
216
|
+
if resp.success?
|
217
|
+
true
|
218
|
+
else
|
219
|
+
fail ApiError.from_response(resp)
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
##
|
224
|
+
# Creates a new table.
|
225
|
+
#
|
226
|
+
# === Parameters
|
227
|
+
#
|
228
|
+
# +table_id+::
|
229
|
+
# The ID of the table. The ID must contain only letters (a-z, A-Z),
|
230
|
+
# numbers (0-9), or underscores (_). The maximum length is 1,024
|
231
|
+
# characters. (+String+)
|
232
|
+
# +options+::
|
233
|
+
# An optional Hash for controlling additional behavior. (+Hash+)
|
234
|
+
# <code>options[:name]</code>::
|
235
|
+
# A descriptive name for the table. (+String+)
|
236
|
+
# <code>options[:description]</code>::
|
237
|
+
# A user-friendly description of the table. (+String+)
|
238
|
+
# <code>options[:schema]</code>::
|
239
|
+
# A schema specifying fields and data types for the table. See the
|
240
|
+
# {Tables resource
|
241
|
+
# }[https://cloud.google.com/bigquery/docs/reference/v2/tables#resource]
|
242
|
+
# for more information. (+Hash+)
|
243
|
+
#
|
244
|
+
# === Returns
|
245
|
+
#
|
246
|
+
# Gcloud::Bigquery::Table
|
247
|
+
#
|
248
|
+
# === Examples
|
249
|
+
#
|
250
|
+
# require "gcloud"
|
251
|
+
#
|
252
|
+
# gcloud = Gcloud.new
|
253
|
+
# bigquery = gcloud.bigquery
|
254
|
+
# dataset = bigquery.dataset "my_dataset"
|
255
|
+
# table = dataset.create_table "my_table"
|
256
|
+
#
|
257
|
+
# A name and description can be provided:
|
258
|
+
#
|
259
|
+
# require "gcloud"
|
260
|
+
#
|
261
|
+
# gcloud = Gcloud.new
|
262
|
+
# bigquery = gcloud.bigquery
|
263
|
+
# dataset = bigquery.dataset "my_dataset"
|
264
|
+
#
|
265
|
+
# schema = {
|
266
|
+
# "fields" => [
|
267
|
+
# {
|
268
|
+
# "name" => "first_name",
|
269
|
+
# "type" => "STRING",
|
270
|
+
# "mode" => "REQUIRED"
|
271
|
+
# },
|
272
|
+
# {
|
273
|
+
# "name" => "cities_lived",
|
274
|
+
# "type" => "RECORD",
|
275
|
+
# "mode" => "REPEATED",
|
276
|
+
# "fields" => [
|
277
|
+
# {
|
278
|
+
# "name" => "place",
|
279
|
+
# "type" => "STRING",
|
280
|
+
# "mode" => "REQUIRED"
|
281
|
+
# },
|
282
|
+
# {
|
283
|
+
# "name" => "number_of_years",
|
284
|
+
# "type" => "INTEGER",
|
285
|
+
# "mode" => "REQUIRED"
|
286
|
+
# }
|
287
|
+
# ]
|
288
|
+
# }
|
289
|
+
# ]
|
290
|
+
# }
|
291
|
+
# table = dataset.create_table "my_table",
|
292
|
+
# name: "My Table",
|
293
|
+
# schema: schema
|
294
|
+
#
|
295
|
+
# :category: Table
|
296
|
+
#
|
297
|
+
def create_table table_id, options = {}
|
298
|
+
ensure_connection!
|
299
|
+
resp = connection.insert_table dataset_id, table_id, options
|
300
|
+
if resp.success?
|
301
|
+
Table.from_gapi resp.data, connection
|
302
|
+
else
|
303
|
+
fail ApiError.from_response(resp)
|
304
|
+
end
|
305
|
+
end
|
306
|
+
|
307
|
+
##
|
308
|
+
# Creates a new view table from the given query.
|
309
|
+
#
|
310
|
+
# === Parameters
|
311
|
+
#
|
312
|
+
# +table_id+::
|
313
|
+
# The ID of the view table. The ID must contain only letters (a-z, A-Z),
|
314
|
+
# numbers (0-9), or underscores (_). The maximum length is 1,024
|
315
|
+
# characters. (+String+)
|
316
|
+
# +query+::
|
317
|
+
# The query that BigQuery executes when the view is referenced.
|
318
|
+
# (+String+)
|
319
|
+
# +options+::
|
320
|
+
# An optional Hash for controlling additional behavior. (+Hash+)
|
321
|
+
# <code>options[:name]</code>::
|
322
|
+
# A descriptive name for the table. (+String+)
|
323
|
+
# <code>options[:description]</code>::
|
324
|
+
# A user-friendly description of the table. (+String+)
|
325
|
+
#
|
326
|
+
# === Returns
|
327
|
+
#
|
328
|
+
# Gcloud::Bigquery::View
|
329
|
+
#
|
330
|
+
# === Examples
|
331
|
+
#
|
332
|
+
# require "gcloud"
|
333
|
+
#
|
334
|
+
# gcloud = Gcloud.new
|
335
|
+
# bigquery = gcloud.bigquery
|
336
|
+
# dataset = bigquery.dataset "my_dataset"
|
337
|
+
# view = dataset.create_view "my_view",
|
338
|
+
# "SELECT name, age FROM [proj:dataset.users]"
|
339
|
+
#
|
340
|
+
# A name and description can be provided:
|
341
|
+
#
|
342
|
+
# require "gcloud"
|
343
|
+
#
|
344
|
+
# gcloud = Gcloud.new
|
345
|
+
# bigquery = gcloud.bigquery
|
346
|
+
# dataset = bigquery.dataset "my_dataset"
|
347
|
+
# view = dataset.create_view "my_view",
|
348
|
+
# "SELECT name, age FROM [proj:dataset.users]",
|
349
|
+
# name: "My View", description: "This is my view"
|
350
|
+
#
|
351
|
+
# :category: Table
|
352
|
+
#
|
353
|
+
def create_view table_id, query, options = {}
|
354
|
+
options[:query] = query
|
355
|
+
create_table table_id, options
|
356
|
+
end
|
357
|
+
|
358
|
+
##
|
359
|
+
# Retrieves an existing table by ID.
|
360
|
+
#
|
361
|
+
# === Parameters
|
362
|
+
#
|
363
|
+
# +table_id+::
|
364
|
+
# The ID of a table. (+String+)
|
365
|
+
#
|
366
|
+
# === Returns
|
367
|
+
#
|
368
|
+
# Gcloud::Bigquery::Table or Gcloud::Bigquery::View or nil if the table
|
369
|
+
# does not exist
|
370
|
+
#
|
371
|
+
# === Example
|
372
|
+
#
|
373
|
+
# require "gcloud"
|
374
|
+
#
|
375
|
+
# gcloud = Gcloud.new
|
376
|
+
# bigquery = gcloud.bigquery
|
377
|
+
# dataset = bigquery.dataset "my_dataset"
|
378
|
+
# table = dataset.table "my_table"
|
379
|
+
# puts table.name
|
380
|
+
#
|
381
|
+
# :category: Table
|
382
|
+
#
|
383
|
+
def table table_id
|
384
|
+
ensure_connection!
|
385
|
+
resp = connection.get_table dataset_id, table_id
|
386
|
+
if resp.success?
|
387
|
+
Table.from_gapi resp.data, connection
|
388
|
+
else
|
389
|
+
nil
|
390
|
+
end
|
391
|
+
end
|
392
|
+
|
393
|
+
##
|
394
|
+
# Retrieves the list of tables belonging to the dataset.
|
395
|
+
#
|
396
|
+
# === Parameters
|
397
|
+
#
|
398
|
+
# +options+::
|
399
|
+
# An optional Hash for controlling additional behavior. (+Hash+)
|
400
|
+
# <code>options[:token]</code>::
|
401
|
+
# A previously-returned page token representing part of the larger set
|
402
|
+
# of results to view. (+String+)
|
403
|
+
# <code>options[:max]</code>::
|
404
|
+
# Maximum number of tables to return. (+Integer+)
|
405
|
+
#
|
406
|
+
# === Returns
|
407
|
+
#
|
408
|
+
# Array of Gcloud::Bigquery::Table or Gcloud::Bigquery::View
|
409
|
+
# (Gcloud::Bigquery::Table::List)
|
410
|
+
#
|
411
|
+
# === Examples
|
412
|
+
#
|
413
|
+
# require "gcloud"
|
414
|
+
#
|
415
|
+
# gcloud = Gcloud.new
|
416
|
+
# bigquery = gcloud.bigquery
|
417
|
+
# dataset = bigquery.dataset "my_dataset"
|
418
|
+
# tables = dataset.tables
|
419
|
+
# tables.each do |table|
|
420
|
+
# puts table.name
|
421
|
+
# end
|
422
|
+
#
|
423
|
+
# If you have a significant number of tables, you may need to paginate
|
424
|
+
# through them: (See Dataset::List#token)
|
425
|
+
#
|
426
|
+
# require "gcloud"
|
427
|
+
#
|
428
|
+
# gcloud = Gcloud.new
|
429
|
+
# bigquery = gcloud.bigquery
|
430
|
+
# dataset = bigquery.dataset "my_dataset"
|
431
|
+
#
|
432
|
+
# all_tables = []
|
433
|
+
# tmp_tables = dataset.tables
|
434
|
+
# while tmp_tables.any? do
|
435
|
+
# tmp_tables.each do |table|
|
436
|
+
# all_tables << table
|
437
|
+
# end
|
438
|
+
# # break loop if no more tables available
|
439
|
+
# break if tmp_tables.token.nil?
|
440
|
+
# # get the next group of tables
|
441
|
+
# tmp_tables = dataset.tables token: tmp_tables.token
|
442
|
+
# end
|
443
|
+
#
|
444
|
+
# :category: Table
|
445
|
+
#
|
446
|
+
def tables options = {}
|
447
|
+
ensure_connection!
|
448
|
+
resp = connection.list_tables dataset_id, options
|
449
|
+
if resp.success?
|
450
|
+
Table::List.from_resp resp, connection
|
451
|
+
else
|
452
|
+
fail ApiError.from_response(resp)
|
453
|
+
end
|
454
|
+
end
|
455
|
+
|
456
|
+
##
|
457
|
+
# Queries data using the {asynchronous
|
458
|
+
# method}[https://cloud.google.com/bigquery/querying-data].
|
459
|
+
#
|
460
|
+
# Sets the current dataset as the default dataset in the query. Useful for
|
461
|
+
# using unqualified table names.
|
462
|
+
#
|
463
|
+
# === Parameters
|
464
|
+
#
|
465
|
+
# +query+::
|
466
|
+
# A query string, following the BigQuery {query
|
467
|
+
# syntax}[https://cloud.google.com/bigquery/query-reference], of the
|
468
|
+
# query to execute. Example: "SELECT count(f1) FROM
|
469
|
+
# [myProjectId:myDatasetId.myTableId]". (+String+)
|
470
|
+
# <code>options[:priority]</code>::
|
471
|
+
# Specifies a priority for the query. Possible values include
|
472
|
+
# +INTERACTIVE+ and +BATCH+. The default value is +INTERACTIVE+.
|
473
|
+
# (+String+)
|
474
|
+
# <code>options[:cache]</code>::
|
475
|
+
# Whether to look for the result in the query cache. The query cache is
|
476
|
+
# a best-effort cache that will be flushed whenever tables in the query
|
477
|
+
# are modified. The default value is +true+. (+Boolean+)
|
478
|
+
# <code>options[:table]</code>::
|
479
|
+
# The destination table where the query results should be stored. If not
|
480
|
+
# present, a new table will be created to store the results. (+Table+)
|
481
|
+
# <code>options[:create]</code>::
|
482
|
+
# Specifies whether the job is allowed to create new tables. (+String+)
|
483
|
+
#
|
484
|
+
# The following values are supported:
|
485
|
+
# * +needed+ - Create the table if it does not exist.
|
486
|
+
# * +never+ - The table must already exist. A 'notFound' error is
|
487
|
+
# raised if the table does not exist.
|
488
|
+
# <code>options[:write]</code>::
|
489
|
+
# Specifies the action that occurs if the destination table already
|
490
|
+
# exists. (+String+)
|
491
|
+
#
|
492
|
+
# The following values are supported:
|
493
|
+
# * +truncate+ - BigQuery overwrites the table data.
|
494
|
+
# * +append+ - BigQuery appends the data to the table.
|
495
|
+
# * +empty+ - A 'duplicate' error is returned in the job result if the
|
496
|
+
# table exists and contains data.
|
497
|
+
# <code>options[:large_results]</code>::
|
498
|
+
# If +true+, allows the query to produce arbitrarily large result tables
|
499
|
+
# at a slight cost in performance. Requires <code>options[:table]</code>
|
500
|
+
# to be set. (+Boolean+)
|
501
|
+
# <code>options[:flatten]</code>::
|
502
|
+
# Flattens all nested and repeated fields in the query results. The
|
503
|
+
# default value is +true+. <code>options[:large_results]</code> must be
|
504
|
+
# +true+ if this is set to +false+. (+Boolean+)
|
505
|
+
#
|
506
|
+
# === Returns
|
507
|
+
#
|
508
|
+
# Gcloud::Bigquery::QueryJob
|
509
|
+
#
|
510
|
+
# === Example
|
511
|
+
#
|
512
|
+
# require "gcloud"
|
513
|
+
#
|
514
|
+
# gcloud = Gcloud.new
|
515
|
+
# bigquery = gcloud.bigquery
|
516
|
+
#
|
517
|
+
# job = bigquery.query_job "SELECT name FROM my_table"
|
518
|
+
#
|
519
|
+
# loop do
|
520
|
+
# break if job.done?
|
521
|
+
# sleep 1
|
522
|
+
# job.refresh!
|
523
|
+
# end
|
524
|
+
# if !job.failed?
|
525
|
+
# job.query_results.each do |row|
|
526
|
+
# puts row["name"]
|
527
|
+
# end
|
528
|
+
# end
|
529
|
+
#
|
530
|
+
# :category: Data
|
531
|
+
#
|
532
|
+
def query_job query, options = {}
|
533
|
+
options[:dataset] ||= self
|
534
|
+
ensure_connection!
|
535
|
+
resp = connection.query_job query, options
|
536
|
+
if resp.success?
|
537
|
+
Job.from_gapi resp.data, connection
|
538
|
+
else
|
539
|
+
fail ApiError.from_response(resp)
|
540
|
+
end
|
541
|
+
end
|
542
|
+
|
543
|
+
##
|
544
|
+
# Queries data using the {synchronous
|
545
|
+
# method}[https://cloud.google.com/bigquery/querying-data].
|
546
|
+
#
|
547
|
+
# Sets the current dataset as the default dataset in the query. Useful for
|
548
|
+
# using unqualified table names.
|
549
|
+
#
|
550
|
+
# === Parameters
|
551
|
+
#
|
552
|
+
# +query+::
|
553
|
+
# A query string, following the BigQuery {query
|
554
|
+
# syntax}[https://cloud.google.com/bigquery/query-reference], of the
|
555
|
+
# query to execute. Example: "SELECT count(f1) FROM
|
556
|
+
# [myProjectId:myDatasetId.myTableId]". (+String+)
|
557
|
+
# <code>options[:max]</code>::
|
558
|
+
# The maximum number of rows of data to return per page of results.
|
559
|
+
# Setting this flag to a small value such as 1000 and then paging
|
560
|
+
# through results might improve reliability when the query result set is
|
561
|
+
# large. In addition to this limit, responses are also limited to 10 MB.
|
562
|
+
# By default, there is no maximum row count, and only the byte limit
|
563
|
+
# applies. (+Integer+)
|
564
|
+
# <code>options[:timeout]</code>::
|
565
|
+
# How long to wait for the query to complete, in milliseconds, before
|
566
|
+
# the request times out and returns. Note that this is only a timeout
|
567
|
+
# for the request, not the query. If the query takes longer to run than
|
568
|
+
# the timeout value, the call returns without any results and with
|
569
|
+
# QueryData#complete? set to false. The default value is 10000
|
570
|
+
# milliseconds (10 seconds). (+Integer+)
|
571
|
+
# <code>options[:dryrun]</code>::
|
572
|
+
# If set to +true+, BigQuery doesn't run the job. Instead, if the query
|
573
|
+
# is valid, BigQuery returns statistics about the job such as how many
|
574
|
+
# bytes would be processed. If the query is invalid, an error returns.
|
575
|
+
# The default value is +false+. (+Boolean+)
|
576
|
+
# <code>options[:cache]</code>::
|
577
|
+
# Whether to look for the result in the query cache. The query cache is
|
578
|
+
# a best-effort cache that will be flushed whenever tables in the query
|
579
|
+
# are modified. The default value is true. For more information, see
|
580
|
+
# {query caching}[https://developers.google.com/bigquery/querying-data].
|
581
|
+
# (+Boolean+)
|
582
|
+
#
|
583
|
+
# === Returns
|
584
|
+
#
|
585
|
+
# Gcloud::Bigquery::QueryData
|
586
|
+
#
|
587
|
+
# === Example
|
588
|
+
#
|
589
|
+
# require "gcloud"
|
590
|
+
#
|
591
|
+
# gcloud = Gcloud.new
|
592
|
+
# bigquery = gcloud.bigquery
|
593
|
+
#
|
594
|
+
# data = bigquery.query "SELECT name FROM my_table"
|
595
|
+
# data.each do |row|
|
596
|
+
# puts row["name"]
|
597
|
+
# end
|
598
|
+
#
|
599
|
+
# :category: Data
|
600
|
+
#
|
601
|
+
def query query, options = {}
|
602
|
+
options[:dataset] ||= dataset_id
|
603
|
+
options[:project] ||= project_id
|
604
|
+
ensure_connection!
|
605
|
+
resp = connection.query query, options
|
606
|
+
if resp.success?
|
607
|
+
QueryData.from_gapi resp.data, connection
|
608
|
+
else
|
609
|
+
fail ApiError.from_response(resp)
|
610
|
+
end
|
611
|
+
end
|
612
|
+
|
613
|
+
##
|
614
|
+
# New Dataset from a Google API Client object.
|
615
|
+
def self.from_gapi gapi, conn #:nodoc:
|
616
|
+
new.tap do |f|
|
617
|
+
f.gapi = gapi
|
618
|
+
f.connection = conn
|
619
|
+
end
|
620
|
+
end
|
621
|
+
|
622
|
+
protected
|
623
|
+
|
624
|
+
##
|
625
|
+
# Raise an error unless an active connection is available.
|
626
|
+
def ensure_connection!
|
627
|
+
fail "Must have active connection" unless connection
|
628
|
+
end
|
629
|
+
|
630
|
+
def patch_gapi! options = {}
|
631
|
+
ensure_connection!
|
632
|
+
resp = connection.patch_dataset dataset_id, options
|
633
|
+
if resp.success?
|
634
|
+
@gapi = resp.data
|
635
|
+
else
|
636
|
+
fail ApiError.from_response(resp)
|
637
|
+
end
|
638
|
+
end
|
639
|
+
|
640
|
+
##
|
641
|
+
# Load the complete representation of the dataset if it has been
|
642
|
+
# only partially loaded by a request to the API list method.
|
643
|
+
def ensure_full_data!
|
644
|
+
reload_gapi! unless data_complete?
|
645
|
+
end
|
646
|
+
|
647
|
+
def reload_gapi!
|
648
|
+
ensure_connection!
|
649
|
+
resp = connection.get_dataset dataset_id
|
650
|
+
if resp.success?
|
651
|
+
@gapi = resp.data
|
652
|
+
else
|
653
|
+
fail ApiError.from_response(resp)
|
654
|
+
end
|
655
|
+
end
|
656
|
+
|
657
|
+
def data_complete?
|
658
|
+
!@gapi["creationTime"].nil?
|
659
|
+
end
|
660
|
+
end
|
661
|
+
end
|
662
|
+
end
|