Nao000のぶろぐ

蝶を追っている少年になりたい

【Elixir Phoenix】Ecto.Adapters.SQL.query でちょっと複雑な生SQLを書く

目次

  1. バージョン情報
  2. 実現したいSQL結果
  3. Ecto.Adapters.SQL.queryによる実装
  4. 所感
  5. 参考サイト

バージョン情報

Elixir: 1.8.1

Phoenix: 1.4.2

実現したいSQL結果

特定のカテゴリの記事一覧を取得するSQLをElixir上で実現したい。

記事一覧を取得する際、フレームワークの Phoenix のドキュメントを参考にして、以下のようにブログ一覧を取得していました。

    Nao000dotcom.Blog |> Nao000dotcom.Repo.all()

最近、記事にカテゴリの概念を追加しました。そこで、特定のカテゴリの記事一覧を取得することにしました。上記のように用意された関数で実現できないか探しましたが見当たらないので生クエリで実行することにしました。

Ecto.Adapters.SQL.queryによる実装

以下コードでカテゴリ記事一覧取得します。以下コードは こちら を参考にしました。

      def fetchBlogSpecificCategory(categoryId) do
        query = "SELECT b.id, b.title, b.summary, b.url_title, b.created_at FROM blogs AS b LEFT JOIN blogcategories bc ON b.category_id = bc.id WHERE b.category_id = $1;"

        res = Ecto.Adapters.SQL.query!(Nao000dotcom.Repo, query, [categoryId])

        cols = Enum.map res.columns, &(String.to_atom(&1))

        roles = Enum.map res.rows, fn(row) ->
          struct(Nao000dotcom.Blog , Enum.zip(cols, row))
        end
      end

Ecto.Adapters.SQL.query の結果は以下のような %Postgrex.Result構造体 になっています。

    %Postgrex.Result{
      columns: ["id", "title", "summary", "url_title", "created_at"],
      command: :select,
      connection_id: 8214,
      messages: [],
      num_rows: 7,
      rows: [
        [2, "Elixirでダイクストラ法を実装してみました",
         "この記事ではダイクストラ法アルゴリズムをElixirで実装したものをご紹介します。",
         "dijkstra_by-elixir", ~N[2019-05-06 15:46:00.000000]],
        ~省略~
      ]
    }

このままでは扱いづらいので以下の部分で Nao000dotcom.Blog構造体 にマッピングしています。カラムのコレクションをAtomのリストに変換、その後 Nao000dotcom.Blog構造体 にマッピングします。

        cols = Enum.map res.columns, &(String.to_atom(&1))
        #   ["id", "title", "summary", "url_title", "created_at"]
        #                          ↓↓↓
        #   [:id, :title, :summary, :url_title, :created_at]

        roles = Enum.map res.rows, fn(row) ->
          struct(Nao000dotcom.Blog , Enum.zip(cols, row))
        end
        #   [
        #     %Nao000dotcom.Blog{
        #       __meta__: #Ecto.Schema.Metadata<:built, "blogs">,
        #       category_id: nil,
        #       created_at: ~N[2019-05-06 15:46:00.000000],
        #       detail: nil,
        #       id: 2,
        #       keywords: nil,
        #       pv: nil,
        #       summary: "この記事ではダイクストラ法アルゴリズムをElixirで実装したものをご紹介します。",
        #       title: "Elixirでダイクストラ法を実装してみました",
        #       updated_at: nil,
        #       url_title: "dijkstra_by-elixir"
        #     },
        #     ~省略~
        #   ]

あとは、コントローラー側で受け取って整理して、templateに出力すればカテゴリ記事一覧を取得できます。

所感

今回は Nao000dotcom.Blog構造体 にマッピングできますが、カテゴリテーブルのデータを含めてマッピングする場合は構造体を用意する必要があります。SELECT用にマッピングする構造体を用意したほうがいいのかな?と思いました。更新系のSQLではテーブルをJOINすることは経験上ないので、用意された関数で間に合うかと思ってます。

書いてて気づいたのですが、今のブログの仕様では記事とカテゴリが1対1になっていました。1記事に複数カテゴリほしくなったらテーブル追加しなければ。

参考サイト