Skip to content

Commit

Permalink
update cred in base; update all paths using service static parsing an…
Browse files Browse the repository at this point in the history
…d forming methods; moved Featurestore.search_features() to Feature.search() and utilize existing class methods; update delete sync wait
  • Loading branch information
morgandu committed Nov 30, 2021
1 parent edc97f6 commit e50e763
Show file tree
Hide file tree
Showing 7 changed files with 210 additions and 222 deletions.
33 changes: 24 additions & 9 deletions google/cloud/aiplatform/_featurestores/entity_type.py
Expand Up @@ -88,6 +88,7 @@ def __init__(
entity_type_name=entity_type_name, featurestore_id=featurestore_id
)

# TODO(b/208269923): Temporary workaround, update when base class supports nested resource
self._resource_noun = f"featurestores/{featurestore_id}/entityTypes"

super().__init__(
Expand All @@ -101,7 +102,14 @@ def __init__(
@property
def featurestore_name(self) -> str:
"""Full qualified resource name of the managed featurestore in which this EntityType is."""
return "/".join(self.resource_name.split("/")[:-2])
entity_type_name_components = featurestore_utils.CompatFeaturestoreServiceClient.parse_entity_type_path(
path=self.resource_name
)
return featurestore_utils.CompatFeaturestoreServiceClient.featurestore_path(
project=entity_type_name_components["project"],
location=entity_type_name_components["location"],
featurestore=entity_type_name_components["featurestore"],
)

def get_featurestore(self) -> _featurestores.Featurestore:
"""Retrieves the managed featurestore in which this EntityType is.
Expand All @@ -120,8 +128,18 @@ def get_feature(self, feature_id: str) -> "_featurestores.Feature":
Returns:
featurestores.Feature - The managed feature resource object.
"""
entity_type_name_components = featurestore_utils.CompatFeaturestoreServiceClient.parse_entity_type_path(
path=self.resource_name
)

return _featurestores.Feature(
feature_name=f"{self.resource_name}/features/{feature_id}"
feature_name=featurestore_utils.CompatFeaturestoreServiceClient.feature_path(
project=entity_type_name_components["project"],
location=entity_type_name_components["location"],
featurestore=entity_type_name_components["featurestore"],
entity_type=entity_type_name_components["entity_type"],
feature=feature_id,
)
)

def update(
Expand Down Expand Up @@ -273,18 +291,14 @@ def list(
List[EntityTypes] - A list of managed entityType resource objects
"""

featurestore_id = featurestore_utils.validate_and_get_featurestore_resource_id(
featurestore_name=featurestore_name
)

return cls._list(
filter=filter,
order_by=order_by,
project=project,
location=location,
credentials=credentials,
parent=utils.full_resource_name(
resource_name=featurestore_id,
resource_name=featurestore_name,
resource_noun="featurestores",
project=project,
location=location,
Expand Down Expand Up @@ -364,5 +378,6 @@ def delete_features(
for feature_id in feature_ids:
feature = self.get_feature(feature_id=feature_id)
feature.delete(sync=sync)
if not sync:
feature.wait()

if not sync:
feature.wait()
161 changes: 159 additions & 2 deletions google/cloud/aiplatform/_featurestores/feature.py
Expand Up @@ -23,6 +23,7 @@
from google.cloud.aiplatform import base
from google.cloud.aiplatform.compat.types import feature as gca_feature
from google.cloud.aiplatform import _featurestores
from google.cloud.aiplatform import initializer
from google.cloud.aiplatform import utils
from google.cloud.aiplatform.utils import featurestore_utils

Expand Down Expand Up @@ -93,6 +94,7 @@ def __init__(
featurestore_id=featurestore_id,
)

# TODO(b/208269923): Temporary workaround, update when base class supports nested resource
self._resource_noun = (
f"featurestores/{featurestore_id}/entityTypes/{entity_type_id}/features"
)
Expand All @@ -108,7 +110,15 @@ def __init__(
@property
def featurestore_name(self) -> str:
"""Full qualified resource name of the managed featurestore in which this Feature is."""
return "/".join(self.resource_name.split("/")[:-4])
feature_path_components = featurestore_utils.CompatFeaturestoreServiceClient.parse_feature_path(
path=self.resource_name
)

return featurestore_utils.CompatFeaturestoreServiceClient.featurestore_path(
project=feature_path_components["project"],
location=feature_path_components["location"],
featurestore=feature_path_components["featurestore"],
)

def get_featurestore(self) -> _featurestores.Featurestore:
"""Retrieves the managed featurestore in which this Feature is.
Expand All @@ -121,7 +131,16 @@ def get_featurestore(self) -> _featurestores.Featurestore:
@property
def entity_type_name(self) -> str:
"""Full qualified resource name of the managed entityType in which this Feature is."""
return "/".join(self.resource_name.split("/")[:-2])
feature_path_components = featurestore_utils.CompatFeaturestoreServiceClient.parse_feature_path(
path=self.resource_name
)

return featurestore_utils.CompatFeaturestoreServiceClient.entity_type_path(
project=feature_path_components["project"],
location=feature_path_components["location"],
featurestore=feature_path_components["featurestore"],
entity_type=feature_path_components["entity_type"],
)

def get_entity_type(self) -> _featurestores.EntityType:
"""Retrieves the managed entityType in which this Feature is.
Expand Down Expand Up @@ -304,3 +323,141 @@ def list(
location=location,
),
)

@classmethod
def search(
cls,
query: Optional[str] = None,
page_size: Optional[int] = None,
project: Optional[str] = None,
location: Optional[str] = None,
credentials: Optional[auth_credentials.Credentials] = None,
) -> List["_featurestores.Feature"]:
"""Searches existing managed Feature resources.
Example Usage:
my_features = aiplatform.Feature.search()
Args:
query (str):
Optional. Query string that is a conjunction of field-restricted
queries and/or field-restricted filters.
Field-restricted queries and filters can be combined
using ``AND`` to form a conjunction.
A field query is in the form FIELD:QUERY. This
implicitly checks if QUERY exists as a substring within
Feature's FIELD. The QUERY and the FIELD are converted
to a sequence of words (i.e. tokens) for comparison.
This is done by:
- Removing leading/trailing whitespace and tokenizing
the search value. Characters that are not one of
alphanumeric ``[a-zA-Z0-9]``, underscore ``_``, or
asterisk ``*`` are treated as delimiters for tokens.
``*`` is treated as a wildcard that matches
characters within a token.
- Ignoring case.
- Prepending an asterisk to the first and appending an
asterisk to the last token in QUERY.
A QUERY must be either a singular token or a phrase. A
phrase is one or multiple words enclosed in double
quotation marks ("). With phrases, the order of the
words is important. Words in the phrase must be matching
in order and consecutively.
Supported FIELDs for field-restricted queries:
- ``feature_id``
- ``description``
- ``entity_type_id``
Examples:
- ``feature_id: foo`` --> Matches a Feature with ID
containing the substring ``foo`` (eg. ``foo``,
``foofeature``, ``barfoo``).
- ``feature_id: foo*feature`` --> Matches a Feature
with ID containing the substring ``foo*feature`` (eg.
``foobarfeature``).
- ``feature_id: foo AND description: bar`` --> Matches
a Feature with ID containing the substring ``foo``
and description containing the substring ``bar``.
Besides field queries, the following exact-match filters
are supported. The exact-match filters do not support
wildcards. Unlike field-restricted queries, exact-match
filters are case-sensitive.
- ``feature_id``: Supports = comparisons.
- ``description``: Supports = comparisons. Multi-token
filters should be enclosed in quotes.
- ``entity_type_id``: Supports = comparisons.
- ``value_type``: Supports = and != comparisons.
- ``labels``: Supports key-value equality as well as
key presence.
- ``featurestore_id``: Supports = comparisons.
Examples:
- ``description = "foo bar"`` --> Any Feature with
description exactly equal to ``foo bar``
- ``value_type = DOUBLE`` --> Features whose type is
DOUBLE.
- ``labels.active = yes AND labels.env = prod`` -->
Features having both (active: yes) and (env: prod)
labels.
- ``labels.env: *`` --> Any Feature which has a label
with ``env`` as the key.
This corresponds to the ``query`` field
on the ``request`` instance; if ``request`` is provided, this
should not be set.
page_size (int):
Optional. The maximum number of Features to return. The
service may return fewer than this value. If
unspecified, at most 100 Features will be
returned. The maximum value is 100; any value
greater than 100 will be coerced to 100.
project (str):
Optional. Project to list features in. If not set, project
set in aiplatform.init will be used.
location (str):
Optional. Location to list features in. If not set, location
set in aiplatform.init will be used.
credentials (auth_credentials.Credentials):
Optional. Custom credentials to use to list features. Overrides
credentials set in aiplatform.init.
Returns:
List[Features] - A list of managed feature resource objects
"""
resource = cls._empty_constructor(
project=project, location=location, credentials=credentials
)

# Fetch credentials once and re-use for all `_empty_constructor()` calls
creds = resource.credentials

search_features_request = {
"location": initializer.global_config.common_location_path(
project=project, location=location
),
"query": query,
}

if page_size:
search_features_request["page_size"] = page_size

resource_list = (
resource.api_client.search_features(request=search_features_request) or []
)

return [
cls._construct_sdk_resource_from_gapic(
gapic_resource, project=project, location=location, credentials=creds
)
for gapic_resource in resource_list
]

0 comments on commit e50e763

Please sign in to comment.