Exploramos el método find() de TypeORM con el que puedes realizar todo tipo de consultas sencillas y complejas con el ORM sin tener que meter las manos en el SQL.
Estamos en el Manual de Nest, pero este artículo lo cierto es que está más relacionado con el ORM TypeORM que con el propio framework. Como hemos dicho en otras ocasiones, NestJS no está casado con un ORM en particular, pero sí es muy habitual que se use TypeORM como primera opción. En este caso necesitarás hacer búsquedas más complejas, por lo que te vendrá bien conocer el método find() de los repositorios TypeORM.
El método find()
de TypeORM nos permite ajustar la búsqueda de los elementos de una entidad. Hasta ahora lo hemos explorado poco, realizando unas búsquedas muy elementales, pero con las opciones de configuración disponibles en el ORM es posible sacarle bastante jugo. Como verás, podemos hacer muchas cosas de las que haríamos con el lenguaje SQL, pero sin tener que escribir las consultas a mano.
En este artículo queremos explorar posibilidades de trabajo con el método find() para hacer búsquedas sencillas y complejas, para obtener datos de las relaciones definidas en las entidades y demás utilidades de consideración para la mayoría de las aplicaciones.
Objeto de configuración del método find de TypeORM
Todos los ejemplos que veremos usarán el mismo mecanismo para alimentar el método find()
, que consiste en pasarle un objeto de configuración.
this.productsRepository.find({
// Opciones de configuración para la búsqueda de objetos de la entidad
});
Para nuestros ejemplos trabajaremos con el método getAll()
en ProductsService
, que ya conocemos de artículos anteriores del Manual de Nest.
Ejemplos prácticos útiles para find() de TypeORM
Los siguientes puntos de este artículo están destinados a ofrecer una lista de diferentes prácticas habituales en el uso del método find()
para las consultas en TypeORM. Hemos seleccionado estos ejercicios por ser sencillos y frecuentes en el día a día con TypeORM, pero ya advertimos que find()
en el fondo tiene muchas otras utilidades muy relevantes también.
Seleccionar solo algunos campos de una entidad con select
Podemos acotar la cantidad de campos de los elementos de la entidad que necesitamos buscar.
Por ejemplo, imagina que solamente quieres recibir los nombres de los productos y su stock.
this.productsRepository.find({
select: ['name', 'stock']
});
Esto nos devolvería el objeto de productos de esta manera:
[
{
"name": "Silla",
"stock": 5
},
{
"name": "Caja madera",
"stock": 33
}
]
Obtener las relaciones en la búsqueda TypeORM
De manera predeterminada, cuando hacemos una consulta para que nos entregue objetos de una entidad no llegan justos los objetos de las entidades relacionadas, a no ser que nosotros los solicitemos. Si deseas disponer en una consulta los datos de altunas entidades relacionadas, es posible conseguirlos mediante la configuración "relations
".
Simplemente en la propiedad "relations
" del objeto de opciones que enviamos a find()
le indicaremos un array con las entidades relacionadas que queremos que se entreguen.
this.productsRepository.find({
relations: ['sizes', 'reviews']
});
Ahora los productos tendrán expandidas las relaciones indicadas:
{
"id": 2,
"name": "Silla",
"description": "Silla victoriana con tapizado",
"stock": 44,
"sizes": [
{
"id": 3,
"size": "XL"
}
],
"reviews": [
{
"id": 5,
"userName": "Sonia M",
"review": "Es un producto malo",
"valoration": 1
}
]
},
Es interesante también que podemos obtener también las relaciones de las relaciones, simplemente concatenando con el operador ".".
this.productsRepository.find({
relations: ['sizes', 'sizes.products']
});
De este modo obtendríamos los productos con sus tallas y, además, para cada talla conseguiríamos todos los productos que tienen esa talla.
Quizás este ejemplo no es el mejor, pero imagina que necesitas todos los productos que se han facturado a todos los clientes. Podrías solicitar las facturas de los clientes y directamente que te entregue los productos de cada factura.
relations: ['invoices', 'invoices.products']
Existe además una opción de configuración de find() que permite especificar los join de SQL directamente, para conseguir datos relacionados, pero de una manera más compleja. Consulta la documentación de las opciones de find para encontrar un ejemplo.
Obtener elementos por búsqueda exacta
Con el método find podemos usar la opción "where
" para enviarle un objeto de claves y valores, de modo que nos busque elementos que concuerden de manera exacta con los valores.
this.productsRepository.find({
where: {
name: 'Silla',
stock: 5
}
});
Esto nos encontraría todos los productos cuyo nombre es "Silla" y que además tienen el valor de stock igual a 5. Por tanto, el operador para este conjunto de condiciones es el AND.
Sería interesante que este método permitiera búsquedas de parecidos o con otros operadores distintos de la igualdad. Esto lo podemos conseguir con TypeORM pero con otros mecanismos más avanzados que veremos unos ejemplos más abajo.
Si lo deseamos, también es posible hacer búsquedas por valores idénticos sin usar where. Sería suficiente con escribir la propiedad en el objeto de opciones, siempre que la propiedad exista en la entidad, por supuesto.
this.productsRepository.find({
id: 8
});
Ahora veamos otro ejemplo combinando dos campos, donde se relacionarán de nuevo los valores por el operador AND.
this.productsRepository.find({
name: "silla",
description: "una silla..."
});
Si deseamos hacer una búsqueda usando el operador OR, en vez de AND podemos enviar como valor de la propiedad where un array con varios objetos de propiedades que deben de casar de manera exacta.
this.productsRepository.find({
where: [
{ name: "Silla" },
{ stock: 1 },
{ name: "caja", description: "Caja de madera" },
],
});
Este ejemplo encontraría todos los productos cuyo nombre es "Silla", o todos los productos que tienen stock de 1, o todos los productos que su nombre es "caja" y a la vez la descripción es "Caja de madera".
Ordenar los resultados
Podemos definir cómo deseamos ordenar los elementos de la búsqueda. Para ello simplemente ndicamos la propiedad "order
", en la que asignamos un objeto con los nombres de las propiedades y si es ascendente o descendente.
this.productsRepository.find({
order: {
stock: 'DESC',
name: 'ASC'
}
});
En este ejemplo tendríamos todos los productos, ordenados por el stock (colocando los que tienen más stock primero) y luego por orden alfabético atendiendo al campo "name".
Limitar el número de elementos de los resultados
Podemos hacer el típico "limit" de SQL con la opción "take
", indicando cuántos elementos queremos que nos retornen.
this.productsRepository.find({
take: 5,
});
Esto nos dará solamente 5 elementos. Aquí lo típico sería indicar un orden para que los 5 que te entreguen sean los que realmente quieres.
this.productsRepository.find({
take: 5,
order: {
name: 'ASC'
}
});
Aquí estaríamos obteniendo los 5 primeros productos, ordenados alfabéticamente.
Cómo hacer paginación con TypeORM usando skip
Más tarde o más temprano necesitarás paginar resultados en tus aplicaciones. Para poder hacer paginación de elementos necesitamos combinar la opción "take
" con la opción "skip
", que nos permite pasar una serie de elementos dada.
Imagina que quieres los resultados en páginas de 5 en 5 y que quieres que te entregue la tercera página de resultados. Entonces, para conseguir paginar y obtener la tercera página necesitas descartar los elementos que te mostraría en la primera y segunda páginas, es decir, 10 elementos.
this.productsRepository.find({
take: 5,
order: {
name: 'ASC'
},
skip: 10
});
Opciones avanzadas de consultas con find()
Todas las anteriores opciones que acabamos de ver las clasifican como básicas en la documentación de opciones de find. Ahora vamos a ver algunas de las opciones más útiles que se encuentran en la clasificación de "avanzadas".
Desde el punto de vista de SQL no son tan avanzadas que digamos, pero sí lo son desde el punto de vista del ORM porque requieren nuevas funciones que no hemos visto y que tendremos que importar de manera específica en las aplicaciones.
Hacer búsquedas por parecidos
Resulta fundamental hacer búsquedas usando el operador LIKE
de SQL, para encontrar elementos que respondan a parecidos y no búsquedas exactas como nos permitía la opción where
a secas.
this.productsRepository.find({
name: Like('%silla%'),
description: Like('%silla%')
});
Aquí conseguimos los productos que tengan "silla" en su nombre de producto y en la descripción, a la vez (operador AND).
Muy importante, todas estas funciones que denominan "avanzadas" se tienen que importar:
import { Like } from "typeorm";
Si lo que queremos es que se relacionen con OR, entonces necesitamos acudir a la opción where
e indicar las condiciones en un array.
this.productsRepository.find({
where: [
{ name: Like('%silla%') },
{ description: Like('%silla%') }
]
});
Esto nos entregará todos los elementos que tengan "silla" en su nombre o en su descripción.
Buscar los elementos que tengan un campo mayor que una cantidad
Ahora vamos a ver cómo conseguir los productos que tengan un campo mayor que alguna cosa que les entreguemos. Para ello hay una función llamada MoreThan
. Lo primero es importarla:
import { MoreThan } from "typeorm";
Luego la podemos usar en la consulta:
this.productsRepository.find({
stock: MoreThan(10),
});
Existen funciones muy similares, como por ejemplo MoreThanOrEqual
o LessThan
. El uso es muy similar a lo que acabamos de ver.
Buscar elementos que están entre una cantidad y otra
La cosa se puede complicar cuando necesitamos buscar un stock entre dos cantidades. Existe una función llamada Between
que viene al rescate.
Vamos a buscar elementos que tengan stock entre 3 y 8. Primero vamos a tener que importar la función Between
.
import { Between } from 'typeorm';
Ahora hacemos la consulta así.
this.productsRepository.find({
where: {
stock: Between(3,8)
}
});
Conclusión
Existen multitud de funciones avanzadas para hacer consultas, como IsNull
, In
, ILike
, Any
, etc. que agregan mucha potencia a las consultas con TypeORM. Lo ideal es tener a mano la documentación de TypeORM.
Por si acaso lo que necesitamos no existe, aún tenemos una función llamada "Raw
" que nos permite hacer búsquedas todavía más avanzadas, indicando nosotros un poco de SQL. Pero ojo en este caso porque tendremos que protegernos ante posibles inyecciones de SQL.
Miguel Angel Alvarez
Fundador de DesarrolloWeb.com y la plataforma de formación online EscuelaIT. Com...