22import tempfile
33import uuid
44import warnings
5+ from dataclasses import asdict , dataclass
56from datetime import datetime , timezone
6- from typing import Any , Callable , Dict , List , Optional , Tuple , Union
7+ from typing import Any , Callable , Dict , List , Optional , Tuple , Union , cast
78
89import numpy as np
910import pandas
@@ -55,6 +56,12 @@ class SparkOfflineStoreConfig(FeastConfigBaseModel):
5556 """ AWS Region if applicable for s3-based staging locations"""
5657
5758
59+ @dataclass (frozen = True )
60+ class SparkFeatureViewQueryContext (offline_utils .FeatureViewQueryContext ):
61+ min_date_partition : Optional [str ]
62+ max_date_partition : str
63+
64+
5865class SparkOfflineStore (OfflineStore ):
5966 @staticmethod
6067 def pull_latest_from_table_or_query (
@@ -101,6 +108,7 @@ def pull_latest_from_table_or_query(
101108 aliases_as_string = ", " .join (aliases )
102109
103110 date_partition_column = data_source .date_partition_column
111+ date_partition_column_format = data_source .date_partition_column_format
104112
105113 start_date_str = _format_datetime (start_date )
106114 end_date_str = _format_datetime (end_date )
@@ -112,7 +120,7 @@ def pull_latest_from_table_or_query(
112120 SELECT { fields_as_string } ,
113121 ROW_NUMBER() OVER({ partition_by_join_key_string } ORDER BY { timestamp_desc_string } ) AS feast_row_
114122 FROM { from_expression } t1
115- WHERE { timestamp_field } BETWEEN TIMESTAMP('{ start_date_str } ') AND TIMESTAMP('{ end_date_str } '){ " AND " + date_partition_column + " >= '" + start_date .strftime ("%Y-%m-%d" ) + "' AND " + date_partition_column + " <= '" + end_date .strftime ("%Y-%m-%d" ) + "' " if date_partition_column != "" and date_partition_column is not None else "" }
123+ WHERE { timestamp_field } BETWEEN TIMESTAMP('{ start_date_str } ') AND TIMESTAMP('{ end_date_str } '){ " AND " + date_partition_column + " >= '" + start_date .strftime (date_partition_column_format ) + "' AND " + date_partition_column + " <= '" + end_date .strftime (date_partition_column_format ) + "' " if date_partition_column != "" and date_partition_column is not None else "" }
116124 ) t2
117125 WHERE feast_row_ = 1
118126 """
@@ -136,8 +144,12 @@ def get_historical_features(
136144 full_feature_names : bool = False ,
137145 ) -> RetrievalJob :
138146 assert isinstance (config .offline_store , SparkOfflineStoreConfig )
147+ date_partition_column_formats = []
139148 for fv in feature_views :
140149 assert isinstance (fv .batch_source , SparkSource )
150+ date_partition_column_formats .append (
151+ fv .batch_source .date_partition_column_format
152+ )
141153
142154 warnings .warn (
143155 "The spark offline store is an experimental feature in alpha development. "
@@ -186,8 +198,27 @@ def get_historical_features(
186198 entity_df_event_timestamp_range ,
187199 )
188200
201+ spark_query_context = [
202+ SparkFeatureViewQueryContext (
203+ ** asdict (context ),
204+ min_date_partition = datetime .fromisoformat (
205+ context .min_event_timestamp
206+ ).strftime (date_format )
207+ if context .min_event_timestamp is not None
208+ else None ,
209+ max_date_partition = datetime .fromisoformat (
210+ context .max_event_timestamp
211+ ).strftime (date_format ),
212+ )
213+ for date_format , context in zip (
214+ date_partition_column_formats , query_context
215+ )
216+ ]
217+
189218 query = offline_utils .build_point_in_time_query (
190- feature_view_query_contexts = query_context ,
219+ feature_view_query_contexts = cast (
220+ List [offline_utils .FeatureViewQueryContext ], spark_query_context
221+ ),
191222 left_table_query_string = tmp_entity_df_table_name ,
192223 entity_df_event_timestamp_col = event_timestamp_col ,
193224 entity_df_columns = entity_schema .keys (),
@@ -651,13 +682,13 @@ def _cast_data_frame(
651682 FROM {{ featureview.table_subquery }}
652683 WHERE {{ featureview.timestamp_field }} <= '{{ featureview.max_event_timestamp }}'
653684 {% if featureview.date_partition_column != "" and featureview.date_partition_column is not none %}
654- AND {{ featureview.date_partition_column }} <= '{{ featureview.max_event_timestamp[:10] }}'
685+ AND {{ featureview.date_partition_column }} <= '{{ featureview.max_date_partition }}'
655686 {% endif %}
656687
657688 {% if featureview.ttl == 0 %}{% else %}
658689 AND {{ featureview.timestamp_field }} >= '{{ featureview.min_event_timestamp }}'
659690 {% if featureview.date_partition_column != "" and featureview.date_partition_column is not none %}
660- AND {{ featureview.date_partition_column }} >= '{{ featureview.min_event_timestamp[:10] }}'
691+ AND {{ featureview.date_partition_column }} >= '{{ featureview.min_date_partition }}'
661692 {% endif %}
662693 {% endif %}
663694 ),
0 commit comments