Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add file with nesting two-level list structure #60

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

wgtmac
Copy link
Member

@wgtmac wgtmac commented Oct 17, 2024

See: apache/arrow#43994

The Parquet file was created by parquet-java 1.14.3 with following code:

package org.example;

import org.apache.avro.Schema;
import org.apache.avro.generic.GenericData;
import org.apache.avro.generic.GenericRecord;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.parquet.avro.AvroParquetWriter;
import org.apache.parquet.hadoop.ParquetWriter;

import java.io.IOException;
import java.util.Arrays;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class TwoLevelList {

  public static void main(String[] args) {
    Schema schema = new Schema.Parser().parse("{"
      + "\"type\":\"record\","
      + "\"name\":\"my_record\","
      + "\"fields\":["
      + "  {"
      + "    \"name\":\"a\","
      + "    \"type\":{\"type\":\"array\", \"items\":{\"type\":\"array\", \"items\":\"int\"}}"
      + "  }"
      + "]"
      + "}");

    GenericRecord record = new GenericData.Record(schema);

    // Write [[1, 2], [3, 4]] to the avro record
    record.put("a", Stream.of(Arrays.asList(1, 2), Arrays.asList(3, 4))
      .map(list -> {
        Schema innerListType = schema.getField("a").schema().getElementType();
        GenericData.Array<Integer> innerList = new GenericData.Array<>(list.size(), innerListType);
        innerList.addAll(list);
        return innerList;
      }).collect(Collectors.toList()));

    Path file = new Path("/tmp/old_list_structure.parquet");
    Configuration conf = new Configuration();
    conf.set("parquet.avro.write-old-list-structure", "true");  // this is the default value
    try (ParquetWriter<GenericRecord> writer = AvroParquetWriter.<GenericRecord>builder(file)
      .withSchema(schema)
      .withConf(conf)
      .build()) {
      writer.write(record);
    } catch (IOException e) {
      throw new RuntimeException(e);
    }
  }

}

File metadata using parquet-cli:

> parquet-cli meta /tmp/old_list_structure.parquet

File path:  /tmp/test.parquet
Created by: parquet-mr version 1.14.3 (build b5e376a2caee767a11e75b783512b14cf8ca90ec)
Properties:
  parquet.avro.schema: {"type":"record","name":"my_record","fields":[{"name":"a","type":{"type":"array","items":{"type":"array","items":"int"}}}]}
    writer.model.name: avro
Schema:
message my_record {
  required group a (LIST) {
    repeated group array (LIST) {
      repeated int32 array;
    }
  }
}


Row group 0:  count: 1  53.00 B records  start: 4  total(compressed): 53 B total(uncompressed):53 B
--------------------------------------------------------------------------------
               type      encodings count     avg size   nulls   min / max
a.array.array  INT32     _   _     4         13.25 B    0       "1" / "4"

Data using parquet-cli:

> parquet-cli cat /tmp/old_list_structure.parquet

{"a": [[1, 2], [3, 4]]}

@wgtmac
Copy link
Member Author

wgtmac commented Oct 17, 2024

@mapleFU @pitrou Could you please take a look? Thanks!

@pitrou
Copy link
Member

pitrou commented Oct 17, 2024

I don't understand how the structure here is different from the "list of lists" example in https://github.com/apache/parquet-format/blob/master/LogicalTypes.md#lists

Witness:

required group a (LIST) {
  repeated group list {
    required group element (LIST) {
      repeated group list {
        required int32 element;
      }
    }
  }
}

vs.

optional group array_of_arrays (LIST) {
  repeated group list {
    required group element (LIST) {
      repeated group list {
        required int32 element;
      }
    }
  }
}

@wgtmac
Copy link
Member Author

wgtmac commented Oct 17, 2024

My bad! I pasted the wrong result which disabled parquet.avro.write-old-list-structure. Now the description is fixed. @pitrou

data/README.md Outdated
@@ -53,6 +53,7 @@
| incorrect_map_schema.parquet | Contains a Map schema without explicitly required keys, produced by Presto. See [note](#incorrect-map-schema) |
| column_chunk_key_value_metadata.parquet | two INT32 columns, one with column chunk key-value metadata {"foo": "bar", "thisiskeywithoutvalue": null} note that the second key "thisiskeywithoutvalue", does not have a value, but the value can be mapped to an empty string "" when read depending on the client |
| sorting_columns.parquet | INT64 and BYTE_ARRAY columns with first column with nulls first and descending, second column with nulls last and ascending. This file contains two row groups with same data and sorting columns. |
| old_list_structure.parquet | Single LIST<LIST<INT32>> column with legacy two-level list structure |
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you document the file like you did in the PR description?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added. Please take a look again. Thanks!

Copy link
Member

@mapleFU mapleFU left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

General LGTM


Here is the file metadata printed by parquet-cli:
```
File path: /tmp/old_list_structure.parquet
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should /tmp be here or not?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This matches the file location in the code above. :)

Here is the file metadata printed by parquet-cli:
```
File path: /tmp/old_list_structure.parquet
Created by: parquet-mr version 1.14.3 (build b5e376a2caee767a11e75b783512b14cf8ca90ec)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this build a valid release? Or just run by master?

Copy link
Member Author

@wgtmac wgtmac Oct 18, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a released version. Otherwise it will be 1.14.3-SNAPSHOT.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants