public static Schema getPinotSchemaFromJsonFile(File jsonFile,
      @Nullable Map<String, FieldSpec.FieldType> fieldTypeMap, @Nullable TimeUnit timeUnit,
      @Nullable List<String> fieldsToUnnest, String delimiter,
      ComplexTypeConfig.CollectionNotUnnestedToJson collectionNotUnnestedToJson)
      throws IOException {
    JsonNode jsonNode = fileToFirstJsonNode(jsonFile);
    if (fieldsToUnnest == null) {
      fieldsToUnnest = new ArrayList<>();
    }
    Preconditions.checkNotNull(jsonNode, "the JSON data shall be an object but it is null");
    Preconditions.checkState(jsonNode.isObject(), "the JSON data shall be an object");
    return getPinotSchemaFromJsonNode(jsonNode, fieldTypeMap, timeUnit, fieldsToUnnest, delimiter,
        collectionNotUnnestedToJson);
  }
@Test
  public void testInferSchema()
      throws Exception {
    ClassLoader classLoader = JsonUtilsTest.class.getClassLoader();
    File file = new File(Objects.requireNonNull(classLoader.getResource(JSON_FILE)).getFile());
    Map<String, FieldSpec.FieldType> fieldSpecMap =
        ImmutableMap.of("d1", FieldSpec.FieldType.DIMENSION, "hoursSinceEpoch", FieldSpec.FieldType.DATE_TIME, "m1",
            FieldSpec.FieldType.METRIC);
    Schema inferredPinotSchema =
        JsonUtils.getPinotSchemaFromJsonFile(file, fieldSpecMap, TimeUnit.HOURS, new ArrayList<>(), ".",
            ComplexTypeConfig.CollectionNotUnnestedToJson.NON_PRIMITIVE);
    Schema expectedSchema = new Schema.SchemaBuilder().addSingleValueDimension("d1", FieldSpec.DataType.STRING)
        .addMetric("m1", FieldSpec.DataType.INT)
        .addSingleValueDimension("tuple.address.streetaddress", FieldSpec.DataType.STRING)
        .addSingleValueDimension("tuple.address.city", FieldSpec.DataType.STRING)
        .addSingleValueDimension("entries", FieldSpec.DataType.STRING)
        .addMultiValueDimension("d2", FieldSpec.DataType.INT)
        .addDateTime("hoursSinceEpoch", FieldSpec.DataType.INT, "1:HOURS:EPOCH", "1:HOURS").build();
    Assert.assertEquals(inferredPinotSchema, expectedSchema);

    // unnest collection entries
    inferredPinotSchema =
        JsonUtils.getPinotSchemaFromJsonFile(file, fieldSpecMap, TimeUnit.HOURS, Collections.singletonList("entries"),
            ".", ComplexTypeConfig.CollectionNotUnnestedToJson.NON_PRIMITIVE);
    expectedSchema = new Schema.SchemaBuilder().addSingleValueDimension("d1", FieldSpec.DataType.STRING)
        .addMetric("m1", FieldSpec.DataType.INT)
        .addSingleValueDimension("tuple.address.streetaddress", FieldSpec.DataType.STRING)
        .addSingleValueDimension("tuple.address.city", FieldSpec.DataType.STRING)
        .addSingleValueDimension("entries.id", FieldSpec.DataType.INT)
        .addSingleValueDimension("entries.description", FieldSpec.DataType.STRING)
        .addMultiValueDimension("d2", FieldSpec.DataType.INT)
        .addDateTime("hoursSinceEpoch", FieldSpec.DataType.INT, "1:HOURS:EPOCH", "1:HOURS").build();
    Assert.assertEquals(inferredPinotSchema, expectedSchema);

    // change delimiter
    inferredPinotSchema =
        JsonUtils.getPinotSchemaFromJsonFile(file, fieldSpecMap, TimeUnit.HOURS, Collections.singletonList(""), "_",
            ComplexTypeConfig.CollectionNotUnnestedToJson.NON_PRIMITIVE);
    expectedSchema = new Schema.SchemaBuilder().addSingleValueDimension("d1", FieldSpec.DataType.STRING)
        .addMetric("m1", FieldSpec.DataType.INT)
        .addSingleValueDimension("tuple_address_streetaddress", FieldSpec.DataType.STRING)
        .addSingleValueDimension("tuple_address_city", FieldSpec.DataType.STRING)
        .addSingleValueDimension("entries", FieldSpec.DataType.STRING)
        .addMultiValueDimension("d2", FieldSpec.DataType.INT)
        .addDateTime("hoursSinceEpoch", FieldSpec.DataType.INT, "1:HOURS:EPOCH", "1:HOURS").build();
    Assert.assertEquals(inferredPinotSchema, expectedSchema);

    // change the handling of collection-to-json option, d2 will become string
    inferredPinotSchema =
        JsonUtils.getPinotSchemaFromJsonFile(file, fieldSpecMap, TimeUnit.HOURS, Collections.singletonList("entries"),
            ".", ComplexTypeConfig.CollectionNotUnnestedToJson.ALL);
    expectedSchema = new Schema.SchemaBuilder().addSingleValueDimension("d1", FieldSpec.DataType.STRING)
        .addMetric("m1", FieldSpec.DataType.INT)
        .addSingleValueDimension("tuple.address.streetaddress", FieldSpec.DataType.STRING)
        .addSingleValueDimension("tuple.address.city", FieldSpec.DataType.STRING)
        .addSingleValueDimension("entries.id", FieldSpec.DataType.INT)
        .addSingleValueDimension("entries.description", FieldSpec.DataType.STRING)
        .addSingleValueDimension("d2", FieldSpec.DataType.STRING)
        .addDateTime("hoursSinceEpoch", FieldSpec.DataType.INT, "1:HOURS:EPOCH", "1:HOURS").build();
    Assert.assertEquals(inferredPinotSchema, expectedSchema);
  }