Skip to content

Commit 0353233

Browse files
committed
Correct pagination logic on SortedCollection
Closes #4.
1 parent 6eb0bfd commit 0353233

File tree

2 files changed

+103
-337
lines changed

2 files changed

+103
-337
lines changed

epoxy/contrib/relay/connections/sorted_collection.py

+12-86
Original file line numberDiff line numberDiff line change
@@ -5,74 +5,6 @@
55

66

77
class SortedCollection(object):
8-
"""Sequence sorted by a key function.
9-
10-
SortedCollection() is much easier to work with than using bisect() directly.
11-
It supports key functions like those use in sorted(), min(), and max().
12-
The result of the key function call is saved so that keys can be searched
13-
efficiently.
14-
15-
Instead of returning an insertion-point which can be hard to interpret, the
16-
five find-methods return a specific item in the sequence. They can scan for
17-
exact matches, the last item less-than-or-equal to a key, or the first item
18-
greater-than-or-equal to a key.
19-
20-
Once found, an item's ordinal position can be located with the index() method.
21-
New items can be added with the insert() and insert_right() methods.
22-
Old items can be deleted with the remove() method.
23-
24-
The usual sequence methods are provided to support indexing, slicing,
25-
length lookup, clearing, copying, forward and reverse iteration, contains
26-
checking, item counts, item removal, and a nice looking repr.
27-
28-
Finding and indexing are O(log n) operations while iteration and insertion
29-
are O(n). The initial sort is O(n log n).
30-
31-
The key function is stored in the 'key' attribute for easy introspection or
32-
so that you can assign a new key function (triggering an automatic re-sort).
33-
34-
In short, the class was designed to handle all of the common use cases for
35-
bisect but with a simpler API and support for key functions.
36-
37-
>>> from pprint import pprint
38-
>>> from operator import itemgetter
39-
40-
>>> s = SortedCollection(key=itemgetter(2))
41-
>>> for record in [
42-
... ('roger', 'young', 30),
43-
... ('angela', 'jones', 28),
44-
... ('bill', 'smith', 22),
45-
... ('david', 'thomas', 32)]:
46-
... s.insert(record)
47-
48-
>>> pprint(list(s)) # show records sorted by age
49-
[('bill', 'smith', 22),
50-
('angela', 'jones', 28),
51-
('roger', 'young', 30),
52-
('david', 'thomas', 32)]
53-
54-
>>> s.find_le(29) # find oldest person aged 29 or younger
55-
('angela', 'jones', 28)
56-
>>> s.find_lt(28) # find oldest person under 28
57-
('bill', 'smith', 22)
58-
>>> s.find_gt(28) # find youngest person over 28
59-
('roger', 'young', 30)
60-
61-
>>> r = s.find_ge(32) # find youngest person aged 32 or older
62-
>>> s.index(r) # get the index of their record
63-
3
64-
>>> s[3] # fetch the record at that index
65-
('david', 'thomas', 32)
66-
67-
>>> s.key = itemgetter(0) # now sort by first name
68-
>>> pprint(list(s))
69-
[('angela', 'jones', 28),
70-
('bill', 'smith', 22),
71-
('david', 'thomas', 32),
72-
('roger', 'young', 30)]
73-
74-
"""
75-
768
def __init__(self, key=None):
779
self._given_key = key
7810
key = (lambda x: x) if key is None else key
@@ -178,38 +110,32 @@ def get_connection(self, relay, type_name, args):
178110
if not count:
179111
return self.empty_connection(relay, type_name)
180112

181-
begin_key = cursor.get_offset(after, None) or self._keys[0]
182-
end_key = cursor.get_offset(before, None) or self._keys[-1]
183-
184-
begin = self.bisect_left(begin_key)
185-
end = self.bisect_right(end_key)
113+
begin_key = cursor.get_offset(after, None)
114+
end_key = cursor.get_offset(before, None)
186115

187-
if begin >= count or begin >= end:
188-
return self.empty_connection(relay, type_name)
116+
lower_bound = begin = self.bisect_left(begin_key) + 1 if begin_key else 0
117+
upper_bound = end = self.bisect_right(end_key) - 1 if end_key else count
189118

190-
first_preslice_cursor = cursor.from_offset(self._keys[begin])
191-
last_preslice_cursor = cursor.from_offset(self._keys[min(end, count) - 1])
119+
if upper_bound < count and self._keys[upper_bound] != end_key:
120+
upper_bound = end = count
192121

193122
if first is not None:
194123
end = min(begin + first, end)
195124
if last is not None:
196125
begin = max(end - last, begin)
197126

198-
if begin >= count or begin >= end:
199-
return self.empty_connection(relay, type_name)
200-
201127
sliced_data = self._items[begin:end]
202128

203129
edges = [Edge(node=node, cursor=cursor.from_offset(self._key(node))) for node in sliced_data]
204-
first_edge = edges[0]
205-
last_edge = edges[-1]
130+
first_edge = edges[0] if edges else None
131+
last_edge = edges[-1] if edges else None
206132

207133
return Connection(
208134
edges=edges,
209135
page_info=relay.PageInfo(
210-
start_cursor=first_edge.cursor,
211-
end_cursor=last_edge.cursor,
212-
has_previous_page=(first_edge.cursor != first_preslice_cursor),
213-
has_next_page=(last_edge.cursor != last_preslice_cursor)
136+
start_cursor=first_edge and first_edge.cursor,
137+
end_cursor=last_edge and last_edge.cursor,
138+
has_previous_page=begin > lower_bound,
139+
has_next_page=end < upper_bound
214140
)
215141
)

0 commit comments

Comments
 (0)