|
47 | 47 | import java.io.ByteArrayOutputStream; |
48 | 48 | import java.io.File; |
49 | 49 | import java.io.IOException; |
| 50 | +import java.nio.ByteBuffer; |
50 | 51 | import java.time.Duration; |
51 | 52 | import java.util.HashMap; |
52 | 53 | import java.util.List; |
53 | 54 | import java.util.Map; |
54 | 55 | import java.util.stream.Collectors; |
55 | 56 | import org.apache.avro.Schema; |
56 | 57 | import org.apache.avro.SchemaBuilder; |
| 58 | +import org.apache.avro.generic.GenericData; |
57 | 59 | import org.apache.avro.generic.GenericDatumWriter; |
58 | 60 | import org.apache.avro.generic.GenericRecord; |
59 | 61 | import org.apache.avro.generic.GenericRecordBuilder; |
60 | | -import org.apache.avro.io.*; |
| 62 | +import org.apache.avro.io.Encoder; |
| 63 | +import org.apache.avro.io.EncoderFactory; |
61 | 64 | import org.junit.ClassRule; |
62 | 65 | import org.junit.jupiter.api.AfterAll; |
63 | 66 | import org.junit.jupiter.api.BeforeAll; |
@@ -140,9 +143,6 @@ static void globalSetup() throws IOException { |
140 | 143 | + ":" |
141 | 144 | + environment.getServicePort("bigtable_1", BIGTABLE_PORT); |
142 | 145 | channel = ManagedChannelBuilder.forTarget(endpoint).usePlaintext().build(); |
143 | | - TransportChannelProvider channelProvider = |
144 | | - FixedTransportChannelProvider.create(GrpcTransportChannel.create(channel)); |
145 | | - NoCredentialsProvider credentialsProvider = NoCredentialsProvider.create(); |
146 | 146 |
|
147 | 147 | /** Feast resource creation Workflow */ |
148 | 148 | String projectName = "default"; |
@@ -210,9 +210,6 @@ static void globalSetup() throws IOException { |
210 | 210 | ImmutableList<String> compoundColumnFamilies = |
211 | 211 | ImmutableList.of(rideMerchantFeatureTableName, metadataColumnFamily); |
212 | 212 |
|
213 | | - createTable(channelProvider, credentialsProvider, btTableName, columnFamilies); |
214 | | - createTable(channelProvider, credentialsProvider, compoundBtTableName, compoundColumnFamilies); |
215 | | - |
216 | 213 | /** Single Entity Ingestion Workflow */ |
217 | 214 | Schema ftSchema = |
218 | 215 | SchemaBuilder.record("DriverData") |
@@ -319,7 +316,9 @@ private static void createTable( |
319 | 316 | for (String columnFamily : columnFamilies) { |
320 | 317 | createTableRequest.addFamily(columnFamily); |
321 | 318 | } |
322 | | - client.createTable(createTableRequest); |
| 319 | + if (!client.exists(tableName)) { |
| 320 | + client.createTable(createTableRequest); |
| 321 | + } |
323 | 322 | } |
324 | 323 | } |
325 | 324 |
|
@@ -348,17 +347,31 @@ private static byte[] createEntityValue( |
348 | 347 | return entityFeatureValue; |
349 | 348 | } |
350 | 349 |
|
| 350 | + private static byte[] schemaReference(Schema schema) { |
| 351 | + return Hashing.murmur3_32().hashBytes(schema.toString().getBytes()).asBytes(); |
| 352 | + } |
| 353 | + |
351 | 354 | private static void ingestData( |
352 | 355 | String featureTableName, |
353 | 356 | String btTableName, |
354 | 357 | byte[] btEntityFeatureKey, |
355 | 358 | byte[] btEntityFeatureValue, |
356 | 359 | byte[] btSchemaKey, |
357 | | - Schema btSchema) { |
| 360 | + Schema btSchema) |
| 361 | + throws IOException { |
358 | 362 | String emptyQualifier = ""; |
359 | 363 | String avroQualifier = "avro"; |
360 | 364 | String metadataColumnFamily = "metadata"; |
361 | 365 |
|
| 366 | + TransportChannelProvider channelProvider = |
| 367 | + FixedTransportChannelProvider.create(GrpcTransportChannel.create(channel)); |
| 368 | + NoCredentialsProvider credentialsProvider = NoCredentialsProvider.create(); |
| 369 | + createTable( |
| 370 | + channelProvider, |
| 371 | + credentialsProvider, |
| 372 | + btTableName, |
| 373 | + ImmutableList.of(featureTableName, metadataColumnFamily)); |
| 374 | + |
362 | 375 | // Update Compound Entity-Feature Row |
363 | 376 | client.mutateRow( |
364 | 377 | RowMutation.create(btTableName, ByteString.copyFrom(btEntityFeatureKey)) |
@@ -601,6 +614,118 @@ public void shouldReturnCorrectRowCount() { |
601 | 614 | assertEquals(expectedFieldValuesList, featureResponse.getFieldValuesList()); |
602 | 615 | } |
603 | 616 |
|
| 617 | + @Test |
| 618 | + public void shouldSupportAllFeastTypes() throws IOException { |
| 619 | + EntityProto.EntitySpecV2 entitySpec = |
| 620 | + EntityProto.EntitySpecV2.newBuilder() |
| 621 | + .setName("entity") |
| 622 | + .setDescription("") |
| 623 | + .setValueType(ValueProto.ValueType.Enum.STRING) |
| 624 | + .build(); |
| 625 | + TestUtils.applyEntity(coreClient, "default", entitySpec); |
| 626 | + |
| 627 | + ImmutableMap<String, ValueProto.ValueType.Enum> allTypesFeatures = |
| 628 | + new ImmutableMap.Builder<String, ValueProto.ValueType.Enum>() |
| 629 | + .put("f_int64", ValueProto.ValueType.Enum.INT64) |
| 630 | + .put("f_int32", ValueProto.ValueType.Enum.INT32) |
| 631 | + .put("f_float", ValueProto.ValueType.Enum.FLOAT) |
| 632 | + .put("f_double", ValueProto.ValueType.Enum.DOUBLE) |
| 633 | + .put("f_string", ValueProto.ValueType.Enum.STRING) |
| 634 | + .put("f_bytes", ValueProto.ValueType.Enum.BYTES) |
| 635 | + .put("f_bool", ValueProto.ValueType.Enum.BOOL) |
| 636 | + .put("f_int64_list", ValueProto.ValueType.Enum.INT64_LIST) |
| 637 | + .put("f_int32_list", ValueProto.ValueType.Enum.INT32_LIST) |
| 638 | + .put("f_float_list", ValueProto.ValueType.Enum.FLOAT_LIST) |
| 639 | + .put("f_double_list", ValueProto.ValueType.Enum.DOUBLE_LIST) |
| 640 | + .put("f_string_list", ValueProto.ValueType.Enum.STRING_LIST) |
| 641 | + .put("f_bytes_list", ValueProto.ValueType.Enum.BYTES_LIST) |
| 642 | + .put("f_bool_list", ValueProto.ValueType.Enum.BOOL_LIST) |
| 643 | + .build(); |
| 644 | + |
| 645 | + TestUtils.applyFeatureTable( |
| 646 | + coreClient, "default", "all_types", ImmutableList.of("entity"), allTypesFeatures, 7200); |
| 647 | + |
| 648 | + Schema schema = |
| 649 | + SchemaBuilder.record("AllTypesRecord") |
| 650 | + .namespace("") |
| 651 | + .fields() |
| 652 | + .requiredLong("f_int64") |
| 653 | + .requiredInt("f_int32") |
| 654 | + .requiredFloat("f_float") |
| 655 | + .requiredDouble("f_double") |
| 656 | + .requiredString("f_string") |
| 657 | + .requiredBytes("f_bytes") |
| 658 | + .requiredBoolean("f_bool") |
| 659 | + .name("f_int64_list") |
| 660 | + .type(SchemaBuilder.array().items(SchemaBuilder.builder().longType())) |
| 661 | + .noDefault() |
| 662 | + .name("f_int32_list") |
| 663 | + .type(SchemaBuilder.array().items(SchemaBuilder.builder().intType())) |
| 664 | + .noDefault() |
| 665 | + .name("f_float_list") |
| 666 | + .type(SchemaBuilder.array().items(SchemaBuilder.builder().floatType())) |
| 667 | + .noDefault() |
| 668 | + .name("f_double_list") |
| 669 | + .type(SchemaBuilder.array().items(SchemaBuilder.builder().doubleType())) |
| 670 | + .noDefault() |
| 671 | + .name("f_string_list") |
| 672 | + .type(SchemaBuilder.array().items(SchemaBuilder.builder().stringType())) |
| 673 | + .noDefault() |
| 674 | + .name("f_bytes_list") |
| 675 | + .type(SchemaBuilder.array().items(SchemaBuilder.builder().bytesType())) |
| 676 | + .noDefault() |
| 677 | + .name("f_bool_list") |
| 678 | + .type(SchemaBuilder.array().items(SchemaBuilder.builder().booleanType())) |
| 679 | + .noDefault() |
| 680 | + .endRecord(); |
| 681 | + |
| 682 | + GenericData.Record record = |
| 683 | + new GenericRecordBuilder(schema) |
| 684 | + .set("f_int64", 10L) |
| 685 | + .set("f_int32", 10) |
| 686 | + .set("f_float", 10.0) |
| 687 | + .set("f_double", 10.0D) |
| 688 | + .set("f_string", "test") |
| 689 | + .set("f_bytes", ByteBuffer.wrap("test".getBytes())) |
| 690 | + .set("f_bool", true) |
| 691 | + .set("f_int64_list", ImmutableList.of(10L)) |
| 692 | + .set("f_int32_list", ImmutableList.of(10)) |
| 693 | + .set("f_float_list", ImmutableList.of(10.0)) |
| 694 | + .set("f_double_list", ImmutableList.of(10.0D)) |
| 695 | + .set("f_string_list", ImmutableList.of("test")) |
| 696 | + .set("f_bytes_list", ImmutableList.of(ByteBuffer.wrap("test".getBytes()))) |
| 697 | + .set("f_bool_list", ImmutableList.of(true)) |
| 698 | + .build(); |
| 699 | + |
| 700 | + ValueProto.Value entity = DataGenerator.createStrValue("key"); |
| 701 | + |
| 702 | + ingestData( |
| 703 | + "all_types", |
| 704 | + "default__entity", |
| 705 | + entity.getStringVal().getBytes(), |
| 706 | + createEntityValue(schema, schemaReference(schema), record), |
| 707 | + createSchemaKey(schemaReference(schema)), |
| 708 | + schema); |
| 709 | + |
| 710 | + GetOnlineFeaturesRequestV2 onlineFeatureRequest = |
| 711 | + TestUtils.createOnlineFeatureRequest( |
| 712 | + "default", |
| 713 | + allTypesFeatures.keySet().stream() |
| 714 | + .map( |
| 715 | + f -> |
| 716 | + FeatureReferenceV2.newBuilder() |
| 717 | + .setFeatureTable("all_types") |
| 718 | + .setName(f) |
| 719 | + .build()) |
| 720 | + .collect(Collectors.toList()), |
| 721 | + ImmutableList.of(DataGenerator.createEntityRow("entity", entity, 100))); |
| 722 | + GetOnlineFeaturesResponse featureResponse = |
| 723 | + servingStub.getOnlineFeaturesV2(onlineFeatureRequest); |
| 724 | + |
| 725 | + assert featureResponse.getFieldValues(0).getStatusesMap().values().stream() |
| 726 | + .allMatch(status -> status.equals(GetOnlineFeaturesResponse.FieldStatus.PRESENT)); |
| 727 | + } |
| 728 | + |
604 | 729 | @TestConfiguration |
605 | 730 | public static class TestConfig { |
606 | 731 | @Bean |
|
0 commit comments