Sheharyar
Sheharyar

Reputation: 75740

Get only specific fields when fetching data using Ecto in Phoenix

I'm trying to return some JSON Data in one of my API Calls in Phoenix. I'm fetching all records of Subject and sending them but Ecto returns some extra fields that I do not want.

What can I do to:


This is my Controller:

# Controller
def index(conn, _) do
    subjects = Subject |> Repo.all
    conn |> render subjects: subjects
end

This is my View:

# View
def render("index.json", %{subjects: subjects}) do
    subjects
end

This is my response:

[
    {
        "teachers": {
            "__owner__": "Elixir.MyApp.Subject",
            "__field__": "teachers",
            "__cardinality__": "many"
        },
        "updated_at": "2015-06-20T15:32:20Z",
        "topics": {
            "__owner__": "Elixir.MyApp.Subject",
            "__field__": "topics",
            "__cardinality__": "many"
        },
        "name": "Physics",
        "inserted_at": "2015-06-20T15:32:20Z",
        "id": 1,
        "__meta__": {
            "state": "loaded",
            "source": "subjects"
        }
    },
    {
        "teachers": {
            "__owner__": "Elixir.MyApp.Subject",
            "__field__": "teachers",
            "__cardinality__": "many"
        },
        "updated_at": "2015-06-20T15:37:59Z",
        "topics": {
            "__owner__": "Elixir.MyApp.Subject",
            "__field__": "topics",
            "__cardinality__": "many"
        },
        "name": "Chemistry",
        "inserted_at": "2015-06-20T15:37:59Z",
        "id": 2,
        "__meta__": {
            "state": "loaded",
            "source": "subjects"
        }
    },
    {
        "teachers": {
            "__owner__": "Elixir.MyApp.Subject",
            "__field__": "teachers",
            "__cardinality__": "many"
        },
        "updated_at": "2015-06-20T15:38:41Z",
        "topics": {
            "__owner__": "Elixir.MyApp.Subject",
            "__field__": "topics",
            "__cardinality__": "many"
        },
        "name": "Mathematics",
        "inserted_at": "2015-06-20T15:38:41Z",
        "id": 3,
        "__meta__": {
            "state": "loaded",
            "source": "subjects"
        }
    },
    {
        "teachers": {
            "__owner__": "Elixir.MyApp.Subject",
            "__field__": "teachers",
            "__cardinality__": "many"
        },
        "updated_at": "2015-06-22T15:40:17Z",
        "topics": {
            "__owner__": "Elixir.MyApp.Subject",
            "__field__": "topics",
            "__cardinality__": "many"
        },
        "name": "Biology",
        "inserted_at": "2015-06-22T15:40:17Z",
        "id": 4,
        "__meta__": {
            "state": "loaded",
            "source": "subjects"
        }
    }
]

Upvotes: 8

Views: 5919

Answers (2)

Sbbs
Sbbs

Reputation: 1670

I use the following macro in MyApp.Repo, which provides a reusable way to pluck specific fields:

defmodule MyApp.Repo do
  use Ecto.Repo,
    otp_app: :my_app,
    adapter: Ecto.Adapters.Postgres

  import Ecto.Query, warn: false, only: [select: 2]
  def pluck(query, fields) when is_atom(fields) do
    pluck(query, [fields])
  end
  def pluck(query, fields) when is_list(fields) do
    query |> select(^fields) |> all |> Enum.map(&Map.take(&1, fields))
  end
end

Example usage:

  • my_query |> MyApp.Repo.pluck([:id, :inserted_at])
  • my_query |> MyApp.Repo.pluck(:id)

Upvotes: 0

Change your view to:

def render("index.json", %{subjects: subjects}) do
  Enum.map(subjects, &Map.take(&1, [:id, :name]))
end

Additionally, you can also ask Ecto to return a subset of fields by changing your controller to:

def index(conn, _) do
  subjects = from(s in Subject, select: %{id: s.id, name: s.name}) |> Repo.all
  conn |> render subjects: subjects
end

Upvotes: 15

Related Questions