Лучшие практики разбивки на страницы API

avatar
2arrs2ells
14 декабря 2012 в 03:34
192912
12
312

Мне бы очень хотелось помочь справиться со странным пограничным случаем с помощью API с разбивкой на страницы, который я создаю.

Как и многие API, этот API разбивает большие результаты на страницы. Если вы запросите / foos, вы получите 100 результатов (например, foo # 1-100) и ссылку на / foos? Page = 2, которая должна вернуть foo # 101-200.

К сожалению, если foo # 10 будет удален из набора данных до того, как потребитель API сделает следующий запрос, / foos? Page = 2 будет смещено на 100 и вернет foos # 102-201.

Это проблема для потребителей API, которые пытаются вытащить все foos - они не получат foo # 101.

Как лучше всего с этим справиться? Мы хотели бы сделать его как можно более легким (т.е. избежать обработки сеансов для запросов API). Будем признательны за примеры из других API!

Источник
2arrs2ells
16 декабря 2012 в 18:49
0

Только что отредактировал вопрос - проблема в том, что foo # 101 не будет отображаться в результатах, и потребитель API, пытающийся вытащить все foo, пропустит один.

Jerry Dodge
21 августа 2014 в 22:53
2

Я столкнулся с этой же проблемой и искал решение. AFAIK, на самом деле нет надежного гарантированного механизма для этого, если каждая страница выполняет новый запрос. Единственное решение, которое я могу придумать, - это сохранить активный сеанс и сохранить набор результатов на стороне сервера, и вместо того, чтобы выполнять новые запросы для каждой страницы, просто захватите следующий кешированный набор записей.

Jerry Dodge
21 августа 2014 в 23:02
0

О, я только что увидел часть вашего вопроса, в которой вы бы хотели избежать этого сценария

java_geek
6 октября 2014 в 07:15
34

Посмотрите, как Twitter достигает этого dev.twitter.com/rest/public/timelines

Petar
14 сентября 2015 в 15:47
1

@java_geek Как обновляется параметр Since_id? На веб-странице Twitter кажется, что они делают оба запроса с одинаковым значением Since_id. Интересно, когда он будет обновлен, чтобы при добавлении новых твитов их можно было учесть?

java_geek
18 ноября 2015 в 03:21
1

@Petar Потребитель API должен обновить параметр Since_id. Если вы видите, приведенный здесь пример относится к клиентам, обрабатывающим твиты.

8bitjunkie
3 сентября 2020 в 21:41
0

Описанная вами архитектура разбиения на страницы называется разбиением на страницы со смещением , а описанная вами проблема известна как перекос страницы . Это признанный недостаток при использовании смещений для запроса наборов результатов, которые можно переупорядочить или удалить элементы в середине сеанса. Решение - выбрать альтернативную архитектуру нумерации страниц. API Paging Built The Right Way предлагает краткое и приятное сравнение архитектур разбиения на страницы: engineering.mixmax.com/blog/api-paging-built-the-right-way

Ответы (12)

avatar
ramblinjan
16 декабря 2012 в 21:12
189

Я не совсем уверен, как обрабатываются ваши данные, поэтому это может работать, а может и не работать, но рассматривали ли вы возможность разбиения на страницы с полем отметки времени?

Когда вы запрашиваете / foos, вы получаете 100 результатов. Затем ваш API должен вернуть что-то вроде этого (при условии JSON, но если ему нужен XML, можно следовать тем же принципам):

{
    "data" : [
        {  data item 1 with all relevant fields    },
        {  data item 2   },
        ...
        {  data item 100 }
    ],
    "paging":  {
        "previous":  "http://api.example.com/foo?since=TIMESTAMP1" 
        "next":  "http://api.example.com/foo?since=TIMESTAMP2"
    }

}

Просто примечание: использование только одной временной метки зависит от неявного «ограничения» в ваших результатах. Вы можете добавить явное ограничение или также использовать свойство until.

Отметка времени может быть определена динамически, используя последний элемент данных в списке. Это похоже на то, как Facebook разбивает страницы на страницы в своем Graph API (прокрутите вниз, чтобы увидеть ссылки для разбивки на страницы в формате, который я дал выше).

Одна проблема может возникнуть, если вы добавите элемент данных, но, судя по вашему описанию, похоже, что они будут добавлены в конец (если нет, дайте мне знать, и я посмотрю, смогу ли я улучшить это).

rouble
16 сентября 2013 в 21:39
36

Не гарантируется, что метки времени будут уникальными. То есть можно создать несколько ресурсов с одной и той же меткой времени. Таким образом, у этого подхода есть обратная сторона: следующая страница может повторять последние (несколько?) Записей с текущей страницы.

ramblinjan
6 марта 2014 в 22:13
4

@prmatta На самом деле, в зависимости от реализации базы данных метка времени гарантированно уникальна.

Nolan Amy
21 апреля 2014 в 23:52
3

@jandjorgensen Из вашей ссылки: «Тип данных timestamp - это просто увеличивающееся число и не сохраняет дату или время. ... В SQL Server 2008 и более поздних версиях тип временной метки был переименован в rowversion , предположительно, чтобы лучше отразить его цель и ценность ". Таким образом, здесь нет никаких доказательств того, что метки времени (те, которые действительно содержат значение времени) уникальны.

ramblinjan
22 апреля 2014 в 18:25
0

«Повторяющиеся значения отметок времени могут быть сгенерированы с помощью оператора SELECT INTO, в котором столбец отметки времени находится в списке SELECT. Мы не рекомендуем использовать отметку времени таким образом». Если вы намеренно не нарушаете назначение метки времени, она уникальна. Кроме того, rowversion является синонимом, а не заменой. Как правило, в Stack Overflow такая информация будет полезна в предлагаемом редактировании, а не будет представлена ​​как противоречие.

longliveenduro
30 июня 2014 в 15:54
3

@jandjorgensen Мне нравится ваше предложение, но разве вам не нужна какая-то информация в ссылках на ресурсы, чтобы мы знали, пойдем ли мы вперед или назад? Как: "предыдущий": "api.example.com/foo?before=TIMESTAMP" "следующий": "api.example.com/foo?since=TIMESTAMP2" Мы также будет использовать наши идентификаторы последовательностей вместо отметки времени. Вы видите в этом какие-то проблемы?

ramblinjan
9 октября 2014 в 22:48
0

@longliveenduro Я добавил примечание о добавлении еще одного свойства в некоторых случаях. Что вы имеете в виду под идентификаторами последовательностей?

longliveenduro
10 октября 2014 в 08:35
0

@jandjorgensen Что касается идентификаторов последовательностей: если ваши ресурсы используют идентификаторы, сгенерированные из возрастающей последовательности в базе данных, вы можете использовать их вместо метки времени создания (если вы хотите упорядочить ресурсы по созданию)

ramblinjan
10 октября 2014 в 15:06
0

Да, это тоже нормально. Функционально он служит той же цели, если идентификатор последовательности гарантированно уникален и всегда возрастает.

Anthony F
21 октября 2014 в 16:28
6

Другой аналогичный вариант - использовать поле заголовка ссылки, указанное в RFC 5988 (раздел 5): tools.ietf.org/html/rfc5988#page-6

Jonas Anso
11 июля 2016 в 16:34
1

@jandjorgensen Может быть, вы хотите обновить ссылку на api графика developers.facebook.com/docs/graph-api/using-graph-api#paging

stephenbez
30 ноября 2016 в 22:58
0

@rouble Чтобы получить уникальную временную метку с приблизительной сортировкой, вы можете сделать что-то вроде Twitter Snowflake: blog.twitter.com/2010/announcing-snowflake

bastian
26 сентября 2018 в 12:05
1

Разве не следует использовать параметры подкачки с и до ?

avatar
zangw
30 декабря 2020 в 12:41
2

Обратитесь к Дизайн разбивки на страницы API, мы могли бы разработать api разбиения на страницы с помощью курсора

У них есть такая концепция, которая называется курсором - это указатель на строку. Таким образом, вы можете сказать базе данных «верните мне 100 строк после этой». А для базы данных это сделать намного проще, поскольку есть большая вероятность, что вы определите строку по полю с индексом. И вдруг вам не нужно будет выбирать и пропускать эти строки, вы просто пропустите их. Пример:

  GET /api/products
  {"items": [...100 products],
   "cursor": "qWe"}

API возвращает (непрозрачную) строку, которую затем можно использовать для получения следующей страницы:

GET /api/products?cursor=qWe
{"items": [...100 products],
 "cursor": "qWr"}

Есть много вариантов реализации. Как правило, у вас есть некоторые критерии заказа, например, идентификатор продукта. В этом случае вы закодируете свой идентификатор продукта с помощью какого-либо обратимого алгоритма (скажем, hashids). И, получив запрос с курсором, вы его декодируете и генерируете запрос типа WHERE id > :cursor LIMIT 100.

Преимущество:

  • Производительность запросов к базе данных может быть улучшена с помощью cursor
  • Хорошо обрабатывать, когда новый контент был вставлен в базу данных при запросе

Недостаток:

  • Невозможно создать ссылку previous page с API без сохранения состояния
avatar
adnanmuttaleb
26 декабря 2019 в 11:14
1

Другой вариант разбивки на страницы в API RESTFul - использовать заголовок ссылки, введенный здесь. Например, Github используйте его следующим образом:

Link: <https://api.github.com/user/repos?page=3&per_page=100>; rel="next",
  <https://api.github.com/user/repos?page=50&per_page=100>; rel="last"

Возможные значения для rel: первый, последний, следующий, предыдущий . Но при использовании заголовка Link может оказаться невозможным указать total_count (общее количество элементов).

avatar
Shubham Srivastava
2 апреля 2018 в 17:35
6

Просто чтобы добавить к этому ответу Камилка: https://www.coderhelper.com/a/13905589

Во многом зависит от того, с каким большим набором данных вы работаете. Небольшие наборы данных действительно работают с смещенной разбивкой на страницы , но большие наборы данных в реальном времени требуют разбивки на страницы курсора.

Нашел замечательную статью о том, как Slack развил разбиение на страницы своего api по мере увеличения количества наборов данных, объясняющих плюсы и минусы на каждом этапе: https://slack.engineering/evolving-api-pagination- at-slack-1c1f644f8e12

avatar
phauer
20 декабря 2017 в 12:02
9

Вариант A: разбивка на страницы набора клавиш с меткой времени

Чтобы избежать упомянутых вами недостатков разбивки на страницы со смещением, вы можете использовать разбиение на страницы на основе набора ключей. Обычно у сущностей есть отметка времени, указывающая время их создания или модификации. Эту отметку времени можно использовать для разбивки на страницы: просто передайте отметку времени последнего элемента в качестве параметра запроса для следующего запроса. Сервер, в свою очередь, использует метку времени в качестве критерия фильтра (например, WHERE modificationDate >= receivedTimestampParameter)

{
    "elements": [
        {"data": "data", "modificationDate": 1512757070}
        {"data": "data", "modificationDate": 1512757071}
        {"data": "data", "modificationDate": 1512757072}
    ],
    "pagination": {
        "lastModificationDate": 1512757072,
        "nextPage": "https://domain.de/api/elements?modifiedSince=1512757072"
    }
}

Таким образом, вы не пропустите ни одного элемента. Этот подход должен быть достаточно хорош для многих случаев использования. Однако имейте в виду следующее:

  • Вы можете столкнуться с бесконечными циклами, когда все элементы одной страницы имеют одинаковую метку времени.
  • Вы можете доставлять много элементов несколько раз клиенту, когда элементы с одинаковой временной меткой перекрывают две страницы.

Вы можете уменьшить вероятность возникновения этих недостатков, увеличив размер страницы и используя временные метки с точностью до миллисекунды.

Вариант B: Расширенная разбивка на страницы с помощью токена продолжения

Чтобы справиться с упомянутыми недостатками обычной разбивки на страницы набора клавиш, вы можете добавить смещение к отметке времени и использовать так называемый «маркер продолжения» или «курсор». Смещение - это позиция элемента относительно первого элемента с такой же меткой времени. Обычно токен имеет формат Timestamp_Offset. Он передается клиенту в ответе и может быть отправлен обратно на сервер для получения следующей страницы.

{
    "elements": [
        {"data": "data", "modificationDate": 1512757070}
        {"data": "data", "modificationDate": 1512757072}
        {"data": "data", "modificationDate": 1512757072}
    ],
    "pagination": {
        "continuationToken": "1512757072_2",
        "nextPage": "https://domain.de/api/elements?continuationToken=1512757072_2"
    }
}

Маркер «1512757072_2» указывает на последний элемент страницы и утверждает, что «клиент уже получил второй элемент с меткой времени 1512757072». Таким образом, сервер знает, где продолжить.

Помните, что вам нужно обрабатывать случаи, когда элементы менялись между двумя запросами. Обычно это делается путем добавления контрольной суммы к токену. Эта контрольная сумма вычисляется по идентификаторам всех элементов с этой меткой времени. Таким образом, мы получаем такой формат токена: Timestamp_Offset_Checksum.

Дополнительные сведения об этом подходе см. В сообщении блога «Разбивка веб-API на страницы с помощью токенов продолжения». Недостатком этого подхода является сложность реализации, поскольку необходимо учитывать множество угловых случаев. Вот почему библиотеки, такие как токен продолжения, могут быть удобны (если вы используете язык Java / JVM). Отказ от ответственности: я являюсь автором сообщения и соавтором библиотеки.

avatar
Stijn de Witt
4 апреля 2017 в 23:58
3

Я долго и упорно думал об этом и, наконец, пришел к решению, которое я опишу ниже. Это довольно большой шаг в плане сложности, но если вы все же сделаете этот шаг, вы получите то, что вам действительно нужно, а именно детерминированные результаты для будущих запросов.

Ваш пример удаления элемента - это только верхушка айсберга. Что делать, если вы выполняете фильтрацию по color=blue, но кто-то меняет цвета элементов между запросами? Надежная выборка всех элементов в постраничном режиме невозможна ... если ... мы не реализуем историю изменений .

Я реализовал это, и на самом деле это менее сложно, чем я ожидал. Вот что я сделал:

  • Я создал одну таблицу changelogs со столбцом идентификатора автоинкремента
  • Мои объекты имеют поле id, но это не первичный ключ
  • У объектов есть поле changeId, которое является как первичным, так и внешним ключом для журналов изменений.
  • Всякий раз, когда пользователь создает, обновляет или удаляет запись, система вставляет новую запись в changelogs, захватывает идентификатор и присваивает его новой версии объекта, которую затем вставляет в БД
  • Мои запросы выбирают максимальный changeId (сгруппированы по идентификатору) и самостоятельно присоединяются к нему, чтобы получить самые последние версии всех записей.
  • Фильтры применяются к самым последним записям
  • Поле состояния отслеживает, удален ли элемент
  • Максимальный идентификатор изменения возвращается клиенту и добавляется в качестве параметра запроса в последующих запросах
  • Поскольку создаются только новые изменения, каждое отдельное changeId представляет собой уникальный моментальный снимок базовых данных на момент создания изменения.
  • Это означает, что вы можете кэшировать результаты запросов, в которых есть параметр changeId, навсегда. Срок действия результатов никогда не истечет, потому что они никогда не изменятся.
  • Это также открывает захватывающие функции, такие как откат / возврат, синхронизация кэша клиента и т. Д. Любые функции, которые выигрывают от истории изменений.
U Avalos
26 марта 2018 в 18:58
0

я запутался. Как это решить упомянутый вами вариант использования? (В кеше изменяется случайное поле, и вы хотите сделать кеш недействительным)

Stijn de Witt
26 марта 2018 в 23:18
0

Для любых изменений, которые вы делаете сами, вы просто смотрите на ответ. Сервер предоставит новый changeId, и вы будете использовать его в следующем запросе. Что касается других изменений (внесенных другими людьми), вы либо время от времени опрашиваете последний changeId, и если он выше вашего собственного, вы знаете, что есть незавершенные изменения. Или вы настраиваете какую-то систему уведомлений (длинный опрос, push-уведомления сервера, веб-сокеты), которая предупреждает клиента о незавершенных изменениях.

avatar
Archimedes Trajano
29 июля 2015 в 19:25
8

Разбиение на страницы, как правило, является «пользовательской» операцией, и для предотвращения перегрузки компьютеров и человеческого мозга вы обычно даете подмножество. Однако вместо того, чтобы думать, что мы не получили весь список, может быть лучше спросить , имеет ли это значение?

Если требуется точное представление с прокруткой в ​​реальном времени, API-интерфейсы REST, которые по своей природе являются запросами / ответами, не подходят для этой цели. Для этого вам следует подумать о WebSockets или HTML5 Server-Sent Events, чтобы сообщить своему интерфейсу при работе с изменениями.

Теперь, если есть потребность для получения моментального снимка данных, я бы просто предоставил вызов API, который предоставляет все данные в одном запросе без разбивки на страницы. Имейте в виду, что вам понадобится что-то, что будет выполнять потоковую передачу вывода без временной загрузки его в память, если у вас большой набор данных.

В моем случае я неявно назначаю некоторые вызовы API, позволяющие получить всю информацию (в основном данные справочной таблицы). Вы также можете защитить эти API, чтобы они не нанесли вред вашей системе.

avatar
mickeymoon
5 февраля 2015 в 11:16
4

Я думаю, что в настоящее время ваш api действительно отвечает так, как должен. Первые 100 записей на странице в общем порядке объектов, которые вы обслуживаете. В вашем объяснении говорится, что вы используете какие-то идентификаторы упорядочения для определения порядка ваших объектов для разбивки на страницы.

Теперь, если вы хотите, чтобы страница 2 всегда начиналась с 101 и заканчивалась на 200, тогда вы должны сделать количество записей на странице переменной, поскольку они подлежат удалению.

Вы должны сделать что-то вроде следующего псевдокода:

page_max = 100
def get_page_results(page_no) :

    start = (page_no - 1) * page_max + 1
    end = page_no * page_max

    return fetch_results_by_id_between(start, end)
John Henckel
18 июля 2016 в 15:16
1

Я согласен. вместо запроса по номеру записи (что ненадежно) вы должны запрашивать по идентификатору. Измените свой запрос (x, m) так, чтобы он означал «вернуть до m записей, СОРТИРОВАННЫХ по идентификатору, с идентификатором> x», тогда вы можете просто установить x на максимальный идентификатор из предыдущего результата запроса.

mickeymoon
1 августа 2016 в 19:38
0

Верно, либо сортируйте по идентификаторам, либо если у вас есть какое-то конкретное бизнес-поле для сортировки, например creation_date и т. Д.

avatar
Mohd Iftekhar Qurashi
20 января 2015 в 10:16
19

В зависимости от логики вашей серверной стороны может быть два подхода.

Подход 1. Когда сервер недостаточно умен для обработки состояний объекта.

Вы можете отправить на сервер уникальные идентификаторы всех кэшированных записей, например ["id1", "id2", "id3", "id4", "id5", "id6", "id7", "id8", " id9 "," id10 "] и логический параметр, чтобы узнать, запрашиваете ли вы новые записи (потяните для обновления) или старые записи (загрузите больше).

Ваш сервер должен отвечать за возврат новых записей (загрузка дополнительных записей или новых записей с помощью pull для обновления), а также идентификаторы удаленных записей из ["id1", "id2", "id3", "id4", "id5" "," id6 "," id7 "," id8 "," id9 "," id10 "].

Пример: - Если вы запрашиваете дополнительную нагрузку, ваш запрос должен выглядеть примерно так: -

{
        "isRefresh" : false,
        "cached" : ["id1","id2","id3","id4","id5","id6","id7","id8","id9","id10"]
}

Теперь предположим, что вы запрашиваете старые записи (загрузите больше) и предположим, что запись «id2» обновлена ​​кем-то, а записи «id5» и «id8» удалены с сервера, тогда ваш ответ сервера должен выглядеть примерно так: -

{
        "records" : [
{"id" :"id2","more_key":"updated_value"},
{"id" :"id11","more_key":"more_value"},
{"id" :"id12","more_key":"more_value"},
{"id" :"id13","more_key":"more_value"},
{"id" :"id14","more_key":"more_value"},
{"id" :"id15","more_key":"more_value"},
{"id" :"id16","more_key":"more_value"},
{"id" :"id17","more_key":"more_value"},
{"id" :"id18","more_key":"more_value"},
{"id" :"id19","more_key":"more_value"},
{"id" :"id20","more_key":"more_value"}],
        "deleted" : ["id5","id8"]
}

Но в этом случае, если у вас много локальных кешированных записей, предположим, что 500, тогда ваша строка запроса будет слишком длинной, например: -

{
        "isRefresh" : false,
        "cached" : ["id1","id2","id3","id4","id5","id6","id7","id8","id9","id10",………,"id500"]//Too long request
}

Подход 2: Когда сервер достаточно умен, чтобы обрабатывать состояния объекта в соответствии с датой.

Вы можете отправить идентификатор первой и последней записи, а также время предыдущего запроса. Таким образом, ваш запрос всегда будет небольшим, даже если у вас большой объем кэшированных записей

Пример: - Если вы запрашиваете дополнительную нагрузку, ваш запрос должен выглядеть примерно так: -

{
        "isRefresh" : false,
        "firstId" : "id1",
        "lastId" : "id10",
        "last_request_time" : 1421748005
}

Ваш сервер отвечает за возврат идентификаторов удаленных записей, которые удаляются после last_request_time, а также за возврат обновленной записи после last_request_time между «id1» и «id10».

{
        "records" : [
{"id" :"id2","more_key":"updated_value"},
{"id" :"id11","more_key":"more_value"},
{"id" :"id12","more_key":"more_value"},
{"id" :"id13","more_key":"more_value"},
{"id" :"id14","more_key":"more_value"},
{"id" :"id15","more_key":"more_value"},
{"id" :"id16","more_key":"more_value"},
{"id" :"id17","more_key":"more_value"},
{"id" :"id18","more_key":"more_value"},
{"id" :"id19","more_key":"more_value"},
{"id" :"id20","more_key":"more_value"}],
        "deleted" : ["id5","id8"]
}

Потяните, чтобы обновить: -

enter image description here

Загрузить еще

enter image description here

avatar
Will Hartung
18 декабря 2012 в 21:27
29

У вас несколько проблем.

Во-первых, у вас есть пример, который вы процитировали.

У вас также есть аналогичная проблема, если строки вставлены, но в этом случае пользователь получает дублирующиеся данные (возможно, легче управлять, чем отсутствующие данные, но все же проблема).

Если вы не делаете снимок исходного набора данных, то это просто факт жизни.

Вы можете попросить пользователя сделать явный снимок:

POST /createquery
filter.firstName=Bob&filter.lastName=Eubanks

Результат:

HTTP/1.1 301 Here's your query
Location: http://www.example.org/query/12345

Затем вы можете публиковать его в течение всего дня, так как теперь он статичен. Это может быть достаточно легковесным, поскольку вы можете просто фиксировать фактические ключи документа, а не целые строки.

Если вариант использования заключается просто в том, что ваши пользователи хотят (и нуждаются) в всех данных, вы можете просто передать их им:

GET /query/12345?all=true

и просто отправьте весь комплект.

2arrs2ells
19 декабря 2012 в 22:55
1

(По умолчанию еда сортируется по дате создания, поэтому вставка строки не проблема.)

Scadge
29 августа 2018 в 14:24
1

На самом деле, захвата только ключей документа недостаточно. Таким образом, вам придется запрашивать полные объекты по идентификатору, когда пользователь запрашивает их, но может оказаться, что они больше не существуют.

avatar
Brent Baisley
16 декабря 2012 в 21:29
15

Может быть сложно найти лучшие практики, поскольку большинство систем с API не подходят для этого сценария, потому что это крайнее преимущество, или они обычно не удаляют записи (Facebook, Twitter). Facebook фактически говорит, что на каждой «странице» может не быть запрошенного количества результатов из-за фильтрации, выполняемой после разбивки на страницы. https://developers.facebook.com/blog/post/478/

Если вам действительно нужно учесть этот крайний случай, вам нужно «запомнить», где вы остановились. Предложение jandjorgensen почти точно, но я бы использовал поле, которое гарантированно будет уникальным, например первичный ключ. Возможно, вам потребуется использовать более одного поля.

Следуя потоку Facebook, вы можете (и должны) кэшировать уже запрошенные страницы и просто возвращать те, у которых удаленные строки отфильтрованы, если они запрашивают страницу, которую они уже запросили.

Deepak Garg
14 августа 2013 в 06:52
2

Это неприемлемое решение. Это требует значительных затрат времени и памяти. Все удаленные данные вместе с запрошенными данными необходимо будет хранить в памяти, которая может вообще не использоваться, если тот же пользователь не запрашивает больше записей.

Brent Baisley
16 августа 2013 в 00:25
3

Я не согласен. Простое сохранение уникальных идентификаторов совсем не требует много памяти. Вы не можете хранить данные бесконечно, только для «сеанса». С memcache это легко сделать, просто установите срок действия (т.е. 10 минут).

U Avalos
26 марта 2018 в 18:49
1

память дешевле, чем скорость сети / процессора. Поэтому, если создание страницы очень дорогое (с точки зрения сети или интенсивного использования ЦП), то кеширование результатов является допустимым подходом @DeepakGarg

avatar
kamilk
16 декабря 2012 в 21:21
30

Если у вас есть разбивка на страницы, вы также сортируете данные по некоторому ключу. Почему бы не позволить клиентам API включать ключ последнего элемента ранее возвращенной коллекции в URL-адрес и добавить предложение WHERE в ваш SQL-запрос (или что-то подобное, если вы не используете SQL), чтобы он возвращал только те элементы, для которых ключ больше этого значения?

Chris Peacock
29 марта 2018 в 15:06
4

Это неплохое предложение, однако то, что вы сортируете по значению, не означает, что это «ключ», то есть уникальный.

Sat Thiru
10 апреля 2018 в 03:02
0

Точно. Например, в моем случае поле сортировки является датой, и оно далеко не уникальное.