Elixirで手軽にDBを使うにはMnesiaがよいらしい。しかもAmnesiaというラッパーを使うともっと手軽に始められるということです。早速使ってみましょう。以下のサイトを参考にしました。
「amnesia - mnesia wrapper for Elixir」
「Store Everything With Elixir and Mnesia」
「Using mnesia database from Elixir」
まずプロジェクトを作成します。
mix new amnesia_test
次にdepsにamnesiaを追加します。
#
defp deps do
[{:amnesia, "~> 0.2.7"}]
end
#
インストールします。
mix deps.get
lib/Database.exにdatabase/tableの定義を行います。Databaseはいくつかのテーブルとhelper関数をグループ化したものです。githubの公式のexampleをそのまま掲載します。コメントがたくさんついていますので、自分なりに翻訳してみました。コメントを読めばだいたい理解できるでしょうが、少し説明を加えておきます。このdatabaseはDatabaseという名前で作られ、UserとMessageという2つのテーブルと小さなhelper関数を定義しています。
# defdatabaseや他のmacrosを使えるようにする。
use Amnesia
# defdatabase macroでdatabaseを定義する。Databaseと名付けます。
# defdatabaseは基本的にdefmoduleを拡張したものです。
defdatabase Database do
# Message関数で参照するUserテーブル名をあらかじめ宣言しておきます。
# ここでは宣言だけで、実際の定義は下の方で行います。
deftable User
# user_id key と content の2つの属性を持つMessageテーブルを定義します。タイプはbagです。
deftable Message, [:user_id, :content], type: :bag do
# これは必要ではないですが、typeをより厳密に定義してくれます。
@type t :: %Message{user_id: integer, content: String.t}
# helper関数です。Message recordからuserを取り出します
def user(self) do
User.read(self.user_id)
end
# 上のdirtyバージョンです
def user!(self) do
User.read!(self.user_id)
end
end
# ユーザの属性を記述したUserテーブルの定義です。タイプはordered_setです。
# emailのインデックスを作ります。検索速度が向上します。
deftable User, [{ :id, autoincrement }, :name, :email], type: :ordered_set, index: [:email] do
# 対応の宣言は必ずしも必要はありませんが、あった方が良いです。
@type t :: %User{id: non_neg_integer, name: String.t, email: String.t}
# helper関数です。userへのmessageを追加します, using write
# Message.writeは引数をMessageテーブルに書き込みます。
def add_message(self, content) do
%Message{user_id: self.id, content: content} |> Message.write
end
# helper関数です。add_messageのdirtyバージョンです。
# 上でwriteを使う代わりに、ここではwrite!を使っています。
def add_message!(self, content) do
%Message{user_id: self.id, content: content} |> Message.write!
end
# helper関数です。特定ユーザの全メッセージを取得します。
def messages(self) do
Message.read(self.id)
end
# helper関数です。messagesのdirtyバージョンです
def messages!(self) do
Message.read!(self.id)
end
end
end
上のテーブル定義ですが、Messageテーブルはtypeをbagとして、Userテーブルはtypeをordered_setとして定義されています。 一般にmnesiaのテーブルは以下の3つのtypeに分かれます。
:set - デフォルトタイプです。 primary keyでユニーク性が保たれ、
同じprimary keyを持つrowは複数存在しません。またrowはソートされません。
:ordered_set - :set と同じですが、primary keyでソートされます。
:bag - 同じprimary keyを持つrowが複数存在しえます。但し全く同じrowは存在しません。
次のコマンドでデータベースを作成します。 --disk ですからカレントnodeのディスクへデータが保存されます。--memoryを指定すればメモリに保存されます。
mix amnesia.create -d Database --disk
ちなみに、データベースを削除するには以下のコマンドを打ちます。
mix amnesia.drop -d Database
さてデータベースを作成したらiexを立ち上げてみましょう。上で定義したDatabaseを使うためにuse Databaseを行います。
$ iex -S mix
iex(1)> use Database
[Amnesia, Amnesia.Fragment, Exquisite, Database, Database.User, Database.User,
Database.Message, Database.Message]
UserテーブルにRichardを追加します。User.write!で書き込んでいます。
iex(2)> richard = %User{name: "Richard", email: "richard@example.com"} |>User.write!
%Database.User{email: "richard@example.com", id: 1, name: "Richard"}
MessageテーブルにRichardへのメッセージを3個書きます。User.add_message!というhelper関数を使っています。
iex(3)> richard |> User.add_message!("aaaaaaaaaa")
%Database.Message{content: "aaaaaaaaaa", user_id: 1}
iex(4)> richard |> User.add_message!("bbbbbbbbbbb")
%Database.Message{content: "bbbbbbbbbbb", user_id: 1}
iex(5)> richard |> User.add_message!("ccccccccccc")
%Database.Message{content: "ccccccccccc", user_id: 1}
Richardのメッセージをすべて読みます。User.messages!というhelper関数を使っています。
iex(6)> User.messages!(richard)
[
%Database.Message{content: "aaaaaaaaaa", user_id: 1},
%Database.Message{content: "bbbbbbbbbbb", user_id: 1},
%Database.Message{content: "ccccccccccc", user_id: 1}
]
ここで一度iexを終了して、再度立ち上げます。
[root@www13134uf amnesia_test]# iex -S mix
iex(1)> use Database
[Amnesia, Amnesia.Fragment, Exquisite, Database, Database.User, Database.User,
Database.Message, Database.Message]
Richard を取得します。データベースはディスクに保存してあるので無事読み込めます。
iex(2)> richard = User.read!(1)
%Database.User{email: "richard@example.com", id: 1, name: "Richard"}
Richard のメッセージを取得します。やはりデータは残っていました。めでたしめでたしです。
iex(3)> User.messages!(richard)
[
%Database.Message{content: "aaaaaaaaaa", user_id: 1},
%Database.Message{content: "bbbbbbbbbbb", user_id: 1},
%Database.Message{content: "ccccccccccc", user_id: 1}
]
この2つの操作をmodule/function化しておきましょう。Reader.exを作成します。
defmodule Reader do
use Database
def getMess(id) do
p = User.read!(id)
User.messages!(p)
end
end
iexを立ち上げて、Reader.getMess(1)を叩くだけで結果が出力できるようになりました。
$ iex -S mix
iex(1)> Reader.getMess(1)
[
%Database.Message{content: "aaaaaaaaaa", user_id: 1},
%Database.Message{content: "bbbbbbbbbbb", user_id: 1},
%Database.Message{content: "ccccccccccc", user_id: 1}
]
以上で終わりです。