Skip to content

Commit f550e0a

Browse files
committed
Add 'ordered_by_direction' option so we can sort the 'ordered_by' field.
1 parent 884b45f commit f550e0a

File tree

2 files changed

+63
-4
lines changed

2 files changed

+63
-4
lines changed

lib/graphql/relay/connection/ecto.ex

Lines changed: 47 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,23 +57,60 @@ if Code.ensure_loaded?(Ecto) do
5757
resolve(repo, query, args)
5858
end
5959

60+
@doc """
61+
Pass in an Ecto.Repo, an Ecto.Query, and Relay args to have the query
62+
executed and results returned in the format Relay requires.
63+
64+
If you wish to have the results ordered by some field other than `id`,
65+
use `ordered_by` and `ordered_by_direction`.
66+
67+
Example for returning data in reverse chronological order (newest first)
68+
like you would do for a page listing blog posts:
69+
70+
args = %{
71+
first: 10,
72+
ordered_by: :inserted_at,
73+
ordered_by_direction: :desc
74+
}
75+
76+
Your GraphQL resolve function for the connection will look something like:
77+
78+
resolve: fn(blog, args, _ctx) ->
79+
# Order by desc inserted_at
80+
args = Map.merge(args, %{ordered_by: :inserted_at, ordered_by_direction: :desc})
81+
query = Ecto.assoc(blog, :posts)
82+
GraphQL.Relay.Connection.Ecto.resolve(Repo, query, args)
83+
end
84+
"""
6085
def resolve(repo, query, args \\ %{}) do
6186
before = cursor_to_offset(args[:before])
6287
# `after` is a keyword http://elixir-lang.org/docs/master/elixir/Kernel.SpecialForms.html#try/1
6388
a_after = cursor_to_offset(args[:after])
6489
first = args[:first]
6590
last = args[:last]
6691
ordered_by_property = args[:ordered_by] || :id
92+
ordered_by_direction = get_ordered_by_direction(args[:ordered_by_direction] || :asc)
93+
opposite_ordered_by_direction = if ordered_by_direction == :asc, do: :desc, else: :asc
6794
limit = Enum.min([first, last, connection_count(repo, query)])
6895

6996
query = if a_after do
70-
query |> where([a], field(a, ^ordered_by_property) > ^a_after)
97+
case ordered_by_direction do
98+
:asc ->
99+
query |> where([a], field(a, ^ordered_by_property) > ^a_after)
100+
:desc ->
101+
query |> where([a], field(a, ^ordered_by_property) < ^a_after)
102+
end
71103
else
72104
query
73105
end
74106

75107
query = if before do
76-
query |> where([a], field(a, ^ordered_by_property) < ^before)
108+
case ordered_by_direction do
109+
:asc ->
110+
query |> where([a], field(a, ^ordered_by_property) < ^before)
111+
:desc ->
112+
query |> where([a], field(a, ^ordered_by_property) > ^before)
113+
end
77114
else
78115
query
79116
end
@@ -98,13 +135,15 @@ if Code.ensure_loaded?(Ecto) do
98135
end
99136

100137
query = if first do
101-
query |> order_by(asc: ^ordered_by_property) |> limit(^limit)
138+
sort_values = Keyword.new([{ordered_by_direction, ordered_by_property}])
139+
query |> order_by(^sort_values) |> limit(^limit)
102140
else
103141
query
104142
end
105143

106144
query = if last do
107-
query |> order_by(desc: ^ordered_by_property) |> limit(^limit)
145+
sort_values = Keyword.new([{opposite_ordered_by_direction, ordered_by_property}])
146+
query |> order_by(^sort_values) |> limit(^limit)
108147
else
109148
query
110149
end
@@ -166,6 +205,10 @@ if Code.ensure_loaded?(Ecto) do
166205
repo.one(count_query)
167206
end
168207

208+
defp get_ordered_by_direction(value) when value in [:asc, :desc] do
209+
value
210+
end
211+
169212
defp make_query_countable(query) do
170213
query
171214
|> remove_order

test/graphql/relay/connection/ecto_test.exs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,22 @@ defmodule GraphQL.Relay.Connection.EctoTest do
235235
assert(Connection.Ecto.resolve(Repo, letters_query, %{first: 2, after: edge_for_object(b, :order).cursor, ordered_by: :order}) == expected)
236236
end
237237

238+
test "pagination: respects first and after with non-default ordered_by and ordered_by_direction" do
239+
expected = %{
240+
edges: [
241+
edge_for_object(d, :order),
242+
edge_for_object(c, :order),
243+
],
244+
pageInfo: %{
245+
startCursor: edge_for_object(d, :order).cursor,
246+
endCursor: edge_for_object(c, :order).cursor,
247+
hasPreviousPage: false,
248+
hasNextPage: true,
249+
}
250+
}
251+
assert(Connection.Ecto.resolve(Repo, letters_query, %{first: 2, after: edge_for_object(e, :order).cursor, ordered_by: :order, ordered_by_direction: :desc}) == expected)
252+
end
253+
238254
test "respects first and after with long first" do
239255
expected = %{
240256
edges: [

0 commit comments

Comments
 (0)