Skip to content

Commit fb1113e

Browse files
authored
Merge pull request #26 from graphql-elixir/ordered_by
Release v0.5.0
2 parents 947bd40 + 7b69352 commit fb1113e

File tree

8 files changed

+259
-285
lines changed

8 files changed

+259
-285
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Changelog
22

3+
## 0.5.0 (2016-07-01)
4+
5+
* Add `ordered_by` and `ordered_by_direction` options for Ecto connections so we can sort a connection by field.
6+
37
## 0.4.0 (2016-06-22)
48

59
* Update optional dependency for Ecto to support both Ecto v1.x and v2.x.

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ This library relies on the [GraphQL Elixir](https://github.com/graphql-elixir/gr
1111
Add `graphql_relay` to your list of dependencies in `mix.exs`:
1212

1313
def deps do
14-
[{:graphql_relay, "~> 0.4"}]
14+
[{:graphql_relay, "~> 0.5"}]
1515
end
1616

1717
## Configuration

examples/todo/mix.exs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ defmodule Todo.Mixfile do
3636
{:phoenix_html, "~> 2.6"},
3737
{:phoenix_live_reload, "~> 1.0", only: :dev},
3838
{:cowboy, "~> 1.0"},
39-
{:graphql_relay, "~> 0.4"},
39+
{:graphql_relay, "~> 0.5"},
4040
{:plug_graphql, "~> 0.3"}]
4141
end
4242

examples/todo/mix.lock

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
%{"connection": {:hex, :connection, "1.0.3", "3145f7416be3df248a4935f24e3221dc467c1e3a158d62015b35bd54da365786", [:mix], []},
22
"cowboy": {:hex, :cowboy, "1.0.4", "a324a8df9f2316c833a470d918aaf73ae894278b8aa6226ce7a9bf699388f878", [:rebar, :make], [{:cowlib, "~> 1.0.0", [hex: :cowlib, optional: false]}, {:ranch, "~> 1.0", [hex: :ranch, optional: false]}]},
33
"cowlib": {:hex, :cowlib, "1.0.2", "9d769a1d062c9c3ac753096f868ca121e2730b9a377de23dec0f7e08b1df84ee", [:make], []},
4-
"db_connection": {:hex, :db_connection, "1.0.0-rc.2", "c5b2329651ef046d85e47ec2c947cb0e3d10a69eb49fdb71e365e72d6758e4c5", [:mix], [{:sbroker, "~> 1.0.0-beta.2", [hex: :sbroker, optional: true]}, {:poolboy, "~> 1.5", [hex: :poolboy, optional: true]}, {:connection, "~> 1.0.2", [hex: :connection, optional: false]}]},
4+
"db_connection": {:hex, :db_connection, "1.0.0-rc.3", "d9ceb670fe300271140af46d357b669983cd16bc0d01206d7d3222dde56cf038", [:mix], [{:sbroker, "~> 1.0.0-beta.3", [hex: :sbroker, optional: true]}, {:poolboy, "~> 1.5", [hex: :poolboy, optional: true]}, {:connection, "~> 1.0.2", [hex: :connection, optional: false]}]},
55
"decimal": {:hex, :decimal, "1.1.2", "79a769d4657b2d537b51ef3c02d29ab7141d2b486b516c109642d453ee08e00c", [:mix], []},
6-
"ecto": {:hex, :ecto, "2.0.1", "cf97a4d353e14af3d3cc3b4452cfbd18b3aeee1fb4075475efeccec3853444a9", [:mix], [{:poison, "~> 1.5 or ~> 2.0", [hex: :poison, optional: true]}, {:sbroker, "~> 1.0-beta", [hex: :sbroker, optional: true]}, {:mariaex, "~> 0.7.7", [hex: :mariaex, optional: true]}, {:postgrex, "~> 0.11.2", [hex: :postgrex, optional: true]}, {:db_connection, "~> 1.0-rc.2", [hex: :db_connection, optional: true]}, {:decimal, "~> 1.0", [hex: :decimal, optional: false]}, {:poolboy, "~> 1.5", [hex: :poolboy, optional: false]}]},
6+
"ecto": {:hex, :ecto, "2.0.2", "b02331c1f20bbe944dbd33c8ecd8f1ccffecc02e344c4471a891baf3a25f5406", [:mix], [{:poison, "~> 1.5 or ~> 2.0", [hex: :poison, optional: true]}, {:sbroker, "~> 1.0-beta", [hex: :sbroker, optional: true]}, {:mariaex, "~> 0.7.7", [hex: :mariaex, optional: true]}, {:postgrex, "~> 0.11.2", [hex: :postgrex, optional: true]}, {:db_connection, "~> 1.0-rc.2", [hex: :db_connection, optional: true]}, {:decimal, "~> 1.0", [hex: :decimal, optional: false]}, {:poolboy, "~> 1.5", [hex: :poolboy, optional: false]}]},
77
"fs": {:hex, :fs, "0.9.2", "ed17036c26c3f70ac49781ed9220a50c36775c6ca2cf8182d123b6566e49ec59", [:rebar], []},
88
"graphql": {:hex, :graphql, "0.3.1", "d3bb5467877456cc2b33debc75407e9216567b10e35e83d5195e2d51e835e8c7", [:mix], []},
9-
"graphql_relay": {:hex, :graphql_relay, "0.0.17", "d5ce633b85c9103ab0507dd3c4e9103d8d14ea9bd316d35fb9cbc9ba31234c5c", [:mix], [{:poison, "~> 1.5 or ~> 2.0", [hex: :poison, optional: false]}, {:graphql, "~> 0.3", [hex: :graphql, optional: false]}, {:ecto, "~> 1.1.4", [hex: :ecto, optional: true]}]},
9+
"graphql_relay": {:hex, :graphql_relay, "0.5.0", "8ea16397d4dc4cc0d7bf8d47df0d4d8ad2710e59ddda78fcb581b90352b48159", [:mix], [{:poison, "~> 1.5 or ~> 2.0", [hex: :poison, optional: false]}, {:graphql, "~> 0.3", [hex: :graphql, optional: false]}]},
1010
"phoenix": {:hex, :phoenix, "1.1.6", "7bf19002669c8f692f5a9c8d30dab7b49f3dc56228d5bde92a12fb426b4783c2", [:mix], [{:poison, "~> 1.5 or ~> 2.0", [hex: :poison, optional: false]}, {:plug, "~> 1.0", [hex: :plug, optional: false]}, {:cowboy, "~> 1.0", [hex: :cowboy, optional: true]}]},
1111
"phoenix_ecto": {:hex, :phoenix_ecto, "3.0.0", "b947aaf03d076f5b1448f87828f22fb7710478ee38455c67cc3fe8e9a4dfd015", [:mix], [{:ecto, "~> 2.0.0-rc", [hex: :ecto, optional: false]}, {:phoenix_html, "~> 2.6", [hex: :phoenix_html, optional: true]}]},
1212
"phoenix_html": {:hex, :phoenix_html, "2.6.0", "b9f7e091eb3d908586d9634596478fb9e577ee033d76f4ff327a745569bdd2d8", [:mix], [{:plug, "~> 0.13 or ~> 1.0", [hex: :plug, optional: false]}]},

lib/graphql/relay/connection/ecto.ex

Lines changed: 61 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -57,22 +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]
91+
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
6694
limit = Enum.min([first, last, connection_count(repo, query)])
6795

6896
query = if a_after do
69-
from things in query, where: things.id > ^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
70103
else
71104
query
72105
end
73106

74107
query = if before do
75-
from things in query, where: things.id < ^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
76114
else
77115
query
78116
end
@@ -97,13 +135,15 @@ if Code.ensure_loaded?(Ecto) do
97135
end
98136

99137
query = if first do
100-
from things in query, order_by: [asc: things.id], limit: ^limit
138+
sort_values = Keyword.new([{ordered_by_direction, ordered_by_property}])
139+
query |> order_by(^sort_values) |> limit(^limit)
101140
else
102141
query
103142
end
104143

105144
query = if last do
106-
from things in query, order_by: [desc: things.id], limit: ^limit
145+
sort_values = Keyword.new([{opposite_ordered_by_direction, ordered_by_property}])
146+
query |> order_by(^sort_values) |> limit(^limit)
107147
else
108148
query
109149
end
@@ -112,7 +152,7 @@ if Code.ensure_loaded?(Ecto) do
112152

113153
edges = Enum.map(records, fn(record) ->
114154
%{
115-
cursor: cursor_for_object_in_connection(record),
155+
cursor: cursor_for_object_in_connection(record, ordered_by_property),
116156
node: record
117157
}
118158
end)
@@ -140,15 +180,23 @@ if Code.ensure_loaded?(Ecto) do
140180
def cursor_to_offset(cursor) do
141181
case Base.decode64(cursor) do
142182
{:ok, decoded_cursor} ->
143-
{int, _} = Integer.parse(String.slice(decoded_cursor, String.length(@prefix)..String.length(decoded_cursor)))
144-
int
183+
string = String.slice(decoded_cursor, String.length(@prefix)..String.length(decoded_cursor))
184+
case Ecto.DateTime.cast(string) do
185+
{:ok, date} -> date
186+
:error -> string
187+
end
145188
:error ->
146189
nil
147190
end
148191
end
149192

150-
def cursor_for_object_in_connection(object) do
151-
Base.encode64("#{@prefix}#{object.id}")
193+
def cursor_for_object_in_connection(object, property \\ :id) do
194+
prop = case Map.get(object, property) do
195+
%Ecto.DateTime{} = date_time -> Ecto.DateTime.to_iso8601(date_time)
196+
prop -> to_string(prop)
197+
end
198+
199+
Base.encode64("#{@prefix}#{prop}")
152200
end
153201

154202
def connection_count(repo, query) do
@@ -157,6 +205,10 @@ if Code.ensure_loaded?(Ecto) do
157205
repo.one(count_query)
158206
end
159207

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

mix.exs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
defmodule GraphQL.Relay.Mixfile do
22
use Mix.Project
33

4-
@version "0.4.0"
4+
@version "0.5.0"
55
@description "Relay helpers for GraphQL Elixir"
66
@repo_url "https://github.com/graphql-elixir/graphql_relay"
77

priv/repo/migrations/20160307040347_create_letter.exs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,13 @@ defmodule EctoTest.Repo.Migrations.CreateLetter do
55
create table(:letters) do
66
add :letter, :string
77
add :second_column, :string
8+
add :order, :integer
9+
timestamps
10+
end
11+
12+
create table(:numbers) do
13+
add :number, :integer
14+
add :letter_id, references(:letters, on_delete: :nothing)
815
timestamps
916
end
1017
end

0 commit comments

Comments
 (0)