Visitar URL original
feat: Add enabled/disabled toggle for feature views by rpathade · Pull Request #6401 · feast-dev/feast · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
85a18dc
feat: Add enabled/disabled toggle for feature views
rpathade May 13, 2026
5e98c7f
feat: Add demo noteboooks for users
ntkathole May 8, 2026
f735cd2
feat: Add CLI enable/disable commands and registry metadata support
rpathade May 15, 2026
426ae09
Added features
rpathade May 16, 2026
365ecb4
fix(compute-engine/local): Honor field_mapping on join keys in dedup …
1fanwang May 13, 2026
5b94d4d
feat: Add Prometheus gauges for FeatureStore installation telemetry (…
ntkathole May 13, 2026
b5297b8
docs: Rename Atlas Vector Search to MongoDB Vector Search and fix cod…
jvincent-mongodb May 13, 2026
04d500c
feat(dynamodb): Use ProjectionExpression when requested_features is set
Jwrede May 3, 2026
c9e368b
fix(dynamodb): Fix mypy type for _build_projection_expression return
Jwrede May 3, 2026
de050ab
fix(bigquery): Enable list inference for parquet loads in offline_wri…
Jwrede May 3, 2026
78fcd72
fix(trino): Clean up temporary entity tables after retrieval (#6381)
Jwrede May 14, 2026
2014acd
feat(bigquery): Support DATE-type event timestamp columns (#6362)
Jwrede May 14, 2026
5e69e5f
fix: Fixes for ray source
ntkathole May 14, 2026
53e4a92
feat: Expose registry endpoints on feature server for MCP access
patelchaitany Apr 21, 2026
e9791b0
fix: Revert state propagation to always update in _update_metadata_fi…
rpathade May 16, 2026
f7fd0f8
fix: Recompile protos for protobuf 4.x compatibility and fix state ma…
rpathade May 18, 2026
a4f7ab8
feat: Add unit tests for state machine and clean up lazy imports in r…
rpathade May 18, 2026
3aa8778
fix: Address review comments for feature view state management
rpathade May 20, 2026
e626433
fix: Resolve integration test failures in apply loop
rpathade May 20, 2026
8130b63
fix: Resolve integration test failures in apply loop
rpathade May 20, 2026
2b54bd0
Apply suggestion from @ntkathole
rpathade May 21, 2026
cb33b6d
fix: Resolve review comments for feature_store
rpathade May 21, 2026
f3b94eb
fix: Resolve review comments for feature_views.py
rpathade May 21, 2026
a4c85dc
Merge branch 'master' into feat/feature-view-enabled-disabled-v2
rpathade May 21, 2026
abb6a41
Merge remote-tracking branch 'origin/master' into feat/feature-view-e…
rpathade May 21, 2026
f4dd0a4
feat: Add FeatureStore methods and update describe for enabled/state
rpathade May 22, 2026
a3f65b0
Merge branch 'master' into feat/feature-view-enabled-disabled-v2
rpathade May 22, 2026
efd41a6
fix: Add type: ignore comments for mypy on BaseFeatureView attr access
rpathade May 22, 2026
436a712
fix: Remove REST API endpoints for enable/disable/set-state (deferred…
rpathade May 22, 2026
7110346
Merge remote-tracking branch 'origin/master' into feat/feature-view-e…
rpathade May 22, 2026
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(compute-engine/local): Honor field_mapping on join keys in dedup …
…+ join nodes (#6395)

* fix: Apply field mapping to join keys in local compute engine nodes

When a batch source defines a `field_mapping` that renames an entity join
key (e.g. `USERID` -> `user_id`), the source-read node renames the columns
on the pulled Arrow table to their mapped names. Downstream `LocalDedupNode`
and `LocalJoinNode` then look up the *pre-mapping* names from
`column_info.join_keys`, which raises `KeyError: Index(['USERID'])` during
materialization (or returns an empty join).

Add a `join_keys_columns` property on `ColumnInfo` that mirrors the existing
`timestamp_column` / `created_timestamp_column` properties — returning join
keys translated through `field_mapping` — and use it from the dedup and
join nodes.

Fixes #5942.

Signed-off-by: 1fanwang <1fannnw@gmail.com>

* test: also cover LocalJoinNode field_mapping case

Signed-off-by: 1fanwang <1fannnw@gmail.com>

---------

Signed-off-by: 1fanwang <1fannnw@gmail.com>
Signed-off-by: RutujaPathade <73137503+RutujaPathade@users.noreply.github.com>
  • Loading branch information
1fanwang authored and rpathade committed May 21, 2026
commit 365ecb412df46784ae8bac87f2f5c969dec48e43
12 changes: 12 additions & 0 deletions sdk/python/feast/infra/compute_engines/dag/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,18 @@ def created_timestamp_column(self) -> Optional[str]:
"""
return self._get_mapped_column(self.created_ts_col)

@property
def join_keys_columns(self) -> List[str]:
"""
Get the join keys, mapped through field_mapping to their post-rename
column names. Use this when looking up columns on a DataFrame that has
already had its source columns renamed (e.g. inside DAG nodes that
consume the output of a source-read node).
"""
if not self.field_mapping:
return list(self.join_keys)
return [self.field_mapping.get(key, key) for key in self.join_keys]

def _get_mapped_column(self, column: Optional[str]) -> Optional[str]:
"""
Helper method to get the mapped column name if it exists in field_mapping.
Expand Down
14 changes: 10 additions & 4 deletions sdk/python/feast/infra/compute_engines/local/nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,14 +79,18 @@ def execute(self, context: ExecutionContext) -> ArrowTableValue:
for val in input_values:
val.assert_format(DAGFormat.ARROW)

# The upstream source-read node has already renamed columns via
# field_mapping, so use the mapped join keys for joining (see #5942).
join_keys = self.column_info.join_keys_columns

# Convert all upstream ArrowTables to backend DataFrames
joined_df = self.backend.from_arrow(input_values[0].data)
for val in input_values[1:]:
next_df = self.backend.from_arrow(val.data)
joined_df = self.backend.join(
joined_df,
next_df,
on=self.column_info.join_keys,
on=join_keys,
how=self.how,
)

Expand All @@ -105,7 +109,7 @@ def execute(self, context: ExecutionContext) -> ArrowTableValue:
joined_df = self.backend.join(
entity_df,
joined_df,
on=self.column_info.join_keys,
on=join_keys,
how="left",
)

Expand Down Expand Up @@ -193,8 +197,10 @@ def execute(self, context: ExecutionContext) -> ArrowTableValue:

# Extract join_keys, timestamp, and created_ts from context

# Dedup strategy: sort and drop_duplicates
dedup_keys = self.column_info.join_keys
# Dedup strategy: sort and drop_duplicates. Use the mapped join key
# names so we look up the columns that the source-read node has
# already renamed (see issue #5942).
dedup_keys = self.column_info.join_keys_columns
if dedup_keys:
sort_keys = [self.column_info.timestamp_column]
if (
Expand Down
116 changes: 116 additions & 0 deletions sdk/python/tests/unit/infra/compute_engines/local/test_nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,122 @@ def test_local_dedup_node():
assert set(df_result["entity_id"]) == {1, 2}


def test_local_dedup_node_with_field_mapping_on_join_key():
"""Regression test for materialization failure when a join key has a field mapping.

The source-read node renames columns via field_mapping (e.g. ``USERID`` -> ``user_id``)
before passing the table to downstream nodes. Without mapping ``column_info.join_keys``
the dedup node would look up the pre-mapping name and raise ``KeyError(['USERID'])``.

See https://github.com/feast-dev/feast/issues/5942.
"""
# Simulate a source-read node output: columns already renamed to the mapped names.
df = pd.DataFrame(
{
"user_id": [1, 1, 2],
"value": [100, 200, 300],
"event_timestamp": [
now - timedelta(seconds=1),
now,
now,
],
}
)

context = create_context(
node_outputs={"source": ArrowTableValue(pa.Table.from_pandas(df))}
)

node = LocalDedupNode(
name="dedup",
backend=backend,
column_info=ColumnInfo(
# The raw join key matches the source column name; field_mapping maps
# it to the user-facing name that the source-read node has already
# renamed the column to.
join_keys=["USERID"],
feature_cols=["value"],
ts_col="EVENT_TIMESTAMP",
created_ts_col=None,
field_mapping={"USERID": "user_id", "EVENT_TIMESTAMP": "event_timestamp"},
),
)
node.add_input(MagicMock())
node.inputs[0].name = "source"

result = node.execute(context)

df_result = result.data.to_pandas()
assert df_result.shape[0] == 2
assert set(df_result["user_id"]) == {1, 2}


def test_local_join_node_with_field_mapping_on_join_key():
"""Regression test for materialization failure when a join key has a field mapping.

The source-read node renames columns via field_mapping (e.g. ``USERID`` -> ``user_id``)
before passing the table to downstream nodes. Without mapping ``column_info.join_keys``
the join node would call ``backend.join(..., on=["USERID"], ...)`` and raise
``KeyError(['USERID'])`` because the columns have already been renamed.

See https://github.com/feast-dev/feast/issues/5942.
"""
# Simulate two source-read node outputs: columns already renamed to the mapped names.
left_df = pd.DataFrame(
{
"user_id": [1, 2],
"value": [10, 20],
"event_timestamp": [now, now],
}
)
right_df = pd.DataFrame(
{
"user_id": [1, 2],
"other_value": [100, 200],
"event_timestamp": [now, now],
}
)

context = create_context(
node_outputs={
"left": ArrowTableValue(pa.Table.from_pandas(left_df)),
"right": ArrowTableValue(pa.Table.from_pandas(right_df)),
}
)
# Bypass the trailing entity_df join — this test exercises the input-table
# join path that consumed the raw (unmapped) join keys before the fix.
context.entity_df = None

join_node = LocalJoinNode(
name="join",
backend=backend,
column_info=ColumnInfo(
# Raw join key matches the source column name; field_mapping maps it
# to the user-facing name that the source-read node has already
# renamed the column to.
join_keys=["USERID"],
feature_cols=["value", "other_value"],
ts_col="EVENT_TIMESTAMP",
created_ts_col=None,
field_mapping={"USERID": "user_id", "EVENT_TIMESTAMP": "event_timestamp"},
),
)
left_input = MagicMock()
left_input.name = "left"
right_input = MagicMock()
right_input.name = "right"
join_node.add_input(left_input)
join_node.add_input(right_input)

result = join_node.execute(context)

df_result = result.data.to_pandas()
assert df_result.shape[0] == 2
assert set(df_result["user_id"]) == {1, 2}
assert "value" in df_result.columns
assert "other_value" in df_result.columns


def test_local_transformation_node():
context = create_context(
node_outputs={"source": ArrowTableValue(pa.Table.from_pandas(sample_df))}
Expand Down