Arrow 資料集#

Arrow C++ 提供了 Datasets 的概念和實作,用於處理分散的資料,這些資料可能大於記憶體,原因可能是產生大量資料、從串流讀取或磁碟上有大型檔案。在本文中,您將

  1. 讀取多分區檔案的資料集,並將其放入表格中,

  2. 從表格中寫出分區資料集。

先決條件#

在繼續之前,請確保您已具備

  1. Arrow 安裝,您可以在此處設定:在您自己的專案中使用 Arrow C++

  2. 基本 Arrow 資料結構 了解基本 Arrow 資料結構

為了見證差異,閱讀 Arrow 檔案 I/O 也可能很有用。但是,這不是必需的。

設定#

在執行一些計算之前,我們需要填補一些空白

  1. 我們需要包含必要的標頭檔。

  2. 需要 main() 將所有內容組合在一起。

  3. 我們需要磁碟上的資料來進行操作。

包含#

在編寫 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());

讀取分區資料集#

讀取資料集與讀取單一檔案是不同的任務。由於需要能夠解析多個檔案和/或資料夾,因此這項任務比讀取單一檔案需要更多工作。此過程可以分解為以下步驟

  1. 取得本機 FS 的 fs::FileSystem 物件

  2. 建立 fs::FileSelector 並使用它來準備 dataset::FileSystemDatasetFactory

  3. 使用 dataset::FileSystemDatasetFactory 建置 dataset::Dataset

  4. 使用 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::FileSystemfs::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 中,在其中我們可以對該 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 中移動,讓我們使用它來取得我們的 Tabledataset::Scanner::ToTable() 提供了我們正在尋找的確切內容,我們可以印出結果

  ARROW_ASSIGN_OR_RAISE(std::shared_ptr<arrow::Table> table, read_scanner->ToTable());
  std::cout << table->ToString();

這讓我們得到一個正常的 Table。同樣地,若要在不移動到 Table 的情況下對 Datasets 執行操作,請考慮使用 Acero。

從表格將資料集寫入磁碟#

寫入 dataset::Dataset 與寫入單一檔案是不同的任務。由於需要能夠解析處理跨多個檔案和資料夾的分區方案,因此這項任務比寫入單一檔案需要更多工作。此過程可以分解為以下步驟

  1. 準備 TableBatchReader

  2. 建立 dataset::Scanner 以從 TableBatchReader 提取資料

  3. 準備結構描述、分區和檔案格式選項

  4. 設定 dataset::FileSystemDatasetWriteOptions – 一個用於設定寫入函數的結構

  5. 將資料集寫入磁碟

準備要寫入的表格資料#

我們有一個 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);

建立 Scanner 以移動表格資料#

一旦資料來源可用,寫入 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。幸運的是,我們在設定它以進行讀取時已經有一個。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 已準備好解析資料,我們可以將 Options 和 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:C++ 串流執行引擎 以取得有關 Acero 的更多資訊。

請參閱以下內容以取得完整程式碼的副本

 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)