Arrow 数据集#
Arrow C++ 提供了数据集
的概念和实现,用于处理碎片化数据,这些数据可能大于内存,这可能是由于生成了大量数据、从流中读取数据或磁盘上存在大文件。在本文中,您将
读取多分区数据集并将其放入表格中,
从表格中写入分区数据集。
先决条件#
在继续之前,请确保您已
安装 Arrow,您可以在此处进行设置:在您自己的项目中使用 Arrow C++
了解基本 Arrow 数据结构中的基本 Arrow 数据结构
为了了解差异,阅读Arrow 文件 I/O可能会有所帮助。但是,这不是必需的。
设置#
在运行某些计算之前,我们需要填补一些空白
我们需要包含必要的头文件。
需要一个
main()
函数将所有内容粘合在一起。我们需要磁盘上的数据才能进行操作。
包含#
在编写 C++ 代码之前,我们需要一些包含文件。我们将获取 iostream
用于输出,然后为本文中将使用的每种文件类型导入 Arrow 的计算功能
#include <arrow/api.h>
#include <arrow/dataset/api.h>
// We use Parquet headers for setting up examples; they are not required for using
// datasets.
#include <parquet/arrow/reader.h>
#include <parquet/arrow/writer.h>
#include <unistd.h>
#include <iostream>
Main()#
对于我们的粘合剂,我们将使用上一个关于数据结构的教程中的 main()
模式
int main() {
arrow::Status st = RunMain();
if (!st.ok()) {
std::cerr << st << std::endl;
return 1;
}
return 0;
}
与我们之前使用它时一样,它与 RunMain()
配对
arrow::Status RunMain() {
生成用于读取的文件#
我们需要一些文件来实际操作。在实践中,您可能会有自己的应用程序的一些输入。但是,在这里,我们希望在不提供或查找数据集的情况下进行探索,因此让我们生成一些数据集以便于理解。您可以随意阅读本文,但本文中会适当地介绍这些概念——现在只需将其复制进去,并意识到它以磁盘上的分区数据集结束
// Generate some data for the rest of this example.
arrow::Result<std::shared_ptr<arrow::Table>> CreateTable() {
// This code should look familiar from the basic Arrow example, and is not the
// focus of this example. However, we need data to work on it, and this makes that!
auto schema =
arrow::schema({arrow::field("a", arrow::int64()), arrow::field("b", arrow::int64()),
arrow::field("c", arrow::int64())});
std::shared_ptr<arrow::Array> array_a;
std::shared_ptr<arrow::Array> array_b;
std::shared_ptr<arrow::Array> array_c;
arrow::NumericBuilder<arrow::Int64Type> builder;
ARROW_RETURN_NOT_OK(builder.AppendValues({0, 1, 2, 3, 4, 5, 6, 7, 8, 9}));
ARROW_RETURN_NOT_OK(builder.Finish(&array_a));
builder.Reset();
ARROW_RETURN_NOT_OK(builder.AppendValues({9, 8, 7, 6, 5, 4, 3, 2, 1, 0}));
ARROW_RETURN_NOT_OK(builder.Finish(&array_b));
builder.Reset();
ARROW_RETURN_NOT_OK(builder.AppendValues({1, 2, 1, 2, 1, 2, 1, 2, 1, 2}));
ARROW_RETURN_NOT_OK(builder.Finish(&array_c));
return arrow::Table::Make(schema, {array_a, array_b, array_c});
}
// Set up a dataset by writing two Parquet files.
arrow::Result<std::string> CreateExampleParquetDataset(
const std::shared_ptr<arrow::fs::FileSystem>& filesystem,
const std::string& root_path) {
// Much like CreateTable(), this is utility that gets us the dataset we'll be reading
// from. Don't worry, we also write a dataset in the example proper.
auto base_path = root_path + "parquet_dataset";
ARROW_RETURN_NOT_OK(filesystem->CreateDir(base_path));
// Create an Arrow Table
ARROW_ASSIGN_OR_RAISE(auto table, CreateTable());
// Write it into two Parquet files
ARROW_ASSIGN_OR_RAISE(auto output,
filesystem->OpenOutputStream(base_path + "/data1.parquet"));
ARROW_RETURN_NOT_OK(parquet::arrow::WriteTable(
*table->Slice(0, 5), arrow::default_memory_pool(), output, 2048));
ARROW_ASSIGN_OR_RAISE(output,
filesystem->OpenOutputStream(base_path + "/data2.parquet"));
ARROW_RETURN_NOT_OK(parquet::arrow::WriteTable(
*table->Slice(5), arrow::default_memory_pool(), output, 2048));
return base_path;
}
arrow::Status PrepareEnv() {
// Get our environment prepared for reading, by setting up some quick writing.
ARROW_ASSIGN_OR_RAISE(auto src_table, CreateTable())
std::shared_ptr<arrow::fs::FileSystem> setup_fs;
// Note this operates in the directory the executable is built in.
char setup_path[256];
char* result = getcwd(setup_path, 256);
if (result == NULL) {
return arrow::Status::IOError("Fetching PWD failed.");
}
ARROW_ASSIGN_OR_RAISE(setup_fs, arrow::fs::FileSystemFromUriOrPath(setup_path));
ARROW_ASSIGN_OR_RAISE(auto dset_path, CreateExampleParquetDataset(setup_fs, ""));
return arrow::Status::OK();
}
为了实际拥有这些文件,请确保在 RunMain()
中调用的第一个函数是我们的辅助函数 PrepareEnv()
,它将获取磁盘上的数据集供我们使用
ARROW_RETURN_NOT_OK(PrepareEnv());
读取分区数据集#
读取数据集与读取单个文件是不同的任务。由于需要能够解析多个文件和/或文件夹,因此该任务比读取单个文件需要更多工作。此过程可以分为以下步骤
获取本地文件系统的
fs::FileSystem
对象创建
fs::FileSelector
并使用它来准备dataset::FileSystemDatasetFactory
使用
dataset::Scanner
读取到Table
中
准备 FileSystem 对象#
为了开始,我们需要能够与本地文件系统进行交互。为此,我们需要一个 fs::FileSystem
对象。fs::FileSystem
是一个抽象概念,无论使用 Amazon S3、Google Cloud Storage 还是本地磁盘,我们都可以使用相同的接口——我们将使用本地磁盘。所以,让我们声明它
// First, we need a filesystem object, which lets us interact with our local
// filesystem starting at a given path. For the sake of simplicity, that'll be
// the current directory.
std::shared_ptr<arrow::fs::FileSystem> fs;
在本例中,我们将 FileSystem
的基路径放在与可执行文件相同的目录中。fs::FileSystemFromUriOrPath()
允许我们为任何类型的受支持文件系统获取 fs::FileSystem
对象。但是,在这里,我们将只传递我们的路径
// Get the CWD, use it to make the FileSystem object.
char init_path[256];
char* result = getcwd(init_path, 256);
if (result == NULL) {
return arrow::Status::IOError("Fetching PWD failed.");
}
ARROW_ASSIGN_OR_RAISE(fs, arrow::fs::FileSystemFromUriOrPath(init_path));
另请参阅
有关其他受支持的文件系统,请参阅 fs::FileSystem
。
创建 FileSystemDatasetFactory#
fs::FileSystem
存储大量元数据,但我们需要能够遍历它并解析该元数据。在 Arrow 中,我们使用 FileSelector
来执行此操作
// A file selector lets us actually traverse a multi-file dataset.
arrow::fs::FileSelector selector;
此 fs::FileSelector
尚无法执行任何操作。为了使用它,我们需要对其进行配置——我们将让它在“parquet_dataset”中开始任何选择,这是环境准备过程为我们留下的数据集的位置,并将 recursive 设置为 true,这允许遍历文件夹。
selector.base_dir = "parquet_dataset";
// Recursive is a safe bet if you don't know the nesting of your dataset.
selector.recursive = true;
要从 fs::FileSystem
获取 dataset::Dataset
,我们需要准备一个 dataset::FileSystemDatasetFactory
。这是一个很长但描述性名称——它将使我们成为一个从我们的 fs::FileSystem
获取数据的工厂。首先,我们通过填充 dataset::FileSystemFactoryOptions
结构来配置它
// Making an options object lets us configure our dataset reading.
arrow::dataset::FileSystemFactoryOptions options;
// We'll use Hive-style partitioning. We'll let Arrow Datasets infer the partition
// schema. We won't set any other options, defaults are fine.
options.partitioning = arrow::dataset::HivePartitioning::MakeFactory();
文件格式有很多种,我们必须选择一种在实际读取时预期使用的格式。Parquet 是我们磁盘上的格式,因此我们当然会在读取时请求该格式
auto read_format = std::make_shared<arrow::dataset::ParquetFileFormat>();
设置 fs::FileSystem
、fs::FileSelector
、选项和文件格式后,我们可以创建 dataset::FileSystemDatasetFactory
。这只需要传入我们准备好的所有内容并将其分配给一个变量即可
// Now, we get a factory that will let us get our dataset -- we don't have the
// dataset yet!
ARROW_ASSIGN_OR_RAISE(auto factory, arrow::dataset::FileSystemDatasetFactory::Make(
fs, selector, read_format, options));
使用 Factory 构建数据集#
通过设置 dataset::FileSystemDatasetFactory
,我们可以使用 dataset::FileSystemDatasetFactory::Finish()
实际构建 dataset::Dataset
,就像在基础教程中使用 ArrayBuilder
一样。
// Now we build our dataset from the factory.
ARROW_ASSIGN_OR_RAISE(auto read_dataset, factory->Finish());
现在,我们内存中有一个 dataset::Dataset
对象。这并不意味着整个数据集都体现在内存中,而是我们现在可以访问允许我们探索和使用磁盘上数据集的工具。例如,我们可以获取构成整个数据集的片段(文件),并将它们连同一些小信息一起打印出来。
// Print out the fragments
ARROW_ASSIGN_OR_RAISE(auto fragments, read_dataset->GetFragments());
for (const auto& fragment : fragments) {
std::cout << "Found fragment: " << (*fragment)->ToString() << std::endl;
std::cout << "Partition expression: "
<< (*fragment)->partition_expression().ToString() << std::endl;
}
将数据集移入表#
我们可以对 Datasets
执行操作的一种方法是将它们放入 Table
中,在其中我们可以对 Tables
执行我们所学到的任何操作。
另请参阅
Acero:一个 C++ 流式执行引擎,用于避免将整个数据集体现在内存中的执行。
为了将 Dataset
的内容移入 Table
中,我们需要一个 dataset::Scanner
,它会扫描数据并将其输出到 Table
。首先,我们从 dataset::Dataset
获取一个 dataset::ScannerBuilder
。
// Scan dataset into a Table -- once this is done, you can do
// normal table things with it, like computation and printing. However, now you're
// also dedicated to being in memory.
ARROW_ASSIGN_OR_RAISE(auto read_scan_builder, read_dataset->NewScan());
当然,Builder 的唯一用途是让我们获得 dataset::Scanner
,所以让我们使用 dataset::ScannerBuilder::Finish()
。
ARROW_ASSIGN_OR_RAISE(auto read_scanner, read_scan_builder->Finish());
现在我们有了一个遍历 dataset::Dataset
的工具,让我们用它来获取 Table
。dataset::Scanner::ToTable()
正好提供了我们想要的功能,我们可以打印结果。
ARROW_ASSIGN_OR_RAISE(std::shared_ptr<arrow::Table> table, read_scanner->ToTable());
std::cout << table->ToString();
这会给我们留下一个普通的 Table
。同样,要对 Datasets
执行操作而不移动到 Table
,请考虑使用 Acero。
将数据集从表写入磁盘#
将 dataset::Dataset
写入磁盘与写入单个文件是不同的任务。由于需要能够解析处理跨多个文件和文件夹的分区方案,因此该任务比写入单个文件需要更多的工作。此过程可以分为以下步骤:
创建
dataset::Scanner
以从TableBatchReader
中提取数据准备模式、分区和文件格式选项
设置
dataset::FileSystemDatasetWriteOptions
- 一个配置我们写入函数的结构体将数据集写入磁盘
准备表中的数据以进行写入#
我们有一个 Table
,我们想在磁盘上获得一个 dataset::Dataset
。事实上,为了便于探索,我们将对数据集使用不同的分区方案——我们不会像原始片段那样简单地分成两半,而是根据“a”列中每行的值进行分区。
为了开始这项工作,让我们获取一个 TableBatchReader
!这使得写入 Dataset
变得非常容易,并且可以在任何需要将 Table
分解成 RecordBatches
流的地方使用。在这里,我们可以直接使用 TableBatchReader
的构造函数,以及我们的表。
// Now, let's get a table out to disk as a dataset!
// We make a RecordBatchReader from our Table, then set up a scanner, which lets us
// go to a file.
std::shared_ptr<arrow::TableBatchReader> write_dataset =
std::make_shared<arrow::TableBatchReader>(table);
创建用于移动表数据的扫描器#
写入 dataset::Dataset
的过程,一旦数据源可用,就类似于读取它的逆过程。之前,我们使用 dataset::Scanner
来扫描到 Table
中——现在,我们需要一个来从我们的 TableBatchReader
中读取数据。为了获得 dataset::Scanner
,我们将基于我们的 TableBatchReader
创建一个 dataset::ScannerBuilder
,然后使用该 Builder 来构建 dataset::Scanner
。
auto write_scanner_builder =
arrow::dataset::ScannerBuilder::FromRecordBatchReader(write_dataset);
ARROW_ASSIGN_OR_RAISE(auto write_scanner, write_scanner_builder->Finish())
准备模式、分区和文件格式变量#
由于我们要根据“a”列进行分区,因此我们需要声明它。在定义我们的分区 Schema
时,我们将只有一个包含“a”的 Field
。
// The partition schema determines which fields are used as keys for partitioning.
auto partition_schema = arrow::schema({arrow::field("a", arrow::utf8())});
这个 Schema
确定了分区的键是什么,但我们需要选择一种算法来处理这个键。我们将再次使用 Hive 样式,这次将我们的模式作为配置传递给它。
// We'll use Hive-style partitioning, which creates directories with "key=value"
// pairs.
auto partitioning =
std::make_shared<arrow::dataset::HivePartitioning>(partition_schema);
有几种文件格式可用,但 Parquet 通常与 Arrow 一起使用,因此我们将写回该格式。
// Now, we declare we'll be writing Parquet files.
auto write_format = std::make_shared<arrow::dataset::ParquetFileFormat>();
配置 FileSystemDatasetWriteOptions#
为了写入磁盘,我们需要一些配置。我们将通过在 dataset::FileSystemDatasetWriteOptions
结构体中设置值来实现。我们将尽可能使用默认值对其进行初始化。
// This time, we make Options for writing, but do much more configuration.
arrow::dataset::FileSystemDatasetWriteOptions write_options;
// Defaults to start.
write_options.file_write_options = write_format->DefaultWriteOptions();
写入文件的一个重要步骤是具有要写入的目标 fs::FileSystem
。幸运的是,我们从设置读取时就有一个。这是一个简单的变量赋值。
// Use the filesystem we already have.
write_options.filesystem = fs;
Arrow 可以创建目录,但它确实需要一个目录名称,所以让我们给它一个名称,称之为“write_dataset”。
// Write to the folder "write_dataset" in current directory.
write_options.base_dir = "write_dataset";
我们之前创建了一个分区方法,声明我们将使用 Hive 样式——这就是我们实际将其传递给写入函数的地方。
// Use the partitioning declared above.
write_options.partitioning = partitioning;
将要发生的部分事情是 Arrow 会拆分文件,从而防止它们太大而无法处理。这就是导致数据集碎片化的原因。为了设置它,我们需要目录中每个片段的基本名称——在本例中,我们将使用“part{i}.parquet”,这意味着第三个文件(在同一目录内) 将被称为“part3.parquet”,例如。
// Define what the name for the files making up the dataset will be.
write_options.basename_template = "part{i}.parquet";
有时,数据将被多次写入同一位置,并且将接受覆盖。由于我们可能希望多次运行此应用程序,因此我们将设置 Arrow 以覆盖现有数据——如果我们不这样做,Arrow 将在第一次运行此应用程序后由于看到现有数据而中止。
// Set behavior to overwrite existing data -- specifically, this lets this example
// be run more than once, and allows whatever code you have to overwrite what's there.
write_options.existing_data_behavior =
arrow::dataset::ExistingDataBehavior::kOverwriteOrIgnore;
将数据集写入磁盘#
一旦配置好 dataset::FileSystemDatasetWriteOptions
,并准备好 dataset::Scanner
来解析数据,我们就可以将选项和 dataset::Scanner
传递给 dataset::FileSystemDataset::Write()
方法将其写入磁盘。
// Write to disk!
ARROW_RETURN_NOT_OK(
arrow::dataset::FileSystemDataset::Write(write_options, write_scanner));
您可以查看您的磁盘,会看到您写入的文件夹包含每个“a”值的子文件夹,每个子文件夹中都有 Parquet 文件!
结束程序#
最后,我们只需返回 Status::OK()
,这样 main()
函数就知道我们已经完成了,并且一切正常,就像前面的教程一样。
return arrow::Status::OK();
}
这样,您就读取和写入了分区数据集!通过一些配置,此方法适用于任何受支持的数据集格式。例如,纽约出租车数据集就是一个众所周知的数据集,您可以在这里找到它。现在您可以将大于内存的数据映射以供使用!
这意味着我们现在必须能够在不将所有数据一次性拉入内存的情况下处理这些数据。为此,请尝试 Acero。
另请参阅
有关 Acero 的更多信息,请参阅Acero:C++ 流式执行引擎。
请参考以下完整代码副本
19// (Doc section: Includes)
20#include <arrow/api.h>
21#include <arrow/dataset/api.h>
22// We use Parquet headers for setting up examples; they are not required for using
23// datasets.
24#include <parquet/arrow/reader.h>
25#include <parquet/arrow/writer.h>
26
27#include <unistd.h>
28#include <iostream>
29// (Doc section: Includes)
30
31// (Doc section: Helper Functions)
32// Generate some data for the rest of this example.
33arrow::Result<std::shared_ptr<arrow::Table>> CreateTable() {
34 // This code should look familiar from the basic Arrow example, and is not the
35 // focus of this example. However, we need data to work on it, and this makes that!
36 auto schema =
37 arrow::schema({arrow::field("a", arrow::int64()), arrow::field("b", arrow::int64()),
38 arrow::field("c", arrow::int64())});
39 std::shared_ptr<arrow::Array> array_a;
40 std::shared_ptr<arrow::Array> array_b;
41 std::shared_ptr<arrow::Array> array_c;
42 arrow::NumericBuilder<arrow::Int64Type> builder;
43 ARROW_RETURN_NOT_OK(builder.AppendValues({0, 1, 2, 3, 4, 5, 6, 7, 8, 9}));
44 ARROW_RETURN_NOT_OK(builder.Finish(&array_a));
45 builder.Reset();
46 ARROW_RETURN_NOT_OK(builder.AppendValues({9, 8, 7, 6, 5, 4, 3, 2, 1, 0}));
47 ARROW_RETURN_NOT_OK(builder.Finish(&array_b));
48 builder.Reset();
49 ARROW_RETURN_NOT_OK(builder.AppendValues({1, 2, 1, 2, 1, 2, 1, 2, 1, 2}));
50 ARROW_RETURN_NOT_OK(builder.Finish(&array_c));
51 return arrow::Table::Make(schema, {array_a, array_b, array_c});
52}
53
54// Set up a dataset by writing two Parquet files.
55arrow::Result<std::string> CreateExampleParquetDataset(
56 const std::shared_ptr<arrow::fs::FileSystem>& filesystem,
57 const std::string& root_path) {
58 // Much like CreateTable(), this is utility that gets us the dataset we'll be reading
59 // from. Don't worry, we also write a dataset in the example proper.
60 auto base_path = root_path + "parquet_dataset";
61 ARROW_RETURN_NOT_OK(filesystem->CreateDir(base_path));
62 // Create an Arrow Table
63 ARROW_ASSIGN_OR_RAISE(auto table, CreateTable());
64 // Write it into two Parquet files
65 ARROW_ASSIGN_OR_RAISE(auto output,
66 filesystem->OpenOutputStream(base_path + "/data1.parquet"));
67 ARROW_RETURN_NOT_OK(parquet::arrow::WriteTable(
68 *table->Slice(0, 5), arrow::default_memory_pool(), output, 2048));
69 ARROW_ASSIGN_OR_RAISE(output,
70 filesystem->OpenOutputStream(base_path + "/data2.parquet"));
71 ARROW_RETURN_NOT_OK(parquet::arrow::WriteTable(
72 *table->Slice(5), arrow::default_memory_pool(), output, 2048));
73 return base_path;
74}
75
76arrow::Status PrepareEnv() {
77 // Get our environment prepared for reading, by setting up some quick writing.
78 ARROW_ASSIGN_OR_RAISE(auto src_table, CreateTable())
79 std::shared_ptr<arrow::fs::FileSystem> setup_fs;
80 // Note this operates in the directory the executable is built in.
81 char setup_path[256];
82 char* result = getcwd(setup_path, 256);
83 if (result == NULL) {
84 return arrow::Status::IOError("Fetching PWD failed.");
85 }
86
87 ARROW_ASSIGN_OR_RAISE(setup_fs, arrow::fs::FileSystemFromUriOrPath(setup_path));
88 ARROW_ASSIGN_OR_RAISE(auto dset_path, CreateExampleParquetDataset(setup_fs, ""));
89
90 return arrow::Status::OK();
91}
92// (Doc section: Helper Functions)
93
94// (Doc section: RunMain)
95arrow::Status RunMain() {
96 // (Doc section: RunMain)
97 // (Doc section: PrepareEnv)
98 ARROW_RETURN_NOT_OK(PrepareEnv());
99 // (Doc section: PrepareEnv)
100
101 // (Doc section: FileSystem Declare)
102 // First, we need a filesystem object, which lets us interact with our local
103 // filesystem starting at a given path. For the sake of simplicity, that'll be
104 // the current directory.
105 std::shared_ptr<arrow::fs::FileSystem> fs;
106 // (Doc section: FileSystem Declare)
107
108 // (Doc section: FileSystem Init)
109 // Get the CWD, use it to make the FileSystem object.
110 char init_path[256];
111 char* result = getcwd(init_path, 256);
112 if (result == NULL) {
113 return arrow::Status::IOError("Fetching PWD failed.");
114 }
115 ARROW_ASSIGN_OR_RAISE(fs, arrow::fs::FileSystemFromUriOrPath(init_path));
116 // (Doc section: FileSystem Init)
117
118 // (Doc section: FileSelector Declare)
119 // A file selector lets us actually traverse a multi-file dataset.
120 arrow::fs::FileSelector selector;
121 // (Doc section: FileSelector Declare)
122 // (Doc section: FileSelector Config)
123 selector.base_dir = "parquet_dataset";
124 // Recursive is a safe bet if you don't know the nesting of your dataset.
125 selector.recursive = true;
126 // (Doc section: FileSelector Config)
127 // (Doc section: FileSystemFactoryOptions)
128 // Making an options object lets us configure our dataset reading.
129 arrow::dataset::FileSystemFactoryOptions options;
130 // We'll use Hive-style partitioning. We'll let Arrow Datasets infer the partition
131 // schema. We won't set any other options, defaults are fine.
132 options.partitioning = arrow::dataset::HivePartitioning::MakeFactory();
133 // (Doc section: FileSystemFactoryOptions)
134 // (Doc section: File Format Setup)
135 auto read_format = std::make_shared<arrow::dataset::ParquetFileFormat>();
136 // (Doc section: File Format Setup)
137 // (Doc section: FileSystemDatasetFactory Make)
138 // Now, we get a factory that will let us get our dataset -- we don't have the
139 // dataset yet!
140 ARROW_ASSIGN_OR_RAISE(auto factory, arrow::dataset::FileSystemDatasetFactory::Make(
141 fs, selector, read_format, options));
142 // (Doc section: FileSystemDatasetFactory Make)
143 // (Doc section: FileSystemDatasetFactory Finish)
144 // Now we build our dataset from the factory.
145 ARROW_ASSIGN_OR_RAISE(auto read_dataset, factory->Finish());
146 // (Doc section: FileSystemDatasetFactory Finish)
147 // (Doc section: Dataset Fragments)
148 // Print out the fragments
149 ARROW_ASSIGN_OR_RAISE(auto fragments, read_dataset->GetFragments());
150 for (const auto& fragment : fragments) {
151 std::cout << "Found fragment: " << (*fragment)->ToString() << std::endl;
152 std::cout << "Partition expression: "
153 << (*fragment)->partition_expression().ToString() << std::endl;
154 }
155 // (Doc section: Dataset Fragments)
156 // (Doc section: Read Scan Builder)
157 // Scan dataset into a Table -- once this is done, you can do
158 // normal table things with it, like computation and printing. However, now you're
159 // also dedicated to being in memory.
160 ARROW_ASSIGN_OR_RAISE(auto read_scan_builder, read_dataset->NewScan());
161 // (Doc section: Read Scan Builder)
162 // (Doc section: Read Scanner)
163 ARROW_ASSIGN_OR_RAISE(auto read_scanner, read_scan_builder->Finish());
164 // (Doc section: Read Scanner)
165 // (Doc section: To Table)
166 ARROW_ASSIGN_OR_RAISE(std::shared_ptr<arrow::Table> table, read_scanner->ToTable());
167 std::cout << table->ToString();
168 // (Doc section: To Table)
169
170 // (Doc section: TableBatchReader)
171 // Now, let's get a table out to disk as a dataset!
172 // We make a RecordBatchReader from our Table, then set up a scanner, which lets us
173 // go to a file.
174 std::shared_ptr<arrow::TableBatchReader> write_dataset =
175 std::make_shared<arrow::TableBatchReader>(table);
176 // (Doc section: TableBatchReader)
177 // (Doc section: WriteScanner)
178 auto write_scanner_builder =
179 arrow::dataset::ScannerBuilder::FromRecordBatchReader(write_dataset);
180 ARROW_ASSIGN_OR_RAISE(auto write_scanner, write_scanner_builder->Finish())
181 // (Doc section: WriteScanner)
182 // (Doc section: Partition Schema)
183 // The partition schema determines which fields are used as keys for partitioning.
184 auto partition_schema = arrow::schema({arrow::field("a", arrow::utf8())});
185 // (Doc section: Partition Schema)
186 // (Doc section: Partition Create)
187 // We'll use Hive-style partitioning, which creates directories with "key=value"
188 // pairs.
189 auto partitioning =
190 std::make_shared<arrow::dataset::HivePartitioning>(partition_schema);
191 // (Doc section: Partition Create)
192 // (Doc section: Write Format)
193 // Now, we declare we'll be writing Parquet files.
194 auto write_format = std::make_shared<arrow::dataset::ParquetFileFormat>();
195 // (Doc section: Write Format)
196 // (Doc section: Write Options)
197 // This time, we make Options for writing, but do much more configuration.
198 arrow::dataset::FileSystemDatasetWriteOptions write_options;
199 // Defaults to start.
200 write_options.file_write_options = write_format->DefaultWriteOptions();
201 // (Doc section: Write Options)
202 // (Doc section: Options FS)
203 // Use the filesystem we already have.
204 write_options.filesystem = fs;
205 // (Doc section: Options FS)
206 // (Doc section: Options Target)
207 // Write to the folder "write_dataset" in current directory.
208 write_options.base_dir = "write_dataset";
209 // (Doc section: Options Target)
210 // (Doc section: Options Partitioning)
211 // Use the partitioning declared above.
212 write_options.partitioning = partitioning;
213 // (Doc section: Options Partitioning)
214 // (Doc section: Options Name Template)
215 // Define what the name for the files making up the dataset will be.
216 write_options.basename_template = "part{i}.parquet";
217 // (Doc section: Options Name Template)
218 // (Doc section: Options File Behavior)
219 // Set behavior to overwrite existing data -- specifically, this lets this example
220 // be run more than once, and allows whatever code you have to overwrite what's there.
221 write_options.existing_data_behavior =
222 arrow::dataset::ExistingDataBehavior::kOverwriteOrIgnore;
223 // (Doc section: Options File Behavior)
224 // (Doc section: Write Dataset)
225 // Write to disk!
226 ARROW_RETURN_NOT_OK(
227 arrow::dataset::FileSystemDataset::Write(write_options, write_scanner));
228 // (Doc section: Write Dataset)
229 // (Doc section: Ret)
230 return arrow::Status::OK();
231}
232// (Doc section: Ret)
233// (Doc section: Main)
234int main() {
235 arrow::Status st = RunMain();
236 if (!st.ok()) {
237 std::cerr << st << std::endl;
238 return 1;
239 }
240 return 0;
241}
242// (Doc section: Main)