Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Hide FeatureViewProjections from user interface & have FeatureViews carry FVProjections that carries the modified info of the FeatureView #1899

Merged
merged 17 commits into from
Oct 5, 2021
Merged
Show file tree
Hide file tree
Changes from 5 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
16 changes: 12 additions & 4 deletions protos/feast/core/FeatureService.proto
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ option java_outer_classname = "FeatureServiceProto";
option java_package = "feast.proto.core";

import "google/protobuf/timestamp.proto";
import "feast/core/FeatureViewProjection.proto";
import "feast/core/OnDemandFeatureView.proto";
import "feast/core/FeatureTable.proto";
import "feast/core/FeatureView.proto";

message FeatureService {
// User-specified specifications of this feature service.
Expand All @@ -25,13 +27,19 @@ message FeatureServiceSpec {

// List of features that this feature service encapsulates.
// Stored as a list of references to other features views and the features from those views.
repeated FeatureViewProjection features = 3;
repeated string features = 3;

repeated FeatureTable feature_tables = 4;

repeated FeatureView feature_views = 5;

repeated OnDemandFeatureView on_demand_feature_views = 6;

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

// Description of the feature service.
string description = 5;
string description = 8;
}


Expand Down
81 changes: 48 additions & 33 deletions sdk/python/feast/feature_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@

from feast.feature_table import FeatureTable
from feast.feature_view import FeatureView
from feast.feature_view_projection import FeatureViewProjection
from feast.on_demand_feature_view import OnDemandFeatureView
from feast.protos.feast.core.FeatureService_pb2 import (
FeatureService as FeatureServiceProto,
Expand All @@ -30,7 +29,10 @@ class FeatureService:
"""

name: str
features: List[FeatureViewProjection]
features: List[str]
feature_tables: List[FeatureTable]
feature_views: List[FeatureView]
on_demand_feature_views: List[OnDemandFeatureView]
tags: Dict[str, str]
description: Optional[str] = None
created_timestamp: Optional[datetime] = None
Expand All @@ -39,9 +41,7 @@ class FeatureService:
def __init__(
self,
name: str,
features: List[
Union[FeatureTable, FeatureView, OnDemandFeatureView, FeatureViewProjection]
],
features: List[Union[FeatureTable, FeatureView, OnDemandFeatureView]],
tags: Optional[Dict[str, str]] = None,
description: Optional[str] = None,
):
Expand All @@ -53,17 +53,26 @@ def __init__(
"""
self.name = name
self.features = []
for feature in features:
if (
isinstance(feature, FeatureTable)
or isinstance(feature, FeatureView)
or isinstance(feature, OnDemandFeatureView)
):
self.features.append(FeatureViewProjection.from_definition(feature))
elif isinstance(feature, FeatureViewProjection):
self.features.append(feature)
self.feature_tables, self.feature_views, self.on_demand_feature_views = (
[],
[],
[],
)

for feature_grouping in features:
if isinstance(feature_grouping, FeatureTable):
self.feature_tables.append(feature_grouping)
elif isinstance(feature_grouping, FeatureView):
self.feature_views.append(feature_grouping)
elif isinstance(feature_grouping, OnDemandFeatureView):
self.on_demand_feature_views.append(feature_grouping)
else:
raise ValueError(f"Unexpected type: {type(feature)}")
raise ValueError(f"Unexpected type: {type(feature_grouping)}")

self.features.extend(
[f"{feature_grouping.name}:{f.name}" for f in feature_grouping.features]
)

self.tags = tags or {}
self.description = description
self.created_timestamp = None
Expand Down Expand Up @@ -102,10 +111,7 @@ def from_proto(feature_service_proto: FeatureServiceProto):
"""
fs = FeatureService(
name=feature_service_proto.spec.name,
features=[
FeatureViewProjection.from_proto(fp)
for fp in feature_service_proto.spec.features
],
features=[],
tags=dict(feature_service_proto.spec.tags),
description=(
feature_service_proto.spec.description
Expand All @@ -114,6 +120,20 @@ def from_proto(feature_service_proto: FeatureServiceProto):
),
)

fs.features = [feature for feature in feature_service_proto.spec.features]
fs.feature_tables = [
FeatureTable.from_proto(table)
for table in feature_service_proto.spec.feature_tables
]
fs.feature_views = [
FeatureView.from_proto(view)
for view in feature_service_proto.spec.feature_views
]
fs.on_demand_feature_views = [
OnDemandFeatureView.from_proto(view)
for view in feature_service_proto.spec.on_demand_feature_views
]

if feature_service_proto.meta.HasField("created_timestamp"):
fs.created_timestamp = (
feature_service_proto.meta.created_timestamp.ToDatetime()
Expand All @@ -136,20 +156,15 @@ def to_proto(self) -> FeatureServiceProto:
if self.created_timestamp:
meta.created_timestamp.FromDatetime(self.created_timestamp)

spec = FeatureServiceSpec()
spec.name = self.name
for definition in self.features:
if isinstance(definition, FeatureTable) or isinstance(
definition, FeatureView
):
feature_ref = FeatureViewProjection(
definition.name, definition.features
)
else:
feature_ref = definition

spec.features.append(feature_ref.to_proto())

spec = FeatureServiceSpec(
name=self.name,
features=self.features,
feature_tables=[table.to_proto() for table in self.feature_tables],
feature_views=[view.to_proto() for view in self.feature_views],
on_demand_feature_views=[
view.to_proto() for view in self.on_demand_feature_views
],
)
if self.tags:
spec.tags.update(self.tags)
if self.description:
Expand Down
47 changes: 28 additions & 19 deletions sdk/python/feast/feature_store.py
Original file line number Diff line number Diff line change
Expand Up @@ -318,10 +318,7 @@ def _get_features(

_feature_refs: List[str]
if isinstance(_features, FeatureService):
# Get the latest value of the feature service, in case the object passed in has been updated underneath us.
_feature_refs = _get_feature_refs_from_feature_services(
self.get_feature_service(_features.name)
)
_feature_refs = _features.features
else:
_feature_refs = _features
return _feature_refs
Expand Down Expand Up @@ -541,8 +538,7 @@ def get_historical_features(
)

_feature_refs = self._get_features(features, feature_refs)

all_feature_views = self.list_feature_views()
all_feature_views = self._get_feature_views_to_use(features)
all_on_demand_feature_views = self._registry.list_on_demand_feature_views(
project=self.project
)
Expand Down Expand Up @@ -804,8 +800,8 @@ def get_online_features(
>>> online_response_dict = online_response.to_dict()
"""
_feature_refs = self._get_features(features, feature_refs)
all_feature_views = self._list_feature_views(
allow_cache=True, hide_dummy_entity=False
all_feature_views = self._get_feature_views_to_use(
features=features, allow_cache=True, hide_dummy_entity=False
)
all_on_demand_feature_views = self._registry.list_on_demand_feature_views(
project=self.project, allow_cache=True
Expand Down Expand Up @@ -1017,6 +1013,30 @@ def _augment_response_with_on_demand_transforms(
] = GetOnlineFeaturesResponse.FieldStatus.PRESENT
return OnlineResponse(GetOnlineFeaturesResponse(field_values=result_rows))

def _get_feature_views_to_use(
self,
features: Optional[Union[List[str], FeatureService]],
allow_cache=False,
hide_dummy_entity: bool = True,
) -> List[FeatureView]:

passed_in_feature_views = (
{view.name: view for view in features.feature_views}
if isinstance(features, FeatureService)
else {}
)

all_feature_views = [
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This name all_feature_views seems inaccurate if we are filtering.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the logic before may have been a bit confusing. The logic in the new commits should make it clear we're not actually filtering. All the FVs from the registry are present

*filter(
lambda view: view.name not in [*passed_in_feature_views.keys()],
self._list_feature_views(
allow_cache=allow_cache, hide_dummy_entity=hide_dummy_entity
),
)
] + [*passed_in_feature_views.values()]

return all_feature_views

@log_exceptions_and_usage
def serve(self, port: int) -> None:
"""Start the feature consumption server locally on a given port."""
Expand Down Expand Up @@ -1115,17 +1135,6 @@ def _group_feature_refs(
return fvs_result, odfvs_result


def _get_feature_refs_from_feature_services(
feature_service: FeatureService,
) -> List[str]:
feature_refs = []
for projection in feature_service.features:
feature_refs.extend(
[f"{projection.name}:{f.name}" for f in projection.features]
)
return feature_refs


def _get_table_entity_keys(
table: FeatureView, entity_keys: List[EntityKeyProto], join_key_map: Dict[str, str],
) -> List[EntityKeyProto]:
Expand Down
15 changes: 12 additions & 3 deletions sdk/python/feast/feature_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
from feast.data_source import DataSource
from feast.errors import RegistryInferenceFailure
from feast.feature import Feature
from feast.feature_view_projection import FeatureViewProjection
from feast.protos.feast.core.FeatureView_pb2 import FeatureView as FeatureViewProto
from feast.protos.feast.core.FeatureView_pb2 import (
FeatureViewMeta as FeatureViewMetaProto,
Expand Down Expand Up @@ -151,15 +150,25 @@ def __str__(self):
def __hash__(self):
return hash(self.name)

def __getitem__(self, item) -> FeatureViewProjection:
def __getitem__(self, item):
assert isinstance(item, list)

referenced_features = []
for feature in self.features:
if feature.name in item:
referenced_features.append(feature)

return FeatureViewProjection(self.name, referenced_features)
return FeatureView(
name=self.name,
entities=self.entities,
ttl=self.ttl,
input=self.input,
batch_source=self.batch_source,
stream_source=self.stream_source,
features=referenced_features,
tags=self.tags,
online=self.online,
)

def __eq__(self, other):
if not isinstance(other, FeatureView):
Expand Down
Empty file modified sdk/python/setup.py
100644 → 100755
Empty file.