ローカルファイル
ローカルファイルConnectorは、ファイルシステムからCSV、Parquet、JSON Lines、Icebergのテーブルを読み、ファイルを一覧してコンテンツとして読み、テーブルとファイルを書き戻し、新しいテーブルを作り、ファイルを削除・移動します。手元のマシンにすでにあるデータ(エクスポート、フィクスチャ、他のツールの出力)のためのConnectorであり、DWHに向ける前の開発段階のプログラムのためのConnectorでもあります。その処理はすべてHost自身で動きます。Siteは常に host:local です。
読み取り元
接続で宣言した各テーブルが読み取り元です。local.orders、local.customers のように参照します。テーブルは、1つのファイル(または同じスキーマを持つファイルのディレクトリ)と形式の組です。
- CSV — 見出しの行が列名になります。型は接続で宣言するか、サンプルから推定したうえで固定します。
- Parquet — 列と型はファイルから取ります。
- JSON Lines — 1行に1オブジェクトです。列と型は宣言します。
- Iceberg — Icebergカタログのディレクトリにあるテーブルです。列と型はテーブルのメタデータから取り、読み取りはスナップショットに固定されます。
テーブルのキーは、接続で宣言したもの("key": ["id"])です。キーを宣言していないテーブルにはキーがなく、キーが必要なEffectは check で拒否されます。
テーブルの宣言とは別に、ファイルも読み取り元です。
local.files— ルートの下のファイル。resource、version(サイズと更新日時のハッシュ。接続で"version": "content"を指定すると内容のハッシュ)、path、size、modified_time。LIST_FILES(local, glob => r'inbound/*.pdf')はパスによる同じ一覧です。一覧の読み取り元が1つなので、接続名だけで書けます。local.contents— ファイルの内容。resource、version、path、media_type(拡張子から決まり、接続で上書きできる)、そしてcontent_refのcontent。LIST_FILESからresourceとversionでJOINしてください。
対応するストレージのConnectorが設定されていれば、パスをバケットの中(gs://…、s3://…)に向けることもできます。ファイルはそのConnectorを通して読まれます。
型
| 形式 | ActQL | 備考 |
|---|---|---|
| CSV | 宣言した型、またはサンプルから推定して固定した型 | string、int64、float64、decimal(p, s)、bool、date、timestamp。解釈できないセルは読み取りを失敗させ、NULL にはならない |
| Parquetの論理型 | int32/int64 → int64、float/double → float64、decimal → decimal(p, s)、string/enum → string、date → date、ゾーン付きの timestamp → timestamp、ゾーンなし → datetime、list → array<…>、struct/map → json | ナノ秒のタイムスタンプは精度を保つ |
| JSON Lines | 宣言した型 | json と宣言した列は、入れ子の値を型付きのJSONとして保つ |
| Iceberg | テーブルの型。Parquetと同じ対応 | uuid → string、fixed/binary → bytes |
| ファイルのメタデータ | path → string、size → int64、modified_time → timestamp、content → content_ref |
Effectと操作
テーブル:
- store rows —
EFFECT STORE local.<table>は、プログラムのレコードを宣言済みのテーブルに書きます。CSV、Parquet、JSON Linesのテーブルはレコードで置き換えられ、Icebergのテーブルには1つのスナップショットとして追記されます。テーブルのパスと形式は接続で決まり、プログラムには書きません。 - create table —
|> CREATE [OR REPLACE] TABLE local.<name>は、レコードをルートの下に新しいテーブルとして、接続の既定の形式("default_format": "parquet")で書き、あとのプログラムが読めるように宣言します。CREATEはテーブルがあれば失敗し、CREATE OR REPLACEは置き換えます。 - delete rows —
EFFECT DELETE local.<table> ID keyは、テーブルが宣言するキーでレコードを削除します。CSV、Parquet、JSON Linesのテーブルはそれらを除いて書き直され、Icebergのテーブルには削除のスナップショットが1つ付きます。
ファイル:
- write file —
EFFECT STORE local.files SET (path = …, content = content [, overwrite = TRUE])は、レコードのcontent(SERIALIZEまたはcontentsの読み取り元から来たもの)をルートの下のパスに書きます。 - delete file —
EFFECT DELETE local.files ID pathは、ファイルを削除します。
操作:
- move —
EFFECT COMMAND local.move TARGET path ID key INPUT (destination = …)は、ルートの下でファイルの名前を変えるか移動します。宛先は、overwrite = TRUEも指定しない限り存在してはいけません。
パスは root の下で解決されます。プログラムからその外のパスを指定することはできず、Effectからもできません。
セットアップ
"local": {
"connector": "local",
"root": "./data",
"default_format": "parquet",
"tables": {
"orders": {"path": "orders.parquet", "key": ["id"]},
"customers": {"path": "customers.csv", "key": ["id"], "columns": {
"id": "string", "name": "string", "segment": "string", "signed_up": "date"
}},
"events": {"path": "warehouse/events", "format": "iceberg"},
"customer_segments": {"path": "out/customer_segments.parquet", "key": ["id"]}
}
}認証情報は不要です。root は設定ファイルからの相対パスです。Hostのプロセスには、読み取り元のためにルートの下の読み取り権限が、Effectのために書き込み権限が必要です。それ以外は要りません。ActQL Cloudでは、local の接続はCloudのワーカー自身のストレージを指し、同じように範囲が限定されます。
Capabilityとプッシュダウン
| 演算 | プッシュダウンされる条件 |
|---|---|
| 列の選択 | ParquetとIcebergはプログラムが使う列だけを読む。CSVとJSON Linesは全体を解析し、エンジンで列を選ぶ |
WHERE | 統計情報から述語に合わないと分かるParquetのrow groupとIcebergのファイルは飛ばす。述語そのものはエンジンで評価するので、残りの評価はない |
ORDER BY、LIMIT | エンジンで。CSVの読み取りに LIMIT があれば、必要な件数がそろった時点で解析を止める |
集計、JOIN | エンジンで。パッケージがDuckDBを同梱していればDuckDBで。どちらでもSiteは同じ host:local |
プレビューと突き合わせ
プレビュー。 テーブルへの保存とファイルの書き込みは targeted です。プレビューには、テーブル(またはパス)、その形式、件数またはコンテンツの識別子とバイト数、既存のファイルを置き換えるかどうかが出ます。削除と移動は full_diff です。現在のファイルのサイズ、更新日時、チェックサムを読んで表示し、操作をそれらの条件付きにするので、その間に変わったファイルは項目が failed(conflict)になります。Icebergへの追記は、続くスナップショットを指定し、別の書き込み側が先にコミットしていれば失敗します。
突き合わせ。 書き込みは一時ファイルに行ってから本来の場所にリネームするので、クラッシュしても書きかけのファイルが残ることはありません。Icebergへの追記は1つのスナップショットをコミットします。ローカルファイルで応答が失われることはありませんが、リネームとその記録の間でクラッシュすることはあります。Connectorはリネームの前に項目(id、宛先、コンテンツの識別子)をルートの下のジャーナルに記録し、突き合わせではそのジャーナルと、Icebergならサマリーにidを持つスナップショットを読みます。チェックサムが偶然一致するファイルや、ファイルがないことだけでは証跡になりません。宛先に別のチェックサムのファイルがあっても証跡になりません。項目は unknown のままで、何も書き直しません。
上限と費用
- ファイルはRunの開始時に読まれ、固定されます。Runの途中で変わったファイルは読み直されず、Icebergの読み取りはそのスナップショットにとどまります。
- 読み取りはプログラムの件数とバイト数の上限で抑えられます。バイト数の上限より大きいファイルは、途中で切られるのではなく拒否されます。
- 2つのHostが同じルートの下に同時に書き込んではいけません。Connectorは書き込み中、テーブルごとにアドバイザリロックを取ります。
- 外部への課金はありません。上限はマシンのディスクとメモリ、そしてHostの容量ポリシーです。
例
顧客セグメント別の注文合計です。
FROM local.orders AS o
JOIN local.customers AS c ON o.customer_id = c.id
|> WHERE o.status = 'open'
|> AGGREGATE COUNT(*) AS orders, SUM(o.amount) AS total GROUP BY c.segment
|> ORDER BY total DESC;顧客をメモから分類し、結果をParquetのテーブルとして書き出します。
FROM local.customers
|> INFER `model-task:segment.v1` INPUT (name, notes) AS seg
|> WHERE seg.outcome = 'ok'
|> SELECT id, seg.value.segment AS segment
|> EFFECT STORE local.customer_segments;Runが処理し終えた受信ファイルをアーカイブします。
FROM LIST_FILES(local, glob => r'inbound/*.csv') AS f
|> WHERE f.modified_time < TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 1 DAY)
|> EFFECT COMMAND local.move TARGET path ID path INPUT (destination = 'archive/' || path);Source: actql/actql/docs/connectors/local-files.md at d904416674e4 · Edit on GitHub