Skip to content

Commit 314447f

Browse files
authored
Changed FVProjection name_to_use field to name_alias and other adjustments (#1929)
Signed-off-by: David Y Liu <davidyliuliu@gmail.com>
1 parent 1e4caea commit 314447f

File tree

7 files changed

+149
-102
lines changed

7 files changed

+149
-102
lines changed

protos/feast/core/FeatureViewProjection.proto

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ message FeatureViewProjection {
1515
string feature_view_name = 1;
1616

1717
// Alias for feature view name
18-
string feature_view_name_to_use = 3;
18+
string feature_view_name_alias = 3;
1919

2020
// The features of the feature view that are a part of the feature reference.
2121
repeated FeatureSpecV2 feature_columns = 2;

sdk/python/feast/feature_store.py

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
14+
import copy
1415
import os
1516
import warnings
1617
from collections import Counter, OrderedDict, defaultdict
@@ -329,7 +330,10 @@ def _get_features(
329330
)
330331
for projection in feature_service_from_registry.feature_view_projections:
331332
_feature_refs.extend(
332-
[f"{projection.name_to_use}:{f.name}" for f in projection.features]
333+
[
334+
f"{projection.name_to_use()}:{f.name}"
335+
for f in projection.features
336+
]
333337
)
334338
else:
335339
assert isinstance(_features, list)
@@ -939,7 +943,7 @@ def _populate_result_rows_from_feature_view(
939943
if feature_data is None:
940944
for feature_name in requested_features:
941945
feature_ref = (
942-
f"{table.projection.name_to_use}__{feature_name}"
946+
f"{table.projection.name_to_use()}__{feature_name}"
943947
if full_feature_names
944948
else feature_name
945949
)
@@ -949,7 +953,7 @@ def _populate_result_rows_from_feature_view(
949953
else:
950954
for feature_name in feature_data:
951955
feature_ref = (
952-
f"{table.projection.name_to_use}__{feature_name}"
956+
f"{table.projection.name_to_use()}__{feature_name}"
953957
if full_feature_names
954958
else feature_name
955959
)
@@ -1009,7 +1013,7 @@ def _augment_response_with_on_demand_transforms(
10091013

10101014
for transformed_feature in selected_subset:
10111015
transformed_feature_name = (
1012-
f"{odfv.projection.name_to_use}__{transformed_feature}"
1016+
f"{odfv.projection.name_to_use()}__{transformed_feature}"
10131017
if full_feature_names
10141018
else transformed_feature
10151019
)
@@ -1041,23 +1045,31 @@ def _get_feature_views_to_use(
10411045
)
10421046
}
10431047

1048+
fvs_to_use, od_fvs_to_use = [], []
10441049
if isinstance(features, FeatureService):
1045-
for fv_name, projection in {
1046-
projection.name: projection
1050+
for fv_name, projection in [
1051+
(projection.name, projection)
10471052
for projection in features.feature_view_projections
1048-
}.items():
1053+
]:
10491054
if fv_name in fvs:
1050-
fvs[fv_name].set_projection(projection)
1055+
fvs_to_use.append(
1056+
fvs[fv_name].with_projection(copy.copy(projection))
1057+
)
10511058
elif fv_name in od_fvs:
1052-
od_fvs[fv_name].set_projection(projection)
1059+
od_fvs_to_use.append(
1060+
od_fvs[fv_name].with_projection(copy.copy(projection))
1061+
)
10531062
else:
10541063
raise ValueError(
10551064
f"The provided feature service {features.name} contains a reference to a feature view"
10561065
f"{fv_name} which doesn't exist. Please make sure that you have created the feature view"
10571066
f'{fv_name} and that you have registered it by running "apply".'
10581067
)
1068+
views_to_use = (fvs_to_use, od_fvs_to_use)
1069+
else:
1070+
views_to_use = ([*fvs.values()], [*od_fvs.values()])
10591071

1060-
return [*fvs.values()], [*od_fvs.values()]
1072+
return views_to_use
10611073

10621074
@log_exceptions_and_usage
10631075
def serve(self, port: int) -> None:

sdk/python/feast/feature_view.py

Lines changed: 56 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,21 @@ def __str__(self):
155155
def __hash__(self):
156156
return hash((id(self), self.name))
157157

158+
def __copy__(self):
159+
fv = FeatureView(
160+
name=self.name,
161+
entities=self.entities,
162+
ttl=self.ttl,
163+
input=self.input,
164+
batch_source=self.batch_source,
165+
stream_source=self.stream_source,
166+
features=self.features,
167+
tags=self.tags,
168+
online=self.online,
169+
)
170+
fv.projection = copy.copy(self.projection)
171+
return fv
172+
158173
def __getitem__(self, item):
159174
assert isinstance(item, list)
160175

@@ -163,7 +178,8 @@ def __getitem__(self, item):
163178
if feature.name in item:
164179
referenced_features.append(feature)
165180

166-
self.projection.features = referenced_features
181+
cp = self.__copy__()
182+
cp.projection.features = referenced_features
167183

168184
return self
169185

@@ -207,30 +223,54 @@ def is_valid(self):
207223

208224
def with_name(self, name: str):
209225
"""
210-
Produces a copy of this FeatureView with the passed name.
226+
Renames this feature view by returning a copy of this feature view with an alias
227+
set for the feature view name. This rename operation is only used as part of query
228+
operations and will not modify the underlying FeatureView.
211229
212230
Args:
213231
name: Name to assign to the FeatureView copy.
214232
215233
Returns:
216234
A copy of this FeatureView with the name replaced with the 'name' input.
217235
"""
218-
fv = FeatureView(
219-
name=self.name,
220-
entities=self.entities,
221-
ttl=self.ttl,
222-
input=self.input,
223-
batch_source=self.batch_source,
224-
stream_source=self.stream_source,
225-
features=self.features,
226-
tags=self.tags,
227-
online=self.online,
228-
)
236+
cp = self.__copy__()
237+
cp.projection.name_alias = name
229238

230-
fv.set_projection(copy.copy(self.projection))
231-
fv.projection.name_to_use = name
239+
return cp
232240

233-
return fv
241+
def with_projection(self, feature_view_projection: FeatureViewProjection):
242+
"""
243+
Sets the feature view projection by returning a copy of this feature view
244+
with its projection set to the given projection. A projection is an
245+
object that stores the modifications to a feature view that is used during
246+
query operations.
247+
248+
Args:
249+
feature_view_projection: The FeatureViewProjection object to link to this
250+
OnDemandFeatureView.
251+
252+
Returns:
253+
A copy of this FeatureView with its projection replaced with the 'feature_view_projection'
254+
argument.
255+
"""
256+
if feature_view_projection.name != self.name:
257+
raise ValueError(
258+
f"The projection for the {self.name} FeatureView cannot be applied because it differs in name. "
259+
f"The projection is named {feature_view_projection.name} and the name indicates which "
260+
"FeatureView the projection is for."
261+
)
262+
263+
for feature in feature_view_projection.features:
264+
if feature not in self.features:
265+
raise ValueError(
266+
f"The projection for {self.name} cannot be applied because it contains {feature.name} which the "
267+
"FeatureView doesn't have."
268+
)
269+
270+
cp = self.__copy__()
271+
cp.projection = feature_view_projection
272+
273+
return cp
234274

235275
def to_proto(self) -> FeatureViewProto:
236276
"""
@@ -416,31 +456,3 @@ def infer_features_from_batch_source(self, config: RepoConfig):
416456
"FeatureView",
417457
f"Could not infer Features for the FeatureView named {self.name}.",
418458
)
419-
420-
def set_projection(self, feature_view_projection: FeatureViewProjection) -> None:
421-
"""
422-
Setter for the projection object held by this FeatureView. A projection is an
423-
object that stores the modifications to a FeatureView that is applied to the FeatureView
424-
when the FeatureView is used such as during feature_store.get_historical_features.
425-
This method also performs checks to ensure the projection is consistent with this
426-
FeatureView before doing the set.
427-
428-
Args:
429-
feature_view_projection: The FeatureViewProjection object to set this FeatureView's
430-
'projection' field to.
431-
"""
432-
if feature_view_projection.name != self.name:
433-
raise ValueError(
434-
f"The projection for the {self.name} FeatureView cannot be applied because it differs in name. "
435-
f"The projection is named {feature_view_projection.name} and the name indicates which "
436-
"FeatureView the projection is for."
437-
)
438-
439-
for feature in feature_view_projection.features:
440-
if feature not in self.features:
441-
raise ValueError(
442-
f"The projection for {self.name} cannot be applied because it contains {feature.name} which the "
443-
"FeatureView doesn't have."
444-
)
445-
446-
self.projection = feature_view_projection

sdk/python/feast/feature_view_projection.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from typing import List
1+
from typing import List, Optional
22

33
from attr import dataclass
44

@@ -11,12 +11,15 @@
1111
@dataclass
1212
class FeatureViewProjection:
1313
name: str
14-
name_to_use: str
14+
name_alias: Optional[str]
1515
features: List[Feature]
1616

17+
def name_to_use(self):
18+
return self.name_alias or self.name
19+
1720
def to_proto(self):
1821
feature_reference_proto = FeatureViewProjectionProto(
19-
feature_view_name=self.name, feature_view_name_to_use=self.name_to_use
22+
feature_view_name=self.name, feature_view_name_alias=self.name_alias
2023
)
2124
for feature in self.features:
2225
feature_reference_proto.feature_columns.append(feature.to_proto())
@@ -27,7 +30,7 @@ def to_proto(self):
2730
def from_proto(proto: FeatureViewProjectionProto):
2831
ref = FeatureViewProjection(
2932
name=proto.feature_view_name,
30-
name_to_use=proto.feature_view_name_to_use,
33+
name_alias=proto.feature_view_name_alias,
3134
features=[],
3235
)
3336
for feature_column in proto.feature_columns:
@@ -39,6 +42,6 @@ def from_proto(proto: FeatureViewProjectionProto):
3942
def from_definition(feature_grouping):
4043
return FeatureViewProjection(
4144
name=feature_grouping.name,
42-
name_to_use=feature_grouping.name,
45+
name_alias=None,
4346
features=feature_grouping.features,
4447
)

sdk/python/feast/infra/offline_stores/offline_utils.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ def get_feature_view_query_context(
128128
created_timestamp_column = feature_view.input.created_timestamp_column
129129

130130
context = FeatureViewQueryContext(
131-
name=feature_view.projection.name_to_use,
131+
name=feature_view.projection.name_to_use(),
132132
ttl=ttl_seconds,
133133
entities=join_keys,
134134
features=features,

sdk/python/feast/infra/provider.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -191,11 +191,11 @@ def _get_requested_feature_views_to_features_dict(
191191

192192
found = False
193193
for fv in feature_views:
194-
if fv.projection.name_to_use == feature_view_from_ref:
194+
if fv.projection.name_to_use() == feature_view_from_ref:
195195
found = True
196196
feature_views_to_feature_map[fv].append(feature_from_ref)
197197
for odfv in on_demand_feature_views:
198-
if odfv.projection.name_to_use == feature_view_from_ref:
198+
if odfv.projection.name_to_use() == feature_view_from_ref:
199199
found = True
200200
on_demand_feature_views_to_feature_map[odfv].append(feature_from_ref)
201201

0 commit comments

Comments
 (0)