Una consulta en XQuery es una expresión que lee los datos de un fichero XML y nos devuelve los resultados en forma de secuencia de datos en XML. Las consultas en XQuery pueden estar compuestas por 5 cláusulas diferentes conocidas con el nombre FLWOR (pronunciado comúnmente como FLOWER) que son For,Let, Where, Order by y Return, además también se le puede añadir Group by. (ninguna de ellas son obligatorias y hay otra forma de ejecutar consultas sin utilizar FLWOR)
Tipo de cláusulas FLWOR
- For: selecciona una secuencia de nodos
- Let: vincula una secuencia a una variable
- Where: filtra los nodos
- Order by: ordena los nodos
- Return: qué tiene que devolver (se evalúa una vez por cada nodo)
Fichero de ejemplo
Los siguientes ejemplos los ejecutaremos con la herramienta gratuita BaseX y utilizaremos el fichero XML de ejemplo de artículos anteriores.
Cláusula For / Return
A utilizar la cláusula For estamos recuperando una serie de elementos mediante una consulta xPath que estamos introduciendo en una variable para poder devolver después con la cláusula Return. Destacar que la cláusula Return se ejecutará tantas veces como elementos devuelve el For.
Sintaxis básica
En este ejemplo puedes comprobar cómo nos devuelve el nodo título con sus etiquetas al buscar dentro de la biblioteca y de cada libro:
for $libro in /biblioteca/libro return $libro/titulo
Modificar etiqueta del elemento
Imagina que por cualquier motivo no te gusta la etiqueta título y quieres que te lo devuelva con la etiqueta Title. Para conseguirlo debes cambiar la cláusula return y cerrar entre llaves la nueva etiqueta. Por ejemplo:
for $libro in /biblioteca/libro return <title>{$libro/titulo/text()}</title>
Numerar elementos
Además, también es posible numerar cada elemento. Mira este ejemplo:
for $libro at $n in /biblioteca/libro return <title>{$n}.- {$libro/titulo/text()}</title>
Englobar etiquetas
Además, también es posible numerar cada elemento. Mira este ejemplo:
<Libros> { for $libro at $n in /biblioteca/libro return <title>{$n}.- {$libro/titulo/text()}</title> } </Libros>
Condicional if
Podemos utilizar el condicional if dentro de la cláusula return para alterar el resultado. Fijate en este ejemplo:
for $libro at $n in /biblioteca/libro return <libro>{if ($libro/fecha = 2023) then (<nuevo/>)} <title>{$n}.- {$libro/titulo/text()}</title> </libro>
Especificar ruta del fichero xml
Importante destacar que en la cláusula for como no le hemos indicado que fichero XML debe abrir, esa utilizando el que esta abierto, pero podríamos indicarle otro explicitamente.
for $libro in doc("C:\tmp\XQuery\MiguelTroyano.xml")/biblioteca/libro return $libro/titulo
Cláusula Let
Utilizamos la cláusula Let para crear variables y almacenar en ellas contenido. Es importante saber que esta clausula solo se ejecuta una vez junto al return a diferencia de la cláusula for que se ejecuta tantas veces como nodos encuentra. Let asigna las variables escribiendo :=. En el siguiente ejemplo puedes comprobar que la etiqueta Title solo nos la muestra una única vez, y si hiciésemos lo mismo con for nos la mostraría varias veces como hemos visto en ejemplos anteriores.
Sintaxis básica
let $libros := /biblioteca/libro return <Title>{$libros/titulo}</Title>
Funciones de agregación
Podemos utilizar let con funciones de agregación para sumar (sum), contar (count), media (avg), máximo (max) y minimo (min). Las funciones son iguales que en Xpath. Por ejemplo, vamos a obtener el año del primer libro que hay en el fichero xml:
let $libros := /biblioteca/libro return <año>El primer libro se publicó en {max($libros/fecha)}</año>
Otra opción es utilizar más de una función. En este caso observa cómo obtenemos la primera fecha y la última.
let $Primero := min(/biblioteca/libro/fecha), $Ultimo := max(/biblioteca/libro/fecha) return <libros> <Primero>{$Primero}</Primero> <Ultimo>{$Ultimo}</Ultimo> </libros>
Asignar el valor de otra consulta
Otro ejemplo un poco más complejo es que el valor de la variable provenga de otra consulta. En este ejemplo se ejecutará una consulta para leer todos los títulos de los libros y el resultado se almacenará en la variable y después mostraremos el resultado de la variable:
let $titulos := ( for $libro in /biblioteca/libro return $libro/titulo) return $titulos
Combinar cláusulas For y Let
Hasta ahora hemos visto las cláusulas por separado, pero también es posible utilizarlas juntas ¿para qué? para conseguir que let se ejecute tantas veces como nodos encuentre. En este ejemplo vamos a contar cuántos autores tiene cada libro:
for $libro in /biblioteca/libro let $autor := count($libro/autor) return <libro> <titulo>{$libro/titulo/text()}</titulo> <autor>{$autor}</autor> </libro>
Cláusula Where
Con la cláusula where conseguimos filtrar los nodos que se obtienen en la cláusula for. Es importante destacar que where no filtra si utilizamos la cláusula let.
Sintaxis básica
En este ejemplo mostraremos solo los libros cuya autora sea Sonsoles Onega:
for $libro in /biblioteca/libro where $libro/autor = "Sonsoles Onega" return $libro/titulo
Utilizando funciones
En la cláusula where también puedes usar funciones (las mismas que XPath). Imagina que quieres los libros que su título empiecen por M ¿como lo harías? Mira este ejemplo:
for $libro in /biblioteca/libro where starts-with($libro/titulo,"M") return $libro/titulo
También es posible combinar varias funciones. En este ejemplo obtenemos los títulos que comiencen en M y acaben en s.
for $libro in /biblioteca/libro where starts-with($libro/titulo,"M") or ends-with($libro/titulo,"s") return $libro/titulo
Cláusula Order by
Utilizamos esta cláusula para ordenar los resultados mostrarlos en un orden específico. La clausula order by se ejecuta antes que el return ya que este último provoca la salida en pantalla.
Sintaxis básica
En este ejemplo vamos a mostrar los títulos de los libros de forma ordenada.
for $libro in /biblioteca/libro order by $libro/titulo return $libro/titulo
También podrías ordenar por varios campos e incluso de forma descendente:
for $libro in /biblioteca/libro order by $libro/titulo, $libro/precio descending return $libro/titulo
Cláusula Group by
Con esta cláusula lo que hacemos es crear agrupaciones en base al contenido del nodo.
Sintaxis básica
En este ejemplo contamos cuantos libros hay por cada año de publicación:
for $libro in /biblioteca/libro group by $fecha := $libro/fecha return <group Año="{$fecha}" Libros="{count($libro)}"/>