From 58caabdbf85e318f8f8d00ac3b57f24a31e993aa Mon Sep 17 00:00:00 2001 From: Felix Wang Date: Thu, 17 Feb 2022 16:36:06 -0800 Subject: [PATCH 1/3] Clean up FeatureViewProjection Signed-off-by: Felix Wang --- sdk/python/feast/feature_view_projection.py | 29 ++++++++++++++------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/sdk/python/feast/feature_view_projection.py b/sdk/python/feast/feature_view_projection.py index 97b3b0ab577..3a4b071d174 100644 --- a/sdk/python/feast/feature_view_projection.py +++ b/sdk/python/feast/feature_view_projection.py @@ -1,4 +1,4 @@ -from typing import Dict, List, Optional +from typing import Dict, List from attr import dataclass @@ -10,15 +10,26 @@ @dataclass class FeatureViewProjection: + """ + A feature view projection represents a set of features from a single feature view. + + Attributes: + name: The unique name of the feature view projection. + name_alias: An optional alias for the name. + features: The list of features represented by the feature view projection. + join_key_map: A map to modify join key columns during retrieval of this feature + view projection. + """ + name: str - name_alias: Optional[str] - features: List[Feature] + name_alias: str = "" + features: List[Feature] = [] join_key_map: Dict[str, str] = {} def name_to_use(self): return self.name_alias or self.name - def to_proto(self): + def to_proto(self) -> FeatureViewProjectionProto: feature_reference_proto = FeatureViewProjectionProto( feature_view_name=self.name, feature_view_name_alias=self.name_alias, @@ -31,21 +42,19 @@ def to_proto(self): @staticmethod def from_proto(proto: FeatureViewProjectionProto): - ref = FeatureViewProjection( + feature_view_projection = FeatureViewProjection( name=proto.feature_view_name, name_alias=proto.feature_view_name_alias, features=[], join_key_map=dict(proto.join_key_map), ) for feature_column in proto.feature_columns: - ref.features.append(Feature.from_proto(feature_column)) + feature_view_projection.features.append(Feature.from_proto(feature_column)) - return ref + return feature_view_projection @staticmethod def from_definition(feature_grouping): return FeatureViewProjection( - name=feature_grouping.name, - name_alias=None, - features=feature_grouping.features, + name=feature_grouping.name, features=feature_grouping.features, ) From b74643940bb13c084352e59e367d45cd700e26c5 Mon Sep 17 00:00:00 2001 From: Felix Wang Date: Thu, 17 Feb 2022 12:46:26 -0800 Subject: [PATCH 2/3] Clean up FeatureService Signed-off-by: Felix Wang --- protos/feast/core/FeatureService.proto | 3 + sdk/python/feast/feature_service.py | 82 +++++++++++++++----------- 2 files changed, 52 insertions(+), 33 deletions(-) diff --git a/protos/feast/core/FeatureService.proto b/protos/feast/core/FeatureService.proto index 952b30eb0a7..4aaa0d5f06f 100644 --- a/protos/feast/core/FeatureService.proto +++ b/protos/feast/core/FeatureService.proto @@ -32,6 +32,9 @@ message FeatureServiceSpec { // Description of the feature service. string description = 5; + + // Owner of the feature service. + string owner = 6; } diff --git a/sdk/python/feast/feature_service.py b/sdk/python/feast/feature_service.py index 16815531a3b..28aa0e79561 100644 --- a/sdk/python/feast/feature_service.py +++ b/sdk/python/feast/feature_service.py @@ -19,21 +19,25 @@ class FeatureService: """ - A feature service is a logical grouping of features for retrieval (training or serving). - The features grouped by a feature service may come from any number of feature views. - - Args: - name: Unique name of the feature service. - features: A list of Features that are grouped as part of this FeatureService. - The list may contain Feature Views, Feature Tables, or a subset of either. - tags (optional): A dictionary of key-value pairs used for organizing Feature - Services. + A feature service defines a logical group of features from one or more feature views. + This group of features can be retrieved together during training or serving. + + Attributes: + name: The unique name of the feature service. + feature_view_projections: A list containing feature views and feature view + projections, representing the features in the feature service. + description: A human-readable description. + tags: A dictionary of key-value pairs to store arbitrary metadata. + owner: The owner of the feature service. + created_timestamp: The time when the feature service was created. + last_updated_timestamp: The time when the feature service was last updated. """ _name: str _feature_view_projections: List[FeatureViewProjection] + _description: str _tags: Dict[str, str] - _description: Optional[str] = None + _owner: str _created_timestamp: Optional[datetime] = None _last_updated_timestamp: Optional[datetime] = None @@ -42,8 +46,9 @@ def __init__( self, name: str, features: List[Union[FeatureView, OnDemandFeatureView]], - tags: Optional[Dict[str, str]] = None, - description: Optional[str] = None, + tags: Dict[str, str] = None, + description: str = "", + owner: str = "", ): """ Creates a FeatureService object. @@ -59,12 +64,13 @@ def __init__( self._feature_view_projections.append(feature_grouping.projection) else: raise ValueError( - "The FeatureService {fs_name} has been provided with an invalid type" + f"The feature service {name} has been provided with an invalid type " f'{type(feature_grouping)} as part of the "features" argument.)' ) - self._tags = tags or {} self._description = description + self._tags = tags or {} + self._owner = owner self._created_timestamp = None self._last_updated_timestamp = None @@ -83,7 +89,13 @@ def __eq__(self, other): raise TypeError( "Comparisons should only involve FeatureService class objects." ) - if self.tags != other.tags or self.name != other.name: + + if ( + self.name != other.name + or self.description != other.description + or self.tags != other.tags + or self.owner != other.owner + ): return False if sorted(self.feature_view_projections) != sorted( @@ -111,6 +123,14 @@ def feature_view_projections( ): self._feature_view_projections = feature_view_projections + @property + def description(self) -> str: + return self._description + + @description.setter + def description(self, description: str): + self._description = description + @property def tags(self) -> Dict[str, str]: return self._tags @@ -120,12 +140,12 @@ def tags(self, tags: Dict[str, str]): self._tags = tags @property - def description(self) -> Optional[str]: - return self._description + def owner(self) -> str: + return self._owner - @description.setter - def description(self, description: str): - self._description = description + @owner.setter + def owner(self, owner: str): + self._owner = owner @property def created_timestamp(self) -> Optional[datetime]: @@ -155,11 +175,8 @@ def from_proto(feature_service_proto: FeatureServiceProto): name=feature_service_proto.spec.name, features=[], tags=dict(feature_service_proto.spec.tags), - description=( - feature_service_proto.spec.description - if feature_service_proto.spec.description != "" - else None - ), + description=feature_service_proto.spec.description, + owner=feature_service_proto.spec.owner, ) fs.feature_view_projections.extend( [ @@ -181,7 +198,7 @@ def from_proto(feature_service_proto: FeatureServiceProto): def to_proto(self) -> FeatureServiceProto: """ - Converts a FeatureService to its protobuf representation. + Converts a feature service to its protobuf representation. Returns: A FeatureServiceProto protobuf. @@ -189,21 +206,20 @@ def to_proto(self) -> FeatureServiceProto: meta = FeatureServiceMeta() if self.created_timestamp: meta.created_timestamp.FromDatetime(self.created_timestamp) + if self.last_updated_timestamp: + meta.last_updated_timestamp.FromDatetime(self.last_updated_timestamp) spec = FeatureServiceSpec( name=self.name, features=[ projection.to_proto() for projection in self.feature_view_projections ], + tags=self.tags, + description=self.description, + owner=self.owner, ) - if self.tags: - spec.tags.update(self.tags) - if self.description: - spec.description = self.description - - feature_service_proto = FeatureServiceProto(spec=spec, meta=meta) - return feature_service_proto + return FeatureServiceProto(spec=spec, meta=meta) def validate(self): pass From aeb1a460c4e2a27bd349d8a728c9e824f9c20198 Mon Sep 17 00:00:00 2001 From: Felix Wang Date: Thu, 17 Feb 2022 18:11:53 -0800 Subject: [PATCH 3/3] Continue FeatureService and FeatureViewProjection cleanup Signed-off-by: Felix Wang --- sdk/python/feast/feature_service.py | 19 +++++++++++-------- sdk/python/feast/feature_view_projection.py | 17 ++++++++++------- 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/sdk/python/feast/feature_service.py b/sdk/python/feast/feature_service.py index 28aa0e79561..bb6ec909bf7 100644 --- a/sdk/python/feast/feature_service.py +++ b/sdk/python/feast/feature_service.py @@ -11,8 +11,10 @@ FeatureService as FeatureServiceProto, ) from feast.protos.feast.core.FeatureService_pb2 import ( - FeatureServiceMeta, - FeatureServiceSpec, + FeatureServiceMeta as FeatureServiceMetaProto, +) +from feast.protos.feast.core.FeatureService_pb2 import ( + FeatureServiceSpec as FeatureServiceSpecProto, ) from feast.usage import log_exceptions @@ -28,7 +30,8 @@ class FeatureService: projections, representing the features in the feature service. description: A human-readable description. tags: A dictionary of key-value pairs to store arbitrary metadata. - owner: The owner of the feature service. + owner: The owner of the feature service, typically the email of the primary + maintainer. created_timestamp: The time when the feature service was created. last_updated_timestamp: The time when the feature service was last updated. """ @@ -163,15 +166,15 @@ def last_updated_timestamp(self) -> Optional[datetime]: def last_updated_timestamp(self, last_updated_timestamp: datetime): self._last_updated_timestamp = last_updated_timestamp - @staticmethod - def from_proto(feature_service_proto: FeatureServiceProto): + @classmethod + def from_proto(cls, feature_service_proto: FeatureServiceProto): """ Converts a FeatureServiceProto to a FeatureService object. Args: feature_service_proto: A protobuf representation of a FeatureService. """ - fs = FeatureService( + fs = cls( name=feature_service_proto.spec.name, features=[], tags=dict(feature_service_proto.spec.tags), @@ -203,13 +206,13 @@ def to_proto(self) -> FeatureServiceProto: Returns: A FeatureServiceProto protobuf. """ - meta = FeatureServiceMeta() + meta = FeatureServiceMetaProto() if self.created_timestamp: meta.created_timestamp.FromDatetime(self.created_timestamp) if self.last_updated_timestamp: meta.last_updated_timestamp.FromDatetime(self.last_updated_timestamp) - spec = FeatureServiceSpec( + spec = FeatureServiceSpecProto( name=self.name, features=[ projection.to_proto() for projection in self.feature_view_projections diff --git a/sdk/python/feast/feature_view_projection.py b/sdk/python/feast/feature_view_projection.py index 3a4b071d174..04d923122c5 100644 --- a/sdk/python/feast/feature_view_projection.py +++ b/sdk/python/feast/feature_view_projection.py @@ -1,4 +1,4 @@ -from typing import Dict, List +from typing import Dict, List, Optional from attr import dataclass @@ -11,10 +11,11 @@ @dataclass class FeatureViewProjection: """ - A feature view projection represents a set of features from a single feature view. + A feature view projection represents a selection of one or more features from a + single feature view. Attributes: - name: The unique name of the feature view projection. + name: The unique name of the feature view from which this projection is created. name_alias: An optional alias for the name. features: The list of features represented by the feature view projection. join_key_map: A map to modify join key columns during retrieval of this feature @@ -22,8 +23,8 @@ class FeatureViewProjection: """ name: str - name_alias: str = "" - features: List[Feature] = [] + name_alias: Optional[str] + features: List[Feature] join_key_map: Dict[str, str] = {} def name_to_use(self): @@ -32,7 +33,7 @@ def name_to_use(self): def to_proto(self) -> FeatureViewProjectionProto: feature_reference_proto = FeatureViewProjectionProto( feature_view_name=self.name, - feature_view_name_alias=self.name_alias, + feature_view_name_alias=self.name_alias or "", join_key_map=self.join_key_map, ) for feature in self.features: @@ -56,5 +57,7 @@ def from_proto(proto: FeatureViewProjectionProto): @staticmethod def from_definition(feature_grouping): return FeatureViewProjection( - name=feature_grouping.name, features=feature_grouping.features, + name=feature_grouping.name, + name_alias=None, + features=feature_grouping.features, )