Разбивка на страницы в CouchDB?
-
10-07-2019 - |
Вопрос
Как бы я приступил к реализации запросов, необходимых для разбивки на страницы?
В принципе, когда запрашивается страница 1, получите первые 5 записей.Для страницы 2 найдите следующие 5 и так далее.
Я планирую использовать это через модуль couchdb-python, но это не должно иметь никакого значения для реализации.
Решение
Тот Самый Руководство CouchDB здесь есть хорошее обсуждение разбивки на страницы, включая множество примеров кода: http://guide.couchdb.org/draft/recipes.html#pagination Вот их алгоритм:
- Запрос
rows_per_page + 1
строки из представления - Дисплей
rows_per_page
строки, сохраните последнюю строку какnext_startkey
- В качестве информации о странице сохраните
startkey
иnext_startkey
- Используйте
next_*
значения для создания следующей ссылки и используйте остальные для создания предыдущей ссылки
Н.Б.:Правильный способ извлечения страниц в CouchDB - это указать начальный ключ, а не начальный индекс, как вы могли бы подумать.Но откуда вы знаете, с какой клавиши начинать 2-ю страницу?Умное решение:"Вместо запроса 10 строк для страницы вы запрашиваете 11 строк, но отображаете только 10 и используете значения в 11-й строке в качестве начальной клавиши для следующей страницы".
Если вы ожидаете, что несколько документов будут выдавать одинаковые ключи, вам нужно будет использовать startdocid
в дополнение к startkey
для правильной разбивки на страницы.Причина в том, что startkey
одного этого уже будет недостаточно для однозначной идентификации строки.Эти параметры бесполезны, если вы не предоставляете startkey
.Фактически, CouchDB сначала рассмотрит startkey
параметр, то он будет использовать startdocid
параметр для дальнейшего переопределения начала диапазона, если несколько потенциальных начальных строк имеют один и тот же ключ, но разные идентификаторы документа.То же самое относится и к enddocid
.
Другие советы
CouchDB API HTTP View предоставляет множество возможностей для эффективной подкачки страниц.
Самый простой метод будет использовать startkey
и count
. Count - это максимальное количество записей, которые CouchDB возвратит для этого запроса на просмотр, что соответствует вашему дизайну, и startkey - это то место, с которого вы хотите запустить CouchDB. Когда вы запрашиваете представление, оно также сообщает вам, сколько существует записей, позволяя подсчитать, сколько страниц будет, если вы хотите показать это пользователям.
Таким образом, в первом запросе не будет указана начальная клавиша, а будет только количество записей, которые вы хотите показать. Затем вы можете отметить ключ последней возвращенной записи и использовать его в качестве ключа запуска для следующей страницы. В этой простой форме вы получите перекрытие, при котором последняя запись на одной странице является первой из следующей. Если это нежелательно, просто не отображать последнюю запись на странице.
Более простой способ сделать это - использовать параметр skip для обработки начального документа для страницы, однако этот метод следует использовать с осторожностью. Параметр skip просто заставляет внутренний движок не возвращать записи, которые он перебирает. Хотя это дает желаемое поведение, это намного медленнее, чем поиск первого документа для страницы по ключу. Чем больше документов будет пропущено, тем медленнее будет запрос.
Это то, что я придумал до сих пор - получить идентификаторы всех сообщений, а затем извлечь фактические элементы для первого x количества идентификаторов.
Это не очень эффективно, но более важно, чем извлекать все сообщения, а затем отбрасывать большую часть. Тем не менее, к моему удивлению, он, кажется, работает довольно быстро - я запустил метод posthelper.page ()
100 раз, и это заняло около 0,5 секунды.
Я не хотел публиковать это в актуальном вопросе, так что это не повлияло бы на ответы так много - вот код:
allPostsUuid = """
function(doc) {
if(doc.type == 'post'){
emit(doc._id, null);
}
}
"""
class PostsHelper:
def __init__(self):
server = Server(config.dbhost)
db = server[config.dbname]
return db
def _getPostByUuid(self, uuid):
return self.db.get(uuid)
def page(self, number = 1):
number -= 1 # start at zero offset
start = number * config.perPage
end = start + config.perPage
allUuids = [
x.key for x in self.db.query(allPostsUuid)
]
ret = [
self._getPostByUuid(x) for x in allUuids[start : end]
]
if len(ret) == 0:
raise Error404("Invalid page (%s results)" % (len(allUuids)))
else:
return ret
Вот ниже приведен рекурсивный способ, который я нашел :
Возьмем две переменные
var lastOffset = 0; var counter = 0;
function someRecursive(lastOffset,counter) {
queryView(db, whereClause).then(result => {
var rows_per_page = 5;
//formula below
var page = Math.floor((lastOffset == 0 ? 0: (result.offset - lastOffset) +
(rows_per_page * counter)) / rows_per_page) + 1;
var skip = page * rows_per_page;
if (somerecursionexitcondition) {
counter = lastOffset == 0 ? lastOffset: counter + 1;
lastOffset =result.offset;
someRecursive(lastOffset, counter).then(result => {
resolve();
});
});
}