Visitar URL original
feat: Add multiple entity support to dbt integration by YassinNouh21 · Pull Request #5901 · feast-dev/feast · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
fix: Infer entity value types from dbt column types
When creating entities from dbt models with integer columns, entities were
being created with default ValueType.STRING, causing validation errors:
"Entity X has type ValueType.STRING, which does not match the inferred type Int64"

Solution: Added mapping dict and helper function for clean type conversion:
- FEAST_TYPE_TO_VALUE_TYPE: Maps FeastType to ValueType
- feast_type_to_value_type(): Helper function for conversion
- _infer_entity_value_type(): Method in DbtToFeastMapper class

This replaces verbose if-else chains with a clean dictionary lookup pattern.

Also corrects schema generation to include entity columns, as FeatureView.__init__
expects to extract entity columns from the schema itself (lines 216-234 in
feature_view.py).

Changes:
- feast/cli/dbt_import.py: Use mapper's _infer_entity_value_type() method
- feast/dbt/mapper.py: Add type mapping dict and helper method

Signed-off-by: yassinnouh21 <yassinnouh21@gmail.com>
  • Loading branch information
YassinNouh21 committed Jan 24, 2026
commit 407cffccf918c928996efaad6198dccdf45016f1
3 changes: 3 additions & 0 deletions sdk/python/feast/cli/dbt_import.py
Original file line number Diff line number Diff line change
Expand Up @@ -225,9 +225,12 @@ def import_command(
model_entities: List[Any] = []
for entity_col in entity_cols:
if entity_col not in entities_created:
# Use mapper's internal method for value type inference
entity_value_type = mapper._infer_entity_value_type(model, entity_col)
entity = mapper.create_entity(
name=entity_col,
description="Entity key for dbt models",
value_type=entity_value_type,
)
entities_created[entity_col] = entity
all_objects.append(entity)
Expand Down
47 changes: 42 additions & 5 deletions sdk/python/feast/dbt/mapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,24 @@
)
from feast.value_type import ValueType

# Mapping from FeastType to ValueType for entity value inference
FEAST_TYPE_TO_VALUE_TYPE: Dict[FeastType, ValueType] = {
String: ValueType.STRING,
Int32: ValueType.INT64,
Int64: ValueType.INT64,
Float32: ValueType.DOUBLE,
Float64: ValueType.DOUBLE,
Bool: ValueType.BOOL,
Bytes: ValueType.BYTES,
UnixTimestamp: ValueType.UNIX_TIMESTAMP,
}


def feast_type_to_value_type(feast_type: FeastType) -> ValueType:
"""Convert a FeastType to its corresponding ValueType for entities."""
return FEAST_TYPE_TO_VALUE_TYPE.get(feast_type, ValueType.STRING)


# Comprehensive mapping from dbt/warehouse types to Feast types
# Covers BigQuery, Snowflake, Redshift, PostgreSQL, and common SQL types
DBT_TO_FEAST_TYPE_MAP: Dict[str, FeastType] = {
Expand Down Expand Up @@ -180,6 +198,14 @@ def __init__(
self.timestamp_field = timestamp_field
self.ttl_days = ttl_days

def _infer_entity_value_type(self, model: DbtModel, entity_col: str) -> ValueType:
"""Infer entity ValueType from dbt model column type."""
for column in model.columns:
if column.name == entity_col:
feast_type = map_dbt_type_to_feast_type(column.data_type)
return feast_type_to_value_type(feast_type)
return ValueType.UNKNOWN

def create_data_source(
self,
model: DbtModel,
Expand Down Expand Up @@ -310,7 +336,9 @@ def create_feature_view(
"""
# Normalize to lists
entity_cols: List[str] = (
[entity_columns] if isinstance(entity_columns, str) else list(entity_columns)
[entity_columns]
if isinstance(entity_columns, str)
else list(entity_columns)
)

entity_objs: List[Entity] = []
Expand All @@ -330,12 +358,14 @@ def create_feature_view(
ts_field = timestamp_field or self.timestamp_field
ttl = timedelta(days=ttl_days if ttl_days is not None else self.ttl_days)

# Columns to exclude from features (all entity columns + timestamp)
excluded = set(entity_cols) | {ts_field}
# Columns to exclude from schema (timestamp + any explicitly excluded)
# Note: entity columns should NOT be excluded - FeatureView.__init__
# expects entity columns to be in the schema and will extract them
excluded = {ts_field}
if exclude_columns:
excluded.update(exclude_columns)

# Create schema from model columns
# Create schema from model columns (includes entity columns)
schema: List[Field] = []
for column in model.columns:
if column.name not in excluded:
Expand All @@ -352,9 +382,12 @@ def create_feature_view(
if not entity_objs:
entity_objs = []
for entity_col in entity_cols:
# Infer entity value type from model column
entity_value_type = self._infer_entity_value_type(model, entity_col)
ent = self.create_entity(
name=entity_col,
description=f"Entity for {model.name}",
value_type=entity_value_type,
)
entity_objs.append(ent)

Expand Down Expand Up @@ -405,16 +438,20 @@ def create_all_from_model(
"""
# Normalize to list
entity_cols: List[str] = (
[entity_columns] if isinstance(entity_columns, str) else list(entity_columns)
[entity_columns]
if isinstance(entity_columns, str)
else list(entity_columns)
)

# Create entities (plural)
entities_list = []
for entity_col in entity_cols:
entity_value_type = self._infer_entity_value_type(model, entity_col)
entity = self.create_entity(
name=entity_col,
description=f"Entity for {model.name}",
tags={"dbt.model": model.name},
value_type=entity_value_type,
)
entities_list.append(entity)

Expand Down
Loading