Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
eafc7e6
feat: Search Projects RPC
zabarn Aug 26, 2024
b91245f
fix: lint failure
zabarn Aug 26, 2024
f7f7a39
fix: lint failure again
zabarn Aug 26, 2024
0a312cc
fix: lint failure
zabarn Aug 26, 2024
07cf958
fix: lint failure
zabarn Aug 26, 2024
75068e4
fix: remove restrictive protobuf bounds
zabarn Aug 27, 2024
9e4b74e
feat: Search Feature Views RPC (and search proj fix)
zabarn Aug 28, 2024
2678c14
fix: lint failure
zabarn Aug 28, 2024
1da3c63
fix: lint failure
zabarn Aug 28, 2024
5e7abe8
fix: order feature views by name
zabarn Aug 28, 2024
dcc4cbc
feat: add total counts to search responses for client to display on UI
zabarn Aug 29, 2024
1ccb020
chore: namespace search methods and proto types to expedia
zabarn Aug 29, 2024
553627c
fix: searching by timestamps
zabarn Sep 3, 2024
8dc8d9b
fix: lint failure
zabarn Sep 3, 2024
fd7b0ec
fix: switch online from bool to BoolValue
zabarn Sep 3, 2024
e7ee21f
chore: remove unneeded logic
zabarn Sep 3, 2024
304a690
fix: protobuf issues with open source fix
zabarn Sep 3, 2024
4302f75
fix: go protos
zabarn Sep 3, 2024
9a4a294
feat: add search for aiwb table
zabarn Sep 12, 2024
803c498
chore: lint fix
zabarn Sep 12, 2024
e155dbc
chore: lint fix
zabarn Sep 12, 2024
38478f6
chore: lint fix
zabarn Sep 12, 2024
c7221dc
lower grpcio versions for build
zabarn Sep 12, 2024
8ba1903
fix: incorrect fieldname
zabarn Sep 12, 2024
d10619b
feat: Search AIWB table by FeatureView name
zabarn Sep 18, 2024
5af0938
chore: lint error
zabarn Sep 18, 2024
8d4e105
fix: final AIWB requirements
zabarn Sep 24, 2024
e6b2e97
fix: explicitly add project to fv.spec in search projects
zabarn Sep 25, 2024
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
Next Next commit
feat: Search Projects RPC
  • Loading branch information
zabarn committed Aug 26, 2024
commit eafc7e6e16272eaeb50a218bb7ab4e19c88ab922
14 changes: 14 additions & 0 deletions protos/feast/registry/RegistryServer.proto
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,20 @@ service RegistryServer{
rpc Refresh (RefreshRequest) returns (google.protobuf.Empty) {}
rpc Proto (google.protobuf.Empty) returns (feast.core.Registry) {}

// Search RPCs
rpc SearchProjects (SearchProjectsRequest) returns (SearchProjectsResponse) {}
}

message SearchProjectsRequest {
string search_text = 1;
google.protobuf.Timestamp updated_at = 2;
int32 page_size = 3;
int32 page_index = 4;
}

message SearchProjectsResponse {
repeated feast.core.ProjectMetadata projects = 1;
int32 total_page_indices = 2;
}

message RefreshRequest {
Expand Down
105 changes: 101 additions & 4 deletions sdk/python/feast/infra/registry/sql.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from datetime import datetime, timezone
from enum import Enum
from pathlib import Path
from typing import Any, Callable, Dict, List, Optional, Union
from typing import Any, Callable, Dict, List, Optional, Tuple, Union

from pydantic import StrictStr
from sqlalchemy import ( # type: ignore
Expand All @@ -21,6 +21,8 @@
insert,
select,
update,
and_,
func
)
from sqlalchemy.engine import Engine

Expand Down Expand Up @@ -1001,14 +1003,14 @@ def get_all_projects(self) -> List[ProjectMetadata]:
project_name=project_id
)

project_metadata_model: ProjectMetadata = project_metadata_dict[
project_metadata: ProjectMetadata = project_metadata_dict[
project_id
]
if metadata_key == FeastMetadataKeys.PROJECT_UUID.value:
project_metadata_model.project_uuid = metadata_value
project_metadata.project_uuid = metadata_value

if metadata_key == FeastMetadataKeys.LAST_UPDATED_TIMESTAMP.value:
project_metadata_model.last_updated_timestamp = (
project_metadata.last_updated_timestamp = (
datetime.fromtimestamp(int(metadata_value), tz=timezone.utc)
)
return list(project_metadata_dict.values())
Expand Down Expand Up @@ -1043,3 +1045,98 @@ def _get_project_metadata(
return project_metadata
else:
return None

def search_projects(
self,
search_text="",
updated_at: datetime = None,
page_size=10,
page_index=0
) -> Tuple[List[ProjectMetadata], int]:
"""
Search for projects based on the provided search parameters with pagination,
using SQL queries to handle filtering efficiently, including a LIKE statement for matching search_text.
Returns a tuple of the list of ProjectMetadata objects, and the total number of pages.

:param search_text: Filter by project name using a LIKE statement.
:param updated_at: Filter projects updated after this timestamp (datetime).
:param page_size: The number of results to return per page.
:param page_index: The index for fetching the next page (used as an offset).
:return: A tuple containing the list of ProjectMetadata objects, and the total number of pages.
"""
project_metadata_dict: Dict[str, ProjectMetadata] = {}

with self.engine.begin() as conn:
# Base SQL query to count total number of matching projects
count_stmt = select(func.count().label(
'total')).select_from(feast_metadata)

if search_text:
count_stmt = count_stmt.where(
feast_metadata.c.project_id.like(f"%{search_text}%"))

if updated_at is not None:
updated_at_timestamp = int(updated_at.timestamp())
count_stmt = count_stmt.where(
and_(
feast_metadata.c.metadata_key == FeastMetadataKeys.LAST_UPDATED_TIMESTAMP.value,
feast_metadata.c.metadata_value >= str(
updated_at_timestamp)
)
)

# Execute the count query to get the total number of matching projects
total_projects = conn.execute(count_stmt).scalar()

# gRPC defaults empty page_size to 0, which overrides the default value of 10. Have to explicitly set it to 10 here.
page_size = page_size if page_size > 0 else 10
# Calculate total pages
total_page_indices = (
total_projects + page_size - 1) // page_size

# Base SQL query for retrieving projects
stmt = select(feast_metadata)

if search_text:
stmt = stmt.where(
feast_metadata.c.project_id.like(f"%{search_text}%"))

if updated_at is not None:
stmt = stmt.where(
and_(
feast_metadata.c.metadata_key == FeastMetadataKeys.LAST_UPDATED_TIMESTAMP.value,
feast_metadata.c.metadata_value >= str(
updated_at_timestamp)
)
)

# Apply ordering and pagination
stmt = stmt.order_by(feast_metadata.c.project_id).limit(
page_size).offset(page_index * page_size)

rows = conn.execute(stmt).all()

if rows:
for row in rows:
project_id = row._mapping["project_id"]
metadata_key = row._mapping["metadata_key"]
metadata_value = row._mapping["metadata_value"]

if project_id not in project_metadata_dict:
project_metadata_dict[project_id] = ProjectMetadata(
project_name=project_id
)

project_metadata: ProjectMetadata = project_metadata_dict[
project_id]

if metadata_key == FeastMetadataKeys.PROJECT_UUID.value:
project_metadata.project_uuid = metadata_value

if metadata_key == FeastMetadataKeys.LAST_UPDATED_TIMESTAMP.value:
project_metadata.last_updated_timestamp = datetime.utcfromtimestamp(
int(metadata_value))

project_list = list(project_metadata_dict.values())

return project_list, total_page_indices