Skip to content

Commit f4aecbc

Browse files
committed
feat: Add python client api for JobStatistics.referenced_property_graphs.
1 parent 3701721 commit f4aecbc

File tree

3 files changed

+192
-1
lines changed

3 files changed

+192
-1
lines changed

packages/google-cloud-bigquery/google/cloud/bigquery/job/query.py

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@
5050
from google.cloud.bigquery.table import _EmptyRowIterator
5151
from google.cloud.bigquery.table import RangePartitioning
5252
from google.cloud.bigquery.table import _table_arg_to_table_ref
53-
from google.cloud.bigquery.table import TableReference
53+
from google.cloud.bigquery.table import TableReference, PropertyGraphReference
5454
from google.cloud.bigquery.table import TimePartitioning
5555
from google.cloud.bigquery._tqdm_helpers import wait_for_query
5656

@@ -1332,6 +1332,35 @@ def referenced_tables(self):
13321332

13331333
return tables
13341334

1335+
@property
1336+
def referenced_property_graphs(self):
1337+
"""Return referenced property graphs from job statistics, if present.
1338+
1339+
See:
1340+
https://cloud.google.com/bigquery/docs/reference/rest/v2/Job#JobStatistics2.FIELDS.referenced_property_graphs
1341+
1342+
Returns:
1343+
List[google.cloud.bigquery.table.PropertyGraphReference]:
1344+
mappings describing the property graphs, or an empty list
1345+
if the query has not yet completed.
1346+
"""
1347+
property_graphs = []
1348+
datasets_by_project_name = {}
1349+
1350+
for pg in self._job_statistics().get("referencedPropertyGraphs", ()):
1351+
pg_project = pg["projectId"]
1352+
1353+
ds_id = pg["datasetId"]
1354+
pg_dataset = datasets_by_project_name.get((pg_project, ds_id))
1355+
if pg_dataset is None:
1356+
pg_dataset = DatasetReference(pg_project, ds_id)
1357+
datasets_by_project_name[(pg_project, ds_id)] = pg_dataset
1358+
1359+
pg_name = pg["propertyGraphId"]
1360+
property_graphs.append(PropertyGraphReference(pg_dataset, pg_name))
1361+
1362+
return property_graphs
1363+
13351364
@property
13361365
def undeclared_query_parameters(self):
13371366
"""Return undeclared query parameters from job statistics, if present.

packages/google-cloud-bigquery/google/cloud/bigquery/table.py

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,127 @@ def __repr__(self):
358358
dataset_ref = DatasetReference(self.project, self.dataset_id)
359359
return f"TableReference({dataset_ref!r}, '{self.table_id}')"
360360

361+
class PropertyGraphReference:
362+
"""PropertyGraphReferences are pointers to property graphs.
363+
364+
Args:
365+
dataset_ref: A pointer to the dataset
366+
property_graph_id: The ID of the property graph
367+
"""
368+
369+
_PROPERTY_TO_API_FIELD = {
370+
"dataset_id": "datasetId",
371+
"project": "projectId",
372+
"property_graph_id": "propertyGraphId",
373+
}
374+
375+
def __init__(self, dataset_ref: "DatasetReference", property_graph_id: str):
376+
self._properties = {}
377+
378+
_helpers._set_sub_prop(
379+
self._properties,
380+
self._PROPERTY_TO_API_FIELD["project"],
381+
dataset_ref.project,
382+
)
383+
_helpers._set_sub_prop(
384+
self._properties,
385+
self._PROPERTY_TO_API_FIELD["dataset_id"],
386+
dataset_ref.dataset_id,
387+
)
388+
_helpers._set_sub_prop(
389+
self._properties,
390+
self._PROPERTY_TO_API_FIELD["property_graph_id"],
391+
property_graph_id,
392+
)
393+
394+
@property
395+
def project(self) -> str:
396+
"""str: Project bound to the property graph."""
397+
return _helpers._get_sub_prop(
398+
self._properties, self._PROPERTY_TO_API_FIELD["project"]
399+
)
400+
401+
@property
402+
def dataset_id(self) -> str:
403+
"""str: ID of dataset containing the property graph."""
404+
return _helpers._get_sub_prop(
405+
self._properties, self._PROPERTY_TO_API_FIELD["dataset_id"]
406+
)
407+
408+
@property
409+
def property_graph_id(self) -> str:
410+
"""str: The property graph ID."""
411+
return _helpers._get_sub_prop(
412+
self._properties, self._PROPERTY_TO_API_FIELD["property_graph_id"]
413+
)
414+
415+
@classmethod
416+
def from_string(
417+
cls, property_graph_id: str, default_project: Optional[str] = None
418+
) -> "PropertyGraphReference":
419+
"""Construct a property graph reference from string.
420+
421+
Args:
422+
property_graph_id (str):
423+
A property graph ID in standard SQL format.
424+
default_project (Optional[str]):
425+
The project ID to use when ``property_graph_id`` does not
426+
include a project ID.
427+
428+
Returns:
429+
PropertyGraphReference: Property graph reference parsed from ``property_graph_id``.
430+
"""
431+
from google.cloud.bigquery.dataset import DatasetReference
432+
433+
(
434+
output_project_id,
435+
output_dataset_id,
436+
output_property_graph_id,
437+
) = _helpers._parse_3_part_id(
438+
property_graph_id, default_project=default_project, property_name="property_graph_id"
439+
)
440+
441+
return cls(
442+
DatasetReference(output_project_id, output_dataset_id), output_property_graph_id
443+
)
444+
445+
@classmethod
446+
def from_api_repr(cls, resource: dict) -> "PropertyGraphReference":
447+
"""Factory: construct a property graph reference given its API representation."""
448+
from google.cloud.bigquery.dataset import DatasetReference
449+
450+
project = resource["projectId"]
451+
dataset_id = resource["datasetId"]
452+
property_graph_id = resource["propertyGraphId"]
453+
454+
return cls(DatasetReference(project, dataset_id), property_graph_id)
455+
456+
def to_api_repr(self) -> dict:
457+
"""Construct the API resource representation of this property graph reference."""
458+
return copy.deepcopy(self._properties)
459+
460+
def __str__(self):
461+
return f"{self.project}.{self.dataset_id}.{self.property_graph_id}"
462+
463+
def __repr__(self):
464+
from google.cloud.bigquery.dataset import DatasetReference
465+
466+
dataset_ref = DatasetReference(self.project, self.dataset_id)
467+
return f"PropertyGraphReference({dataset_ref!r}, '{self.property_graph_id}')"
468+
469+
def __eq__(self, other):
470+
if isinstance(other, PropertyGraphReference):
471+
return (
472+
self.project == other.project
473+
and self.dataset_id == other.dataset_id
474+
and self.property_graph_id == other.property_graph_id
475+
)
476+
else:
477+
return NotImplemented
478+
479+
def __hash__(self):
480+
return hash((self.project, self.dataset_id, self.property_graph_id))
481+
361482

362483
class Table(_TableBase):
363484
"""Tables represent a set of rows whose values correspond to a schema.

packages/google-cloud-bigquery/tests/unit/job/test_query.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -680,6 +680,47 @@ def test_referenced_tables(self):
680680
self.assertEqual(remote.dataset_id, "other-dataset")
681681
self.assertEqual(remote.project, "other-project-123")
682682

683+
def test_referenced_property_graphs(self):
684+
from google.cloud.bigquery.table import PropertyGraphReference
685+
686+
ref_pg_resource = [
687+
{"projectId": self.PROJECT, "datasetId": "dataset", "propertyGraphId": "pg1"},
688+
{"projectId": self.PROJECT, "datasetId": "dataset", "propertyGraphId": "pg2"},
689+
{
690+
"projectId": "other-project-123",
691+
"datasetId": "other-dataset",
692+
"propertyGraphId": "other-pg",
693+
},
694+
]
695+
client = _make_client(project=self.PROJECT)
696+
job = self._make_one(self.JOB_ID, self.QUERY, client)
697+
self.assertEqual(job.referenced_property_graphs, [])
698+
699+
statistics = job._properties["statistics"] = {}
700+
self.assertEqual(job.referenced_property_graphs, [])
701+
702+
query_stats = statistics["query"] = {}
703+
self.assertEqual(job.referenced_property_graphs, [])
704+
705+
query_stats["referencedPropertyGraphs"] = ref_pg_resource
706+
707+
pg1, pg2, remote = job.referenced_property_graphs
708+
709+
self.assertIsInstance(pg1, PropertyGraphReference)
710+
self.assertEqual(pg1.property_graph_id, "pg1")
711+
self.assertEqual(pg1.dataset_id, "dataset")
712+
self.assertEqual(pg1.project, self.PROJECT)
713+
714+
self.assertIsInstance(pg2, PropertyGraphReference)
715+
self.assertEqual(pg2.property_graph_id, "pg2")
716+
self.assertEqual(pg2.dataset_id, "dataset")
717+
self.assertEqual(pg2.project, self.PROJECT)
718+
719+
self.assertIsInstance(remote, PropertyGraphReference)
720+
self.assertEqual(remote.property_graph_id, "other-pg")
721+
self.assertEqual(remote.dataset_id, "other-dataset")
722+
self.assertEqual(remote.project, "other-project-123")
723+
683724
def test_timeline(self):
684725
timeline_resource = [
685726
{

0 commit comments

Comments
 (0)