Paginado¶
Documentación sobre Paginación usando Offset-based y Marker-based¶
La paginación es una técnica común utilizada en APIs para dividir grandes conjuntos de datos en fragmentos más pequeños, lo que permite una mejor administración y recuperación de la información. En este documento, exploraremos dos enfoques populares para la paginación: Offset-based y Marker-based.
Offset-based Pagination¶
La paginación basada en offset implica especificar cuántos elementos se deben omitir desde el principio antes de recuperar un número determinado de elementos. Esto se logra utilizando los parámetros "offset" y "limit" en la llamada a la API.
Request¶
La petición al endpoint debe contener los siguientes QueryParam
Query Param | Type | Default | |
---|---|---|---|
offset | Integer | 0 | El cero indica que arranca a recorrer desde el inicio |
limit | Integer | Depende de la API | El número máximo de entradas a devolver. Si el valor excede el máximo, se utilizará el valor máximo. |
Response¶
La respuesta de la API debe contener la siguiente información
Field | Type | |
---|---|---|
entries | Array | Lista de elementos con información pertinente a la busqueda. |
offset | Integer | El offset usado en la petición |
limit | Integer | El limit utilizado en la petición |
totalCount | Integer | Total de elementos encontrados para la petición |
Pasos para implementar Offset-based Pagination:¶
-
Definición de Parámetros:
-
limit
: El número máximo de elementos que se recuperarán en una sola solicitud. -
offset
: La cantidad de elementos que se deben omitir desde el principio. -
Solicitud Inicial: Realiza una solicitud al servicio web con los parámetros
limit
yoffset
adecuados para obtener la primera página de resultados. -
Siguiente Página: Para obtener la siguiente página de resultados, realiza otra solicitud con el mismo valor de
limit
y un valor deoffset
ajustado. El nuevo valor deoffset
sería el valor deloffset
de la solicitud anterior más ellimit
.
Ejemplos programaticos básicos¶
using System;
using System.Collections.Generic;
namespace OffsetPaginationExample
{
class Program
{
static void Main(string[] args)
{
List<string> data = GenerateSampleData();
int pageSize = 5; // Tamaño de página
int offset = 0; // Valor de offset inicial
while (offset < data.Count)
{
List<string> currentPage = GetPage(data, offset, pageSize);
Console.WriteLine("Página actual:");
foreach (string item in currentPage)
{
Console.WriteLine(item);
}
Console.WriteLine("Presiona Enter para obtener la siguiente página...");
Console.ReadLine();
offset += pageSize; // Incrementar el offset para obtener la siguiente página
}
Console.WriteLine("Fin de la paginación.");
}
static List<string> GetPage(List<string> data, int offset, int pageSize)
{
return data.Skip(offset).Take(pageSize).ToList();
}
static List<string> GenerateSampleData()
{
List<string> data = new List<string>();
for (int i = 1; i <= 20; i++)
{
data.Add($"Elemento {i}");
}
return data;
}
}
}
package main
import (
"fmt"
)
func getPage(data []string, offset int, pageSize int) []string {
if offset >= len(data) {
return nil
}
end := offset + pageSize
if end > len(data) {
end = len(data)
}
return data[offset:end]
}
func main() {
data := make([]string, 20)
for i := 0; i < 20; i++ {
data[i] = fmt.Sprintf("Element %d", i+1)
}
pageSize := 5
offset := 0
for offset < len(data) {
currentPage := getPage(data, offset, pageSize)
fmt.Println("Página actual:")
for _, item := range currentPage {
fmt.Println(item)
}
var input string
fmt.Println("Presiona Enter para obtener la siguiente página...")
fmt.Scanln(&input)
offset += pageSize
}
fmt.Println("Fin de la paginación.")
}
def get_page(data, offset, page_size):
return data[offset : offset + page_size]
def main():
data = [f"Element {i}" for i in range(1, 21)]
page_size = 5
offset = 0
while offset < len(data):
current_page = get_page(data, offset, page_size)
print("Página actual:")
for item in current_page:
print(item)
input("Presiona Enter para obtener la siguiente página...")
offset += page_size
print("Fin de la paginación.")
if __name__ == "__main__":
main()
function get_page(data, offset, page_size) {
return data.slice(offset, offset + page_size);
}
function main() {
let data = [];
for (let i = 1; i <= 20; i++) {
data.push(`Elemento ${i}`);
}
let page_size = 5;
let offset = 0;
while (offset < data.length) {
let current_page = get_page(data, offset, page_size);
console.log("Página actual:");
for (let item of current_page) {
console.log(item);
}
prompt("Presiona Enter para obtener la siguiente página...");
offset += page_size;
}
console.log("Fin de la paginación.");
}
main();
Ejemplos APIs¶
curl -i -X GET "https://api.box.com/2.0/folders/0/items?offset=2000&limit=1000" \
-H "Authorization: Bearer <ACCESS_TOKEN>"
{
"entries": [
{
"id": "12345",
"etag": "1",
"type": "file",
"sequence_id": "3",
"name": "Contract.pdf",
"sha1": "85136C79CBF9FE36BB9D05D0639C70C265C18D37",
"file_version": {
"id": "12345",
"type": "file_version",
"sha1": "134b65991ed521fcfe4724b7d814ab8ded5185dc"
}
}
],
"limit": 1000,
"offset": 2000,
"order": [
{
"by": "type",
"direction": "ASC"
}
],
"totalCount": 5000
}
Marker-based Pagination¶
La paginación basada en marcadores implica el uso de un marcador o un identificador único para obtener la siguiente página de resultados. Aquí tienes los pasos para implementar la paginación basada en marcadores:
Request¶
La petición al endpoint debe contener los siguientes QueryParam
Query Param | Type | Default | |
---|---|---|---|
marker | string | vacio o null indican que se debe arrancar a recorrer por el inicio |
|
limit | Integer | Depende de la API | El número máximo de entradas a devolver. Si el valor excede el máximo, se utilizará el valor máximo. |
Response¶
La respuesta de la API debe contener la siguiente información
Field | Type | |
---|---|---|
entries | Array | Lista de elementos con información pertinente a la busqueda. |
next | String | El valor que se puede utilizar como valor de marcador para obtener la siguiente página de resultados. Si este valor es null o una cadena vacía, no hay más resultados que obtener. |
limit | Integer | El limit utilizado en la petición |
Pasos para implementar Marker-based Pagination:¶
-
Definición de Parámetros:
limit
: El número máximo de elementos que se recuperarán en una sola solicitud.marker
: El marcador o identificador del último elemento de la página anterior.
-
Solicitud Inicial: Realiza una solicitud al servicio web con el parámetro limit para obtener la primera página de resultados.
-
Siguiente Página: Para obtener la siguiente página de resultados, toma el marcador o identificador del último elemento de la página actual y úsalo como valor para el parámetro
marker
en la siguiente solicitud.
Ejemplos programaticos básicos¶
using System;
using System.Collections.Generic;
using System.Linq;
namespace MarkerPaginationExample
{
class Program
{
static void Main(string[] args)
{
List<string> data = GenerateData(50); // Genera una lista de datos ficticios
int pageSize = 10; // Tamaño de cada página
string startMarker = null; // Marcador de inicio de la página
do
{
List<string> currentPage = GetPage(data, startMarker, pageSize, out startMarker);
Console.WriteLine("Página:");
foreach (string item in currentPage)
{
Console.WriteLine(item);
}
Console.WriteLine("Presiona Enter para cargar la siguiente página...");
Console.ReadLine();
} while (startMarker != null);
Console.WriteLine("Fin de la paginación.");
}
static List<string> GenerateData(int count)
{
List<string> data = new List<string>();
for (int i = 1; i <= count; i++)
{
data.Add($"Elemento {i}");
}
return data;
}
static List<string> GetPage(List<string> data, string startMarker, int pageSize, out string nextStartMarker)
{
List<string> page = new List<string>();
int startIndex = string.IsNullOrEmpty(startMarker) ? 0 : data.IndexOf(startMarker)-1 ;
page = data.Skip(startIndex).Take(pageSize).ToList();
nextStartMarker = (startIndex + page.Count < data.Count) ? data[startIndex + page.Count] : null;
return page;
}
}
}
package main
import (
"fmt"
)
func main() {
data := generateData(50) // Genera una lista de datos ficticios
pageSize := 10 // Tamaño de cada página
var startMarker string // Marcador de inicio de la página
for {
currentPage, newStartMarker := getPage(data, startMarker, pageSize)
startMarker = newStartMarker
fmt.Println("Página:")
for _, item := range currentPage {
fmt.Println(item)
}
fmt.Println("Presiona Enter para cargar la siguiente página...")
fmt.Scanln()
if newStartMarker == "" {
break
}
}
fmt.Println("Fin de la paginación.")
}
func generateData(count int) []string {
data := make([]string, 0)
for i := 1; i <= count; i++ {
data = append(data, fmt.Sprintf("Elemento %d", i))
}
return data
}
func getPage(data []string, startMarker string, pageSize int) ([]string, string) {
var page []string
startIndex := 0
for i, item := range data {
if item == startMarker {
startIndex = i -1
break
}
}
for i := startIndex; i < len(data) && len(page) < pageSize; i++ {
page = append(page, data[i])
}
nextStartMarker := ""
if startIndex+len(page) < len(data) {
nextStartMarker = data[startIndex+len(page)]
}
return page, nextStartMarker
}
def main():
data = generate_data(50) # Genera una lista de datos ficticios
page_size = 10 # Tamaño de cada página
start_marker = None # Marcador de inicio de la página
while True:
current_page, new_start_marker = get_page(data, start_marker, page_size)
start_marker = new_start_marker
print("Página:")
for item in current_page:
print(item)
input("Presiona Enter para cargar la siguiente página...")
if not new_start_marker:
break
print("Fin de la paginación.")
def generate_data(count):
data = []
for i in range(1, count + 1):
data.append(f"Elemento {i}")
return data
def get_page(data, start_marker, page_size):
page = []
start_index = 0
if start_marker:
start_index = data.index(start_marker) -1
for i in range(start_index, len(data)):
page.append(data[i])
if len(page) == page_size:
break
next_start_marker = None
if start_index + len(page) < len(data):
next_start_marker = data[start_index + len(page)]
return page, next_start_marker
if __name__ == "__main__":
main()
function main() {
const data = generateData(50); // Genera un array de datos ficticios
const pageSize = 10; // Tamaño de cada página
let startMarker = null; // Marcador de inicio de la página
while (true) {
const { currentPage, newStartMarker } = getPage(data, startMarker, pageSize);
startMarker = newStartMarker;
console.log("Página:");
currentPage.forEach(item => {
console.log(item);
});
prompt("Presiona Enter para cargar la siguiente página...");
if (!newStartMarker) {
break;
}
}
console.log("Fin de la paginación.");
}
function generateData(count) {
const data = [];
for (let i = 1; i <= count; i++) {
data.push(`Elemento ${i}`);
}
return data;
}
function getPage(data, startMarker, pageSize) {
const page = [];
let startIndex = 0;
if (startMarker) {
startIndex = data.indexOf(startMarker) -1;
}
for (let i = startIndex; i < data.length && page.length < pageSize; i++) {
page.push(data[i]);
}
const nextStartMarker = startIndex + page.length < data.length ? data[startIndex + page.length] : null;
return { currentPage: page, newStartMarker: nextStartMarker };
}
main();
Ejemplos APIs¶
curl -i -X GET "https://api.box.com/2.0/folders/0/items?marker=897f33c7-5c4d-4cb4-8326-75b694751387&limit=1" \
-H "Authorization: Bearer <ACCESS_TOKEN>"
{
"entries": [
{
"id": "12345",
"etag": "1",
"type": "file",
"sequence_id": "3",
"name": "Contract.pdf",
"sha1": "85136C79CBF9FE36BB9D05D0639C70C265C18D37",
"file_version": {
"id": "12345",
"type": "file_version",
"sha1": "134b65991ed521fcfe4724b7d814ab8ded5185dc"
}
}
],
"limit": 1,
"next": "ba8d7715-8db0-48e0-a7e3-fce81a3997fb",
}
¿Cuál Método de Paginación Elegir?¶
Cada método de paginación tiene un ámbito de aplicación específico. El paginado basado en "offset" es adecuado cuando calcular el número total de elementos almacenados no implica un alto costo computacional. Sin embargo, a medida que la base de datos crece, este cálculo puede volverse más lento y afectar el rendimiento.
En situaciones donde el rendimiento es una preocupación, especialmente cuando la base de datos es grande, se recomienda utilizar el paginado basado en "marker" (marcador). Este método tiene la ventaja de que no requiere conocer el número total de registros. Simplemente nos posicionamos en el siguiente registro de interés, lo que lo hace más eficiente para bases de datos en crecimiento.