Pagination on dynamic (manual) relationship fails when queried
- Dominant language
- Elixir
- Stars
- 2.5k
- Forks
- 426
- Avg merge
- 23h 26m
- Merged PRs (30d)
- 46
Description
### Code of Conduct
- [x] I agree to follow this project's Code of Conduct
### AI Policy
- [x] I agree to follow this project's AI Policy, or I agree that AI was not used while creating this issue.
### Versions
- Elixir 1.18.4, OTP 28
- Ash 3.7.6
- AshPostgres 2.6.65
- AshGraphQL 1.8.5
I assume it's also present in some earlier versions of Ash, but I didn't investigate
### Operating system
Ubuntu 24.04
### Current Behavior
This issue arises when trying to paginate a manual relationship on a resource (relay pagination in my case)
```ex
defmodule Source do
use Ash.Resource # ...
graphql do
type :source
paginate_relationship_with: dest: :relay # <---- problematic line
end
# ...
relationships do
has_many :dest, Destination do
public? true
description "The `source`s belonging to the destination."
writable? false
manual ManualRelationship
end
end
# ...
end
```
More specifically, the reason I'm using a manual relationship in this case is because I dynamically compute the related records on runtime. For example, it takes one or more attribute from the records and creates an Ash expression starting from them to create the appropriate filter:
```ex
defmodule ManualRelationship do
use Ash.Resource.ManualRelationship
@impl ManualRelationship
def load(sources, _opts, %{query: query}) do
source_id_to_dests =
Map.new(sources, fn source ->
# Example inspired from the actual code
{:ok, ast_root} = Lang.parse(source.filter_attr)
filter = Lang.to_ash_expr(ast_root) # filter obtained from the attribute at runtime
destinations = query |> Ash.Query.filter(^filter) |> Ash.read!()
{source.id, destinations}
end)
{:ok, source_id_to_dests}
end
end
```
If the best solution would be to remove the relationship and substitute it with something else, I'm very open to change. I've already tried some options a calculation returning an Ash expression, or changing the type of the filter attribute to Ash expression, but both proved problematic for various reasons.
The result is that the graphql query fails. Here's a the error obtained from the reproduction repo linked below:
```
[debug] QUERY OK source="elements" db=0.2ms idle=1667.0ms
SELECT e0."id", e0."tags" FROM "elements" AS e0 ORDER BY e0."id" LIMIT $1 [251]
↳ anonymous fn/3 in AshPostgres.DataLayer.run_query/2, at: lib/data_layer.ex:835
[error] ba0013a6-872b-4dd8-b4e2-86acb57f73ac: Exception raised while resolving query.
** (Ash.Error.Unknown)
Bread Crumbs:
> Exception raised in: DynRelRepro.Domain.Group.read
> Exception raised in: DynRelRepro.Domain.Element.read
Unknown Error
* ** (UndefinedFunctionError) function DynRelRepro.ManualRelationships.ElementToGroup.ash_postgres_subquery/4 is undefined or private
(dyn_rel_repro 0.1.0) DynRelRepro.ManualRelationships.ElementToGroup.ash_postgres_subquery([], 0, 0, #Ecto.Query)
(ash_postgres 2.6.25) lib/data_layer.ex:1114: AshPostgres.DataLayer.lateral_join_query/3
(ash_postgres 2.6.25) lib/data_layer.ex:1046: AshPostgres.DataLayer.run_query_with_lateral_join/4
(ash 3.12.0) lib/ash/actions/read/read.ex:4089: Ash.Actions.Read.run_query/4
(ash 3.12.0) lib/ash/actions/read/read.ex:806: anonymous fn/9 in Ash.Actions.Read.do_read/5
(ash 3.12.0) lib/ash/actions/read/read.ex:1591: Ash.Actions.Read.maybe_in_transaction/3
(ash 3.12.0) lib/ash/actions/read/read.ex:436: Ash.Actions.Read.do_run/3
(ash 3.12.0) lib/ash/actions/read/read.ex:90: anonymous fn/3 in Ash.Actions.Read.run/3
(ash 3.12.0) lib/ash/actions/read/read.ex:89: Ash.Actions.Read.run/3
(ash 3.12.0) lib/ash.ex:2786: Ash.read/2
(ash 3.12.0) lib/ash.ex:2721: Ash.read!/2
(dyn_rel_repro 0.1.0) lib/dyn_rel_repro/manual_relationships/element_to_group.ex:20: DynRelRepro.ManualRelationships.ElementToGroup.load/3
(ash 3.12.0) lib/ash/actions/read/relationships.ex:489: anonymous fn/7 in Ash.Actions.Read.Relationships.do_fetch_related_records/5
(ash 3.12.0) lib/ash/actions/read/relationships.ex:85: Ash.Actions.Read.Relationships.fetch_related_records/5
(ash 3.12.0) lib/ash/actions/read/relationships.ex:30: Ash.Actions.Read.Relationships.load/4
(ash 3.12.0) lib/ash/actions/read/read.ex:473: Ash.Actions.Read.do_run/3
(ash 3.12.0) lib/ash
(dyn_rel_repro 0.1.0) DynRelRepro.ManualRelationships.ElementToGroup.ash_postgres_subquery([], 0, 0, #Ecto.Query)
(ash_postgres 2.6.25) lib/data_layer.ex:1114: AshPostgres.DataLayer.lateral_join_query/3
(ash_postgres 2.6.25) lib/data_layer.ex:1046: AshPostgres.DataLayer.run_query_with_lateral_join/4
(ash 3.12.0) lib/ash/actions/read/read.ex:4089: Ash.Actions.Read.run_query/4
(ash 3.12.0) lib/ash/actions/read/read.ex:806: anonymous fn/9 in Ash.Actions.Read.do_read/5
(ash 3.12.0) lib/ash/actions/read/read.ex:1591: Ash.Actions.Read.maybe_in_transaction/3
(ash 3.12.0) lib/ash/actions/read/read.ex:436: Ash.Actions.Read.do_run/3
(ash 3.12.0) lib/ash/actions/read/read.ex:90: anonymous fn/3 in Ash.Actions.Read.run/3
(ash 3.12.0) lib/ash/actions/read/read.ex:89: Ash.Actions.Read.run/3
(ash 3.12.0) lib/ash.ex:2786: Ash.read/2
(ash 3.12.0) lib/ash.ex:2721: Ash.read!/2
(dyn_rel_repro 0.1.0) lib/dyn_rel_repro/manual_relationships/element_to_group.ex:20: DynRelRepro.ManualRelationships.ElementToGroup.load/3
(ash 3.12.0) lib/ash/actions/read/relationships.ex:489: anonymous fn/7 in Ash.Actions.Read.Relationships.do_fetch_related_records/5
(ash 3.12.0) lib/ash/actions/read/relationships.ex:85: Ash.Actions.Read.Relationships.fetch_related_records/5
(ash 3.12.0) lib/ash/actions/read/relationships.ex:30: Ash.Actions.Read.Relationships.load/4
(ash 3.12.0) lib/ash/actions/read/read.ex:473: Ash.Actions.Read.do_run/3
(ash 3.12.0) lib/ash/actions/read/read.ex:90: anonymous fn/3 in Ash.Actions.Read.run/3
(ash 3.12.0) lib/ash/actions/read/read.ex:89: Ash.Actions.Read.run/3
(ash 3.12.0) lib/ash.ex:2786: Ash.read/2
(ash_graphql 1.8.5) lib/graphql/resolver.ex:516: AshGraphql.Graphql.Resolver.resolve/2
```
### Reproduction
[Minimal reproduction repo](https://github.com/Dam-99/ash-dynamic-relationship-repro).
You can see the steps via the commit history.
### Expected Behavior
Ideally the relationship is paginated automatically, but if not possible an easy method to do so is added and documented (via errors and docs).
Alternatively, a different method for a dynamic relationship for which pagination already works
Contributor guide
Research direction
Start with the minimal reproduction repository and its commit history, then inspect the manual relationship's load/3 in lib/dyn_rel_repro/manual_relationships/element_to_group.ex. Reproduce the GraphQL relay-pagination query and trace the AshPostgres lateral join failure involving ash_postgres_subquery/4. Done would require a confirmed supported behavior or documented solution for paginating dynamic manual relationships.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- elixir, graphql, postgresql
- Domain
- api, backend, databases
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Needs clarification
- Newbie friendliness
- 30/100