特徴量エンジンKaskada〜CLIによる「ハローワールド」体験

# はじめに

先日、以下の記事を発表しました。

https://qiita.com/yoshiyuki_kono/items/7dc7bdad5e02ff986f5d

公式ドキュメントの学習過程の記録として、以下の記事をまとめてみました。

本稿情報のソースとして、下記ドキュメントを参照ください。

https://kaskada.io/docs-site/kaskada/main/getting-started/hello-world-cli.html

# CLIによるKaskada「ハローワールド」体験

## インストール
コマンドラインで Kaskada を使用するには、次の 3 つのコンポーネントをインストールする必要があります。

 - Kaskada **CLI実行ファイル**
 - Kaskada API を提供する **Kaskada マネージャー**
 - クエリを実行する **Kaskada エンジン**

Github のリリースページから、プラットフォーム用の最新の Kaskada リリース バージョンのバイナリを入手できます。

以下は、最新の Kaskada バイナリをダウンロードし、Linux（および OSX） に適用するコマンド例です。


```shell
curl -s https://api.github.com/repos/kaskada-ai/kaskada/releases/latest |\
grep "browser_download_url.*" |\
grep $(uname -m | sed 's/x86_64/amd64/') |\
grep $(uname -s | tr '[:upper:]' '[:lower:]') |\
cut -d ':' -f2,3 |\
tr -d \" |\
xargs -I {} sh -c 'curl -L {} -o $(basename {}| cut -d '-' -f1,2)'

chmod +x kaskada-*
```

最後に、ファイルを、適宜（PATHの通っている場所などに）保存します。


### OSX でのアプリケーションの認証
OSX を使用している場合は、アプリケーションに対するブロックを解除する必要がある場合があります。

OSX は、ダウンロードしたアプリケーションに対するセキュリティ機能として実行を抑制するブロックを適用します。

次のコマンドを使用して、ファイルのダウンロード時にファイルに配置されたブロックを削除できます。
```shell
xattr -w com.apple.quarantine kaskada-cli
xattr -w com.apple.quarantine kaskada-manager
xattr -w com.apple.quarantine kaskada-engine
```

## サービス開始

マネージャーとエンジンを実行して、Kaskada サービスのローカル インスタンスを開始します。
```
./kaskada-manager 2>&1 > manager.log 2>&1 &
./kaskada-engine serve > engine.log 2>&1 &
```
### OSX でのサービスへのAPIリスナーの作成
OSX を使用する場合、これらのコマンドを初めて実行するときに、これらのサービスによる API リスナーの作成を許可する必要がある場合があります（これは正常であり、サービスが期待どおりに動作していることを示します）。
APIリスナーにより、サービス間の通信が可能になります。

## サービス実行確認

正しくインストールされ、実行可能であることを確認するには、次のコマンドを実行してみてください (作成したリソースがリストされます)。
```
./kaskada-cli sync export --all
```
次のような出力が表示されるはずです。
```
10:18AM INF starting export
{}
10:18AM INF Success!
```

## テーブルへのデータのロード
Kaskada はデータを（Kaskadaが管理する）**テーブル**に保存します。テーブルは複数の行で構成され、各行は同じ型の値です。

### テーブルの作成
Kaskadaのすべてのテーブルは、スキーマに関連付けられています。スキーマはテーブルにロードしたデータから推論されますが、Kaskada のデータ モデルにはいくつかの必須の列があります。つまり、すべてのテーブルには、各行に関連付けられた時刻とエンティティを識別する列が含まれている必要があります。

テーブルを作成するときは、どの列に各行の時間とエンティティキーが含まれるかを Kaskada に指示する必要があります。

 - 時刻列は`time_column_name`パラメータを使用して指定します。このパラメータにより、時間値を含むテーブルのデータ内の列名を識別します。この時刻は、イベントが発生した時刻を指すものとして扱われます。

 - エンティティキーは`entity_key_column_name`パラメータを使用して指定します。このパラメータにより、エンティティ キー値を含むテーブル データ内の列名を識別します。エンティティ キーは、各イベントが関連付けられている世界のエンティティを識別するものとして扱われます。なお、`with_key()`関数を使用して、事後的にエンティティキーを変更することができます。

以下は、テーブル作成の実行例です。

```shell
./kaskada-cli table create Purchase  --timeColumn purchase_time --entityKeyColumn customer_id
```

これにより、 `Purchase`という名前のテーブルが作成されます。このテーブルにロードされるデータには、 `purchase_time`という名前のタイムスタンプ フィールドと `customer_id`という名前のフィールド が必要です。

### Kaskadaにおける慣用
テーブルの名前付けには キャメルケースを使用することが推奨されています。この慣用により、値や関数名からデータ ソース（テーブル）を区別することができます。

## データのロード
テーブルを作成したので、そこにデータをロードする準備が整いました(データをロードする前に、テーブルを作成する必要があります)。

データは複数の方法でテーブルにロードできます。この例では、Parquet ファイルの内容をテーブルにロードします。

```shell
# Download a file to load and save it to path 'purchase.parquet'
curl -L "https://drive.google.com/uc?export=download&id=1SLdIw9uc0RGHY-eKzS30UBhN0NJtslkk" -o purchase.parquet
```

```shell
# Load the file into the Purchase table (which was created in the previous step)
./kaskada-cli table load Purchase file://${PWD}/purchase.parquet
```


これにより、ファイルの内容がテーブルに追加されます。

## データのクエリ
Kaskada にロードされたデータは、Fenl クエリを実行することによってアクセスされます。

まずはフィルターなしで`Purchace`テーブルの中身を見てみましょう。
まず、次のクエリを含むテキスト ファイルを作成します。

**query.fenl**
```text
Purchase
```
このクエリは、テーブルに含まれるすべての列と行を返します。クエリを次のように実行します。

```shell
cat query.fenl | ./kaskada-cli query run --stdout
```

## 単一エンティティによるフィルタリング
結果を 1 つのエンティティに限定すると役立つ場合があります。これにより、単一のエンティティが時間の経過とともにどのように変化するかを確認しやすくなります。

**query.fenl**
```
Purchase | when(Purchase.customer_id == "patrick")
```

```shell
cat query.fenl | ./kaskada-cli query run --stdout
```


## Fenl 関数を使用した複雑な例
この例では、`|`キャラクターを使用して関数のパイプラインを構築します。
また、`when()`関数を使用して、時間に対するフィルターをかけます。

Kaskada のクエリ言語は、時間について推論するための豊富な操作セットを提供します。

以下は、Kaskada クエリの多くのユニークな機能を用いた例です。

query.fenl
```javascript
# How many big purchases happen each hour and where?
let cadence = hourly()

# Anything can be named and re-used
let hourly_big_purchases = Purchase
| when(Purchase.amount > 10)

# Filter anywhere
| count(window=since(cadence))

# Aggregate anything
| when(cadence)

# Shift timelines relative to each other
let purchases_now = count(Purchase)
let purchases_yesterday =
   purchases_now | shift_by(days(1))

# Records are just another type
in { hourly_big_purchases, purchases_in_last_day: purchases_now - purchases_yesterday }
| extend({
  # …modify them sequentially
  last_visit_region: last(Pageview.region)
})
```


## クエリ実行の構成
CLI コマンドに引数を指定することで、クエリの実行方法を構成できます。

### 結果のタイムラインの出力方法を変更する
クエリを作成すると、結果として得られるタイムラインは、履歴またはスナップショットの 2 つの方法のいずれかで解釈されます。

 - タイムライン履歴は、エンティティの値が変更されるたびに値を生成し、各行は異なるエンティティおよび時点に関連付けられます。

 - タイムラインスナップショットは、同じ時点で各エンティティの値を生成します。各行は異なるエンティティに関連付けられますが、すべての行は同じ時間に関連付けられます。

デフォルトではタイムラインが履歴として出力されます。`--result-behavior`引数を`final-results`に設定すると、タイムラインをスナップショットとして出力できます。

```shell
cat query.fenl | ./kaskada-cli query run --result-behavior final-results
```

### 返される行数を制限する
クエリから返される行数を制限できます。

```shell
cat query.fenl | ./kaskada-cli query run --preview-rows 10
```

ここで指定した行数は一定の基準値として、いわば最低10以上、キリのよいところまで、という扱いになります。
つまり、要求したより多くの行が返される可能性があります。
というのは、Kaskada はデータをバッチで計算しているからです。
`--preview-rows`を利用すると、指定された数の行が計算されるとバッチの最後で処理が停止され、計算されたすべての行が返されます。

## クリーンアップ
このチュートリアルが完了したら、リソースを解放するために作成したテーブルを削除できます。これにより、テーブルにロードされたすべてのデータも削除されることに注意してください。

```
# Delete the Purchase table
kaskada-cli table delete --table Purchase
```