もくじ
したいこと
Rubyで書いたプログラムからデータベース(以下、「DB」という)にアクセスしたい。
とりあえず今回はPostgreSQLで作成したDBからPG
というgemを使って全てのレコードを取り出してみることにします。
PG
のインストール、DB・テーブルの作成等は済んでいて、コードさえ正しく書けばDBを使える状態が前提になります。
DBとプログラムは別の世界
以前どこかで、DBというのはプログラムから見ると全く別の概念で構成された世界、異界であるときいたことがあります。
言われてみると確かにプログラムはコンピュータに話しかけるための言語(Ruby、JavaScript等)によって表現された世界ですが、DBに話しかけるためにはSQLという専用の言語を使わなくてはならず、コミュニケーションの方法からして全然違うわけです。
僕の場合、普段はRubyの世界でオブジェクトが~とかメソッドが~とか言って遊んでいるわけですが、DBの世界でオブジェクトだのメソッドだの言っても向こうでは「???」となってしまいます。そもそもそんな概念は向こうの世界にはありません。
そりゃそうです。 宇宙人に日本語で戦車道を嗜む女子高生の話をして通じるわけがないのと一緒です。
あくまで僕個人のイメージですが、Rubyの世界が僕たちの住んでいる「モノ」が存在する世界だとすると、DBの世界はふわふわした雲みたいなものしかない世界、という感じです。
ということは、DBからプログラムにデータをもってきていじくりまわすためには、(あくまでもイメージですが)そんなふわふわの世界に存在する雲のようなものを僕たちが住んでいる世界の「モノ」に変換して、手で触れられる存在にする必要があります。味もみておこう。
で、そんなすんばらしいことを実現してくれるのが本日の主役PG
パイセンです。
PG
Rubyのコード内でSQLを発行してDBにアクセスするためのgemです。
手順としては、
- DBとのコネクションを構築
- SQLを発行
- レコードをゲット!
という感じです。
順番にやってみます。
手順
1. DBとのコネクションを構築
これができれば勝ったようなもんです。
いくつか方法はあるようですが、今回はPG::connection
クラスに接続先のDBの情報を渡してインスタンスを作成するパターンでやってみます。
require 'pg' #DBの情報を変数へ格納 host = '{IPアドレス}' port = {ポート番号} db = '{DB名}' user = '{ログインユーザー名}' password = '{パスワード}' #上で定義した変数を`PG::Connection.new`の引数として渡してインスタンスを作成 connection = PG::Connection.new(host: host, port: port, dbname: db, user: user, password: password)
変数connection
がDBとのコネクションということになります。
なんだかイメージが湧きにくいのですが、PG::Connection
クラスのインスタンスはワームホールみたいなものだと思っています。
余談ですが、ワームホールといえば個人的には映画『インターステラー』に登場したワームホールが印象に残っています。
普通ワームホールというと「地面に空いた穴」みたいなものや、ドラえもんに登場する通りぬけフープのような平面的なものを想像しがちなんですが、『インターステラー』のワームホールはなんと「球体状の穴」という摩訶不思議なものでした。要は360度どこから見ても穴なので、どこからでもその中に入っていけるという脳がパニックになりそうな代物です。
ちなみに、この記事の本筋とは関係ないですが『インターステラー』は傑作だと思うのでご興味があれば是非ご覧になってみてください。「ウラシマ効果」や「事象の地平面」あたりのワードに反応できる人は間違いなく楽しめます。
話がだいぶそれましたが、僕の頭の中では異界であるDBにつながっているPG::Connection
クラスのインスタンスはまさに「球体状の穴」のイメージがぴったりで、このコードを書きながら楽しんでいました。
だってただの穴より立体的な穴のほうがオブジェクトっぽいじゃないですか(?)
というわけで、異界につながる穴ができました。
あとはここに何かを投げ入れれば向こうの世界に情報が登録され、正しい呼び方をすれば向こうの世界にある情報がこちらの世界で使えるオブジェクトになって穴から出てくるわけです。
2. SQLを発行 → 3. レコードをゲット!
さて今回はDB内の全ての情報をまるっと取ってきたいと思います。
やり方は簡単で、先ほど用意した変数connection
にexec
メソッドを使ってSQLを投げてやればおkです。
all_data = connection.exec("SELECT * FROM テーブル名")
これでテーブル名
で指定したテーブル内の全てのレコードの全てのカラムから情報を引っ張り出すことができました。
もう少し詳しく説明すると、connection.exec
の返り値はPG::Result
クラスのインスタンスです。
構造としてはハッシュを要素として持つ配列です。
ハッシュ1つで1レコード、各カラム名と値がそれぞれkeyとvalueになります。
例えばDBが以下のような構造の場合、
カラム1 | カラム2 | カラム3 |
---|---|---|
1 | foo | hoge |
2 | bar | fuga |
3 | buzz | puni |
返り値は
[ { カラム1 => 1, カラム2 => foo, カラム3 => hoge }, { カラム1 => 2, カラム2 => bar, カラム3 => fuga }, { カラム1 => 3, カラム2 => buzz, カラム3 => puni } ]
という形になります。
この返り値について一点気を付けなければいけないのは、実態としては配列であっても、あくまでもPG::Result
クラスのインスタンスだということです。
なので、Array
クラスの全ての機能が使えると思っていると変なところで詰まるかもしれません。(未確認)
が、ご安心ください。
PG::Result
にはEnumerable
モジュールがincludeされているため、to_a
メソッドが使えます。
そのままでは扱いにくいという場合はto_a
でArray
クラスにしてしまいましょう。
まとめ
というわけで、これで異界と現世をつないでモノをやり取りすることに成功しました。
あとは煮るなり焼くなりお好きにどうぞ。
どの記事でもそうなのですが、例え話などはあくまでも僕のイメージや感覚的なものを文章にしているので、僕と感性が合わない人にとってはあまりピンとこない部分もあるかと思います。
そのあたりは各自の脳内で読み替えたり補完したりしてください。
では、今回はこんな感じで。
記載内容に間違い等があった場合にはこっそり教えていただければ幸いです。
最後までお読みいただきありがとうございました。