|
18 | 18 | import pytest |
19 | 19 |
|
20 | 20 | from feast.aggregation import Aggregation |
| 21 | +from feast.errors import SpecifiedFeaturesNotPresentError |
21 | 22 | from feast.feature_view import FeatureView |
22 | 23 | from feast.field import Field |
23 | 24 | from feast.infra.offline_stores.file_source import FileSource |
|
27 | 28 | PythonTransformation, |
28 | 29 | on_demand_feature_view, |
29 | 30 | ) |
30 | | -from feast.types import Float32 |
| 31 | +from feast.types import Array, Float32, Float64, Int64, String, Struct |
31 | 32 |
|
32 | 33 |
|
33 | 34 | def udf1(features_df: pd.DataFrame) -> pd.DataFrame: |
@@ -748,3 +749,59 @@ def test_on_demand_feature_view_org_field(): |
748 | 749 | odfv_org_a = OnDemandFeatureView(name="odfv-eq", org="ads", **common) |
749 | 750 | odfv_org_b = OnDemandFeatureView(name="odfv-eq", org="search", **common) |
750 | 751 | 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