API Criteria para expresiones regulares con PostgreSql
En este artículo vamos a ver como a través del API Criteria de Spring Data vamos a poder, para expresiones regulares, hacer una búsqueda en PostgreSql.
Vamos analizar dos casos concretos, por un lado teniendo un tipo rango de postgreSql, como podemos obtener todos aquellos que sean menor que número mayor; y por otro lado como a partir de una expresión, obtener todos los resultados que hace match con esa expresión.
Para los ejemplos siguientes vamos a usar la interfaz Specification, tal y como ya vimos en un artículo anterior.
Expresión regular para PostgreSql en un tipo Int4Range
Este tipo de dato se representa como un rango de enteros, por ejemplo [7, 12], y para hacer uso de ese tipo en nuestra aplicación Spring Boot, vamos a utilizar la librería:
<dependency> <groupId>com.vladmihalcea</groupId> <artifactId>hibernate-types-52</artifactId> <version>${hibernate-types-52.version}</version> </dependency>
Esta librería nos va a permitir utilizar rangos como si estuviéramos trabando con los tipos de PostgreSql.
Ejemplo de API Criteria de una expresión regular para PostgreSql con tipo rango.
Tenemos el siguiente objeto de base de datos:
@Getter @Setter @NoArgsConstructor @Table(name = "car") @Entity @EqualsAndHashCode(of = {"id"}, callSuper = false) public class CarDbo{ @Id private Long id; @NotNull @Column private String description; @Column private String brand; @Column private Range<Integer> kilometresRange; }
Como se puede observar hemos creado un campo Range de tipo integer, es decir, ese campo nos va a devolver un rango de kilometros. Para nuestro caso [0,10000].
Queremos obtener todos aquellos coches en donde los kilómetros es inferior a 10000, por lo que vamos a crear la siguiente clase haciendo uso del API de Criteria:
@Component public class CarSpecification implements Specification<CarDbo> { private static Long MAX_KM = 10000L; private static String REG_EXP_EXTRACT_MAX_OF_RANGE = "(\\d+)(?!.*\\d)"; private static Specification<CarDbo> findCarLessThanTenThousand() { return (Specification<CarDbo>) (carRoot, query, cb) -> { List<Predicate> predicates = new ArrayList<>(); Expression<String> range = carRoot.get(CarDbo_.kilometresRange) .as(String.class); Expression<Long> valueMaxRange = cb .function("substring", String.class, range, cb.literal(REG_EXP_EXTRACT_MAX_OF_RANGE)).as(Long.class); predicates.add(cb.lessThan(valueMaxRange, MAX_KM)); return cb.and(predicates.toArray(Predicate[]::new)); }
Con este ejemplo lo que hemos hecho ha sido convertir en String el tipo rango y pasarle una expresión regular, para poder obtener todos aquellos que son menores que el valor que pasamos por parámetro.
Hay que tener en cuenta, que hemos creado una función que hemos llamado «substring» porque es una función propia de PostreSql.
Ejemplo de API Criteria de una expresión regular para PostgreSql en un tipo String.
Partiendo de la clase CarDbo anterior, vamos a querer buscar en el campo descripción, todos aquellos coches que contengan la palabra Ford. Como el campo descripción puede tener escrito de todo, lo mejor sería hacer una expresión regular en la que encuentre esa palabra. Para poder buscar en todo el texto la palabra Ford, creamos la siguiente expresión regular: ‘.*(Ford)+.*’.
@Component public class CarSpecification implements Specification<CarDbo> { private static String REG_EXP_EXTRACT_WORD = ".*(_)+.*"; private static Specification<CarDbo> findCarsByNameInDescription(String name) { return (Specification<CarDbo>) (carRoot, query, cb) -> { List<Predicate> predicates = new ArrayList<>(); Expression<String> nameExpression = carRoot.get(CarDbo_.description) .as(String.class); Expression<String> findWordExpression = cb .function("substring", String.class, nameExpression, cb.literal(name)) .as(String.class); predicates.add( cb.equal(findWordExpression, REG_EXP_EXTRACT_WORD.replace("_",name))); return cb.and(predicates.toArray(Predicate[]::new)); }
En la clase anterior lo que hemos hecho ha sido crear una expresión a partir de un criteria builder, en donde creamos una función llamada «substring» (para que postgreSql la reconozca), en la que le decimos en que campo vamos a buscar; para después meter en la expresión regular que hemos definido el parámetro «name», que hemos recibido en la función «findCarsByNameInDescription». Es decir, a la expresión regular le sustituimos «_» por el nombre, así nos quedaría «.(Ford)+.«.
Si esto lo traducimos a una sentencia sql quedaría así:
select car.name from car car where substring(car_.nombre, '.*(Ford)+.*') = 'Ford'
Conclusión
En este artículo API Criteria para expresiones regulares con PostgreSql, hemos visto como podemos obtener resultados en PostgreSql a partir de una expresión regular.