Skip to content
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

feat: Add description, tags, owner fields to all feature view classes #2440

Merged
merged 3 commits into from
Mar 23, 2022
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions protos/feast/core/FeatureView.proto
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ message FeatureView {
FeatureViewMeta meta = 2;
}

// Next available id: 12
// TODO(adchia): refactor common fields from this and ODFV into separate metadata proto
message FeatureViewSpec {
// Name of the feature view. Must be unique. Not updated.
Expand All @@ -50,9 +51,15 @@ message FeatureViewSpec {
// List of features specifications for each feature defined with this feature view.
repeated FeatureSpecV2 features = 4;

// Description of the feature view.
string description = 10;

// User defined metadata
map<string,string> tags = 5;

// Owner of the feature view.
string owner = 11;

// Features in this feature view can only be retrieved from online serving
// younger than ttl. Ttl is measured as the duration of time between
// the feature's event timestamp and when the feature is retrieved
Expand Down
10 changes: 10 additions & 0 deletions protos/feast/core/OnDemandFeatureView.proto
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ message OnDemandFeatureView {
OnDemandFeatureViewMeta meta = 2;
}

// Next available id: 9
message OnDemandFeatureViewSpec {
// Name of the feature view. Must be unique. Not updated.
string name = 1;
Expand All @@ -48,6 +49,15 @@ message OnDemandFeatureViewSpec {
map<string, OnDemandInput> inputs = 4;

UserDefinedFunction user_defined_function = 5;

// Description of the on demand feature view.
string description = 6;

// User defined metadata.
map<string,string> tags = 7;

// Owner of the on demand feature view.
string owner = 8;
}

message OnDemandFeatureViewMeta {
Expand Down
10 changes: 10 additions & 0 deletions protos/feast/core/RequestFeatureView.proto
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ message RequestFeatureView {
RequestFeatureViewSpec spec = 1;
}

// Next available id: 7
message RequestFeatureViewSpec {
// Name of the feature view. Must be unique. Not updated.
string name = 1;
Expand All @@ -38,4 +39,13 @@ message RequestFeatureViewSpec {

// Request data which contains the underlying data schema and list of associated features
DataSource request_data_source = 3;

// Description of the request feature view.
string description = 4;

// User defined metadata.
map<string,string> tags = 5;

// Owner of the request feature view.
string owner = 6;
}
64 changes: 45 additions & 19 deletions sdk/python/feast/base_feature_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,33 +11,61 @@
# 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 warnings
from abc import ABC, abstractmethod
from datetime import datetime
from typing import List, Optional, Type
from typing import Dict, List, Optional, Type

from google.protobuf.json_format import MessageToJson
from proto import Message

from feast.feature import Feature
from feast.feature_view_projection import FeatureViewProjection

warnings.simplefilter("once", DeprecationWarning)


class BaseFeatureView(ABC):
"""A FeatureView defines a logical grouping of features to be served."""

"""
A BaseFeatureView defines a logical group of features.

Attributes:
name: The unique name of the base feature view.
features: The list of features defined as part of this base feature view.
description: A human-readable description.
tags: A dictionary of key-value pairs to store arbitrary metadata.
owner: The owner of the base feature view, typically the email of the primary
maintainer.
projection: The feature view projection to be applied to this base feature view
at retrieval time.
created_timestamp (optional): The time when the base feature view was created.
last_updated_timestamp (optional): The time when the base feature view was last
updated.
"""

name: str
features: List[Feature]
description: str
tags: Dict[str, str]
owner: str
projection: FeatureViewProjection
created_timestamp: Optional[datetime]
last_updated_timestamp: Optional[datetime]

@abstractmethod
def __init__(self, name: str, features: List[Feature]):
def __init__(
self,
name: str,
features: List[Feature],
description: str = "",
tags: Optional[Dict[str, str]] = None,
owner: str = "",
):
self.name = name
self.features = features
self.description = description
self.tags = tags or {}
self.owner = owner
self.projection = FeatureViewProjection.from_definition(self)
self.created_timestamp: Optional[datetime] = None
self.last_updated_timestamp: Optional[datetime] = None
self.created_timestamp = None
self.last_updated_timestamp = None

@property
@abstractmethod
Expand All @@ -55,12 +83,7 @@ def from_proto(cls, feature_view_proto):

@abstractmethod
def __copy__(self):
"""
Generates a deep copy of this feature view

Returns:
A copy of this FeatureView
"""
"""Returns a deep copy of this base feature view."""
pass

def __repr__(self):
Expand Down Expand Up @@ -92,10 +115,13 @@ def __eq__(self, other):
"Comparisons should only involve BaseFeatureView class objects."
)

if self.name != other.name:
return False

if sorted(self.features) != sorted(other.features):
if (
self.name != other.name
or sorted(self.features) != sorted(other.features)
or self.description != other.description
or self.tags != other.tags
or self.owner != other.owner
):
return False

return True
Expand Down
3 changes: 1 addition & 2 deletions sdk/python/feast/entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,7 @@ class Entity:
with their associated features. If not specified, defaults to the name.
description: A human-readable description.
tags: A dictionary of key-value pairs to store arbitrary metadata.
owner: The owner of the feature service, typically the email of the primary
maintainer.
owner: The owner of the entity, typically the email of the primary maintainer.
created_timestamp: The time when the entity was created.
last_updated_timestamp: The time when the entity was last updated.
"""
Expand Down
40 changes: 26 additions & 14 deletions sdk/python/feast/feature_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,28 +50,36 @@

class FeatureView(BaseFeatureView):
"""
A FeatureView defines a logical grouping of serveable features.
A FeatureView defines a logical group of features.

Args:
name: Name of the group of features.
entities: The entities to which this group of features is associated.
Attributes:
name: The unique name of the feature view.
entities: The list of entities with which this group of features is associated.
ttl: The amount of time this group of features lives. A ttl of 0 indicates that
this group of features lives forever. Note that large ttl's or a ttl of 0
can result in extremely computationally intensive queries.
batch_source: The batch source of data where this group of features is stored.
stream_source (optional): The stream source of data where this group of features
is stored.
features (optional): The set of features defined as part of this FeatureView.
felixwang9817 marked this conversation as resolved.
Show resolved Hide resolved
tags (optional): A dictionary of key-value pairs used for organizing
FeatureViews.
features: The list of features defined as part of this feature view.
online: A boolean indicating whether online retrieval is enabled for this feature
view.
description: A human-readable description.
tags: A dictionary of key-value pairs to store arbitrary metadata.
owner: The owner of the feature view, typically the email of the primary
maintainer.
"""

name: str
entities: List[str]
tags: Optional[Dict[str, str]]
ttl: timedelta
online: bool
batch_source: DataSource
stream_source: Optional[DataSource]
features: List[Feature]
online: bool
description: str
tags: Dict[str, str]
owner: str
materialization_intervals: List[Tuple[datetime, datetime]]

@log_exceptions
Expand All @@ -83,8 +91,10 @@ def __init__(
batch_source: DataSource,
stream_source: Optional[DataSource] = None,
features: Optional[List[Feature]] = None,
tags: Optional[Dict[str, str]] = None,
online: bool = True,
description: str = "",
tags: Optional[Dict[str, str]] = None,
owner: str = "",
):
"""
Creates a FeatureView object.
Expand All @@ -106,9 +116,8 @@ def __init__(
f"Entity or Feature name."
)

super().__init__(name, _features)
super().__init__(name, _features, description, tags, owner)
self.entities = entities if entities else [DUMMY_ENTITY_NAME]
self.tags = tags if tags is not None else {}

if isinstance(ttl, Duration):
self.ttl = timedelta(seconds=int(ttl.seconds))
Expand All @@ -123,10 +132,9 @@ def __init__(
else:
self.ttl = ttl

self.online = online
self.batch_source = batch_source
self.stream_source = stream_source

self.online = online
self.materialization_intervals = []

# Note: Python requires redefining hash in child classes that override __eq__
Expand Down Expand Up @@ -312,7 +320,9 @@ def to_proto(self) -> FeatureViewProto:
name=self.name,
entities=self.entities,
features=[feature.to_proto() for feature in self.features],
description=self.description,
tags=self.tags,
owner=self.owner,
ttl=(ttl_duration if ttl_duration is not None else None),
online=self.online,
batch_source=batch_source_proto,
Expand Down Expand Up @@ -349,7 +359,9 @@ def from_proto(cls, feature_view_proto: FeatureViewProto):
)
for feature in feature_view_proto.spec.features
],
description=feature_view_proto.spec.description,
tags=dict(feature_view_proto.spec.tags),
owner=feature_view_proto.spec.owner,
online=feature_view_proto.spec.online,
ttl=(
None
Expand Down
40 changes: 30 additions & 10 deletions sdk/python/feast/on_demand_feature_view.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import copy
import functools
from types import MethodType
from typing import Dict, List, Type, Union
from typing import Dict, List, Optional, Type, Union

import dill
import pandas as pd
Expand Down Expand Up @@ -33,20 +33,31 @@

class OnDemandFeatureView(BaseFeatureView):
"""
[Experimental] An OnDemandFeatureView defines on demand transformations on existing feature view values and request
data.

Args:
name: Name of the group of features.
features: Output schema of transformation with feature names
inputs: The input feature views passed into the transform.
udf: User defined transformation function that takes as input pandas dataframes
[Experimental] An OnDemandFeatureView defines a logical group of features, along with
transformations to be applied on those features and additional request data.

Attributes:
name: The unique name of the on demand feature view.
features: The list of features in the output of the on demand feature view, after
the transformation has been applied.
inputs: The feature views and request data sources passed into the transformation.
udf: The user defined transformation function, which must take pandas dataframes
as inputs.
description: A human-readable description.
tags: A dictionary of key-value pairs to store arbitrary metadata.
owner: The owner of the on demand feature view, typically the email of the primary
maintainer.
"""

# TODO(adchia): remove inputs from proto and declaration
name: str
features: List[Feature]
input_feature_view_projections: Dict[str, FeatureViewProjection]
input_request_data_sources: Dict[str, RequestDataSource]
udf: MethodType
description: str
tags: Dict[str, str]
owner: str

@log_exceptions
def __init__(
Expand All @@ -55,11 +66,14 @@ def __init__(
features: List[Feature],
inputs: Dict[str, Union[FeatureView, FeatureViewProjection, RequestDataSource]],
udf: MethodType,
description: str = "",
tags: Optional[Dict[str, str]] = None,
owner: str = "",
):
"""
Creates an OnDemandFeatureView object.
"""
super().__init__(name, features)
super().__init__(name, features, description, tags, owner)
self.input_feature_view_projections: Dict[str, FeatureViewProjection] = {}
self.input_request_data_sources: Dict[str, RequestDataSource] = {}
for input_ref, odfv_input in inputs.items():
Expand Down Expand Up @@ -138,6 +152,9 @@ def to_proto(self) -> OnDemandFeatureViewProto:
user_defined_function=UserDefinedFunctionProto(
name=self.udf.__name__, body=dill.dumps(self.udf, recurse=True),
),
description=self.description,
tags=self.tags,
owner=self.owner,
)

return OnDemandFeatureViewProto(spec=spec, meta=meta)
Expand Down Expand Up @@ -184,6 +201,9 @@ def from_proto(cls, on_demand_feature_view_proto: OnDemandFeatureViewProto):
udf=dill.loads(
on_demand_feature_view_proto.spec.user_defined_function.body
),
description=on_demand_feature_view_proto.spec.description,
tags=dict(on_demand_feature_view_proto.spec.tags),
owner=on_demand_feature_view_proto.spec.owner,
)

# FeatureViewProjections are not saved in the OnDemandFeatureView proto.
Expand Down
Loading