ジェームズ・ウィロー:
私が使用していますツール持ってorg.apache.parquet.hadoop.ParquetWriterを寄木細工のデータファイルへのCSVデータファイルを変換することを。
現在のところ、それだけでハンドルint32
、double
と、string
私は寄木細工をサポートする必要がありますtimestamp
(int96として注釈)論理型を、そして私は私がオンライン正確な仕様を見つけることができないので、それを行う方法で失われています。
このタイムスタンプのエンコード(int96)は稀ではないだけでなく、サポートされ表示されます。私はオンライン非常に少ない仕様の詳細を発見しました。このgithubののREADMEは、と述べています:
int96は1日にナノ秒(最初の8バイト)とユリウス日(最後の4バイト)で構成されているようにタイムスタンプを保存しました。
具体的に:
- どの寄木細工タイプ Iは、列のために使うのですかのMessageTypeのスキーマ?私は、私がプリミティブ型を使用する必要がありますと仮定し
PrimitiveTypeName.INT96
ますが、私は論理型を指定する方法があるかもしれないかどうかわからないんだけど? - どうやってデータを書き込むのですか?つまり、どのような形式で、私はグループにタイムスタンプを書くのですか?INT96タイムスタンプのために、私はいくつかのバイナリ型を記述する必要がありますと仮定しますか?
ここで私が何をしようとしています何を示している私のコードの簡易版です。具体的には、「TODO」コメントを見て、これらはコード内の2つの点があること上記の質問への相関。
List<Type> fields = new ArrayList<>();
fields.add(new PrimitiveType(Type.Repetition.OPTIONAL, PrimitiveTypeName.INT32, "int32_col", null));
fields.add(new PrimitiveType(Type.Repetition.OPTIONAL, PrimitiveTypeName.DOUBLE, "double_col", null));
fields.add(new PrimitiveType(Type.Repetition.OPTIONAL, PrimitiveTypeName.STRING, "string_col", null));
// TODO:
// Specify the TIMESTAMP type.
// How? INT96 primitive type? Is there a logical timestamp type I can use w/ MessageType schema?
fields.add(new PrimitiveType(Type.Repetition.OPTIONAL, PrimitiveTypeName.INT96, "timestamp_col", null));
MessageType schema = new MessageType("input", fields);
// initialize writer
Configuration configuration = new Configuration();
configuration.setQuietMode(true);
GroupWriteSupport.setSchema(schema, configuration);
ParquetWriter<Group> writer = new ParquetWriter<Group>(
new Path("output.parquet"),
new GroupWriteSupport(),
CompressionCodecName.SNAPPY,
ParquetWriter.DEFAULT_BLOCK_SIZE,
ParquetWriter.DEFAULT_PAGE_SIZE,
1048576,
true,
false,
ParquetProperties.WriterVersion.PARQUET_1_0,
configuration
);
// write CSV data
CSVParser parser = CSVParser.parse(new File(csv), StandardCharsets.UTF_8, CSVFormat.TDF.withQuote(null));
ArrayList<String> columns = new ArrayList<>(schemaMap.keySet());
int colIndex;
int rowNum = 0;
for (CSVRecord csvRecord : parser) {
rowNum ++;
Group group = f.newGroup();
colIndex = 0;
for (String record : csvRecord) {
if (record == null || record.isEmpty() || record.equals( "NULL")) {
colIndex++;
continue;
}
record = record.trim();
String type = schemaMap.get(columns.get(colIndex)).get("type").toString();
MessageTypeConverter.addTypeValueToGroup(type, record, group, colIndex++);
switch (colIndex) {
case 0: // int32
group.add(colIndex, Integer.parseInt(record));
break;
case 1: // double
group.add(colIndex, Double.parseDouble(record));
break;
case 2: // string
group.add(colIndex, record);
break;
case 3:
// TODO: convert CSV string value to TIMESTAMP type (how?)
throw new NotImplementedException();
}
}
writer.write(group);
}
writer.close();
ジェームズ・ウィロー:
私が使用して、それを考え出し、このコードを基準としてスパークSQLから。
INT96バイナリエンコーディングは、2つの部分に分割される:真夜中最後の4バイトであるため、最初の8つのバイトはナノ秒であるユリウス日
String value = "2019-02-13 13:35:05";
final long NANOS_PER_HOUR = TimeUnit.HOURS.toNanos(1);
final long NANOS_PER_MINUTE = TimeUnit.MINUTES.toNanos(1);
final long NANOS_PER_SECOND = TimeUnit.SECONDS.toNanos(1);
// Parse date
SimpleDateFormat parser = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
cal.setTime(parser.parse(value));
// Calculate Julian days and nanoseconds in the day
LocalDate dt = LocalDate.of(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH)+1, cal.get(Calendar.DAY_OF_MONTH));
int julianDays = (int) JulianFields.JULIAN_DAY.getFrom(dt);
long nanos = (cal.get(Calendar.HOUR_OF_DAY) * NANOS_PER_HOUR)
+ (cal.get(Calendar.MINUTE) * NANOS_PER_MINUTE)
+ (cal.get(Calendar.SECOND) * NANOS_PER_SECOND);
// Write INT96 timestamp
byte[] timestampBuffer = new byte[12];
ByteBuffer buf = ByteBuffer.wrap(timestampBuffer);
buf.order(ByteOrder.LITTLE_ENDIAN).putLong(nanos).putInt(julianDays);
// This is the properly encoded INT96 timestamp
Binary tsValue = Binary.fromReusedByteArray(timestampBuffer);