Oxyrus

Oxyrus

Absinthe - Many to many assoc with index table

Hello.

I’m using the excellent Absinthe package, I’m trying to create a many to many association with an Index table, it looks like this:

defmodule Vulkan.Course do
  use Vulkan.Web, :model

  schema "courses" do
    field :name, :string
    field :description, :string
    
    many_to_many :users, Vulkan.User, join_through: "user_courses"

    timestamps()
  end
  .
  .
  .
end
defmodule Vulkan.User do
  use Vulkan.Web, :model

  schema "users" do
    field :name, :string
    field :handle, :string
    field :email, :string
    field :is_admin, :boolean, default: false
    field :encrypted_password, :string
    
    field :password, :string, virtual: true
    
    many_to_many :courses, Vulkan.Course, join_through: "user_courses"

    timestamps()
  end
  .
  .
  .
end

And the index looks like this:

defmodule Vulkan.UserCourse do
  use Vulkan.Web, :model

  schema "user_courses" do
    belongs_to :user, Vulkan.User
    belongs_to :course, Vulkan.Course

    timestamps()
  end
end

Just in case, the migration:

defmodule Vulkan.Repo.Migrations.CreateUserCourse do
  use Ecto.Migration

  def change do
    create table(:user_courses) do
      add :user_id, references(:users, on_delete: :nothing), null: false
      add :course_id, references(:courses, on_delete: :nothing), null: false

      timestamps()
    end
    create index(:user_courses, [:user_id])
    create index(:user_courses, [:course_id])
    create index(:user_courses, [:user_id, :course_id], unique: true)
  end
end

Now my question is, how would I resolve the association? Should I create a type and resolve the association through it?

object :user_course do
    field :id, :id
    field :user_id, :id
    field :course_id, :id
end

object :user do
    field :id, :id
    field :name, :string
    field :handle, :string
    field :email, :string
    field :is_admin, :boolean
    field :posts, list_of(:post), resolve: assoc(:posts)
    field :courses, list_of(:course), resolve: assoc(:user_courses)
end

This approach doesn’t seem to work, because I’ve inserted a UserCourse with user_id; 1 and course_id: 1 (both exist in the database) and when I run the query

query posts {
  users {
    id
    name
    courses {
      id
      name
    }
  }
}

I get the response

{
  "data": {
    "users": [
      {
        "name": "Andrés Pérez",
        "id": "1",
        "courses": null
      },
      {
        "name": "Jon Doe",
        "id": "2",
        "courses": null
      }
    ]
  }
}

I can’t manage to find the docs for Absinthe-Ecto, so I’m quite lost.

How should I be doing it?

Any help is highly appreciated, thank you so much for your time.

Most Liked

benwilson512

benwilson512

Author of Craft GraphQL APIs in Elixir with Absinthe

woot! glad it’s working :slight_smile:

benwilson512

benwilson512

Author of Craft GraphQL APIs in Elixir with Absinthe

I’m not sure how this ends up fitting in. Where are you putting this, inside the field :courses resolver? The problem with this is that if you do

{
  users {
    courses { name }
  }
}

You’ve now got an N + 1 pattern. If you use the assoc helper getting each user’s courses are batched together so you do just 1 database query regardless of how many users there are.

benwilson512

benwilson512

Author of Craft GraphQL APIs in Elixir with Absinthe

You’ll want to update Absinthe.Ecto, support for many to many was only merged last night.

Oxyrus

Oxyrus

Working, thank you and @bruce so much for making Absinthe!

For future reference:

field :courses, list_of(:course), resolve: assoc(:courses)

def all(_args, _info) do
    users = Repo.all(User) |> Repo.preload(:courses)
    {:ok, users}
end
benwilson512

benwilson512

Author of Craft GraphQL APIs in Elixir with Absinthe

You don’t need to preload the courses. If the query asks for them they’ll get loaded by assoc and if the query does not, it won’t. Right now you’re loading them whether the query asks for it or not.

EDIT:
You can make your resolver as simple as

def all(_args, _info) do
  {:ok, Repo.all(User)}
end

Yay batching!

Where Next?

Popular in Questions Top

Werner
Hi, I’m using Ubuntu 18.04 and after updating to OTP-24.0 yesterday i have this warning when I run “mix local.hex”: 14:57:30.512 [warn] ...
New
chensan
I have a User schema with a :from_id field set to type :string: defmodule TweetBot.Repo.Migrations.CreateUsers do use Ecto.Migration ...
New
Fl4m3Ph03n1x
Background Let’s assume I have a typical GenServer that receives messages as requests, does some operation in a DB and returns responses....
New
vonH
When I run the Plug and I recompile I wind up having to use Ctrl C to quit iex and start again. Witht the help of rlwrap I can use the cu...
New
fireproofsocks
Forgive me if this is obvious, but how does one delete a database record WITHOUT selecting it first? https://hexdocs.pm/ecto/Ecto.Repo.h...
New
kostonstyle
Hi all I want to have a unix time, from the current time plus 1 hour. DateTime.now + 1 hour How to get it in elixir? Thanks
New
Patoshizzle
After calling mix ecto.create I get this error: 17:00:32.162 [error] GenServer #PID<0.412.0> terminating ** (Postgrex.Error) FATAL...
New
sabri
Can someone explain the settings of pool_size of Ecto in config file? and what is the recommend size? Thanks
New
romenigld
I am trying to run a deploy with docker and I successfully runned with this command: docker build -t romenigld/blog-prod . but when I t...
New
lanycrost
Hi everyone! I need implement if…else if…else condition from my elixir code, and anymore of this control flow structures not work proper...
New

Other popular topics Top

dotdotdotPaul
Okay, I'm having a heck of a time trying to figure out how to best handle the validation of belongs_to associations in Ecto. I'm sure I'...
New
vonH
In asking this question I am more interested about the expressiveness of the language itself and less concerned about the availability of...
New
joaquinalcerro
Hi there, I am working with Ecto-Postgresql and I need to call all of the records from a specific table but the table has 40,000 record...
New
SoCreat
i’m a new one to elixir which editor can i use vs code? or atom? Thanks! :smiley:
New
chrismccord
As promised, the first release candidate of Phoenix 1.3.0 is out! This release focuses on code generators with improved project structure...
New
shahryarjb
Hello, I have map which I want to convert it to string like this: the map: %{last_name: "tavakkoli", name: "shahryar"} the string I ne...
New
ovidiubadita
Hey all, I discovered Elixir and I love it. I always wanted to learn a functional programming and I intended to go for Haskell, but afte...
New
Patoshizzle
After calling mix ecto.create I get this error: 17:00:32.162 [error] GenServer #PID<0.412.0> terminating ** (Postgrex.Error) FATAL...
New
AstonJ
by Lance Halvorsen Elixir and Phoenix are generating tremendous excitement as an unbeatable platform for building modern web application...
460 27162 124
New
romenigld
I am trying to run a deploy with docker and I successfully runned with this command: docker build -t romenigld/blog-prod . but when I t...
New

We're in Beta

About us Mission Statement