Using an inline aggregate of unrelated resource filtering by related resources

I believe I could do this using a custom relationship and falling back to Ecto, but I was wondering if there was a "Ash way" of doing what I'm trying to do here. I have four resources/tables SceneWidget
scene_id
widget_version_id
scene_id
widget_version_id
Scene
id
channel_id
id
channel_id
WidgetVersion
id
widget_id
id
widget_id
WidgetMemory
channel_id (primary)
widget_id (primary)
channel_id (primary)
widget_id (primary)
I'm trying to write a calculation to go from SceneWidget => WidgetMemory Using SQL I would normally write something like
SELECT widget_memories.*
FROM scene_widgets
JOIN scenes ON scene_widgets.scene_id = scenes.id
JOIN widget_versions ON scene_widgets.widget_version_id = widget_versions.id
JOIN widget_memories ON widget_memories.widget_id = widget_versions.widget_id AND widget_memories.channel_id = scenes.channel_id
SELECT widget_memories.*
FROM scene_widgets
JOIN scenes ON scene_widgets.scene_id = scenes.id
JOIN widget_versions ON scene_widgets.widget_version_id = widget_versions.id
JOIN widget_memories ON widget_memories.widget_id = widget_versions.widget_id AND widget_memories.channel_id = scenes.channel_id
Trying to do this as a calculation I've tried:
calculate :memory,
:map,
expr(
first(EKG.Scenes.WidgetMemory,
query: [
filter: expr(channel_id == parent(scene.channel_id) and widget_id == parent(widget_version.widget_id))
],
field: :data
)
)
calculate :memory,
:map,
expr(
first(EKG.Scenes.WidgetMemory,
query: [
filter: expr(channel_id == parent(scene.channel_id) and widget_id == parent(widget_version.widget_id))
],
field: :data
)
)
and
calculate :memory,
:map,
expr(
first(EKG.Scenes.WidgetMemory,
join_filters: %{
scene: expr(channel_id == parent(channel_id)),
widget_version: expr(widget_id == parent(widget_id))
},
field: :data
)
)
calculate :memory,
:map,
expr(
first(EKG.Scenes.WidgetMemory,
join_filters: %{
scene: expr(channel_id == parent(channel_id)),
widget_version: expr(widget_id == parent(widget_id))
},
field: :data
)
)
But neither seem to work. Does Ash have a suggestion on how to to model these complex relationships? Or are you meant to just fallback to CustomRelationship/Ecto in these situations?
2 Replies
E
EOP4w ago
I found a solution, but there still seems to be a bug here?
calculate :channel_id, EKG.ShortId, expr(scene.channel_id)
calculate :widget_id, EKG.ShortId, expr(widget_version.widget_id)

calculate :memory,
:map,
expr(
first(EKG.Scenes.WidgetMemory,
query: [
filter: expr(channel_id == parent(channel_id) and widget_id == parent(widget_id))
],
field: :data
)
),
load: [:channel_id, :widget_id]
calculate :channel_id, EKG.ShortId, expr(scene.channel_id)
calculate :widget_id, EKG.ShortId, expr(widget_version.widget_id)

calculate :memory,
:map,
expr(
first(EKG.Scenes.WidgetMemory,
query: [
filter: expr(channel_id == parent(channel_id) and widget_id == parent(widget_id))
],
field: :data
)
),
load: [:channel_id, :widget_id]
It now works if I call EKG.Scenes.SceneWidget |> Ash.read_one(load: [:channel_id, :widget_id, :memory], authorize?: false) But if I only call EKG.Scenes.SceneWidget |> Ash.read_one(load: [:memory], authorize?: false) I get the following error:
** (Ash.Error.Unknown)
Bread Crumbs:
> Exception raised in: EKG.Scenes.SceneWidget.read

Unknown Error

* ** (RuntimeError) Error while building parent reference: scene.channel_id

Query so far:

#Ecto.Query<from w0 in EKG.Scenes.WidgetMemory, as: 2, select: struct(w0, [:data, :channel_id, :created_at, :updated_at, :widget_id])>

Current bindings:

%{0 => %{type: :root, path: [], source: EKG.Scenes.SceneWidget}}
** (Ash.Error.Unknown)
Bread Crumbs:
> Exception raised in: EKG.Scenes.SceneWidget.read

Unknown Error

* ** (RuntimeError) Error while building parent reference: scene.channel_id

Query so far:

#Ecto.Query<from w0 in EKG.Scenes.WidgetMemory, as: 2, select: struct(w0, [:data, :channel_id, :created_at, :updated_at, :widget_id])>

Current bindings:

%{0 => %{type: :root, path: [], source: EKG.Scenes.SceneWidget}}
I would have though the load option would ensure these other two calulations where loaded? This is all with Ash 3.5.37 BTW
barnabasj
barnabasj4w ago
It should, can you try with the newest version and if it does not work there submit a but report / failing test please

Did you find this page helpful?