LoginSignup
10
4

More than 5 years have passed since last update.

ElixirのMnesiaラッパー、Amnesiaを使ってみる

Last updated at Posted at 2018-02-11

 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を追加します。

mix.exs
#
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関数を定義しています。

lib/Database.ex
# 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を作成します。

lib/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}
]

 以上で終わりです。

10
4
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
10
4