HaskellでDBを操作しよう Beamチュートリアル Part 2
2023-12-30
2024-01-24
今回はBeamチュートリアルの第二回目です。最初から読みたい方はBeamチュートリアル Part 1をご覧ください。今回は基本的なデータの取得を学んでいきます。
データの準備
まずはこのチュートリアルで使うデータを用意しましょう。Github上のmigrations/0002\_insert\_data.sql
にデータ挿入のSQLが用意してあります。以下のようにコマンドを実行してデータを挿入しましょう。
psql -U <username> beam < migrations/0002_insert_data.sql
Beamが生成するSQLの確認方法
データの操作をするにあたり、まずはBeamが生成するSQLを確認する方法を見ておきましょう。Beamを実行するには各バックエンドをインポートする必要があります。今回はPostgreSQL用のbeam-postgresを用いて説明します。もし他のバックエンドに興味がある場合はhackageで探して下さい。
さて、beam-postgresでBeamを実行するにはrunBeamPostgres
関数を使います。もし生成されたSQLを確認したいときにはrunBeamPostgresDebug
を使います。
runBeamPostgresDebug :: (String -> IO ()) -> Connection -> Pg a -> IO a
runBeamPostgresDebug
の第一引数にputStrLn
を適用することで実行時にSQLを出力できます。今回のサンプルでもいくつかの箇所でrunBeamPostgresDebug
を使用したコードを書いています。
単純なクエリ
まず単純にテーブルの全レコードリストを全フィールドで取得することを考えます。今回は全開データの挿入の項でインプットしたCategoryデータを読み込んでみましょう。
sample01 :: Connection -> IO ()
sample01 c = do
li <- runBeamPostgres c $ runSelectReturningList $ select query
print li
where
query = all_ (users blogDatabase)
最も単純なクエリですね。Beamで構築されるSQLとは異なりますが、意味としては以下のSQLに相当します。
SELECT * FROM users;
尚、本チュートリアルで紹介するコードはgithub上にあり、これをクローンすることですぐに実行してみることができます。Config.hs
のデータベース接続情報は適宜書き換えて下さい。
実行例
git clone https://github.com/kurocode25/beam-tutorial.git
cd beam-tutorial
stack run -- sample01
任意のフィールドを取得
場合によってはすべてのフィールドが必要ではないかも知れません。必要なフィールドのみを取得したい場合について考えます。なんと先程と同じように全フィールドを取得した後にreturn
もしくはpure
で必要な項目のタプルを返すだけす。
sample02 :: Connection -> IO ()
sample02 c = do
li <- runBeamPostgresDebug putStrLn c $ runSelectReturningList $ select $ do
ca <- all_ (categories blogDatabase)
return (categorySlug ca, categoryName ca)
print li
条件指定によるデータ取得
データベースを扱う上で欠かせないのが条件によるデータ取得です。ここではSQLのWHER句に相当する使い方を見ていきます。userNameが’Kuro’のユーザーを取得してみましょう。
sample03 :: Connection -> IO ()
sample03 c = do
li <- runBeamPostgresDebug putStrLn c $ runSelectReturningList $ select $ do
filter_ (\u -> userName u ==. "Kuro") $ all_ (users blogDatabase)
print li
guard_とfilter_
データ取得の際にSQLのwhere
句に相当する条件によるフィルタリングをするにはguard_
関数を使う場合とfilter_
関数を使う場合があります。いずれも同様に条件づけを行うことができます。
sample03をguard_を使って書くと以下のようになります。
sample04 :: Connection -> IO ()
sample04 c = do
li <- runBeamPostgresDebug putStrLn c $ runSelectReturningList $ select $ do
u <- all_ (users blogDatabase)
guard_ (userName u ==. "Kuro")
return u
print li
guard_
に関してはテーブルの結合のところで再度詳しく見てみたいと思います。
Limit/Offset句
APIでページネーションを行いたい場合などLIMIT/OFFSETを使いたい場合も多いと思います。そのような場合は以下のように書きます。
sample05 :: Connection -> IO ()
sample05 c = do
let limitNum = 3 :: Integer
let offsetNum = 1 :: Integer
li <- runBeamPostgres c $ runSelectReturningList $ select $ do
limit_ limitNum $ offset_ offsetNum $ all_ (categories blogDatabase)
print li
次のOrder句の部分でも触れますが、関数を適用する順番に気をつけてください。limit_関数を一番外側で適用することで選んだデータから最終的に個数を絞ることになります。
OrderBy句
データを取得する際に並び替えたい場合はorderBy_
関数を使い、昇順、降順の順はasc_
とdesc_
関数を使うことができます。
sample06 :: Connection -> IO ()
sample06 c = do
let limitNum = 3 :: Integer
let offsetNum = 1 :: Integer
li <- runBeamPostgres c $ runSelectReturningList $ select $ do
limit_ limitNum $ offset_ offsetNum $
orderBy_ (desc_ . categoryName) $ all_ (categories blogDatabase)
print li
もし並び順の条件を複数つけたい場合はタプルにします。
sample07 :: Connection -> IO ()
sample07 c = do
let limitNum = 3 :: Integer
let offsetNum = 1 :: Integer
li <- runBeamPostgres c $ runSelectReturningList $ select $ do
limit_ limitNum $ offset_ offsetNum $
orderBy_ (\ca -> (asc_ (categoryName ca) , desc_ (categorySlug ca))) $ all_ (categories blogDatabase)
print li
Distinctサポート
重複した結果を集約して結果を返すSQLのDISTINCTをBeamで行ってみましょう。nub_
関数を使うとSQLでDISTINCT
を使うことができます。
sample08 c = do
li <- runBeamPostgresDebug putStrLn c $ runSelectReturningList $ select $ do
nub_ $ all_ (posts_tags blogDatabase)
print li
まとめ
ここまででBeamを使った基本的なデータベースの操作を見てきました。できるだけ重複なく様々な書き方を紹介しながらBeamに親しめるように構成を考えたつもりですが、まだ紹介しきれなかった部分も多くあります。一度ユーザーガイドに目を通して見て下さい。
この記事との出会いが読者の方のHaskellライフを充実させるものであれば良いなと思っています。次回はテーブルの結合について学んでいきます。