Skip to content

Commit 3576216

Browse files
Allow arbitrary columns to be used as the object's cursor.
1 parent 6e37453 commit 3576216

File tree

3 files changed

+205
-117
lines changed

3 files changed

+205
-117
lines changed

lib/graphql/relay/connection/ecto.ex

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,19 @@ if Code.ensure_loaded?(Ecto) do
1515
a_after = cursor_to_offset(args[:after])
1616
first = args[:first]
1717
last = args[:last]
18+
where_property = args[:where] || :id
1819
limit = Enum.min([first, last, connection_count(repo, query)])
1920

2021
query = if a_after do
21-
from things in query, where: things.inserted_at > ^a_after
22+
frag = [{where_property, [>: a_after]}]
23+
query |> where(fragment(^frag))
2224
else
2325
query
2426
end
2527

2628
query = if before do
27-
from things in query, where: things.inserted_at < ^before
29+
frag = [{where_property, [<: before]}]
30+
query |> where(fragment(^frag))
2831
else
2932
query
3033
end
@@ -49,13 +52,13 @@ if Code.ensure_loaded?(Ecto) do
4952
end
5053

5154
query = if first do
52-
from things in query, order_by: [asc: things.inserted_at], limit: ^limit
55+
query |> order_by(asc: ^where_property) |> limit(^limit)
5356
else
5457
query
5558
end
5659

5760
query = if last do
58-
from things in query, order_by: [desc: things.inserted_at], limit: ^limit
61+
query |> order_by(desc: ^where_property) |> limit(^limit)
5962
else
6063
query
6164
end
@@ -64,7 +67,7 @@ if Code.ensure_loaded?(Ecto) do
6467

6568
edges = Enum.map(records, fn(record) ->
6669
%{
67-
cursor: cursor_for_object_in_connection(record),
70+
cursor: cursor_for_object_in_connection(record, where_property),
6871
node: record
6972
}
7073
end)
@@ -92,19 +95,23 @@ if Code.ensure_loaded?(Ecto) do
9295
def cursor_to_offset(cursor) do
9396
case Base.decode64(cursor) do
9497
{:ok, decoded_cursor} ->
95-
date_string = String.slice(decoded_cursor, String.length(@prefix)..String.length(decoded_cursor))
96-
case Ecto.DateTime.cast(date_string) do
98+
string = String.slice(decoded_cursor, String.length(@prefix)..String.length(decoded_cursor))
99+
case Ecto.DateTime.cast(string) do
97100
{:ok, date} -> date
98-
_ -> nil
101+
:error -> string
99102
end
100103
:error ->
101104
nil
102105
end
103106
end
104107

105-
def cursor_for_object_in_connection(object) do
106-
date_string = Ecto.DateTime.to_iso8601(object.inserted_at)
107-
Base.encode64("#{@prefix}#{date_string}")
108+
def cursor_for_object_in_connection(object, property \\ :id) do
109+
prop = case Map.get(object, property) do
110+
%Ecto.DateTime{} = date_time -> Ecto.DateTime.to_iso8601(date_time)
111+
prop -> to_string(prop)
112+
end
113+
114+
Base.encode64("#{@prefix}#{prop}")
108115
end
109116

110117
def connection_count(repo, query) do
@@ -117,6 +124,7 @@ if Code.ensure_loaded?(Ecto) do
117124
query
118125
|> remove_select
119126
|> remove_order
127+
|> remove_preload
120128
end
121129

122130
# Remove select if it exists so that we avoid `only one select
@@ -130,5 +138,11 @@ if Code.ensure_loaded?(Ecto) do
130138
defp remove_order(query) do
131139
Ecto.Query.exclude(query, :order_by)
132140
end
141+
142+
# Remove preload if it exists so that we avoid unnecessary joins
143+
# when trying to retrieve a count
144+
defp remove_preload(query) do
145+
Ecto.Query.exclude(query, :preload)
146+
end
133147
end
134148
end

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)