Repository: feast-dev/feast

Issue:
Keep labels in Field api
I found that new api 'Field' will take place of 'Feature' in 0.21+ feast. but `Field` only have 'name' and 'dtype' parameters. The parameter 'labels' is disappeared. 
In my use case 'labels' is very import.  'labels' stores the default value, descriptions,and other things. for example

```python
comic_feature_view = FeatureView(
    name="comic_featureV1",
    entities=["item_id"],
    ttl=Duration(seconds=86400 * 1),
    features=[
        Feature(name="channel_id", dtype=ValueType.INT32, labels={"default": "14", "desc":"channel"}),
        Feature(name="keyword_weight", dtype=ValueType.FLOAT, labels={"default": "0.0", "desc":"keyword's weight"}),
        Feature(name="comic_vectorv1", dtype=ValueType.FLOAT, labels={"default": ";".join(["0.0" for i in range(32)]), "desc":"deepwalk vector","faiss_index":"/data/faiss_index/comic_featureV1__comic_vectorv1.index"}),
        Feature(name="comic_vectorv2", dtype=ValueType.FLOAT, labels={"default": ";".join(["0.0" for i in range(32)]), "desc":"word2vec vector","faiss_index":"/data/faiss_index/comic_featureV1__comic_vectorv2.index"}),
        Feature(name="gender", dtype=ValueType.INT32, labels={"default": "0", "desc":" 0-femal 1-male"}),
        Feature(name="pub_time", dtype=ValueType.STRING, labels={"default": "1970-01-01 00:00:00", "desc":"comic's publish time"}),
        Feature(name="update_time", dtype=ValueType.STRING, labels={"default": "1970-01-01 00:00:00", "desc":"comic's update time"}),
        Feature(name="view_cnt", dtype=ValueType.INT64, labels={"default": "0", "desc":"comic's hot score"}),
        Feature(name="collect_cnt", dtype=ValueType.INT64, labels={"default": "0", "desc":"collect count"}),
        Feature(name="source_id", dtype=ValueType.INT32, labels={"default": "0", "desc":"comic is from(0-unknown，1-japen，2-usa，3- other)"}),
```

So please keep the parameter 'labels' in Field api



Discussion:
Thanks for giving feedback and nice catch! 
Thanks for the feedback @hsz1273327 , this was definitely an oversight. We're planning on adding this back in soon (but we may be possibly changing the name of this field to `tags` to more accurately convey the purpose of the field).

Resolving patch:
diff --git a/sdk/python/feast/feature.py b/sdk/python/feast/feature.py
--- a/sdk/python/feast/feature.py
+++ b/sdk/python/feast/feature.py
@@ -91,7 +91,7 @@ def to_proto(self) -> FeatureSpecProto:
         value_type = ValueTypeProto.Enum.Value(self.dtype.name)
 
         return FeatureSpecProto(
-            name=self.name, value_type=value_type, labels=self.labels,
+            name=self.name, value_type=value_type, tags=self.labels,
         )
 
     @classmethod
@@ -106,7 +106,7 @@ def from_proto(cls, feature_proto: FeatureSpecProto):
         feature = cls(
             name=feature_proto.name,
             dtype=ValueType(feature_proto.value_type),
-            labels=dict(feature_proto.labels),
+            labels=dict(feature_proto.tags),
         )
 
         return feature
diff --git a/sdk/python/feast/field.py b/sdk/python/feast/field.py
--- a/sdk/python/feast/field.py
+++ b/sdk/python/feast/field.py
@@ -12,6 +12,8 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+from typing import Dict, Optional
+
 from feast.feature import Feature
 from feast.protos.feast.core.Feature_pb2 import FeatureSpecV2 as FieldProto
 from feast.types import FeastType, from_value_type
@@ -25,13 +27,15 @@ class Field:
     Attributes:
         name: The name of the field.
         dtype: The type of the field, such as string or float.
+        tags: User-defined metadata in dictionary form.
     """
 
     name: str
     dtype: FeastType
+    tags: Dict[str, str]
 
     def __init__(
-        self, *, name: str, dtype: FeastType,
+        self, *, name: str, dtype: FeastType, tags: Optional[Dict[str, str]] = None,
     ):
         """
         Creates a Field object.
@@ -39,12 +43,18 @@ def __init__(
         Args:
             name: The name of the field.
             dtype: The type of the field, such as string or float.
+            tags (optional): User-defined metadata in dictionary form.
         """
         self.name = name
         self.dtype = dtype
+        self.tags = tags or {}
 
     def __eq__(self, other):
-        if self.name != other.name or self.dtype != other.dtype:
+        if (
+            self.name != other.name
+            or self.dtype != other.dtype
+            or self.tags != other.tags
+        ):
             return False
         return True
 
@@ -58,12 +68,12 @@ def __repr__(self):
         return f"{self.name}-{self.dtype}"
 
     def __str__(self):
-        return f"Field(name={self.name}, dtype={self.dtype})"
+        return f"Field(name={self.name}, dtype={self.dtype}, tags={self.tags})"
 
     def to_proto(self) -> FieldProto:
         """Converts a Field object to its protobuf representation."""
         value_type = self.dtype.to_value_type()
-        return FieldProto(name=self.name, value_type=value_type.value)
+        return FieldProto(name=self.name, value_type=value_type.value, tags=self.tags)
 
     @classmethod
     def from_proto(cls, field_proto: FieldProto):
@@ -74,7 +84,11 @@ def from_proto(cls, field_proto: FieldProto):
             field_proto: FieldProto protobuf object
         """
         value_type = ValueType(field_proto.value_type)
-        return cls(name=field_proto.name, dtype=from_value_type(value_type=value_type))
+        return cls(
+            name=field_proto.name,
+            dtype=from_value_type(value_type=value_type),
+            tags=dict(field_proto.tags),
+        )
 
     @classmethod
     def from_feature(cls, feature: Feature):
@@ -84,4 +98,6 @@ def from_feature(cls, feature: Feature):
         Args:
             feature: Feature object to convert.
         """
-        return cls(name=feature.name, dtype=from_value_type(feature.dtype))
+        return cls(
+            name=feature.name, dtype=from_value_type(feature.dtype), tags=feature.labels
+        )


Test patch:
diff --git a/java/serving/src/test/java/feast/serving/util/DataGenerator.java b/java/serving/src/test/java/feast/serving/util/DataGenerator.java
--- a/java/serving/src/test/java/feast/serving/util/DataGenerator.java
+++ b/java/serving/src/test/java/feast/serving/util/DataGenerator.java
@@ -126,11 +126,11 @@ public static EntityProto.EntitySpecV2 createEntitySpecV2(
   }
 
   public static FeatureProto.FeatureSpecV2 createFeatureSpecV2(
-      String name, ValueProto.ValueType.Enum valueType, Map<String, String> labels) {
+      String name, ValueProto.ValueType.Enum valueType, Map<String, String> tags) {
     return FeatureProto.FeatureSpecV2.newBuilder()
         .setName(name)
         .setValueType(valueType)
-        .putAllLabels(labels)
+        .putAllTags(tags)
         .build();
   }
 
@@ -140,7 +140,7 @@ public static FeatureTableSpec createFeatureTableSpec(
       List<String> entities,
       Map<String, ValueProto.ValueType.Enum> features,
       int maxAgeSecs,
-      Map<String, String> labels) {
+      Map<String, String> tags) {
 
     return FeatureTableSpec.newBuilder()
         .setName(name)
@@ -152,7 +152,7 @@ public static FeatureTableSpec createFeatureTableSpec(
                         FeatureSpecV2.newBuilder()
                             .setName(entry.getKey())
                             .setValueType(entry.getValue())
-                            .putAllLabels(labels)
+                            .putAllTags(tags)
                             .build())
                 .collect(Collectors.toList()))
         .setMaxAge(Duration.newBuilder().setSeconds(3600).build())
@@ -169,7 +169,7 @@ public static FeatureTableSpec createFeatureTableSpec(
                         .setUri("/dev/null")
                         .build())
                 .build())
-        .putAllLabels(labels)
+        .putAllLabels(tags)
         .build();
   }
 
@@ -178,7 +178,7 @@ public static FeatureTableSpec createFeatureTableSpec(
       List<String> entities,
       ImmutableMap<String, ValueProto.ValueType.Enum> features,
       int maxAgeSecs,
-      Map<String, String> labels) {
+      Map<String, String> tags) {
 
     return FeatureTableSpec.newBuilder()
         .setName(name)
@@ -190,11 +190,11 @@ public static FeatureTableSpec createFeatureTableSpec(
                         FeatureSpecV2.newBuilder()
                             .setName(entry.getKey())
                             .setValueType(entry.getValue())
-                            .putAllLabels(labels)
+                            .putAllTags(tags)
                             .build())
                 .collect(Collectors.toList()))
         .setMaxAge(Duration.newBuilder().setSeconds(maxAgeSecs).build())
-        .putAllLabels(labels)
+        .putAllLabels(tags)
         .build();
   }