7. Trabajando con colecciones y filtros
7.1. Introducción al sistema de colecciones
El framework incorpora un potente sistema de colecciones que permite gestionar conjuntos de datos de manera eficiente, con capacidades integradas de paginación, filtrado y ordenamiento. Este sistema es fundamental para la creación de APIs que manejan grandes volúmenes de datos.
¿Qué es una colección?
Una colección es un conjunto estructurado de registros que incluye:
- Los registros recuperados de la base de datos
- Información de paginación (página actual, total de páginas, etc.)
- Metadatos sobre los filtros aplicados
- Estadísticas sobre los registros (total de registros, total filtrado, etc.)
Cuando una petición llega al endpoint index de un controlador, el servicio devuelve una colección de este tipo:
{
"collection": [
{ "id": "1", "name": "Producto 1", "price": 19.99 },
{ "id": "2", "name": "Producto 2", "price": 29.99 }
],
"totalRows": 150,
"totalFilteredRows": 2,
"pages": 1,
"page": 1,
"itemsPerPage": 10
}
7.2. Paginación de resultados
La paginación es esencial para manejar grandes conjuntos de datos sin sobrecargar la memoria o la red.
7.2.1. Parámetros de paginación
Para paginar resultados, incluye estos parámetros en la petición:
| Parámetro | Tipo | Descripción | Valor por defecto |
|---|---|---|---|
page | número | Número de página a recuperar | 1 |
itemsPerPage | número | Cantidad de registros por página | 30 |
7.2.2. Ejemplo de petición con paginación
GET /products?page=2&itemsPerPage=15
Esta petición recupera la segunda página de resultados, con 15 registros por página.
7.2.3. Estructura de respuesta paginada
Una respuesta paginada incluye:
{
"collection": [...], // Array de registros para la página actual
"totalRows": 150, // Total de registros en la base de datos
"totalFilteredRows": 45, // Total de registros después de aplicar filtros
"pages": 3, // Número total de páginas
"page": 2, // Página actual
"itemsPerPage": 15 // Registros por página
}
Esta estructura permite a las interfaces de usuario implementar controles de paginación eficientes.
7.3. Filtrado de datos
El framework ofrece un sistema de filtrado potente y flexible basado en JSON.
7.3.1. Sintaxis básica de filtros
Los filtros se pasan en un parámetro filters que contiene un objeto JSON con las condiciones.
Estructura básica:
{
"campo1": {"operador": "valor"},
"campo2": {"operador": "valor"}
}
Ejemplo simple:
{
"name": {"like": "smartphone"},
"price": {">": 100}
}
Este filtro seleccionará registros donde:
- El campo "name" contenga "smartphone"
- Y el campo "price" sea mayor que 100
7.3.2. Operadores de filtrado disponibles
| Operador | Descripción | Ejemplo |
|---|---|---|
= | Igual | {"status": {"=": "active"}} |
!= | No igual | {"status": {"!=": "deleted"}} |
> | Mayor que | {"price": {">": 100}} |
< | Menor que | {"price": {"<": 50}} |
>= | Mayor o igual que | {"stock": {">=": 10}} |
<= | Menor o igual que | {"weight": {"<=": 5}} |
like | Contiene | {"name": {"like": "phone"}} |
not like | No contiene | {"name": {"not like": "discontinued"}} |
in | En lista | {"category": {"in": ["A", "B", "C"]}} |
not in | No en lista | {"status": {"not in": ["deleted", "archived"]}} |
between | Entre dos valores | {"price": {"between": [10, 50]}} |
is null | Es nulo | {"parent_id": {"is null": true}} |
is not null | No es nulo | {"email": {"is not null": true}} |
7.3.3. Filtros compuestos (AND, OR)
Para crear condiciones más complejas, puedes usar los operadores lógicos $and y $or:
Operador $and:
{
"$and": [
{"category": {"=": "electronics"}},
{"price": {">": 500}},
{"stock": {">": 0}}
]
}
Este filtro seleccionará productos de la categoría "electronics" que cuesten más de 500 y tengan stock disponible.
Operador $or:
{
"$or": [
{"status": {"=": "new"}},
{"status": {"=": "sale"}},
{"discount": {">": 20}}
]
}
Este filtro seleccionará productos que sean nuevos, estén en oferta o tengan un descuento mayor del 20%.
Filtros anidados:
Puedes crear condiciones complejas anidando operadores:
{
"$and": [
{"category": {"=": "electronics"}},
{
"$or": [
{"status": {"=": "new"}},
{"discount": {">": 15}}
]
},
{"price": {"between": [100, 1000]}}
]
}
Este filtro seleccionará:
- Productos de electrónica
- Que sean nuevos o tengan un descuento mayor del 15%
- Y cuyo precio esté entre 100 y 1000
7.3.4. Filtro simpleSearch
El sistema ofrece una forma simplificada de búsqueda llamada simpleSearch, ideal para implementar campos de búsqueda rápida en interfaces de usuario.
Formato de simpleSearch:
{
"search": "texto a buscar",
"fields": ["campo1", "campo2", "campo3"]
}
Al usar simpleSearch, el sistema creará automáticamente un filtro $or que buscará el texto en cada uno de los campos especificados.
Ejemplo de uso:
GET /products?simpleSearch={"search":"smartphone","fields":["name","description","model"]}
Esto es equivalente a usar el siguiente filtro:
{
"$or": [
{"name": {"like": "smartphone"}},
{"description": {"like": "smartphone"}},
{"model": {"like": "smartphone"}}
]
}
7.3.5. Filtros permanentes (permanentFilters)
En ocasiones, necesitamos aplicar ciertos filtros de manera consistente en todas las peticiones, independientemente de los filtros específicos que envíe el cliente.
El parámetro permanentFilters permite definir filtros que se aplicarán siempre, y se combinarán (mediante AND) con los filtros específicos del parámetro filters.
Ejemplo de uso:
GET /products?permanentFilters={"active":{"=":1}}&filters={"category":{"=":"electronics"}}
En este ejemplo:
permanentFiltersasegura que solo se muestren productos activosfiltersfiltra adicionalmente por la categoría "electronics"
El sistema combinará ambos filtros automáticamente.
7.4. Ejemplos prácticos de filtros
Veamos algunos ejemplos completos para diferentes casos de uso:
Ejemplo 1: Filtrado básico en productos
GET /products?filters={"price":{">":100},"stock":{">":0}}
Recupera productos con precio mayor a 100 y que tengan stock disponible.
Ejemplo 2: Búsqueda con operador OR
GET /products?filters={"$or":[{"name":{"like":"smartphone"}},{"description":{"like":"smartphone"}}]}
Busca productos que contengan "smartphone" en el nombre O en la descripción.
Ejemplo 3: Filtrado por rango de fechas
GET /sales?filters={"date":{"between":["2023-01-01","2023-12-31"]}}
Recupera ventas realizadas durante el año 2023.
Ejemplo 4: Filtrado con condiciones anidadas complejas
GET /products?filters={"$and":[{"category":{"=":"electronics"}},{"$or":[{"discount":{">":20}},{"new_arrival":{"=":1}}]},{"price":{"between":[50,500]}}]}
Recupera productos que:
- Pertenecen a la categoría "electronics"
- Tienen un descuento mayor al 20% O son novedades
- Su precio está entre 50 y 500
Ejemplo 5: Combinando filtros y paginación
GET /customers?page=2&itemsPerPage=20&filters={"$or":[{"last_purchase":{">=":"2023-01-01"}},{"total_purchases":{">":1000}}]}
Recupera la segunda página de clientes que hayan comprado desde el 1 de enero de 2023 o que hayan realizado compras por más de 1000.
Ejemplo 6: SimpleSearch con filtros adicionales
GET /products?simpleSearch={"search":"iPhone","fields":["name","model"]}&filters={"price":{"between":[600,1200]}}
Busca productos con "iPhone" en el nombre o modelo, filtrando adicionalmente por un rango de precios.
Ejemplo 7: PermanentFilters con filtros y búsqueda
GET /products?permanentFilters={"store_id":{"=":5},"active":{"=":1}}&filters={"category":{"=":"phones"}}&simpleSearch={"search":"pro","fields":["name"]}
En este ejemplo:
permanentFiltersgarantiza que solo se muestren productos de la tienda 5 que estén activosfiltersfiltra adicionalmente por la categoría "phones"simpleSearchbusca productos con "pro" en el nombre
7.5. Cómo funciona internamente el sistema de filtros
Cuando envías una petición con filtros, el sistema realiza los siguientes pasos:
- Decodifica los parámetros JSON (
filters,permanentFilters,simpleSearch) - Transforma
simpleSearchen un filtro$orestándar - Combina
permanentFiltersyfiltersusando el operador$and - Convierte el filtro resultante en condiciones SQL válidas
- Ejecuta la consulta con las condiciones generadas
- Cuenta el total de registros antes y después de aplicar filtros
- Recupera solo los registros para la página solicitada
- Envuelve todo en la estructura de respuesta de colección
Este proceso asegura que las consultas sean eficientes, aplicando los filtros directamente en la base de datos en lugar de en memoria.
7.6. Implementación en el frontend
Para implementar un sistema de filtrado en el frontend, puedes seguir estas pautas:
Filtrado básico:
// Ejemplo en JavaScript para un frontend
const filters = {
category: { "=": "electronics" },
price: { ">=": 100 }
};
const queryParams = new URLSearchParams({
page: 1,
itemsPerPage: 20,
filters: JSON.stringify(filters)
});
fetch(`/api/products?${queryParams.toString()}`)
.then(response => response.json())
.then(data => {
// Procesar la colección recibida
console.log(`Mostrando ${data.collection.length} de ${data.totalFilteredRows} productos filtrados`);
});
Formulario de búsqueda avanzada:
// Función para construir filtros basados en un formulario
function buildFilters(formData) {
const conditions = [];
if (formData.minPrice) {
conditions.push({ price: { ">=": parseFloat(formData.minPrice) } });
}
if (formData.maxPrice) {
conditions.push({ price: { "<=": parseFloat(formData.maxPrice) } });
}
if (formData.category) {
conditions.push({ category: { "=": formData.category } });
}
if (formData.inStock) {
conditions.push({ stock: { ">": 0 } });
}
// Si solo hay una condición, la devolvemos directamente
if (conditions.length === 1) {
return conditions[0];
}
// Si hay múltiples condiciones, las combinamos con $and
if (conditions.length > 1) {
return { "$and": conditions };
}
// Si no hay condiciones, retornamos un objeto vacío
return {};
}
// Uso:
const formData = {
minPrice: 100,
maxPrice: 500,
category: "electronics",
inStock: true
};
const filters = buildFilters(formData);
Campo de búsqueda simple:
// Ejemplo de implementación de simpleSearch
const searchTerm = "smartphone";
const searchFields = ["name", "description", "model"];
const simpleSearch = {
search: searchTerm,
fields: searchFields
};
const queryParams = new URLSearchParams({
simpleSearch: JSON.stringify(simpleSearch),
page: 1,
itemsPerPage: 20
});
fetch(`/api/products?${queryParams.toString()}`)
.then(response => response.json())
.then(data => {
// Procesar los resultados
});
7.7. Ordenamiento de resultados
Además del filtrado y la paginación, el sistema de colecciones permite ordenar los resultados.
7.7.1. Parámetros de ordenamiento
Para ordenar los resultados, se utilizan dos parámetros:
| Parámetro | Tipo | Descripción |
|---|---|---|
sortBy | array JSON | Campos por los que ordenar |
sortDesc | array JSON | Indica si cada campo se ordena de manera descendente (true) o ascendente (false) |
7.7.2. Ejemplos de ordenamiento
Ordenar por un solo campo:
GET /products?sortBy=["price"]&sortDesc=[false]
Ordena los productos por precio, de menor a mayor.
Ordenar por múltiples campos:
GET /products?sortBy=["category","price"]&sortDesc=[false,true]
Ordena los productos primero por categoría (ascendente) y luego por precio (descendente).
7.7.3. Combinando ordenamiento, filtrado y paginación
GET /products?page=2&itemsPerPage=15&filters={"category":{"=":"electronics"}}&sortBy=["price"]&sortDesc=[true]
Esta petición completa:
- Filtra productos de la categoría "electronics"
- Ordena por precio de mayor a menor
- Muestra la segunda página con 15 elementos por página
7.8. Consejos y mejores prácticas
7.8.1. Rendimiento
- Utiliza
itemsPerPageadecuadamente. Valores muy altos pueden afectar el rendimiento. - Crea índices en la base de datos para los campos que se utilizan frecuentemente en filtros.
- Prefiere
permanentFilterspara condiciones que siempre se aplican.
7.8.2. Diseño de API
- Documenta claramente los campos por los que se puede filtrar.
- Proporciona ejemplos de filtros comunes para los usuarios de la API.
- Considera implementar endpoints específicos para búsquedas muy complejas.
7.8.3. Seguridad
- Valida y sanea todos los parámetros de filtrado.
- Limita la complejidad máxima de los filtros si es necesario.
- Implementa rate limiting para evitar abusos.
7.8.4. UX para desarrolladores
- Proporciona mensajes de error claros cuando los filtros son inválidos.
- Implementa un endpoint de documentación que muestre los campos filtrables.
- Considera implementar un constructor visual de filtros para los desarrolladores.
7.9. Resumen
El sistema de colecciones y filtros del framework proporciona una solución potente y flexible para trabajar con conjuntos de datos:
- Paginación para gestionar grandes volúmenes de datos.
- Filtrado con una sintaxis JSON flexible y operadores potentes.
- SimpleSearch para implementar búsquedas rápidas.
- PermanentFilters para aplicar condiciones consistentes.
- Ordenamiento para controlar la presentación de los resultados.
Con estas herramientas, puedes implementar APIs eficientes y expresivas que satisfagan diversas necesidades de consumo de datos.