44import com .google .cloud .bigquery .BigQueryException ;
55import com .google .cloud .bigquery .DatasetId ;
66import com .google .cloud .bigquery .ExtractJobConfiguration ;
7+ import com .google .cloud .bigquery .Field ;
8+ import com .google .cloud .bigquery .FormatOptions ;
79import com .google .cloud .bigquery .Job ;
810import com .google .cloud .bigquery .JobInfo ;
11+ import com .google .cloud .bigquery .LoadJobConfiguration ;
912import com .google .cloud .bigquery .QueryJobConfiguration ;
13+ import com .google .cloud .bigquery .Schema ;
14+ import com .google .cloud .bigquery .Table ;
15+ import com .google .cloud .bigquery .TableDefinition ;
16+ import com .google .cloud .bigquery .TableId ;
17+ import com .google .cloud .bigquery .TableInfo ;
1018import com .google .cloud .storage .Blob ;
1119import com .google .cloud .storage .Storage ;
1220import com .google .cloud .storage .Storage .BlobListOption ;
1321import com .google .common .collect .Lists ;
1422import feast .core .FeatureSetProto .FeatureSetSpec ;
1523import feast .serving .ServingAPIProto ;
1624import feast .serving .ServingAPIProto .DataFormat ;
25+ import feast .serving .ServingAPIProto .DatasetSource ;
1726import feast .serving .ServingAPIProto .FeastServingType ;
27+ import feast .serving .ServingAPIProto .GetBatchFeaturesRequest ;
1828import feast .serving .ServingAPIProto .GetBatchFeaturesResponse ;
19- import feast .serving .ServingAPIProto .GetFeastServingTypeRequest ;
20- import feast .serving .ServingAPIProto .GetFeastServingTypeResponse ;
21- import feast .serving .ServingAPIProto .GetFeaturesRequest ;
22- import feast .serving .ServingAPIProto .GetFeaturesRequest .EntityRow ;
29+ import feast .serving .ServingAPIProto .GetFeastServingInfoRequest ;
30+ import feast .serving .ServingAPIProto .GetFeastServingInfoResponse ;
2331import feast .serving .ServingAPIProto .GetJobRequest ;
2432import feast .serving .ServingAPIProto .GetJobResponse ;
33+ import feast .serving .ServingAPIProto .GetOnlineFeaturesRequest ;
2534import feast .serving .ServingAPIProto .GetOnlineFeaturesResponse ;
2635import feast .serving .ServingAPIProto .JobStatus ;
2736import feast .serving .ServingAPIProto .JobType ;
@@ -62,22 +71,31 @@ public BigQueryServingService(
6271 this .storage = storage ;
6372 }
6473
74+ /**
75+ * {@inheritDoc}
76+ */
6577 @ Override
66- public GetFeastServingTypeResponse getFeastServingType (
67- GetFeastServingTypeRequest getFeastServingTypeRequest ) {
68- return GetFeastServingTypeResponse .newBuilder ()
78+ public GetFeastServingInfoResponse getFeastServingInfo (
79+ GetFeastServingInfoRequest getFeastServingInfoRequest ) {
80+ return GetFeastServingInfoResponse .newBuilder ()
6981 .setType (FeastServingType .FEAST_SERVING_TYPE_BATCH )
82+ .setJobStagingLocation (jobStagingLocation )
7083 .build ();
7184 }
7285
86+ /**
87+ * {@inheritDoc}
88+ */
7389 @ Override
74- public GetOnlineFeaturesResponse getOnlineFeatures (GetFeaturesRequest getFeaturesRequest ) {
90+ public GetOnlineFeaturesResponse getOnlineFeatures (GetOnlineFeaturesRequest getFeaturesRequest ) {
7591 throw Status .UNIMPLEMENTED .withDescription ("Method not implemented" ).asRuntimeException ();
7692 }
7793
94+ /**
95+ * {@inheritDoc}
96+ */
7897 @ Override
79- public GetBatchFeaturesResponse getBatchFeatures (GetFeaturesRequest getFeaturesRequest ) {
80- // TODO: Consider default maxAge in featureSetSpec during retrieval
98+ public GetBatchFeaturesResponse getBatchFeatures (GetBatchFeaturesRequest getFeaturesRequest ) {
8199
82100 List <FeatureSetSpec > featureSetSpecs =
83101 getFeaturesRequest .getFeatureSetsList ().stream ()
@@ -93,30 +111,17 @@ public GetBatchFeaturesResponse getBatchFeatures(GetFeaturesRequest getFeaturesR
93111 .asRuntimeException ();
94112 }
95113
96- if (getFeaturesRequest .getEntityRowsCount () < 1 ) {
97- throw Status .INVALID_ARGUMENT
98- .withDescription (
99- "entity_dataset_rows is required for batch retrieval in order to filter the retrieved entities." )
100- .asRuntimeException ();
101- }
102-
103- for (EntityRow entityRow :
104- getFeaturesRequest .getEntityRowsList ()) {
105- if (entityRow .getEntityTimestamp ().getSeconds () == 0 ) {
106- throw Status .INVALID_ARGUMENT
107- .withDescription (
108- "entity_timestamp field in entity_dataset_row is required for batch retrieval." )
109- .asRuntimeException ();
110- }
111- }
114+ Table entityTable = loadEntities (getFeaturesRequest .getDatasetSource ());
115+ Schema entityTableSchema = entityTable .getDefinition ().getSchema ();
116+ List <String > entityNames = entityTableSchema .getFields ().stream ().map (Field ::getName )
117+ .collect (Collectors .toList ());
112118
113119 final String query =
114120 BigQueryUtil .createQuery (
115121 getFeaturesRequest .getFeatureSetsList (),
116122 featureSetSpecs ,
117- Lists .newArrayList (getFeaturesRequest .getEntityRows (0 ).getFieldsMap ().keySet ()),
118- getFeaturesRequest .getEntityRowsList (),
119- datasetId );
123+ entityNames ,
124+ datasetId , entityTable .getFriendlyName ());
120125 log .debug ("Running BigQuery query: {}" , query );
121126
122127 String feastJobId = UUID .randomUUID ().toString ();
@@ -210,6 +215,9 @@ public GetBatchFeaturesResponse getBatchFeatures(GetFeaturesRequest getFeaturesR
210215 return GetBatchFeaturesResponse .newBuilder ().setJob (feastJob ).build ();
211216 }
212217
218+ /**
219+ * {@inheritDoc}
220+ */
213221 @ Override
214222 public GetJobResponse getJob (GetJobRequest getJobRequest ) {
215223 Optional <ServingAPIProto .Job > job = jobService .get (getJobRequest .getJob ().getId ());
@@ -220,4 +228,42 @@ public GetJobResponse getJob(GetJobRequest getJobRequest) {
220228 }
221229 return GetJobResponse .newBuilder ().setJob (job .get ()).build ();
222230 }
231+
232+ private Table loadEntities (DatasetSource datasetSource ) {
233+ switch (datasetSource .getDatasetSourceCase ()) {
234+ case FILE_SOURCE :
235+ String tableName = generateTemporaryTableName ();
236+ TableId tableId = TableId .of (projectId , datasetId , tableName );
237+ // Currently only avro supported
238+ if (datasetSource .getFileSource ().getDataFormat () != DataFormat .DATA_FORMAT_AVRO ) {
239+ throw Status .INVALID_ARGUMENT
240+ .withDescription ("Invalid file format, only avro supported" )
241+ .asRuntimeException ();
242+ }
243+ LoadJobConfiguration loadJobConfiguration = LoadJobConfiguration .of (tableId ,
244+ datasetSource .getFileSource ().getFileUrisList (),
245+ FormatOptions .avro ());
246+ Job job = bigquery .create (JobInfo .of (loadJobConfiguration ));
247+ try {
248+ job .waitFor ();
249+ return bigquery .getTable (tableId );
250+ } catch (InterruptedException e ) {
251+ throw Status .INTERNAL
252+ .withDescription ("Failed to load entity dataset into store" )
253+ .withCause (e )
254+ .asRuntimeException ();
255+ }
256+ case DATASETSOURCE_NOT_SET :
257+ default :
258+ throw Status .INVALID_ARGUMENT
259+ .withDescription ("Data source must be set." )
260+ .asRuntimeException ();
261+ }
262+ }
263+
264+ private String generateTemporaryTableName () {
265+ String source = String .format ("feast_serving_%d" , System .currentTimeMillis ());
266+ UUID uuid = UUID .fromString (source );
267+ return uuid .toString ().replaceAll ("-" , "_" );
268+ }
223269}
0 commit comments