Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
179 changes: 176 additions & 3 deletions docs/reference/feature-servers/registry-server.md
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,137 @@ Most endpoints support these common query parameters:
}
```

### Features

#### List Features
- **Endpoint**: `GET /api/v1/features`
- **Description**: Retrieve all features in a project
- **Parameters**:
- `project` (required): Project name
- `feature_view` (optional): Filter by feature view name
- `name` (optional): Filter by feature name
- `include_relationships` (optional): Include relationships for each feature (relationships are keyed by feature name)
- `allow_cache` (optional): Whether to allow cached data
- `page` (optional): Page number for pagination
- `limit` (optional): Number of items per page
- `sort_by` (optional): Field to sort by
- `sort_order` (optional): Sort order ("asc" or "desc")
- **Examples**:
```bash
# Basic list
curl -H "Authorization: Bearer <token>" \
"http://localhost:6572/api/v1/features?project=my_project"

# With pagination and relationships
curl -H "Authorization: Bearer <token>" \
"http://localhost:6572/api/v1/features?project=my_project&include_relationships=true&page=1&limit=5"
```
- **Response Example**:
```json
{
"features": [
{ "name": "conv_rate", "featureView": "driver_hourly_stats_fresh", "type": "Float32" },
{ "name": "acc_rate", "featureView": "driver_hourly_stats_fresh", "type": "Float32" },
{ "name": "avg_daily_trips", "featureView": "driver_hourly_stats_fresh", "type": "Int64" },
{ "name": "conv_rate", "featureView": "driver_hourly_stats", "type": "Float32" },
{ "name": "acc_rate", "featureView": "driver_hourly_stats", "type": "Float32" }
],
"pagination": {
"page": 1,
"limit": 5,
"totalCount": 10,
"totalPages": 2,
"hasNext": true
},
"relationships": {
"conv_rate": [
{ "source": { "type": "feature", "name": "conv_rate" }, "target": { "type": "featureView", "name": "driver_hourly_stats_fresh" } },
{ "source": { "type": "feature", "name": "conv_rate" }, "target": { "type": "featureView", "name": "driver_hourly_stats" } },
{ "source": { "type": "feature", "name": "conv_rate" }, "target": { "type": "featureService", "name": "driver_activity_v1" } }
]
}
}
```

#### Get Feature
- **Endpoint**: `GET /api/v1/features/{feature_view}/{name}`
- **Description**: Retrieve a specific feature by feature view and name
- **Parameters**:
- `feature_view` (path): Feature view name
- `name` (path): Feature name
- `project` (required): Project name
- `include_relationships` (optional): Include relationships for this feature
- `allow_cache` (optional): Whether to allow cached data
- **Examples**:
```bash
# Basic get
curl -H "Authorization: Bearer <token>" \
"http://localhost:6572/api/v1/features/driver_hourly_stats/conv_rate?project=my_project"

# With relationships
curl -H "Authorization: Bearer <token>" \
"http://localhost:6572/api/v1/features/driver_hourly_stats/conv_rate?project=my_project&include_relationships=true"
```
- **Response Example**:
```json
{
"name": "conv_rate",
"featureView": "driver_hourly_stats",
"type": "Float32",
"relationships": [
{ "source": { "type": "feature", "name": "conv_rate" }, "target": { "type": "featureView", "name": "driver_hourly_stats_fresh" } },
{ "source": { "type": "feature", "name": "conv_rate" }, "target": { "type": "featureView", "name": "driver_hourly_stats" } },
{ "source": { "type": "feature", "name": "conv_rate" }, "target": { "type": "featureService", "name": "driver_activity_v1" } }
]
}
```

#### List All Features (All Projects)
- **Endpoint**: `GET /api/v1/features/all`
- **Description**: Retrieve all features across all projects. Each feature includes a `project` field.
- **Parameters**:
- `page` (optional): Page number for pagination
- `limit` (optional): Number of items per page
- `sort_by` (optional): Field to sort by
- `sort_order` (optional): Sort order ("asc" or "desc")
- `include_relationships` (optional): Include relationships for each feature
- `allow_cache` (optional): Whether to allow cached data
- **Examples**:
```bash
curl -H "Authorization: Bearer <token>" \
"http://localhost:6572/api/v1/features/all?page=1&limit=5&sort_by=name"
# With relationships
curl -H "Authorization: Bearer <token>" \
"http://localhost:6572/api/v1/features/all?include_relationships=true"
```
- **Response Example**:
```json
{
"features": [
{ "name": "conv_rate", "featureView": "driver_hourly_stats_fresh", "type": "Float32", "project": "multiproject" },
{ "name": "acc_rate", "featureView": "driver_hourly_stats_fresh", "type": "Float32", "project": "multiproject" },
{ "name": "avg_daily_trips", "featureView": "driver_hourly_stats_fresh", "type": "Int64", "project": "multiproject" },
{ "name": "conv_rate", "featureView": "driver_hourly_stats", "type": "Float32", "project": "multiproject" },
{ "name": "acc_rate", "featureView": "driver_hourly_stats", "type": "Float32", "project": "multiproject" }
],
"pagination": {
"page": 1,
"limit": 5,
"total_count": 20,
"total_pages": 4,
"has_next": true,
"has_previous": false
},
"relationships": {
"conv_rate": [
{ "source": { "type": "feature", "name": "conv_rate" }, "target": { "type": "featureView", "name": "driver_hourly_stats" } },
{ "source": { "type": "feature", "name": "conv_rate" }, "target": { "type": "featureView", "name": "driver_hourly_stats_fresh" } },
{ "source": { "type": "feature", "name": "conv_rate" }, "target": { "type": "featureService", "name": "driver_activity_v3" } }
]
}
}
```

### Feature Services

#### List Feature Services
Expand Down Expand Up @@ -356,7 +487,7 @@ Most endpoints support these common query parameters:
- **Parameters**:
- `project` (required): Project name
- `allow_cache` (optional): Whether to allow cached data
- `filter_object_type` (optional): Filter by object type (`dataSource`, `entity`, `featureView`, `featureService`)
- `filter_object_type` (optional): Filter by object type (`dataSource`, `entity`, `featureView`, `featureService`, `feature`)
- `filter_object_name` (optional): Filter by object name
- **Example**:
```bash
Expand All @@ -368,15 +499,25 @@ Most endpoints support these common query parameters:
- **Endpoint**: `GET /api/v1/lineage/objects/{object_type}/{object_name}`
- **Description**: Retrieve relationships for a specific object
- **Parameters**:
- `object_type` (path): Type of object (`dataSource`, `entity`, `featureView`, `featureService`)
- `object_type` (path): Type of object (`dataSource`, `entity`, `featureView`, `featureService`, `feature`)
- `object_name` (path): Name of the object
- `project` (required): Project name
- `include_indirect` (optional): Whether to include indirect relationships
- `allow_cache` (optional): Whether to allow cached data
- **Example**:
```bash
curl -H "Authorization: Bearer <token>" \
"http://localhost:6572/api/v1/lineage/objects/featureView/user_features?project=my_project&include_indirect=true"
"http://localhost:6572/api/v1/lineage/objects/feature/conv_rate?project=my_project&include_indirect=true"
```
- **Response Example**:
```json
{
"relationships": [
{ "source": { "type": "feature", "name": "conv_rate" }, "target": { "type": "featureView", "name": "driver_hourly_stats_fresh" } },
{ "source": { "type": "feature", "name": "conv_rate" }, "target": { "type": "featureView", "name": "driver_hourly_stats" } }
],
"pagination": { "totalCount": 2, "totalPages": 1 }
}
```

#### Get Complete Registry Data
Expand All @@ -390,6 +531,35 @@ Most endpoints support these common query parameters:
curl -H "Authorization: Bearer <token>" \
"http://localhost:6572/api/v1/lineage/complete?project=my_project"
```
- **Response Example**:
```json
{
"project": "multiproject",
"objects": {
"entities": [ ... ],
"dataSources": [ ... ],
"featureViews": [ ... ],
"featureServices": [ ... ],
"features": [
{ "name": "conv_rate", "featureView": "driver_hourly_stats_fresh", "type": "Float32" },
{ "name": "acc_rate", "featureView": "driver_hourly_stats_fresh", "type": "Float32" },
{ "name": "avg_daily_trips", "featureView": "driver_hourly_stats_fresh", "type": "Int64" },
{ "name": "conv_rate", "featureView": "driver_hourly_stats", "type": "Float32" },
{ "name": "acc_rate", "featureView": "driver_hourly_stats", "type": "Float32" },
{ "name": "conv_rate_plus_val1", "featureView": "transformed_conv_rate_fresh", "type": "Float64" },
{ "name": "conv_rate_plus_val2", "featureView": "transformed_conv_rate_fresh", "type": "Float64" },
{ "name": "conv_rate_plus_val1", "featureView": "transformed_conv_rate", "type": "Float64" },
{ "name": "conv_rate_plus_val2", "featureView": "transformed_conv_rate", "type": "Float64" }
]
},
"relationships": [ ... ],
"indirectRelationships": [ ... ],
"pagination": {
"features": { "totalCount": 10, "totalPages": 1 },
...
}
}
```

#### Get Registry Lineage (All Projects)
- **Endpoint**: `GET /api/v1/lineage/registry/all`
Expand Down Expand Up @@ -716,6 +886,7 @@ Relationships show how different Feast objects connect to each other, providing
- `entity` - Feast entities
- `dataSource` - Data sources
- `featureView` - Feature views (including regular, on-demand, and stream)
- `feature` - Features (including features from on-demand feature views)
- `featureService` - Feature services
- `permission` - Permissions
- `savedDataset` - Saved datasets
Expand All @@ -724,6 +895,8 @@ Relationships show how different Feast objects connect to each other, providing
- Feature Views → Data Sources (feature views depend on data sources)
- Feature Views → Entities (feature views use entities as join keys)
- Feature Services → Feature Views (feature services consume feature views)
- Features → Feature Views (features belong to feature views, including on-demand feature views)
- Features → Feature Services (features are consumed by feature services)
- Entities → Data Sources (entities connect to data sources through feature views)
- Entities → Feature Services (entities connect to feature services through feature views)

Expand Down
32 changes: 32 additions & 0 deletions protos/feast/registry/RegistryServer.proto
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,9 @@ service RegistryServer{
rpc GetRegistryLineage (GetRegistryLineageRequest) returns (GetRegistryLineageResponse) {}
rpc GetObjectRelationships (GetObjectRelationshipsRequest) returns (GetObjectRelationshipsResponse) {}

// Feature RPCs
rpc ListFeatures (ListFeaturesRequest) returns (ListFeaturesResponse) {}
rpc GetFeature (GetFeatureRequest) returns (Feature) {}
}

// Common pagination and sorting messages
Expand Down Expand Up @@ -524,3 +527,32 @@ message GetObjectRelationshipsResponse {
repeated EntityRelation relationships = 1;
PaginationMetadata pagination = 2;
}

// Feature messages
message Feature {
string name = 1;
string feature_view = 2;
string type = 3;
string description = 4;
map<string, string> tags = 7;
}

message ListFeaturesRequest {
string project = 1;
string feature_view = 2;
string name = 3;
bool allow_cache = 6;
PaginationParams pagination = 4;
SortingParams sorting = 5;
}

message ListFeaturesResponse {
repeated Feature features = 1;
PaginationMetadata pagination = 2;
}

message GetFeatureRequest {
string project = 1;
string feature_view = 2;
string name = 3;
}
2 changes: 2 additions & 0 deletions sdk/python/feast/api/registry/rest/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from feast.api.registry.rest.entities import get_entity_router
from feast.api.registry.rest.feature_services import get_feature_service_router
from feast.api.registry.rest.feature_views import get_feature_view_router
from feast.api.registry.rest.features import get_feature_router
from feast.api.registry.rest.lineage import get_lineage_router
from feast.api.registry.rest.permissions import get_permission_router
from feast.api.registry.rest.projects import get_project_router
Expand All @@ -15,6 +16,7 @@ def register_all_routes(app: FastAPI, grpc_handler):
app.include_router(get_data_source_router(grpc_handler))
app.include_router(get_feature_service_router(grpc_handler))
app.include_router(get_feature_view_router(grpc_handler))
app.include_router(get_feature_router(grpc_handler))
app.include_router(get_lineage_router(grpc_handler))
app.include_router(get_permission_router(grpc_handler))
app.include_router(get_project_router(grpc_handler))
Expand Down
97 changes: 97 additions & 0 deletions sdk/python/feast/api/registry/rest/features.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
from fastapi import APIRouter, Depends, Query

from feast.api.registry.rest.rest_utils import (
aggregate_across_projects,
create_grpc_pagination_params,
create_grpc_sorting_params,
get_object_relationships,
get_pagination_params,
get_relationships_for_objects,
get_sorting_params,
grpc_call,
)
from feast.registry_server import RegistryServer_pb2


def get_feature_router(grpc_handler) -> APIRouter:
router = APIRouter()

@router.get("/features")
def list_features(
project: str = Query(...),
feature_view: str = Query(None),
name: str = Query(None),
include_relationships: bool = Query(
False, description="Include relationships for each feature"
),
allow_cache: bool = Query(
True, description="Allow using cached registry data (default: true)"
),
pagination_params: dict = Depends(get_pagination_params),
sorting_params: dict = Depends(get_sorting_params),
):
req = RegistryServer_pb2.ListFeaturesRequest(
project=project,
feature_view=feature_view or "",
name=name or "",
allow_cache=allow_cache,
pagination=create_grpc_pagination_params(pagination_params),
sorting=create_grpc_sorting_params(sorting_params),
)
response = grpc_call(grpc_handler.ListFeatures, req)
if include_relationships:
relationships = get_relationships_for_objects(
grpc_handler, response["features"], "feature", project, allow_cache
)
response["relationships"] = relationships
return response

@router.get("/features/{feature_view}/{name}")
def get_feature(
feature_view: str,
name: str,
project: str = Query(...),
include_relationships: bool = Query(
False, description="Include relationships for this feature"
),
):
req = RegistryServer_pb2.GetFeatureRequest(
project=project,
feature_view=feature_view,
name=name,
)
response = grpc_call(grpc_handler.GetFeature, req)
if include_relationships:
response["relationships"] = get_object_relationships(
grpc_handler, "feature", name, project
)
return response

@router.get("/features/all")
def list_features_all(
page: int = Query(1, ge=1),
limit: int = Query(50, ge=1, le=100),
sort_by: str = Query(None),
sort_order: str = Query("asc"),
include_relationships: bool = Query(
False, description="Include relationships for each feature"
),
allow_cache: bool = Query(
True, description="Allow using cached registry data (default: true)"
),
):
return aggregate_across_projects(
grpc_handler=grpc_handler,
list_method=grpc_handler.ListFeatures,
request_cls=RegistryServer_pb2.ListFeaturesRequest,
response_key="features",
object_type="feature",
include_relationships=include_relationships,
allow_cache=allow_cache,
page=page,
limit=limit,
sort_by=sort_by,
sort_order=sort_order,
)

return router
Loading
Loading