メインコンテンツへスキップ
メインコンテンツへスキップ

chDB を使い始める

このガイドでは、Python 版の chDB を使って、すぐに使い始めるための手順を説明します。 まず S3 上の JSON ファイルに対してクエリを実行し、その JSON ファイルを元に chDB 内にテーブルを作成し、データに対していくつかクエリを実行します。 また、Apache Arrow や Pandas など、複数のフォーマットでクエリ結果を取得する方法を確認し、最後に Pandas の DataFrame をクエリする方法を学びます。

セットアップ

まずは仮想環境を作成します。

python -m venv .venv
source .venv/bin/activate

次に、chDB をインストールします。 バージョン 2.0.3 以降であることを確認してください。

pip install "chdb>=2.0.2"

では次に、IPython をインストールします。

pip install ipython

このガイドの以降の手順では、ipython を使ってコマンドを実行します。次のコマンドを実行して ipython を起動します。

ipython

このガイドでは Pandas と Apache Arrow も使用するので、これらのライブラリもインストールしておきましょう。

pip install pandas pyarrow

S3 内の JSON ファイルをクエリする

ここでは、S3 バケットに保存されている JSON ファイルをどのようにクエリするかを見ていきます。 YouTube dislikes dataset には、2021 年までの YouTube 動画に対する 40 億行以上の低評価データが含まれています。 このデータセットに含まれる JSON ファイルの 1 つを使って進めます。

chdb をインポートします:

import chdb

次のクエリで、ある JSON ファイルの構造を確認できます。

chdb.query(
  """
  DESCRIBE s3(
    's3://clickhouse-public-datasets/youtube/original/files/' ||
    'youtubedislikes_20211127161229_18654868.1637897329_vid.json.zst',
    'JSONLines'
  )
  SETTINGS describe_compact_output=1
  """
)
"id","Nullable(String)"
"fetch_date","Nullable(String)"
"upload_date","Nullable(String)"
"title","Nullable(String)"
"uploader_id","Nullable(String)"
"uploader","Nullable(String)"
"uploader_sub_count","Nullable(Int64)"
"is_age_limit","Nullable(Bool)"
"view_count","Nullable(Int64)"
"like_count","Nullable(Int64)"
"dislike_count","Nullable(Int64)"
"is_crawlable","Nullable(Bool)"
"is_live_content","Nullable(Bool)"
"has_subtitles","Nullable(Bool)"
"is_ads_enabled","Nullable(Bool)"
"is_comments_enabled","Nullable(Bool)"
"description","Nullable(String)"
"rich_metadata","Array(Tuple(
    call Nullable(String),
    content Nullable(String),
    subtitle Nullable(String),
    title Nullable(String),
    url Nullable(String)))"
"super_titles","Array(Tuple(
    text Nullable(String),
    url Nullable(String)))"
"uploader_badges","Nullable(String)"
"video_badges","Nullable(String)"

また、そのファイルの行数を数えることもできます:

chdb.query(
  """
  SELECT count()
  FROM s3(
    's3://clickhouse-public-datasets/youtube/original/files/' ||
    'youtubedislikes_20211127161229_18654868.1637897329_vid.json.zst',
    'JSONLines'
  )"""
)
336432

このファイルには 30 万件強のレコードが含まれています。

chdb はまだクエリパラメータの受け渡しをサポートしていませんが、パスを取り出して f 文字列で渡すことができます。

path = 's3://clickhouse-public-datasets/youtube/original/files/youtubedislikes_20211127161229_18654868.1637897329_vid.json.zst'
chdb.query(
  f"""
  SELECT count()
  FROM s3('{path}','JSONLines')
  """
)
注記

これはプログラム内で定義した変数に対して行う分には問題ありませんが、ユーザーからの入力に対しては決して行わないでください。そうしないと、クエリが SQL インジェクション攻撃に対して無防備になります。

出力フォーマットの設定

デフォルトの出力フォーマットは CSV ですが、output_format パラメータで変更できます。 chDB は ClickHouse のデータフォーマットに加えて、DataFrame を含む独自のフォーマットもサポートしており、DataFrame フォーマットでは Pandas DataFrame が返されます。

result = chdb.query(
  f"""
  SELECT is_ads_enabled, count()
  FROM s3('{path}','JSONLines')
  GROUP BY ALL
  """,
  output_format="DataFrame"
)

print(type(result))
print(result)
<class 'pandas.core.frame.DataFrame'>
   is_ads_enabled  count()
0           False   301125
1            True    35307

また、結果を Apache Arrow のテーブルとして取得したい場合は次のようにします。

result = chdb.query(
  f"""
  SELECT is_live_content, count()
  FROM s3('{path}','JSONLines')
  GROUP BY ALL
  """,
  output_format="ArrowTable"
)

print(type(result))
print(result)
<class 'pyarrow.lib.Table'>
pyarrow.Table
is_live_content: bool
count(): uint64 not null
----
is_live_content: [[false,true]]
count(): [[315746,20686]]

JSON ファイルからテーブルを作成する

次に、chDB でテーブルを作成する方法を見ていきます。 これを行うには別の API を利用する必要があるので、まずはそれをインポートします。

from chdb import session as chs

次にセッションを初期化します。 セッションをディスク上に永続化したい場合は、ディレクトリ名を指定する必要があります。 何も指定しない場合、データベースはメモリ上だけに保持され、Python プロセスを終了した時点で失われます。

sess = chs.Session("gettingStarted.chdb")

次にデータベースを作成します。

sess.query("CREATE DATABASE IF NOT EXISTS youtube")

これで、JSON ファイルのスキーマに基づいて CREATE...EMPTY AS 手法を使い、dislikes テーブルを作成できます。 すべてのカラム型が Nullable になってしまわないようにするため、schema_inference_make_columns_nullable 設定を使用します。

sess.query(f"""
  CREATE TABLE youtube.dislikes
  ORDER BY fetch_date 
  EMPTY AS 
  SELECT * 
  FROM s3('{path}','JSONLines')
  SETTINGS schema_inference_make_columns_nullable=0
  """
)

次に、DESCRIBE 句を使用してスキーマを確認します。

sess.query(f"""
   DESCRIBE youtube.dislikes
   SETTINGS describe_compact_output=1
   """
)
"id","String"
"fetch_date","String"
"upload_date","String"
"title","String"
"uploader_id","String"
"uploader","String"
"uploader_sub_count","Int64"
"is_age_limit","Bool"
"view_count","Int64"
"like_count","Int64"
"dislike_count","Int64"
"is_crawlable","Bool"
"is_live_content","Bool"
"has_subtitles","Bool"
"is_ads_enabled","Bool"
"is_comments_enabled","Bool"
"description","String"
"rich_metadata","Array(Tuple(
    call String,
    content String,
    subtitle String,
    title String,
    url String))"
"super_titles","Array(Tuple(
    text String,
    url String))"
"uploader_badges","String"
"video_badges","String"

次に、そのテーブルにデータを投入します。

sess.query(f"""
  INSERT INTO youtube.dislikes
  SELECT * 
  FROM s3('{path}','JSONLines')
  SETTINGS schema_inference_make_columns_nullable=0
  """
)

CREATE...AS 手法を使えば、これら両方の手順を一度にまとめて実行することもできます。 では、その手法を使って別のテーブルを作成してみましょう。

sess.query(f"""
  CREATE TABLE youtube.dislikes2
  ORDER BY fetch_date 
  AS 
  SELECT * 
  FROM s3('{path}','JSONLines')
  SETTINGS schema_inference_make_columns_nullable=0
  """
)

テーブルをクエリする

最後に、そのテーブルに対してクエリを実行してみましょう。

df = sess.query("""
  SELECT uploader, sum(view_count) AS viewCount, sum(like_count) AS likeCount, sum(dislike_count) AS dislikeCount
  FROM youtube.dislikes
  GROUP BY ALL
  ORDER BY viewCount DESC
  LIMIT 10
  """,
  "DataFrame"
)
df
                             アップローダー  視聴回数  高評価数  低評価数
0                             Jeremih  139066569     812602         37842
1                     TheKillersMusic  109313116     529361         11931
2  LetsGoMartin- Canciones Infantiles  104747788     236615        141467
3                    Xiaoying Cuisine   54458335    1031525         37049
4                                Adri   47404537     279033         36583
5                  Diana and Roma IND   43829341     182334        148740
6                      ChuChuTV Tamil   39244854     244614        213772
7                            Cheez-It   35342270        108            27
8                            Anime Uz   33375618    1270673         60013
9                    RC Cars OFF Road   31952962     101503         49489

次に、DataFrame に「高評価と低評価の比率」を計算するための列を追加するとします。 その場合、次のようなコードを記述できます。

df["likeDislikeRatio"] = df["likeCount"] / df["dislikeCount"]

Pandas データフレームをクエリする

その後、chDB からその DataFrame に対してクエリを実行できます。

chdb.query(
  """
  SELECT uploader, likeDislikeRatio
  FROM Python(df)
  """,
  output_format="DataFrame"
)
                             uploader  likeDislikeRatio
0                             Jeremih         21.473548
1                     TheKillersMusic         44.368536
2  LetsGoMartin- Canciones Infantiles          1.672581
3                    Xiaoying Cuisine         27.842182
4                                Adri          7.627395
5                  Diana and Roma IND          1.225857
6                      ChuChuTV Tamil          1.144275
7                            Cheez-It          4.000000
8                            Anime Uz         21.173296
9                    RC Cars OFF Road          2.051021

Pandas の DataFrame に対するクエリについては、Querying Pandas developer guide も参照してください。

次のステップ

このガイドを通じて、chDB の概要を把握していただけたかと思います。 さらに詳しい使い方については、以下の開発者向けガイドを参照してください。