Get only specific fields when fetching data using Ecto in Phoenix
Asked Answered
C

2

8

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:

  • Get only specific attributes (E.g. only id and name)
  • Not get unnecessary fields in my response (such as __meta__ and __owner__)

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"
        }
    }
]
Concretion answered 6/7, 2015 at 20:35 Comment(0)
P
15

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
Pritchett answered 6/7, 2015 at 20:49 Comment(2)
Thanks for the answer. I understand how to select specific fields, but what if I want to reject a specific field and get everything else. For example, if I don't want teachers and topics in the above example.Concretion
Change Map.take/2 to Map.drop/2. You can find all map functions at elixir-lang.org/docs/stable/elixir.Chervonets
V
0

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)
Vizier answered 2/8 at 17:39 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.