Skip to content

Commit d7b8c74

Browse files
nallwhyclaude
andcommitted
feat: support offset option in relationship joins
Adds offset support to the SQL join layer for has_one and has_many relationships. This works alongside the offset option being added to Ash core (ash-project/ash#2584). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 3f89d47 commit d7b8c74

File tree

1 file changed

+30
-5
lines changed

1 file changed

+30
-5
lines changed

lib/join.ex

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -349,7 +349,8 @@ defmodule AshSql.Join do
349349
has_parent_expr? =
350350
opts[:require_lateral?] ||
351351
!!query.__ash_bindings__.context[:data_layer][:has_parent_expr?] ||
352-
not is_nil(query.limit)
352+
not is_nil(query.limit) ||
353+
not is_nil(query.offset)
353354

354355
query =
355356
if has_parent_expr? do
@@ -457,6 +458,13 @@ defmodule AshSql.Join do
457458
|> Ash.Query.unset(:sort)
458459
end
459460
end)
461+
|> then(fn query ->
462+
if not is_nil(Map.get(relationship, :offset)) do
463+
Ash.Query.offset(query, relationship.offset)
464+
else
465+
query
466+
end
467+
end)
460468
|> set_has_parent_expr_context(relationship)
461469
|> case do
462470
%{valid?: true} = related_query ->
@@ -544,14 +552,23 @@ defmodule AshSql.Join do
544552

545553
defp limit_from_many(
546554
query,
547-
%{from_many?: true, destination: destination},
555+
%{from_many?: true, destination: destination} = relationship,
548556
filter,
549557
filter_subquery?,
550558
opts
551559
) do
560+
offset = Map.get(relationship, :offset)
561+
552562
if filter_subquery? do
563+
inner_query =
564+
if offset do
565+
from(row in query, limit: 1, offset: ^offset)
566+
else
567+
from(row in query, limit: 1)
568+
end
569+
553570
query =
554-
from(row in Ecto.Query.subquery(from(row in query, limit: 1)),
571+
from(row in Ecto.Query.subquery(inner_query),
555572
as: ^query.__ash_bindings__.root_binding
556573
)
557574
|> Map.put(:__ash_bindings__, query.__ash_bindings__)
@@ -578,7 +595,7 @@ defmodule AshSql.Join do
578595

579596
defp limit_from_many(
580597
query,
581-
%{limit: limit, destination: destination},
598+
%{limit: limit, destination: destination} = relationship,
582599
filter,
583600
filter_subquery?,
584601
opts
@@ -587,11 +604,19 @@ defmodule AshSql.Join do
587604
# Check if query has parent expressions - if so, we can't wrap in a non-lateral subquery
588605
# because parent references won't resolve across the subquery boundary
589606
has_parent_expr? = !!query.__ash_bindings__.context[:data_layer][:has_parent_expr?]
607+
offset = Map.get(relationship, :offset)
590608

591609
if filter_subquery? && !has_parent_expr? do
592610
# Wrap the limited query in a subquery, then apply filter on top
611+
inner_query =
612+
if offset do
613+
from(row in query, limit: ^limit, offset: ^offset)
614+
else
615+
from(row in query, limit: ^limit)
616+
end
617+
593618
query =
594-
from(row in Ecto.Query.subquery(from(row in query, limit: ^limit)),
619+
from(row in Ecto.Query.subquery(inner_query),
595620
as: ^query.__ash_bindings__.root_binding
596621
)
597622
|> Map.put(:__ash_bindings__, query.__ash_bindings__)

0 commit comments

Comments
 (0)