XQuery (XML Query Language) és un llenguatge de consultes normalment utilitzat per a consultar bases de dades XML. En el nostre cas substituirem la base de dades XML per a documents XML.
Utilitzant XQuery no tan sols podem consultar la informació, sinó que tenim també la possibilitat de transformar-la. XPath és un subconjunt de XQuery i, per tant, tota la funcionalitat aplicable a XPath és també aplicable a XQuery. Una de les parts importants de XQuery són les expressions FLOWR que ens permeten fer consultes a l'estil SQL.
Per a poder executar les consultes XQuery necessitem un intèrpret del llenguatge. Utilitzarem la comanda xquilla per a executar les consultes. Hem de tenir en compte que aquesta comanda no s'instal·la per defecte i, per tant, el primer que haurem de fer és instal·lar-la en un sistema Linux. Utilitzem les instruccions:
apt update
apt install xqilla -y
Per a mostrar el funcionament de XQUERY utilitzarem el següent document XML
Nom Fitxer: univers.xml
<?xml version="1.0" ?>
<planets>
<planet id="3">
<name>A Place</name>
<value>3000</value>
<xcood>-125.18</xcood>
<ycood>274.88</ycood>
<faction>LA</faction>
<factionChange id="8">
<date>3040-01-01</date>
<faction>FC</faction>
</factionChange>
<factionChange id="12">
<date>3057-12-01</date>
<faction>ARDC</faction>
</factionChange>
<factionChange id="16">
<date>3067-04-24</date>
<faction>LA</faction>
</factionChange>
</planet>
<planet id="21">
<name>Abadan</name>
<value>3200</value>
<xcood>-70.94</xcood>
<ycood>-96.76</ycood>
<faction>FWL</faction>
</planet>
<planet id="27">
<name>Abagnar</name>
<value>2800</value>
<xcood>383.64</xcood>
<ycood>317.65</ycood>
<faction>DC</faction>
</planet>
<planet id="33">
<name>Abbadiyah</name>
<value>4300</value>
<xcood>-320.78</xcood>
<ycood>248.8</ycood>
<faction>LA</faction>
<factionChange id="38">
<date>3040-01-01</date>
<faction>FC</faction>
</factionChange>
<factionChange id="42">
<date>3057-09-18</date>
<faction>LA</faction>
</factionChange>
</planet>
<planet id="47">
<name>Abbeville</name>
<value>3300</value>
<xcood>611.32</xcood>
<ycood>-40.16</ycood>
<faction>FS</faction>
<factionChange id="52">
<date>3040-01-01</date>
<faction>FC</faction>
</factionChange>
<factionChange id="56">
<date>3067-01-01</date>
<faction>FS</faction>
</factionChange>
</planet>
<planet id="61">
<name>Abejorral</name>
<value>3900</value>
<xcood>-302.01</xcood>
<ycood>128.57</ycood>
<faction>LA</faction>
<factionChange id="66">
<date>3040-01-01</date>
<faction>FC</faction>
</factionChange>
<factionChange id="70">
<date>3057-09-18</date>
<faction>LA</faction>
</factionChange>
</planet>
<planet id="75">
<name>Aberystwyth</name>
<value>2500</value>
<xcood>-434.75</xcood>
<ycood>59.2</ycood>
<faction>LA</faction>
<factionChange id="80">
<date>3040-01-01</date>
<faction>FC</faction>
</factionChange>
<factionChange id="84">
<date>3057-09-18</date>
<faction>LA</faction>
</factionChange>
</planet>
<planet id="89">
<name>Abiy Adi</name>
<value>4500</value>
<xcood>352.6</xcood>
<ycood>317.39</ycood>
<faction>DC</faction>
<spectralClass>G</spectralClass>
<subtype>2</subtype>
<luminosity>V</luminosity>
<sysPos>3</sysPos>
<gravity>1.2</gravity>
<pressure>3</pressure>
<temperature>45</temperature>
<climate>4</climate>
<percentWater>18</percentWater>
<hpg>b</hpg>
<lifeform>0</lifeform>
<satellite>Amarigna</satellite>
<socioIndustrial>C-B-C-C-C</socioIndustrial>
<landmass>Tsechi (Tigray)</landmass>
<landmass>Simien</landmass>
<landmass>Adwa</landmass>
<desc>Abiy Adi is a largely desert world in the Pesht Military District. The industy on the planet uses so much of the planet's indigenous water that the locals depend on imported water.</desc>
</planet>
<planet id="112">
<name>Abramkovo</name>
<value>3400</value>
<xcood>-333.82</xcood>
<ycood>-28.95</ycood>
<faction>LA</faction>
<factionChange id="117">
<date>3040-01-01</date>
<faction>FC</faction>
</factionChange>
<factionChange id="121">
<date>3057-09-18</date>
<faction>LA</faction>
</factionChange>
</planet>
</planets>
Estructura FLOWR
L'expressió FLOWR respon als termes: FOR , LET (assignació), ORDER , WHERE i RESULT.
FOR $var1 in Expressió1
LET $var2 := Expressió2
WHERE Condition
ORDER BY Expressió3
RETURN Expressió4
Per veure com funciona aquest llenguatge utilitzarem diferents exemples il·lustratius.
Consideracions vàries
- Totes les variables comencen pel símbol $ .
- Per assignar un valor a una variable utilitzem el símbol := . El símbol = s'utilitza per comparar.
- Per a obrir un comentari utilitzem (:
- Per a tancar un comentari utilitzem :)
- Tots els comentaris són multilínia
Exemples introductoris
For i where
En aquest primer exemple es desmostra com XQuery és utilitzat per a realitzar consultes a dades XML. Observem com amb la instrucció FOR hem iterat pels diferents nodes de l'Expressió1 , en aquest cas els diferents planetes del fitxer univers.xml. Amb la sentència WHERE hem filtrat els nodes segons el valor del node value.
Per a llegir el document XML podem utilitzar una ruta relativa o absoluta. Cal tenir en compte que si el document XML està en el mateix directori que el fitxer de XQuery, haurem d'utilitzar "./" per a referenciar el fitxer XML.
Per a incloure comentaris en les consultes de XQuery ho farem amb un somriure. "(:" per a obrir el comentari i ":)" per a tancar el comentari. Dins del comentari podem insertar salts de línia si és necessari.
La variable $p , a cada iteració, selecciona un node planet diferent.
Transformació
Utilitzant XQuery podem transformar un document XML. En aquest cas, el que farem és traduir el nom dels nodes per a generar un nou document XML que tingui els nodes en català.
En la consulta observem la utilització de la funció data per eliminar el node associada a l'element que ha de retornar la nostra consulta. També cal tenir en compte l'utilitació de les claus "{ }" per tal que l'intèrpret executi el codi. Si no s'inclou el bloc de la instrucció for entre les claus, l'intèrpret de XQuery entendria com a text tota l'expressió (verificar amb imatge)
Exercici transformació
1.- Es vol obtenir una taula HTML amb el nom del planeta, i les seves coordenades x i y. Cada un dels valors s'ha de mostrar en una columna de la taula
Order
Utilitzem ORDER BY per a ordenar segons un determinat criteri. L'ordre d'ordenació, per defecte, és ascendent. Utilitzem descending per a indicar un ordre descendent.
For anidats
Utilitzem FOR per a iterar sobre els ítems d'un conjunt resultant de l'execució de l'expressió associada a la instrucció. La variable del for queda associada a cada un dels elements. Podem pensar que cada un dels elements és una dupla.
Tot i que podem simplificar aquesta expressió utilitzant múltiples variables en una única sentència for
Una sentència més complexe utilitzant for anidats seria la d'obtenir els planetes que el seu valor és superior a la mitja de valors de tots els planetes.
Tot i que podem simplificar molt aquesta estructura amb la instrucció LET.
Let "binding"
La comanda LET ens permet assignar un valor a una variable o bé enllaçar "binding" una variable al conjunt de resultats associats a la instrucció for. Com que el funcionament és un xic complex d'entendre anem a veure un exemple.
Si observem el codi de la imatge anterior i el resultat de la consulta, observem com aquest no coincideix amb el que en un principi havíem pensat. L'assignació de la variable dins la sentència for només es fa una vegada!
Observem el comportament de la comanda LET utilitzant un altre exemple.
En aquest segon exemple, el comportament de la comanda LET és una mica diferent. Enlloc d'assignar un valor, és una referència a la variable $p. Quan canvia el valor de la variable $p també canvia el valor de la variable $v de la instrucció LET.
Contabilitzar els nodes
Podem utilitzar la funció count per a obtenir el número de node de cada element. Així, per exemple, la següent consulta, ens enumera tots els noms de planetes.
Posició del node
for $p at $count in doc("/home/miquel/univers.xml")/planets/planet
where $p/@id=47
return $count
Aquesta consulta ens retorna el número 5 que és la posició del planeta amb identificador 47
for $p at $count in doc("/home/miquel/univers.xml")/planets/planet
where $p/@id=47
return $count
La següent consulta ens retorna el valor 1, ja que només hi ha un node planets, que és l'escollit al complir-se la condició.
for $p at $count in doc("/home/miquel/univers.xml")/planets
where $p/planet/@id=47
return $count
Exercicis
2.- Obtenir el nom de tots els planetes ordenats per les coordenades x i y. En primer lloc cal mostrar els planetes que tenen la coordenada x més petita. Si dos planetes tenen la mateixa coordenada x, caldrà ordenar per la coordenada y de més gran a més petita.
2.a.- Retorna la temperatura mínima de tots els planetes. . Pots utilitzar la funció min 2.b.- Retorna el nom del planeta que tingui la temperatura mínima
3.- Obtenir l'identificador de tots els planetes que el seu valor sigui superior al valor del primer planeta del fitxer.
3.a.- Retorna la informació de tots els planetes que tenen una temperatura superior a 10 3.b.- Retorna el nom de tots els planetes que tenen una temperatura superior a 10
4.- Retornar totes les combinacions possibles de tots els noms de planetes. El nom del primer planeta combinat amb el nom de tots els altres planetes, el nom del segon planeta combinat amb el nom de tots els altres planetes...
Mostra d'exemple de sortida
4.a.- Combina el nom del primer planeta amb el nom de tots els altres planetes, excepte el primer.
5.- Retornar totes les combinacions possibles de tots els noms de planetes, tenint en compte que cal eliminar aquells casos que els dos planetes tinguin el mateix nom. El format de la sortida és el mateix que el de l'exercici anterior
6.- Retornar totes les combinacions possibles de tots els noms de planetes, eliminant les repeticions. Es considera una repetició quan els dos noms de planeta coincideixen. Aixi, per exemple, si X és un nom de planeta i Y és un altre nom de planeta, (X,Y) és una repetició de (Y,X) . Evidentement (X,X) també és una repetició. El format de la sortida és el mateix que el de l'exercici anterior
7.- Retornar les parelles d'identificadors de planetes que el seu valor sigui inferior a 200. Per exemple, el planeta 47 té un valor de 3300 i el planeta 112 té un valor de 3400. Com que la diferència de valor entre els 2 planetes és inferior a 200, la consulta hauria de retornar els identificadors dels dos planetes.
8.- Retornar la distància que hi ha entre cada un dels planetes. Per a calcular la distancia utilitzeu la funció dist que s'adjunta a continuació
declare function local:dist($coodx1 as xs:decimal?,$coodx2 as xs:decimal?,$coody1 as xs:decimal?,$coody2 as xs:decimal)
as xs:decimal?
{
(: Per a calcular la distancia és necessari fer l'arrel quadrada de $r. Com més gran és el valor més lluny estan dos planetes :)
let $x := ($coodx1 - $coodx2)
let $y := ($coody1 - $coody2)
let $r := ( $x * $x + $y * $y )
return $r
};
Exemple de crida a la funció dist
La sortida que es vol és la de la imatge.
9.- Retorna el valor de la coordenada x que tingui el valor més petit. Pots utilitzar la funció min
10.- Quina és la distància més petita que hi ha entre 2 planetes?
11.- Quin és l'identificador dels dos planetes que estan més aprop?
12.- Retorna el número de node associat al planeta amb el node value mínim. Així, per exemple, si el que té el node value més baix és el tercer, la consulta ens ha de retornar el número 3.
Per a realitzar els següents exercicis és necessari un segon fitxer XML: factions.xml
<?xml version="1.0" encoding="UTF-8"?>
13.- Retorna el nom complet de cada faction, segons el faction associat a cada planeta. Cal enllaçar la informació de shortname del fitxer factions.xml amb el faction (principal) del fitxer univers.xml. La sortida del fitxer és:
- Retorna totes les etiquetes full_name per a tots els nodes faction associats al planeta Abbadiyah. El resultat correcte amb les dades ha de ser una única dada
Lyran Commonwealth -> LA