Skip to content

Commit 8ef6e75

Browse files
Revinandshuchu
authored andcommitted
fix: Keep description, tags and nested types of ODFV schema fields
Signed-off-by: Revinand <[email protected]>
1 parent 72013d3 commit 8ef6e75

2 files changed

Lines changed: 65 additions & 13 deletions

File tree

‎sdk/python/feast/on_demand_feature_view.py‎

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -858,16 +858,7 @@ def _handle_backward_compatible_udf(
858858
@classmethod
859859
def _parse_features_from_proto(cls, proto: OnDemandFeatureViewProto) -> List[Field]:
860860
"""Parse features from the protobuf representation."""
861-
return [
862-
Field(
863-
name=feature.name,
864-
dtype=from_value_type(ValueType(feature.value_type)),
865-
vector_index=feature.vector_index,
866-
vector_length=feature.vector_length,
867-
vector_search_metric=feature.vector_search_metric,
868-
)
869-
for feature in proto.spec.features
870-
]
861+
return [Field.from_proto(feature) for feature in proto.spec.features]
871862

872863
@classmethod
873864
def _parse_optional_fields_from_proto(cls, proto: OnDemandFeatureViewProto) -> dict:
@@ -1207,8 +1198,12 @@ def _feature_exists_in_inferred(
12071198
Returns:
12081199
True if the feature exists in the inferred features, False otherwise
12091200
"""
1210-
# Check for exact feature match first
1211-
if specified_feature in inferred_features:
1201+
# Inferred features carry only a name and a type, so ignore the
1202+
# description, tags and vector settings of the specified feature
1203+
if any(
1204+
f.name == specified_feature.name and f.dtype == specified_feature.dtype
1205+
for f in inferred_features
1206+
):
12121207
return True
12131208

12141209
# For array types, we need to check by name since array types

‎sdk/python/tests/unit/test_on_demand_feature_view.py‎

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import pytest
1919

2020
from feast.aggregation import Aggregation
21+
from feast.errors import SpecifiedFeaturesNotPresentError
2122
from feast.feature_view import FeatureView
2223
from feast.field import Field
2324
from feast.infra.offline_stores.file_source import FileSource
@@ -27,7 +28,7 @@
2728
PythonTransformation,
2829
on_demand_feature_view,
2930
)
30-
from feast.types import Float32
31+
from feast.types import Array, Float32, Float64, Int64, String, Struct
3132

3233

3334
def udf1(features_df: pd.DataFrame) -> pd.DataFrame:
@@ -748,3 +749,59 @@ def test_on_demand_feature_view_org_field():
748749
odfv_org_a = OnDemandFeatureView(name="odfv-eq", org="ads", **common)
749750
odfv_org_b = OnDemandFeatureView(name="odfv-eq", org="search", **common)
750751
assert odfv_org_a != odfv_org_b
752+
753+
754+
def test_schema_field_metadata_survives_inference_and_proto_round_trip():
755+
"""Schema fields keep their description, tags and nested types end to end."""
756+
file_source = FileSource(name="my-file-source", path="test.parquet")
757+
feature_view = FeatureView(
758+
name="my-feature-view",
759+
entities=[],
760+
schema=[
761+
Field(name="feature1", dtype=Float64),
762+
Field(name="feature2", dtype=Float64),
763+
],
764+
source=file_source,
765+
)
766+
transformation = PandasTransformation(udf=udf1, udf_string="udf1 source code")
767+
odfv = OnDemandFeatureView(
768+
name="described-odfv",
769+
sources=[feature_view],
770+
schema=[
771+
Field(name="output1", dtype=Float64, description="Copy of feature1"),
772+
Field(name="output2", dtype=Float64, tags={"team": "risk"}),
773+
],
774+
feature_transformation=transformation,
775+
)
776+
777+
odfv.infer_features()
778+
779+
assert odfv.features[0].description == "Copy of feature1"
780+
assert odfv.features[1].tags == {"team": "risk"}
781+
assert OnDemandFeatureView.from_proto(odfv.to_proto()).features == odfv.features
782+
783+
nested_schema = [
784+
Field(name="output1", dtype=Struct({"a": Int64})),
785+
Field(name="output2", dtype=Array(Array(Int64))),
786+
]
787+
nested_odfv = OnDemandFeatureView(
788+
name="nested-odfv",
789+
sources=[feature_view],
790+
schema=nested_schema,
791+
feature_transformation=transformation,
792+
)
793+
restored = OnDemandFeatureView.from_proto(nested_odfv.to_proto())
794+
assert restored.features == nested_schema
795+
796+
wrong_type_odfv = OnDemandFeatureView(
797+
name="wrong-type-odfv",
798+
sources=[feature_view],
799+
schema=[
800+
Field(name="output1", dtype=String, description="Copy of feature1"),
801+
Field(name="output2", dtype=Float64),
802+
],
803+
feature_transformation=transformation,
804+
)
805+
806+
with pytest.raises(SpecifiedFeaturesNotPresentError):
807+
wrong_type_odfv.infer_features()

0 commit comments

Comments
 (0)