22
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

癒しを求めて Elixir

Posted at

はじめに

気づけばもう9月も半分が過ぎました。
この調子だと、2025年もあっという間に終わってしまいそうで、つい
遠い目になってしまいます。

そんな慌ただしい毎日に、ちょっとした「癒し」を求めて
今回は Elixir を触ってみようと思います。

ちなみに

Elixir は、Stack Overflow 2025 survey
Admired and Desired 部門で Rust・Gleam に次ぐ第3位 にランクインしていました!

作者もリアクションされている 2位の Gleam ってなんだ?と調べてみたら
Elixir と同じく Erlang VM(BEAM)上で動く言語 なんですね。

また、今回は触れませんが Elixir の Web Framework Phoenix も
3年連続満足度1位を記録しています🏆

Elixir/Erlang が盛り上がってますね!

さて、今回 Elixir で何をしようかなと思い
以前 Rust で触れた DuckDB を 今度は Elixir で操作してみようと思います!

環境準備

プロジェクトを作成します名前は duckdb_demo とします

> mix new duckdb_demo
* creating README.md
* creating .formatter.exs
* creating .gitignore
* creating mix.exs
* creating lib
* creating lib/duckdb_demo.ex
* creating test
* creating test/test_helper.exs
* creating test/duckdb_demo_test.exs

Your Mix project was created successfully.
You can use "mix" to compile it, test it, and more:

    cd duckdb_demo
    mix test

Run "mix help" for more commands.

elixir で DuckDB を操作するためのライブラリ Duckdbex を mix.exs に追加します

  defp deps do
    [
      {:duckdbex, "~> 0.3.9"}
    ]
  end

mix.exs に追加したら依存ライブラリを取得します

> mix deps.get
Resolving Hex dependencies...
Resolution completed in 0.031s
New:
  cc_precompiler 0.1.11
  duckdbex 0.3.14
  elixir_make 0.9.0
* Getting duckdbex (Hex package)
* Getting cc_precompiler (Hex package)
* Getting elixir_make (Hex package)

やってみる

Duckdbex を追加できたら今回はお手軽に iex から操作します
mix.exs に追加した duckdbex を使いたいので mix.exs をロードして iex を起動します

> iex -S mix
Erlang/OTP 28 [erts-16.0.2] [source] [64-bit] [smp:10:10] [ds:10:10:10] [async-threads:1] [jit] [dtrace]

==> elixir_make
Compiling 8 files (.ex)
Generated elixir_make app
==> cc_precompiler
Compiling 3 files (.ex)
Generated cc_precompiler app
==> duckdbex
Compiling 3 files (.ex)
Generated duckdbex app
==> duckdb_demo
Compiling 1 file (.ex)
Generated duckdb_demo app
Interactive Elixir (1.18.4) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> 

では早速やってみましょう

# DBを開く (メモリ上)
iex> {:ok, db} = Duckdbex.open()

# 接続を作成
iex> {:ok, conn} = Duckdbex.connection(db)

# テーブル作成
iex> {:ok, _res} = Duckdbex.query(conn, "CREATE TABLE users(id INTEGER, name VARCHAR);")

# データ挿入
iex> {:ok, _res} = Duckdbex.query(conn, "INSERT INTO users VALUES (1, 'nakamura'), (2, 'yuzuriha');")

# データ取得
iex> {:ok, ref} = Duckdbex.query(conn, "SELECT * FROM users;")
iex> rows = Duckdbex.fetch_all(ref)
iex> IO.inspect(rows)
# => [[1, "nakamura"], [2, "yuzuriha"]]

# 繋げるとこんな書き方もできます (直接パイプ演算子で繋げれないので then/2 を挟む)
iex> rows = Duckdbex.query(conn, "SELECT * FROM users;") |> then(fn {:ok, ref} -> Duckdbex.fetch_all(ref) end)
iex> IO.inspect(rows)
# => [[1, "nakamura"], [2, "yuzuriha"]]

できましたね 🎉
(今回は手軽さを求めて DB の永続化は行なっていません)

次に CSV ファイルを読み込んでみようと思います
まずは、CVS ファイルを用意

> cat users.csv
id,name
1,nakamura
2,yuzuriha
3,tanaka
4,yamada
5,sakamoto

ではこれを Duckdbex に投入して users テーブルを作ってみます

# データベースを開く
iex> {:ok, db} = Duckdbex.open()
iex> {:ok, conn} = Duckdbex.connection(db)

# users テーブルが存在している場合は DROP しておく
iex> {:ok, _ref} = Duckdbex.query(conn, "DROP TABLE IF EXISTS users")

# CSV ファイルを読み込み、テーブルを作成
iex> {:ok, _ref} = Duckdbex.query(conn, "CREATE TABLE users AS SELECT * FROM 'users.csv';")

CSV ファイルから作成した users テーブルの中身を確認してみます

iex> {:ok, ref} = Duckdbex.query(conn, "SELECT * FROM users")
iex> rows = Duckdbex.fetch_all(ref)
iex> IO.inspect(rows)
[
  [1, "nakamura"],
  [2, "yuzuriha"],
  [3, "tanaka"],
  [4, "yamada"],
  [5, "sakamoto"]
]

# iex では、`iex> rows` としても rows の内容を確認することができます
# iex> rows
# [
#   [1, "nakamura"],
#   [2, "yuzuriha"],
#   [3, "tanaka"],
#   [4, "yamada"],
#   [5, "sakamoto"]
# ]

できましたね 🎉
このままテーブルから取得した情報をリストで扱ってもよいのですが
カラム名と値がペアのマップに変換してみます

users テーブルのカラム名を取得したいので PRAGMA コマンドに table_info オプションを使ってテーブルの情報を取得します

iex> {:ok, ref} = Duckdbex.query(conn, "PRAGMA table_info('users')")
iex> cols = Duckdbex.fetch_all(ref)
[
  [0, "id", "BIGINT", false, nil, false],
  [1, "name", "VARCHAR", false, nil, false]
]

cols からカラム名を抽出します

iex> colnames = Enum.map(cols, fn [_cid, name | _] -> name end)
["id", "name"]

users テーブルから取得したデータ(rows)とカラム名(colnames)でマップを作ります

iex> users =
...>   Enum.map(rows, fn row ->
...>     Enum.zip(colnames, row) |> Enum.into(%{})
...>   end)
[
  %{"id" => 1, "name" => "nakamura"},
  %{"id" => 2, "name" => "yuzuriha"},
  %{"id" => 3, "name" => "tanaka"},
  %{"id" => 4, "name" => "yamada"},
  %{"id" => 5, "name" => "sakamoto"}
]

作成したマップ(users)からid:2のデータを取得してみます

iex> user = Enum.find(users, & &1["id"] == 2)
%{"id" => 2, "name" => "yuzuriha"}

# これでもOK
# iex> user = Enum.find(users, fn row -> row["id"] == 2 end)
# %{"id" => 2, "name" => "yuzuriha"}

続いて name を取得してみます

iex> name = user |> Map.get("name")
"yuzuriha"

# これでもOK
# iex> name = Map.get(user, "name")
# "yuzuriha"

Elixir ではデータを左から右へ渡すことでデータの流れをわかりやすくしています。なので、
Map.get(user, “name”) よりも
user |> Map.get(”name”) の方が Elixir らしい書き方になりますね!

おわり

Elixir と DuckDB を組み合わせると、ちょっとしたデータ遊びがとても気軽にできます。
複雑なセットアップも不要で、IEx からすぐに試せるのは「癒し」そのもの。
疲れたときに、ちょっと Elixir を開いてデータをいじってみる──
そんな小さな時間が、意外と良いリフレッシュになるかもしれません。

22
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
22
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?