Consultas en XQuery

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)}"/>

Escribir un comentario