@@ -1144,6 +1144,9 @@ def get_online_features(
11441144 # Also create entity values to append to the result
11451145 result_rows .append (_entity_row_to_field_values (entity_row_proto ))
11461146
1147+ # Keep track of what has been requested from the OnlineStore
1148+ # to avoid requesting the same thing twice for ODFVs.
1149+ retrieved_feature_refs : Set [str ] = set ()
11471150 for table , requested_features in grouped_refs :
11481151 table_join_keys = [
11491152 entity_name_to_join_key_map [entity_name ]
@@ -1158,6 +1161,11 @@ def get_online_features(
11581161 table ,
11591162 union_of_entity_keys ,
11601163 )
1164+ table_feature_names = {feature .name for feature in table .features }
1165+ retrieved_feature_refs |= {
1166+ f"{ table .name } :{ feature } " if feature in table_feature_names else feature
1167+ for feature in requested_features
1168+ }
11611169
11621170 requested_result_row_names = self ._get_requested_result_fields (
11631171 result_rows , needed_request_fv_features
@@ -1170,6 +1178,7 @@ def get_online_features(
11701178 request_data_features ,
11711179 result_rows ,
11721180 union_of_entity_keys ,
1181+ retrieved_feature_refs ,
11731182 )
11741183
11751184 self ._augment_response_with_on_demand_transforms (
@@ -1205,6 +1214,7 @@ def _populate_odfv_dependencies(
12051214 request_data_features : Dict [str , List [Any ]],
12061215 result_rows : List [GetOnlineFeaturesResponse .FieldValues ],
12071216 union_of_entity_keys : List [EntityKeyProto ],
1217+ retrieved_feature_refs : Set [str ],
12081218 ):
12091219 # Add more feature values to the existing result rows for the request data features
12101220 for feature_name , feature_values in request_data_features .items ():
@@ -1223,19 +1233,32 @@ def _populate_odfv_dependencies(
12231233 if len (grouped_odfv_refs ) > 0 :
12241234 for odfv , _ in grouped_odfv_refs :
12251235 for fv in odfv .input_feature_views .values ():
1226- table_join_keys = [
1227- entity_name_to_join_key_map [entity_name ]
1228- for entity_name in fv .entities
1229- ]
1230- self ._populate_result_rows_from_feature_view (
1231- table_join_keys ,
1232- full_feature_names ,
1233- provider ,
1234- [feature .name for feature in fv .features ],
1235- result_rows ,
1236- fv ,
1237- union_of_entity_keys ,
1238- )
1236+ # Find the set of required Features which have not yet
1237+ # been retrieved.
1238+ not_yet_retrieved = {
1239+ feature .name
1240+ for feature in fv .projection .features
1241+ if f"{ fv .name } :{ feature .name } " not in retrieved_feature_refs
1242+ }
1243+ # If there are required Features which have not yet been retrieved
1244+ # retrieve them.
1245+ if not_yet_retrieved :
1246+ table_join_keys = [
1247+ entity_name_to_join_key_map [entity_name ]
1248+ for entity_name in fv .entities
1249+ ]
1250+ self ._populate_result_rows_from_feature_view (
1251+ table_join_keys ,
1252+ full_feature_names ,
1253+ provider ,
1254+ list (not_yet_retrieved ),
1255+ result_rows ,
1256+ fv ,
1257+ union_of_entity_keys ,
1258+ )
1259+ # Update the set of retrieved Features with any newly retrieved
1260+ # Features.
1261+ retrieved_feature_refs |= not_yet_retrieved
12391262
12401263 def get_needed_request_data (
12411264 self ,
0 commit comments