1414import logging
1515import multiprocessing
1616import shutil
17+ import uuid
18+ from itertools import groupby
1719from typing import Any , Dict , List , Optional , Union
1820
1921import grpc
3032 CONFIG_SERVING_ENABLE_SSL_KEY ,
3133 CONFIG_SERVING_SERVER_SSL_CERT_KEY ,
3234 CONFIG_SERVING_URL_KEY ,
35+ CONFIG_SPARK_HISTORICAL_FEATURE_OUTPUT_FORMAT ,
36+ CONFIG_SPARK_HISTORICAL_FEATURE_OUTPUT_LOCATION ,
3337 FEAST_DEFAULT_OPTIONS ,
3438)
3539from feast .core .CoreService_pb2 import (
7074 _write_partitioned_table_from_source ,
7175)
7276from feast .online_response import OnlineResponse , _infer_online_entity_rows
77+ from feast .pyspark .abc import RetrievalJob
78+ from feast .pyspark .launcher import (
79+ start_historical_feature_retrieval_job ,
80+ start_historical_feature_retrieval_spark_session ,
81+ )
7382from feast .serving .ServingService_pb2 import (
7483 GetFeastServingInfoRequest ,
7584 GetOnlineFeaturesRequestV2 ,
@@ -723,7 +732,6 @@ def get_online_features(
723732 ) -> OnlineResponse :
724733 """
725734 Retrieves the latest online feature data from Feast Serving.
726-
727735 Args:
728736 feature_refs: List of feature references that will be returned for each entity.
729737 Each feature reference should have the following format:
@@ -733,12 +741,10 @@ def get_online_features(
733741 entity_rows: A list of dictionaries where each key-value is an entity-name, entity-value pair.
734742 project: Optionally specify the the project override. If specified, uses given project for retrieval.
735743 Overrides the projects specified in Feature References if also are specified.
736-
737744 Returns:
738745 GetOnlineFeaturesResponse containing the feature data in records.
739746 Each EntityRow provided will yield one record, which contains
740747 data fields with data value and field status metadata (if included).
741-
742748 Examples:
743749 >>> from feast import Client
744750 >>>
@@ -767,3 +773,113 @@ def get_online_features(
767773
768774 response = OnlineResponse (response )
769775 return response
776+
777+ def get_historical_features (
778+ self ,
779+ feature_refs : List [str ],
780+ entity_source : Union [FileSource , BigQuerySource ],
781+ project : str = None ,
782+ ) -> RetrievalJob :
783+ """
784+ Launch a historical feature retrieval job.
785+
786+ Args:
787+ feature_refs: List of feature references that will be returned for each entity.
788+ Each feature reference should have the following format:
789+ "feature_table:feature" where "feature_table" & "feature" refer to
790+ the feature and feature table names respectively.
791+ entity_source (Union[FileSource, BigQuerySource]): Source for the entity rows.
792+ The user needs to make sure that the source is accessible from the Spark cluster
793+ that will be used for the retrieval job.
794+ project: Specifies the project that contains the feature tables
795+ which the requested features belong to.
796+
797+ Returns:
798+ Returns a retrieval job object that can be used to monitor retrieval
799+ progress asynchronously, and can be used to materialize the
800+ results.
801+
802+ Examples:
803+ >>> from feast import Client
804+ >>> from datetime import datetime
805+ >>> feast_client = Client(core_url="localhost:6565")
806+ >>> feature_refs = ["bookings:bookings_7d", "bookings:booking_14d"]
807+ >>> entity_source = FileSource("event_timestamp", "parquet", "gs://some-bucket/customer")
808+ >>> feature_retrieval_job = feast_client.get_historical_features(
809+ >>> feature_refs, entity_source, project="my_project")
810+ >>> output_file_uri = feature_retrieval_job.get_output_file_uri()
811+ "gs://some-bucket/output/
812+ """
813+ feature_tables = self ._get_feature_tables_from_feature_refs (
814+ feature_refs , project
815+ )
816+ output_location = self ._config .get (
817+ CONFIG_SPARK_HISTORICAL_FEATURE_OUTPUT_LOCATION
818+ )
819+ output_format = self ._config .get (CONFIG_SPARK_HISTORICAL_FEATURE_OUTPUT_FORMAT )
820+ job_id = f"historical-feature-{ str (uuid .uuid4 ())} "
821+
822+ return start_historical_feature_retrieval_job (
823+ self , entity_source , feature_tables , output_format , output_location , job_id
824+ )
825+
826+ def get_historical_features_df (
827+ self ,
828+ feature_refs : List [str ],
829+ entity_source : Union [FileSource , BigQuerySource ],
830+ project : str = None ,
831+ ):
832+ """
833+ Launch a historical feature retrieval job.
834+
835+ Args:
836+ feature_refs: List of feature references that will be returned for each entity.
837+ Each feature reference should have the following format:
838+ "feature_table:feature" where "feature_table" & "feature" refer to
839+ the feature and feature table names respectively.
840+ entity_source (Union[FileSource, BigQuerySource]): Source for the entity rows.
841+ The user needs to make sure that the source is accessible from the Spark cluster
842+ that will be used for the retrieval job.
843+ project: Specifies the project that contains the feature tables
844+ which the requested features belong to.
845+
846+ Returns:
847+ Returns the historical feature retrieval result in the form of Spark dataframe.
848+
849+ Examples:
850+ >>> from feast import Client
851+ >>> from datetime import datetime
852+ >>> from pyspark.sql import SparkSession
853+ >>> spark = SparkSession.builder.getOrCreate()
854+ >>> feast_client = Client(core_url="localhost:6565")
855+ >>> feature_refs = ["bookings:bookings_7d", "bookings:booking_14d"]
856+ >>> entity_source = FileSource("event_timestamp", "parquet", "gs://some-bucket/customer")
857+ >>> df = feast_client.get_historical_features(
858+ >>> feature_refs, entity_source, project="my_project")
859+ """
860+ feature_tables = self ._get_feature_tables_from_feature_refs (
861+ feature_refs , project
862+ )
863+ return start_historical_feature_retrieval_spark_session (
864+ self , entity_source , feature_tables
865+ )
866+
867+ def _get_feature_tables_from_feature_refs (
868+ self , feature_refs : List [str ], project : Optional [str ]
869+ ):
870+ feature_refs_grouped_by_table = [
871+ (feature_table_name , list (grouped_feature_refs ))
872+ for feature_table_name , grouped_feature_refs in groupby (
873+ feature_refs , lambda x : x .split (":" )[0 ]
874+ )
875+ ]
876+
877+ feature_tables = []
878+ for feature_table_name , grouped_feature_refs in feature_refs_grouped_by_table :
879+ feature_table = self .get_feature_table (feature_table_name , project )
880+ feature_names = [f .split (":" )[- 1 ] for f in grouped_feature_refs ]
881+ feature_table .features = [
882+ f for f in feature_table .features if f .name in feature_names
883+ ]
884+ feature_tables .append (feature_table )
885+ return feature_tables
0 commit comments