|
29 | 29 | import com.google.protobuf.InvalidProtocolBufferException; |
30 | 30 | import io.grpc.Status; |
31 | 31 | import io.grpc.Status.Code; |
| 32 | +import java.util.ArrayList; |
| 33 | +import java.util.HashMap; |
| 34 | +import java.util.List; |
| 35 | +import java.util.Map; |
32 | 36 | import java.util.logging.Level; |
33 | 37 | import java.util.logging.Logger; |
34 | 38 |
|
@@ -163,4 +167,73 @@ public static FetchFeatureValuesResponse rowToResponse(Row row, InternalFetchReq |
163 | 167 | responseBuilder.setKeyValues(internalStorageToKeyValuesList(cell, request)); |
164 | 168 | return responseBuilder.build(); |
165 | 169 | } |
| 170 | + |
| 171 | + /** |
| 172 | + * Converts a list of Bigtable Rows to a list of FetchFeatureValuesResponse. |
| 173 | + * The output list is ordered to match the keys in {@code request.dataKeys}. |
| 174 | + * |
| 175 | + * @param rows The list of Rows returned from Bigtable. These are only the rows that were found. |
| 176 | + * @param request An {@link InternalFetchRequest} containing the ordered list of keys in {@code request.dataKeys}. |
| 177 | + * @return A {@link List<FetchFeatureValuesResponse>} where each element corresponds to a key in {@code request.dataKeys}. |
| 178 | + * If a key was not found in Bigtable, the response for that key will contain an empty {@link FeatureNameValuePairList}. |
| 179 | + * @throws UnimplementedException if {@code request.format} is PROTO_STRUCT. |
| 180 | + * @throws InternalException if conversion of any *found* row fails due to unexpected data format, |
| 181 | + * propagating the underlying issue. |
| 182 | + */ |
| 183 | + public static List<FetchFeatureValuesResponse> rowsToResponses(List<Row> rows, InternalFetchRequest request) throws Exception { |
| 184 | + if (request.format == FeatureViewDataFormat.PROTO_STRUCT) { |
| 185 | + throw new UnimplementedException( |
| 186 | + new Throwable("PROTO_STRUCT is not supported for batch fetch"), |
| 187 | + /* statusCode= */ GrpcStatusCode.of(Code.UNIMPLEMENTED), |
| 188 | + /* retryable= */ false); |
| 189 | + } |
| 190 | + |
| 191 | + if (request.dataKeys == null || request.dataKeys.isEmpty()) { |
| 192 | + return new ArrayList<>(); // Return empty list if no keys were requested. |
| 193 | + } |
| 194 | + |
| 195 | + // 1. Create a map for quick lookup of Rows by their key. |
| 196 | + Map<String, Row> keyToRowMap = new HashMap<>(); |
| 197 | + for (Row row : rows) { |
| 198 | + // Row.getKey() returns a ByteString, convert to String. |
| 199 | + keyToRowMap.put(row.getKey().toStringUtf8(), row); |
| 200 | + } |
| 201 | + |
| 202 | + // 2. Build the ordered list of responses. |
| 203 | + List<FetchFeatureValuesResponse> responses = new ArrayList<>(request.dataKeys.size()); |
| 204 | + |
| 205 | + // Iterate through the *ordered* requested keys from InternalFetchRequest.dataKeys. |
| 206 | + for (String dataKey : request.dataKeys) { |
| 207 | + Row foundRow = keyToRowMap.get(dataKey); |
| 208 | + FetchFeatureValuesResponse response; |
| 209 | + |
| 210 | + if (foundRow != null) { |
| 211 | + // Key was found in Bigtable. Convert the Row to a FeatureViewCell and then to a Response. |
| 212 | + try { |
| 213 | + FeatureViewCell cell = rowToFeatureViewCell(foundRow, request); |
| 214 | + response = FetchFeatureValuesResponse.newBuilder() |
| 215 | + .setKeyValues(internalStorageToKeyValuesList(cell, request)) |
| 216 | + .build(); |
| 217 | + } catch (Exception e) { |
| 218 | + // If conversion of a *found* row fails, it indicates an internal data issue. |
| 219 | + // Following the pattern of rowToResponse, we throw an InternalException. |
| 220 | + throw new InternalException( |
| 221 | + new Throwable(String.format("Failed to convert Bigtable Row for key '%s': %s", dataKey, e.getMessage()), e), |
| 222 | + /* statusCode= */ GrpcStatusCode.of(Status.Code.INTERNAL), |
| 223 | + /* retryable= */ false); |
| 224 | + } |
| 225 | + } else { |
| 226 | + // Key was not found in Bigtable. Create a response indicating this. |
| 227 | + // For KEY_VALUE format, an empty FeatureNameValuePairList signifies that no features |
| 228 | + // were found for the requested key. |
| 229 | + response = FetchFeatureValuesResponse.newBuilder() |
| 230 | + .setKeyValues(FeatureNameValuePairList.getDefaultInstance()) |
| 231 | + .build(); |
| 232 | + logger.log(Level.FINE, String.format("Entity id '%s' not found in Bigtable during batch fetch.", dataKey)); |
| 233 | + } |
| 234 | + responses.add(response); |
| 235 | + } |
| 236 | + |
| 237 | + return responses; |
| 238 | + } |
166 | 239 | } |
0 commit comments