Lets assume we have something like this:
defmodule From do
relationships do
has_many :questions, Question
end
end
defmodule Question do
relationships do
belongs_to :form, Form
has_many :answers, Answer
end
end
defmodule Document do
relationships do
belongs_to :form, Form
has_many :answers, Answer
end
end
defmodule Answer do
relationships do
belongs_to :question, Question
belongs_to :document, Document
end
end
My main entry point is a LiveView which loads the document like
document = Ash.read_one!(Document, load: [answers: :question, form: [:questions]])
# the "double" question join might seem excessive
# but its required for some validations further down the line, so ignore it for now
In different parets of the liveview I need to access as an example the answer for a given question / question id. Of course I can just do
Enum.find(document.answers, fn answer -> answer.question_id == question_id end)
However, doing this in the LiveView feels spaghetti (and not in a good way).
I couldn’t find anyting in the docs about accessing patterns to access already loaded relationship data. In my head I would imagine something like:
Ash.calculate(document, :answer_for_question, args: %{question: question})
# perhaps a bit wordy?
MyContextOrCodeInterface.get_answer(%{question: question, document: document})
# deciding on finding the correct answer for the current document feels
# like business logic that should live in the document and is
# perhaps not code interface dependent?
# or perhaps?
defmodue Document do
# same relationships as above
calculations do
calculate :get_answer, :struct do
constraints instance_of: Answer
load :answers
argument :question, :struct, constraints: [instance_of: Question]
argument :question_id, :uuid
calculation &get_answer/2
end
end
defp get_answer(records, %{arguments: %{question: %Question{id: question_id}}}), do: find_answers(records, question_id)
defp get_answer(records, %{arguments: %{question_id: question_id}}), do: find_answers(records, question_id)
defp find_answers(records, question_id) do
Enum.map(records, fn record ->
Enum.find(record.answers, fn answer -> answer.question_id == question_id end)
end)
end
end
I can’t decide which way is better. I could totally imagine that in the future deciding on returning an answer for a given document and question could totally depend on the actor as an example, so the code interface and liveview feel very wrong.
Much regards