Omet navegació

4.2.2.4 - Expressions FLWOR

Les expressions FLWOR són la part més potent d’XQuery. Estan formades per cinc instruccions opcionals que permeten fer les consultes i alhora processar-les per seleccionar els ítems que interessen d’una seqüència.

           ClàusulaÚs
f for Permet recórrer una seqüència, agafant cada vegada un element d'aquesta
l let Serveix per assignar valors a una variable
w where Permet filtrar els resultats segons condicions
o order by Ordena els resultats abans de mostrar-los
r return Retorna el resultat de tota l’expressió

"FOR ... RETURN"

La instrucció for es fa servir per avaluar individualment cadascun dels resultats d’una seqüència de nodes. En un for es defineixen dues coses:

  • Una variable, que serà la que anirà agafant els elements de la seqüència un per un.
  • La paraula clau in, en la qual es defineix quina és la seqüència d’elements per processar.

La manera més senzilla d’expressió FLWOR és combinar el for amb el return per retornar els valors obtinguts de manera seqüencial: 

for $i in ('Hola' , 'Adéu' )
return $i

generarà:

Hola
Adéu

Les seqüències de valors poden ser de qualsevol tipus. Per exemple, poden ser números. Així, el següent exemple:

for $i in (1,2,3)
return $i * $i

generarà:

1
4
9

O bé una seqüència de nodes, per mig d'una expressió XPath. Per exemple amb l’expressió següent capturem una seqüència de nodes i els fem servir per obtenir-ne el cognom:

for $alumne in //alumne
return <alumne> { $alumne/cognoms } </alumne>

generarà:

<alumne>
   <cognoms>Alegre</cognoms>
</alumne>
<alumne>
   <cognoms>Balaguer</cognoms>
</alumne>
<alumne>
   <cognoms>Centelles</cognoms>
</alumne>

Es pot veure que els valors del return s’han anat processant un per un i per aquest motiu es repeteixen les etiquetes <alumne>.

Un aspecte important és que no cal que el for siga la primera expressió de la consulta. També es pot posar com a paràmetre en una funció XPath.

Per exemple, l’expressió següent ens tornarà el nombre d’alumnes: 

count (
for $alumne in //alumne
return $alumne
)

3

En el for es pot incloure l’operador at, que permet obtenir la posició del node que s’està processant. Amb aquest operador podem fer que aparega el número d’ordre en els resultats.

for $alumne at $pos in //alumne 
return <alumne numero="{$pos}">
{ $alumne/cognoms/text() }
</alumne>

que donarà

<alumne numero="1">Alegre</alumne>
<alumne numero="2">Balaguer</alumne>
<alumne numero="3">Centelles</alumne>

 

"FOR NIUAT" ("anidat")

Les ordres for es poden posar unes dins d'unes altres, d’una manera similar a com ho fa SQL amb les subconsultes. Així es poden aconseguir resultats més complexos que es basen en els resultats obtinguts anteriorment.

for $selec in //alumne 
return 
    <persona> 
        { $selec/nom } 
        { 
            for $selec2 in $selec 
            return $selec2//cognom 
        } 
    </persona>

<persona>
    <nom>Albert</nom>
</persona>
<persona>
    <nom>Bernat</nom>
</persona>
<persona>
    <nom>Joan</nom>
</persona> 

Aquesta sentència anterior queda un poc pobra, ja que en el document hi ha molt poques dades.

Un exemple un poc més complet (i comprensiu) seria el següent:

for $selec in //ruta
return
    <ruta>
        <nomRuta>
        { $selec/nom/text() }
        </nomRuta>
        <punts>
        {
            for $selec2 in $selec//punt
            return <punt> {$selec2/nom/text()} </punt>
        }
        </punts>
    </ruta>

que donaria el sagüent resultat:

<ruta>
<nomRuta>Pujada a Penyagolosa</nomRuta>
<punts>
<punt>Sant Joan</punt>
<punt>Encreuament</punt>
<punt>Barranc de la Pegunta</punt>
<punt>El Corralico</punt>
<punt>Penyagolosa</punt>
</punts>
</ruta>
<ruta>
<nomRuta>La Magdalena</nomRuta>
<punts>
<punt>Primer Molí</punt>
<punt>Segon Molí</punt>
<punt>Caminàs</punt>
<punt>Riu Sec</punt>
<punt>Sant Roc</punt>
<punt>Explanada</punt>
<punt>La Magdalena</punt>
</punts>
</ruta>

...

 

LET

Ens permet declarar variables i assignar-los un valor. Sobretot es fa servir per guardar valors que s’han de fer servir més tard.

let $num := 1
let $nom := "Pere"
let $i := (1 to 3)

En les expressions FLWOR hi pot haver tants let com calga.

for $alumne in doc("db/Tema9/classe.xml")//alumne
let $nom := $alumne/nom
let $nota := $alumne/nota
return <nom>{ concat($nom,": ",$nota) }</nom>

<nom>Albert: 6</nom>
<nom>Bernat: 3</nom>
<nom>Joan: 8</nom>

S’ha d’anar amb compte amb let perquè el seu funcionament és molt diferent del de for:

  • for s’executa per a cada membre d’una seqüència.
  • let fa referència al seu valor en conjunt. Si és una seqüència, el que es processa són tots els valors de cop.

Podem veure la diferència amb un exemple. Aquesta instrucció amb for:

for $selec in //alumne
return <persona>{$selec/nom}</persona>

Dóna:

persona>
    <nom>Albert</nom>
</persona>
<persona>
    <nom>Bernat</nom>
</persona>
<persona>
    <nom>Joan</nom>
</persona>

I en canvi amb let:

let $selec := //alumne
return <persona>{$selec/nom}</persona>

Donarà:

<persona>
    <nom>Albert</nom>
    <nom>Bernat</nom>
    <nom>Joan</nom>
</persona>

Els resultats són bastant diferents! Es pot veure clarament que let ha tractat tots els valors de cop.

 

WHERE

La instrucció where és la part que indicarà quin filtre s’ha de posar a les dades rebudes abans d’enviar-les a la sortida del return. Normalment en el filtre es fa servir algun tipus de predicat XPath:

for $alumne in //alumne
where $alumne/@aprovat="si"
return $alumne/nom

<nom>Albert</nom>
<nom>Joan</nom>

Com que XPath forma part d’XQuery moltes vegades el mateix filtre es pot definir de diverses maneres. Per exemple, aquesta expressió és equivalent a l’anterior:

for $alumne in //alumne[@aprovat="si"]
return $alumne/nom

Això sí, en un where hi pot haver tantes condicions com faça falta simplement encadenant-les amb les operacions lògiques and, or o not().

Aquesta expressió retorna el cognom dels alumnes que han aprovat i que es diuen Joan:

for $alumne in //alumne
where $alumne/@aprovat="si" and $alumne/nom="Joan"
return $alumne/cognoms

<cognoms>Centelles</cognoms>

El where afegeix més potència del que sembla, ja que ens permet fer els inner joins d’SQL en fitxers XML. Per exemple, a partir de dos fitxers amb les dades de dues classes diferents es poden obtenir només els alumnes que es repeteixen entre les dues classes amb:

for $alum1 in doc("db/Tema9/classe.xml")//alumne
let $alum2 in doc("db/Tema9/classe2.xml")//alumne
where $alum1 = $alum2
return $alum1

Atenció

L'anterior només és un exemple que no funcionarà, ja que no disposem del document classe2.xml 

ORDER BY

Una de les operacions habituals en les cerques és representar els resultats en un ordre determinat. Aquesta és la funció que fa order by.

Es pot ordenar de manera ascendent amb ascending (que és el que fa per defecte) o bé descendent amb descending.

for $alumne in //alumne
order by $alumne/cognoms descending
return $alumne

<alumne delegat="si" aprovat="si">
    <nom>Joan</nom>
    <cognoms>Centelles</cognoms>
    <nota>8</nota>
</alumne>
<alumne aprovat="no">
    <nom>Bernat</nom>
    <cognoms>Balaguer</cognoms>
    <nota>3</nota>
</alumne>
<alumne aprovat="si">
    <nom>Albert</nom>
    <cognoms>Alegre</cognoms>
    <nota>6</nota>
</alumne>

També es poden especificar diferents conceptes en l’ordenació. En aquest exemple els resultats s’ordenaran primer per cognom i després per nom:

for $alumne in //alumne
order by $alumne/cognoms, $alumne/nom
return $alumne

En aquest altre el cognom s’ordena de manera descendent i el nom de manera ascendent.

for $alumne in //alumne
order by $alumne/cognoms descending, $alumne/nom ascending
return $alumne

Per defecte ordena com si el contingut de les etiquetes fóra text. Si hem d'obtenir una ordenació numèrica, caldrà convertir els valors a números amb la funció d’XPath number().

for $ruta in doc("db/Tema9/Rutes.xml")//ruta
order by number($ruta/desnivellAcumulat)
return concat($ruta/nom, " --> ", $ruta/desnivellAcumulat)

La Magdalena --> 84
Pujada a Penyagolosa --> 530
Catí - Sant Pere de Castellfort --> 1286
Pelegrins de Les Useres --> 1738