Skip to content

Commit

Permalink
add tags filter capability to 'feature-views list'
Browse files Browse the repository at this point in the history
Signed-off-by: Tommy Hughes <tohughes@redhat.com>
  • Loading branch information
tchughesiv committed Jun 3, 2024
1 parent c08e6f9 commit d80b733
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 11 deletions.
11 changes: 8 additions & 3 deletions sdk/python/feast/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -370,16 +370,21 @@ def feature_view_describe(ctx: click.Context, name: str):


@feature_views_cmd.command(name="list")
@click.option(
"--tags",
help="Filter by tags (e.g. 'key:value, key:value, ...')",
multiple=True,
)
@click.pass_context
def feature_view_list(ctx: click.Context):
def feature_view_list(ctx: click.Context, tags: str):
"""
List all feature views
"""
store = create_feature_store(ctx)
table = []
for feature_view in [
*store.list_feature_views(),
*store.list_on_demand_feature_views(),
*store.list_feature_views(tags=tags),
*store.list_on_demand_feature_views(tags=tags),
]:
entities = set()
if isinstance(feature_view, FeatureView):
Expand Down
27 changes: 21 additions & 6 deletions sdk/python/feast/feature_store.py
Original file line number Diff line number Diff line change
Expand Up @@ -247,23 +247,28 @@ def list_feature_services(self) -> List[FeatureService]:
"""
return self._registry.list_feature_services(self.project)

def list_feature_views(self, allow_cache: bool = False) -> List[FeatureView]:
def list_feature_views(
self, allow_cache: bool = False, tags: str = ""
) -> List[FeatureView]:
"""
Retrieves the list of feature views from the registry.
Args:
tags: Tags to filter by.
allow_cache: Whether to allow returning entities from a cached registry.
Returns:
A list of feature views.
"""
return self._list_feature_views(allow_cache)
return self._list_feature_views(allow_cache=allow_cache, tags=tags)

def _list_feature_views(
self,
allow_cache: bool = False,
hide_dummy_entity: bool = True,
tags: str = "",
) -> List[FeatureView]:
tagDict = utils.tags_str_to_dict(tags)
feature_views = []
for fv in self._registry.list_feature_views(
self.project, allow_cache=allow_cache
Expand All @@ -275,7 +280,8 @@ def _list_feature_views(
):
fv.entities = []
fv.entity_columns = []
feature_views.append(fv)
if all(fv.tags.get(key, None) == val for key, val in tagDict.items()):
feature_views.append(fv)
return feature_views

def _list_stream_feature_views(
Expand All @@ -294,17 +300,26 @@ def _list_stream_feature_views(
return stream_feature_views

def list_on_demand_feature_views(
self, allow_cache: bool = False
self, allow_cache: bool = False, tags: str = ""
) -> List[OnDemandFeatureView]:
"""
Retrieves the list of on demand feature views from the registry.
Args:
tags: Tags to filter by.
allow_cache: Whether to allow returning entities from a cached registry.
Returns:
A list of on demand feature views.
"""
return self._registry.list_on_demand_feature_views(
tagDict = utils.tags_str_to_dict(tags)
on_demand_feature_views = []
for odfv in self._registry.list_on_demand_feature_views(
self.project, allow_cache=allow_cache
)
):
if all(odfv.tags.get(key, None) == val for key, val in tagDict.items()):
on_demand_feature_views.append(odfv)
return on_demand_feature_views

def list_stream_feature_views(
self, allow_cache: bool = False
Expand Down
12 changes: 11 additions & 1 deletion sdk/python/feast/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from collections import defaultdict
from datetime import datetime
from pathlib import Path
from typing import Dict, List, Optional, Tuple, Union
from typing import Dict, List, Optional, Tuple, Union, cast

import pandas as pd
import pyarrow
Expand Down Expand Up @@ -256,3 +256,13 @@ def _convert_arrow_to_proto(
created_timestamps = [None] * table.num_rows

return list(zip(entity_keys, features, event_timestamps, created_timestamps))


def tags_str_to_dict(tags: str) -> Dict[str, str]:
tagPairs = (
str(tags).strip().strip("()").replace('"', "").replace("'", "").split(",")
)
tagDict = dict(
cast(tuple[str, str], tag.split(":", 1)) for tag in tagPairs if ":" in tag
)
return {key.strip(): value.strip() for key, value in tagDict.items()}
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ def test_apply_feature_view(test_feature_store):
Field(name="entity_id", dtype=Int64),
],
entities=[entity],
tags={"team": "matchmaking"},
tags={"team": "matchmaking", "tag": "two"},
source=batch_source,
ttl=timedelta(minutes=5),
)
Expand All @@ -114,6 +114,47 @@ def test_apply_feature_view(test_feature_store):
and feature_views[0].entities[0] == "fs1_my_entity_1"
)

feature_views = test_feature_store.list_feature_views(tags="('team:matchmaking',)")

# List Feature Views
assert (
len(feature_views) == 2
and feature_views[0].name == "my_feature_view_1"
and feature_views[0].features[0].name == "fs1_my_feature_1"
and feature_views[0].features[0].dtype == Int64
and feature_views[0].features[1].name == "fs1_my_feature_2"
and feature_views[0].features[1].dtype == String
and feature_views[0].features[2].name == "fs1_my_feature_3"
and feature_views[0].features[2].dtype == Array(String)
and feature_views[0].features[3].name == "fs1_my_feature_4"
and feature_views[0].features[3].dtype == Array(Bytes)
and feature_views[0].entities[0] == "fs1_my_entity_1"
)

feature_views = test_feature_store.list_feature_views(
tags="(' team :matchmaking, tag: two ',)"
)

# List Feature Views
assert (
len(feature_views) == 1
and feature_views[0].name == "batch_feature_view"
and feature_views[0].features[0].name == "fs1_my_feature_1"
and feature_views[0].features[0].dtype == Int64
and feature_views[0].features[1].name == "fs1_my_feature_2"
and feature_views[0].features[1].dtype == String
and feature_views[0].features[2].name == "fs1_my_feature_3"
and feature_views[0].features[2].dtype == Array(String)
and feature_views[0].features[3].name == "fs1_my_feature_4"
and feature_views[0].features[3].dtype == Array(Bytes)
and feature_views[0].entities[0] == "fs1_my_entity_1"
)

feature_views = test_feature_store.list_feature_views(tags="('missing:tag',)")

# List Feature Views
assert len(feature_views) == 0

test_feature_store.teardown()


Expand Down

0 comments on commit d80b733

Please sign in to comment.