Search code examples
phoenix-frameworkecto

Create composable query for many_to_many association


I'm trying to create a composable ecto query for listing all Document for a specific Contributor. I want the api to look something like this:

Document
|> Document.for_contributor(contributor)
|> Repo.all()

But I'm at a loss as to where to begin. I've been doing composable queries before and in a has_many relation where a Contributor could have many Document I would do something like this:

  def for_contributor(query, %Contributor{} = contributor) do
    from(document in query, where: document.contributor_id == ^contributor.id)
  end

But I'm not sure how I would go about doing something similar but with a many_to_many relation.

What would go in my for_contributor function?

defmodule MyApp.Document do
  use Ecto.Schema
  import Ecto.Changeset

  alias MyApp.Contributor

  schema "documents" do
    many_to_many(:contributors, Contributor, join_through: "contributors_documents")
    timestamps()
  end

  def for_contributor(query, %Contributor{} = contributor) do
    # ???
  end
end

My join table looks like this:

defmodule MyApp.Repo.Migrations.CreateContributorsDocuments do
  use Ecto.Migration

  def change do
    create table(:contributors_documents, primary_key: false) do
      add :contributor_id, references(:contributors)
      add :document_id, references(:documents)
    end
  end
end

Solution

  • I made it more complicated in my head than it needed to be. I solved it with a simple join.

      def for_contributor(query, %Contributor{} = contributor) do
        from(
          document in query,
          join: c in assoc(document, :contributors),
          where: c.id == ^contributor.id
        )
      end