Permalink
Show file tree
Hide file tree
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
feat: add support for materialized views (#408)
Thank you for opening a Pull Request! Before submitting your PR, there are a few things you can do to make sure it goes smoothly: - [x] Make sure to open an issue as a [bug/issue](https://github.com/googleapis/python-bigquery/issues/new/choose) before writing your code! That way we can discuss the change, evaluate designs, and agree on the general idea - [x] Ensure the tests and linter pass - [x] Code coverage does not decrease (if any source code was changed) - [x] Appropriate docs were updated (if necessary) Fixes #407🦕
- Loading branch information
Showing
4 changed files
with
317 additions
and
18 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
# Copyright 2020 Google LLC | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
|
||
|
||
def create_materialized_view(override_values={}): | ||
# [START bigquery_create_materialized_view] | ||
from google.cloud import bigquery | ||
|
||
bigquery_client = bigquery.Client() | ||
|
||
view_id = "my-project.my_dataset.my_materialized_view" | ||
base_table_id = "my-project.my_dataset.my_base_table" | ||
# [END bigquery_create_materialized_view] | ||
# To facilitate testing, we replace values with alternatives | ||
# provided by the testing harness. | ||
view_id = override_values.get("view_id", view_id) | ||
base_table_id = override_values.get("base_table_id", view_id) | ||
# [START bigquery_create_materialized_view] | ||
view = bigquery.Table(view_id) | ||
view.mview_query = f""" | ||
SELECT product_id, SUM(clicks) AS sum_clicks | ||
FROM `{base_table_id}` | ||
GROUP BY 1 | ||
""" | ||
|
||
# Make an API request to create the materialized view. | ||
view = bigquery_client.create_table(view) | ||
print(f"Created {view.table_type}: {str(view.reference)}") | ||
# [END bigquery_create_materialized_view] | ||
return view | ||
|
||
|
||
def update_materialized_view(override_values={}): | ||
# [START bigquery_update_materialized_view] | ||
import datetime | ||
from google.cloud import bigquery | ||
|
||
bigquery_client = bigquery.Client() | ||
|
||
view_id = "my-project.my_dataset.my_materialized_view" | ||
# [END bigquery_update_materialized_view] | ||
# To facilitate testing, we replace values with alternatives | ||
# provided by the testing harness. | ||
view_id = override_values.get("view_id", view_id) | ||
# [START bigquery_update_materialized_view] | ||
view = bigquery.Table(view_id) | ||
view.mview_enable_refresh = True | ||
view.mview_refresh_interval = datetime.timedelta(hours=1) | ||
|
||
# Make an API request to update the materialized view. | ||
view = bigquery_client.update_table( | ||
view, | ||
# Pass in a list of any fields you need to modify. | ||
["mview_enable_refresh", "mview_refresh_interval"], | ||
) | ||
print(f"Updated {view.table_type}: {str(view.reference)}") | ||
# [END bigquery_update_materialized_view] | ||
return view | ||
|
||
|
||
def delete_materialized_view(override_values={}): | ||
# [START bigquery_delete_materialized_view] | ||
from google.cloud import bigquery | ||
|
||
bigquery_client = bigquery.Client() | ||
|
||
view_id = "my-project.my_dataset.my_materialized_view" | ||
# [END bigquery_delete_materialized_view] | ||
# To facilitate testing, we replace values with alternatives | ||
# provided by the testing harness. | ||
view_id = override_values.get("view_id", view_id) | ||
# [START bigquery_delete_materialized_view] | ||
# Make an API request to delete the materialized view. | ||
bigquery_client.delete_table(view_id) | ||
# [END bigquery_delete_materialized_view] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
# Copyright 2020 Google LLC | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
|
||
import datetime | ||
import uuid | ||
|
||
from google.api_core import exceptions | ||
from google.cloud import bigquery | ||
import pytest | ||
|
||
import materialized_view | ||
|
||
|
||
def temp_suffix(): | ||
return str(uuid.uuid4()).replace("-", "_") | ||
|
||
|
||
@pytest.fixture(scope="module") | ||
def bigquery_client(): | ||
bigquery_client = bigquery.Client() | ||
return bigquery_client | ||
|
||
|
||
@pytest.fixture(autouse=True) | ||
def bigquery_client_patch(monkeypatch, bigquery_client): | ||
monkeypatch.setattr(bigquery, "Client", lambda: bigquery_client) | ||
|
||
|
||
@pytest.fixture(scope="module") | ||
def project_id(bigquery_client): | ||
return bigquery_client.project | ||
|
||
|
||
@pytest.fixture(scope="module") | ||
def dataset_id(bigquery_client): | ||
dataset_id = f"mvdataset_{temp_suffix()}" | ||
bigquery_client.create_dataset(dataset_id) | ||
yield dataset_id | ||
bigquery_client.delete_dataset(dataset_id, delete_contents=True) | ||
|
||
|
||
@pytest.fixture(scope="module") | ||
def base_table_id(bigquery_client, project_id, dataset_id): | ||
base_table_id = f"{project_id}.{dataset_id}.base_{temp_suffix()}" | ||
# Schema from materialized views guide: | ||
# https://cloud.google.com/bigquery/docs/materialized-views#create | ||
base_table = bigquery.Table(base_table_id) | ||
base_table.schema = [ | ||
bigquery.SchemaField("product_id", bigquery.SqlTypeNames.INT64), | ||
bigquery.SchemaField("clicks", bigquery.SqlTypeNames.INT64), | ||
] | ||
bigquery_client.create_table(base_table) | ||
yield base_table_id | ||
bigquery_client.delete_table(base_table_id) | ||
|
||
|
||
@pytest.fixture(scope="module") | ||
def view_id(bigquery_client, project_id, dataset_id): | ||
view_id = f"{project_id}.{dataset_id}.mview_{temp_suffix()}" | ||
yield view_id | ||
bigquery_client.delete_table(view_id, not_found_ok=True) | ||
|
||
|
||
def test_materialized_view(capsys, bigquery_client, base_table_id, view_id): | ||
override_values = { | ||
"base_table_id": base_table_id, | ||
"view_id": view_id, | ||
} | ||
view = materialized_view.create_materialized_view(override_values) | ||
assert base_table_id in view.mview_query | ||
out, _ = capsys.readouterr() | ||
assert view_id in out | ||
|
||
view = materialized_view.update_materialized_view(override_values) | ||
assert view.mview_enable_refresh | ||
assert view.mview_refresh_interval == datetime.timedelta(hours=1) | ||
out, _ = capsys.readouterr() | ||
assert view_id in out | ||
|
||
materialized_view.delete_materialized_view(override_values) | ||
with pytest.raises(exceptions.NotFound): | ||
bigquery_client.get_table(view_id) |
Oops, something went wrong.