2424
2525from feast import utils
2626from feast .entity import Entity
27- from feast .errors import FeastProviderLoginError , FeatureViewNotFoundException
27+ from feast .errors import (
28+ FeastProviderLoginError ,
29+ FeatureNameCollisionError ,
30+ FeatureViewNotFoundException ,
31+ )
2832from feast .feature_view import FeatureView
2933from feast .inference import infer_entity_value_type_from_feature_views
3034from feast .infra .provider import Provider , RetrievalJob , get_provider
@@ -244,7 +248,10 @@ def apply(
244248
245249 @log_exceptions_and_usage
246250 def get_historical_features (
247- self , entity_df : Union [pd .DataFrame , str ], feature_refs : List [str ],
251+ self ,
252+ entity_df : Union [pd .DataFrame , str ],
253+ feature_refs : List [str ],
254+ full_feature_names : bool = False ,
248255 ) -> RetrievalJob :
249256 """Enrich an entity dataframe with historical feature values for either training or batch scoring.
250257
@@ -266,6 +273,10 @@ def get_historical_features(
266273 SQL query. The query must be of a format supported by the configured offline store (e.g., BigQuery)
267274 feature_refs: A list of features that should be retrieved from the offline store. Feature references are of
268275 the format "feature_view:feature", e.g., "customer_fv:daily_transactions".
276+ full_feature_names: By default, this value is set to False. This strips the feature view prefixes from the data
277+ and returns only the feature name, changing them from the format "feature_view__feature" to "feature"
278+ (e.g., "customer_fv__daily_transactions" changes to "daily_transactions"). Set the value to True for
279+ the feature names to be prefixed by the feature view name in the format "feature_view__feature".
269280
270281 Returns:
271282 RetrievalJob which can be used to materialize the results.
@@ -278,12 +289,12 @@ def get_historical_features(
278289 >>> fs = FeatureStore(config=RepoConfig(provider="gcp"))
279290 >>> retrieval_job = fs.get_historical_features(
280291 >>> entity_df="SELECT event_timestamp, order_id, customer_id from gcp_project.my_ds.customer_orders",
281- >>> feature_refs=["customer:age", "customer:avg_orders_1d", "customer:avg_orders_7d"]
282- >>> )
292+ >>> feature_refs=["customer:age", "customer:avg_orders_1d", "customer:avg_orders_7d"],
293+ >>> full_feature_names=False
294+ >>> )
283295 >>> feature_data = retrieval_job.to_df()
284296 >>> model.fit(feature_data) # insert your modeling framework here.
285297 """
286-
287298 all_feature_views = self ._registry .list_feature_views (project = self .project )
288299 try :
289300 feature_views = _get_requested_feature_views (
@@ -301,6 +312,7 @@ def get_historical_features(
301312 entity_df ,
302313 self ._registry ,
303314 self .project ,
315+ full_feature_names ,
304316 )
305317 except FeastProviderLoginError as e :
306318 sys .exit (e )
@@ -467,7 +479,10 @@ def tqdm_builder(length):
467479
468480 @log_exceptions_and_usage
469481 def get_online_features (
470- self , feature_refs : List [str ], entity_rows : List [Dict [str , Any ]],
482+ self ,
483+ feature_refs : List [str ],
484+ entity_rows : List [Dict [str , Any ]],
485+ full_feature_names : bool = False ,
471486 ) -> OnlineResponse :
472487 """
473488 Retrieves the latest online feature data.
@@ -535,7 +550,7 @@ def get_online_features(
535550 project = self .project , allow_cache = True
536551 )
537552
538- grouped_refs = _group_refs (feature_refs , all_feature_views )
553+ grouped_refs = _group_refs (feature_refs , all_feature_views , full_feature_names )
539554 for table , requested_features in grouped_refs :
540555 entity_keys = _get_table_entity_keys (
541556 table , union_of_entity_keys , entity_name_to_join_key_map
@@ -552,13 +567,21 @@ def get_online_features(
552567
553568 if feature_data is None :
554569 for feature_name in requested_features :
555- feature_ref = f"{ table .name } __{ feature_name } "
570+ feature_ref = (
571+ f"{ table .name } __{ feature_name } "
572+ if full_feature_names
573+ else feature_name
574+ )
556575 result_row .statuses [
557576 feature_ref
558577 ] = GetOnlineFeaturesResponse .FieldStatus .NOT_FOUND
559578 else :
560579 for feature_name in feature_data :
561- feature_ref = f"{ table .name } __{ feature_name } "
580+ feature_ref = (
581+ f"{ table .name } __{ feature_name } "
582+ if full_feature_names
583+ else feature_name
584+ )
562585 if feature_name in requested_features :
563586 result_row .fields [feature_ref ].CopyFrom (
564587 feature_data [feature_name ]
@@ -587,7 +610,9 @@ def _entity_row_to_field_values(
587610
588611
589612def _group_refs (
590- feature_refs : List [str ], all_feature_views : List [FeatureView ]
613+ feature_refs : List [str ],
614+ all_feature_views : List [FeatureView ],
615+ full_feature_names : bool = False ,
591616) -> List [Tuple [FeatureView , List [str ]]]:
592617 """ Get list of feature views and corresponding feature names based on feature references"""
593618
@@ -597,12 +622,23 @@ def _group_refs(
597622 # view name to feature names
598623 views_features = defaultdict (list )
599624
625+ feature_set = set ()
626+ feature_collision_set = set ()
627+
600628 for ref in feature_refs :
601629 view_name , feat_name = ref .split (":" )
630+ if feat_name in feature_set :
631+ feature_collision_set .add (feat_name )
632+ else :
633+ feature_set .add (feat_name )
602634 if view_name not in view_index :
603635 raise FeatureViewNotFoundException (view_name )
604636 views_features [view_name ].append (feat_name )
605637
638+ if not full_feature_names and len (feature_collision_set ) > 0 :
639+ err = ", " .join (x for x in feature_collision_set )
640+ raise FeatureNameCollisionError (err )
641+
606642 result = []
607643 for view_name , feature_names in views_features .items ():
608644 result .append ((view_index [view_name ], feature_names ))
0 commit comments