Skip to content

Instantly share code, notes, and snippets.

@MartinElvar
Created November 8, 2016 13:44
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save MartinElvar/f2e8d197673ea6f78c9615dc158a056c to your computer and use it in GitHub Desktop.
Save MartinElvar/f2e8d197673ea6f78c9615dc158a056c to your computer and use it in GitHub Desktop.
defmodule GraphqlHelpers do
import Ecto.Query
@doc """
## example
field :storages, list_of(:storage), do: has_many(:storages)
"""
defmacro has_many(model) do
quote do
resolve fn subject, _, _ ->
assoc = subject.__struct__.__schema__(:association, unquote(model))
batch({GraphqlHelpers, :process_has_many, {assoc.queryable, assoc.related_key}}, subject.id, fn (batch_result) ->
{:ok, Map.get(batch_result, subject.id, [])}
end)
end
end
end
def process_has_many({model, foreign_key}, ids) do
model
|> where([m], field(m, ^foreign_key) in ^ids)
|> ApiEndpoint.Repo.all
|> Enum.group_by(fn record -> Map.get(record, foreign_key) end)
end
@doc """
## example
field :user, :user, do: belongs_to(:user)
"""
defmacro belongs_to(model) do
quote do
resolve fn subject, _, _ ->
assoc = subject.__struct__.__schema__(:association, unquote(model))
id = Map.get(subject, assoc.owner_key)
batch({GraphqlHelpers, :process_belongs_to, assoc.queryable}, id, fn batch_result ->
{:ok, Map.get(batch_result, id, %{})}
end)
end
end
end
def process_belongs_to(model, ids) do
model
|> where([m], m.id in ^ids)
|> ApiEndpoint.Repo.all
|> Map.new(&{&1.id, &1})
end
@doc """
## example
field :storage_facilities, list_of(:storage_facility), do: many_to_many(:storage_facilities)
"""
defmacro many_to_many(model) do
quote do
resolve fn (subject, _, _) ->
assoc = subject.__struct__.__schema__(:association, unquote(model))
batch({GraphqlHelpers, :process_many_to_many, {assoc.queryable, assoc}}, subject.id, fn (batch_result) ->
{:ok, Map.get(batch_result, subject.id, [])}
end)
end
end
end
def process_many_to_many({model, assoc}, ids) do
# I'm a but uncertain bout this; might always be ordered this way?
[{owner_key, :id}, {assoc_key, :id}] = assoc.join_keys
ApiEndpoint.Repo.all(
from m in model,
join: jt in ^assoc.join_through,
on: field(jt, ^assoc_key) == m.id,
where: field(jt, ^owner_key) in ^ids,
select: {field(jt, ^owner_key), m}
)
|> group_many_to_many(%{})
end
@doc """
Turns
[{23, %SomeStruct{...}},
{23, %SomeStruct{...}},
{23, %SomeStruct{...}},
{21, %SomeStruct{...}}]
Into
%{
23 => [
%SomeStruct{...},
%SomeStruct{...},
%SomeStruct{...},
]
21 => [
%SomeStruct{...},
]
}
"""
def group_many_to_many([head | tail], acc) do
key = elem(head, 0)
if acc[key] do
acc = %{acc | key => List.insert_at(acc[key], 0, elem(head, 1)) }
group_many_to_many(tail, acc)
else
group_many_to_many(tail, Map.put(acc, key, [elem(head, 1)]))
end
end
def group_many_to_many([], acc) do
acc
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment