Allow paginating both forwards and backwards

This commit is contained in:
Erik Johnston 2016-09-15 10:15:37 +01:00
parent 4131381123
commit f3eead0660
2 changed files with 66 additions and 25 deletions

View file

@ -63,7 +63,7 @@ class RoomListHandler(BaseHandler):
rooms_to_latest_event_ids = {} rooms_to_latest_event_ids = {}
if next_batch: if next_batch:
current_stream_token = next_batch.sstream_ordering current_stream_token = next_batch.stream_ordering
else: else:
current_stream_token = yield self.store.get_room_max_stream_ordering() current_stream_token = yield self.store.get_room_max_stream_ordering()
@ -100,17 +100,25 @@ class RoomListHandler(BaseHandler):
sorted_rooms = [room_id for room_id, _ in sorted_entries] sorted_rooms = [room_id for room_id, _ in sorted_entries]
if next_batch: if next_batch:
sorted_rooms = sorted_rooms[next_batch.current_limit:] if next_batch.direction_is_forward:
sorted_rooms = sorted_rooms[next_batch.current_limit:]
else:
sorted_rooms = sorted_rooms[:next_batch.current_limit]
sorted_rooms.reverse()
new_limit = None new_limit = None
if limit: if limit:
if sorted_rooms[limit:]: if sorted_rooms[limit:]:
new_limit = limit new_limit = limit
if next_batch: if next_batch:
new_limit += next_batch.current_limit if next_batch.direction_is_forward:
new_limit += next_batch.current_limit
else:
new_limit = next_batch.current_limit - new_limit
new_limit = max(0, new_limit)
sorted_rooms = sorted_rooms[:limit] sorted_rooms = sorted_rooms[:limit]
results = [] chunk = []
@defer.inlineCallbacks @defer.inlineCallbacks
def handle_room(room_id): def handle_room(room_id):
@ -190,31 +198,45 @@ class RoomListHandler(BaseHandler):
if avatar_url: if avatar_url:
result["avatar_url"] = avatar_url result["avatar_url"] = avatar_url
results.append(result) chunk.append(result)
yield concurrently_execute(handle_room, sorted_rooms, 10) yield concurrently_execute(handle_room, sorted_rooms, 10)
if new_limit: chunk.sort(key=lambda e: (-e["num_joined_members"], e["room_id"]))
end_token = RoomListNextBatch(
stream_ordering=current_stream_token,
current_limit=new_limit,
).to_token()
else:
end_token = "END"
if next_batch: results = {
start_token = next_batch.to_token() "chunk": chunk,
else: }
start_token = "START"
defer.returnValue({ if not next_batch or next_batch.direction_is_forward:
"start": start_token, if new_limit:
"end": end_token, results["next_batch"] = RoomListNextBatch(
"chunk": results, stream_ordering=current_stream_token,
}) current_limit=new_limit,
direction_is_forward=True,
).to_token()
if next_batch:
results["prev_batch"] = next_batch.copy_and_replace(
direction_is_forward=False,
).to_token()
else:
if new_limit:
results["prev_batch"] = RoomListNextBatch(
stream_ordering=current_stream_token,
current_limit=new_limit,
direction_is_forward=False,
).to_token()
if next_batch:
results["next_batch"] = next_batch.copy_and_replace(
direction_is_forward=True,
).to_token()
defer.returnValue(results)
@defer.inlineCallbacks @defer.inlineCallbacks
def get_remote_public_room_list(self, server_name): def get_remote_public_room_list(self, server_name, limit=None, next_batch=None):
res = yield self.hs.get_replication_layer().get_public_rooms( res = yield self.hs.get_replication_layer().get_public_rooms(
[server_name] [server_name]
) )
@ -227,11 +249,13 @@ class RoomListHandler(BaseHandler):
class RoomListNextBatch(namedtuple("RoomListNextBatch", ( class RoomListNextBatch(namedtuple("RoomListNextBatch", (
"stream_ordering", # stream_ordering of the first public room list "stream_ordering", # stream_ordering of the first public room list
"current_limit", # The number of previous rooms returned "current_limit", # The number of previous rooms returned
"direction_is_forward", # Bool if this is a next_batch, false if prev_batch
))): ))):
KEY_DICT = { KEY_DICT = {
"stream_ordering": "s", "stream_ordering": "s",
"current_limit": "n", "current_limit": "n",
"direction_is_forward": "d",
} }
REVERSE_KEY_DICT = {v: k for k, v in KEY_DICT.items()} REVERSE_KEY_DICT = {v: k for k, v in KEY_DICT.items()}
@ -248,3 +272,8 @@ class RoomListNextBatch(namedtuple("RoomListNextBatch", (
self.KEY_DICT[key]: val self.KEY_DICT[key]: val
for key, val in self._asdict().items() for key, val in self._asdict().items()
})) }))
def copy_and_replace(self, **kwds):
return self._replace(
**kwds
)

View file

@ -23,7 +23,9 @@ from synapse.api.constants import EventTypes, Membership
from synapse.api.filtering import Filter from synapse.api.filtering import Filter
from synapse.types import UserID, RoomID, RoomAlias from synapse.types import UserID, RoomID, RoomAlias
from synapse.events.utils import serialize_event, format_event_for_client_v2 from synapse.events.utils import serialize_event, format_event_for_client_v2
from synapse.http.servlet import parse_json_object_from_request, parse_string from synapse.http.servlet import (
parse_json_object_from_request, parse_string, parse_integer
)
import logging import logging
import urllib import urllib
@ -317,11 +319,21 @@ class PublicRoomListRestServlet(ClientV1RestServlet):
else: else:
pass pass
limit = parse_integer(request, "limit", 0)
next_batch = parse_string(request, "since", None)
handler = self.hs.get_room_list_handler() handler = self.hs.get_room_list_handler()
if server: if server:
data = yield handler.get_remote_public_room_list(server) data = yield handler.get_remote_public_room_list(
server,
limit=limit,
next_batch=next_batch,
)
else: else:
data = yield handler.get_local_public_room_list() data = yield handler.get_local_public_room_list(
limit=limit,
next_batch=next_batch,
)
defer.returnValue((200, data)) defer.returnValue((200, data))