-
Notifications
You must be signed in to change notification settings - Fork 60
Refactoring table creation in Big Query Pipeline #343
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
c7cdd47
9aa71aa
5ce0002
36237d4
bc75310
17a88b7
5684b9a
63a25aa
cb6b473
e3ca3b1
324b538
820c34c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -17,6 +17,7 @@ | |
| import json | ||
| import logging | ||
| import os | ||
| import re | ||
| import typing as t | ||
| from pprint import pformat | ||
|
|
||
|
|
@@ -130,6 +131,12 @@ def validate_arguments(cls, known_args: argparse.Namespace, pipeline_args: t.Lis | |
| pipeline_options = PipelineOptions(pipeline_args) | ||
| pipeline_options_dict = pipeline_options.get_all_options() | ||
|
|
||
| if known_args.output_table: | ||
| # checking if the output table is in format (<project>.<dataset>.<table>). | ||
| output_table_pattern = r'^[\w-]+\.[\w-]+\.[\w-]+$' | ||
| if not bool(re.match(output_table_pattern, known_args.output_table)): | ||
| raise RuntimeError("output_table is not in correct format (<project>.<dataset_id>.<table_id>). ") | ||
|
|
||
| if known_args.area: | ||
| assert len(known_args.area) == 4, 'Must specify exactly 4 lat/long values for area: N, W, S, E boundaries.' | ||
|
|
||
|
|
@@ -149,10 +156,20 @@ def validate_arguments(cls, known_args: argparse.Namespace, pipeline_args: t.Lis | |
| logger.info('Region validation completed successfully.') | ||
|
|
||
| def __post_init__(self): | ||
| """Initializes Sink by creating a BigQuery table based on user input.""" | ||
| """Initializes BigQuery table based on user input.""" | ||
| self.project, self.dataset_id, self.table_id = self.output_table.split('.') | ||
| self.table = None | ||
|
|
||
| if self.zarr: | ||
| self.xarray_open_dataset_kwargs = self.zarr_kwargs | ||
| with open_dataset(self.first_uri, self.xarray_open_dataset_kwargs, | ||
|
|
||
| def create_bq_table(self, uri: str) -> str: | ||
|
alxmrs marked this conversation as resolved.
|
||
| """Create a big query table for the first uri. After table is created, subsequent uris are returned.""" | ||
| # Skip table creation. | ||
| if self.table: | ||
| return uri | ||
|
|
||
| with open_dataset(uri, self.xarray_open_dataset_kwargs, | ||
| self.disable_grib_schema_normalization, self.tif_metadata_for_datetime, | ||
| is_zarr=self.zarr) as open_ds: | ||
| # Define table from user input | ||
|
|
@@ -170,12 +187,13 @@ def __post_init__(self): | |
| if self.dry_run: | ||
| logger.debug('Created the BigQuery table with schema...') | ||
| logger.debug(f'\n{pformat(table_schema)}') | ||
| return | ||
| return uri | ||
|
|
||
| # Create the table in BigQuery | ||
| try: | ||
| table = bigquery.Table(self.output_table, schema=table_schema) | ||
| self.table = bigquery.Client().create_table(table, exists_ok=True) | ||
| return uri | ||
| except Exception as e: | ||
| logger.error(f'Unable to create table in BigQuery: {e}') | ||
| raise | ||
|
|
@@ -243,6 +261,7 @@ def expand(self, paths): | |
| """Extract rows of variables from data paths into a BigQuery table.""" | ||
| extracted_rows = ( | ||
| paths | ||
| | 'CreateTable' >> beam.Map(self.create_bq_table) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Concern: I am pretty sure this will try to create a BQ table for every element of
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider using something like |
||
| | 'PrepareCoordinates' >> beam.FlatMap(self.prepare_coordinates) | ||
| | beam.Reshuffle() | ||
| | 'ExtractRows' >> beam.FlatMapTuple(self.extract_rows) | ||
|
|
@@ -252,9 +271,9 @@ def expand(self, paths): | |
| ( | ||
| extracted_rows | ||
| | 'WriteToBigQuery' >> WriteToBigQuery( | ||
| project=self.table.project, | ||
| dataset=self.table.dataset_id, | ||
| table=self.table.table_id, | ||
| project=self.project, | ||
| dataset=self.dataset_id, | ||
| table=self.table_id, | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we wanted to create the BQ table in the pipeline, a simpler solution would be to change this step's disposition: https://beam.apache.org/documentation/io/built-in/google-bigquery/#create-disposition
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. On second thought, we may want to keep your step since we create an opinionated schema...
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @alxmrs
I think there is potential in using the cc: @mahrsee1997
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Now that you mention it, I agree that using the create disposition is easiest! We need to make sure that we pass in our computed
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Was surfing web for some other issue & found this. It might be helpful. https://beam.apache.org/releases/pydoc/current/apache_beam.io.gcp.bigquery.html#schemas |
||
| write_disposition=BigQueryDisposition.WRITE_APPEND, | ||
| create_disposition=BigQueryDisposition.CREATE_NEVER) | ||
| ) | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are the
[]s necessary? Can it just be?r'^\w+\.\w+\.\w+$'(Please test my hand-written regex :))There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
project_ids can contain
hypenshttps://cloud.google.com/resource-manager/docs/creating-managing-projects\w+ doesn't match when word is like
my-project.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, that makes sense! Then, would it be
[\w\-]+? IIRC,[]and-together are often used to express a range.