Skip to content

Commit cff6fbf

Browse files
feat: Add decimal to supported feature types (#6029) (#6226)
* feat: Add decimal to supported feature types #6029 Signed-off-by: Nick Quinn <nicholas_quinn@apple.com> * Fixed linter issue Signed-off-by: Nick Quinn <nicholas_quinn@apple.com> * Devin feedback Signed-off-by: Nick Quinn <nicholas_quinn@apple.com> --------- Signed-off-by: Nick Quinn <nicholas_quinn@apple.com> Co-authored-by: Nick Quinn <nicholas_quinn@apple.com>
1 parent e72b983 commit cff6fbf

9 files changed

Lines changed: 744 additions & 372 deletions

File tree

docs/reference/type-system.md

Lines changed: 45 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ Feast supports the following data types:
2626
| `UnixTimestamp` | `datetime` | Unix timestamp (nullable) |
2727
| `Uuid` | `uuid.UUID` | UUID (any version) |
2828
| `TimeUuid` | `uuid.UUID` | Time-based UUID (version 1) |
29+
| `Decimal` | `decimal.Decimal` | Arbitrary-precision decimal number |
2930

3031
### Domain-Specific Primitive Types
3132

@@ -56,6 +57,7 @@ All primitive types have corresponding array (list) types:
5657
| `Array(UnixTimestamp)` | `List[datetime]` | List of timestamps |
5758
| `Array(Uuid)` | `List[uuid.UUID]` | List of UUIDs |
5859
| `Array(TimeUuid)` | `List[uuid.UUID]` | List of time-based UUIDs |
60+
| `Array(Decimal)` | `List[decimal.Decimal]` | List of arbitrary-precision decimals |
5961

6062
### Set Types
6163

@@ -73,6 +75,7 @@ All primitive types (except `Map` and `Json`) have corresponding set types for s
7375
| `Set(UnixTimestamp)` | `Set[datetime]` | Set of unique timestamps |
7476
| `Set(Uuid)` | `Set[uuid.UUID]` | Set of unique UUIDs |
7577
| `Set(TimeUuid)` | `Set[uuid.UUID]` | Set of unique time-based UUIDs |
78+
| `Set(Decimal)` | `Set[decimal.Decimal]` | Set of unique arbitrary-precision decimals |
7679

7780
**Note:** Set types automatically remove duplicate values. When converting from lists or other iterables to sets, duplicates are eliminated.
7881

@@ -194,7 +197,7 @@ from datetime import timedelta
194197
from feast import Entity, FeatureView, Field, FileSource
195198
from feast.types import (
196199
Int32, Int64, Float32, Float64, String, Bytes, Bool, UnixTimestamp,
197-
Uuid, TimeUuid, Array, Set, Map, Json, Struct
200+
Uuid, TimeUuid, Decimal, Array, Set, Map, Json, Struct
198201
)
199202

200203
# Define a data source
@@ -226,6 +229,7 @@ user_features = FeatureView(
226229
Field(name="last_login", dtype=UnixTimestamp),
227230
Field(name="session_id", dtype=Uuid),
228231
Field(name="event_id", dtype=TimeUuid),
232+
Field(name="price", dtype=Decimal),
229233

230234
# Array types
231235
Field(name="daily_steps", dtype=Array(Int32)),
@@ -238,6 +242,7 @@ user_features = FeatureView(
238242
Field(name="login_timestamps", dtype=Array(UnixTimestamp)),
239243
Field(name="related_session_ids", dtype=Array(Uuid)),
240244
Field(name="event_chain", dtype=Array(TimeUuid)),
245+
Field(name="historical_prices", dtype=Array(Decimal)),
241246

242247
# Set types (unique values only — see backend caveats above)
243248
Field(name="visited_pages", dtype=Set(String)),
@@ -246,6 +251,7 @@ user_features = FeatureView(
246251
Field(name="preferred_languages", dtype=Set(String)),
247252
Field(name="unique_device_ids", dtype=Set(Uuid)),
248253
Field(name="unique_event_ids", dtype=Set(TimeUuid)),
254+
Field(name="unique_prices", dtype=Set(Decimal)),
249255

250256
# Map types
251257
Field(name="user_preferences", dtype=Map),
@@ -313,9 +319,44 @@ related_sessions = [uuid.uuid4(), uuid.uuid4(), uuid.uuid4()]
313319
unique_devices = {uuid.uuid4(), uuid.uuid4()}
314320
```
315321

316-
### Nested Collection Type Usage Examples
322+
### Decimal Type Usage Examples
323+
324+
The `Decimal` type stores arbitrary-precision decimal numbers using Python's `decimal.Decimal`.
325+
Values are stored as strings in the proto to preserve full precision — no floating-point rounding occurs.
326+
327+
```python
328+
import decimal
329+
330+
# Scalar decimal — e.g., a financial price
331+
price = decimal.Decimal("19.99")
332+
333+
# High-precision value — all digits preserved
334+
tax_rate = decimal.Decimal("0.08750000000000000000")
335+
336+
# Decimal values are returned as decimal.Decimal objects from get_online_features()
337+
response = store.get_online_features(
338+
features=["product_features:price"],
339+
entity_rows=[{"product_id": 42}],
340+
)
341+
result = response.to_dict()
342+
# result["price"][0] is a decimal.Decimal object
343+
344+
# Decimal lists — e.g., a history of prices
345+
historical_prices = [
346+
decimal.Decimal("18.50"),
347+
decimal.Decimal("19.00"),
348+
decimal.Decimal("19.99"),
349+
]
350+
351+
# Decimal sets — unique price points seen
352+
unique_prices = {decimal.Decimal("9.99"), decimal.Decimal("19.99"), decimal.Decimal("29.99")}
353+
```
317354

318-
Nested collections allow storing multi-dimensional data with unlimited depth:
355+
{% hint style="warning" %}
356+
`Decimal` is **not** inferred from any backend schema. You must declare it explicitly in your feature view schema. The pandas dtype for `Decimal` columns is `object` (holding `decimal.Decimal` instances), not a numeric dtype.
357+
{% endhint %}
358+
359+
### Nested Collection Type Usage Examples
319360

320361
```python
321362
# List of lists — e.g., weekly score history per user
@@ -420,7 +461,7 @@ Each of these columns must be associated with a Feast type, which requires conve
420461
* `source_datatype_to_feast_value_type` calls the appropriate method in `type_map.py`. For example, if a `SnowflakeSource` is being examined, `snowflake_python_type_to_feast_value_type` from `type_map.py` will be called.
421462

422463
{% hint style="info" %}
423-
**Types that cannot be inferred:** `Set`, `Json`, `Struct`, `PdfBytes`, and `ImageBytes` types are never inferred from backend schemas. If you use these types, you must declare them explicitly in your feature view schema.
464+
**Types that cannot be inferred:** `Set`, `Json`, `Struct`, `Decimal`, `PdfBytes`, and `ImageBytes` types are never inferred from backend schemas. If you use these types, you must declare them explicitly in your feature view schema.
424465
{% endhint %}
425466

426467
### Materialization

protos/feast/types/Value.proto

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,9 @@ message ValueType {
6565
TIME_UUID_SET = 41;
6666
VALUE_LIST = 42;
6767
VALUE_SET = 43;
68+
DECIMAL = 44;
69+
DECIMAL_LIST = 45;
70+
DECIMAL_SET = 46;
6871
}
6972
}
7073

@@ -112,6 +115,9 @@ message Value {
112115
StringSet time_uuid_set_val = 41;
113116
RepeatedValue list_val = 42;
114117
RepeatedValue set_val = 43;
118+
string decimal_val = 44;
119+
StringList decimal_list_val = 45;
120+
StringSet decimal_set_val = 46;
115121
}
116122
}
117123

0 commit comments

Comments
 (0)