XQuery

De MediaWiki
Ir a la navegación Ir a la búsqueda


Introdución

XQuery é unha linguaxe de consulta para coleccións de datos expresadas en XML. XQuery é a XML o mesmo que SQL é as bases de datos relacionais. E unha especificación do W3C, para a consulta e procesamento de bases de datos e documentos XML.

XQuery é unha linguaxe funcional: en vez de executar unha lista de comandos coma unha linguaxe procedimental clásica, cada consulta é unha expresión que é avaliada e devolve un resultado (do mesmo xeito que en SQL). As expresións poden combinarse de xeito flexible para crear novas expresions máis complexas e de maior potencia semántica.

Funciona recorrendo os nodos da árbore con XPath, xa que este está incluído dentro de XQuery.

OLLO!. XQuery é unha linguaxe case-sensitve (é sensible o uso de maiúsculas/minúsculas) e debe escribirse sempre en minúsculas.

XQuery permite:

  • Consultar información dun documento XML
  • Actualizar (inserir, eliminar ou modificar) información dun documento XML.

Funcións para acceder a información

Unha consulta en XQuery é unha expresión que lee unha secuencia de datos en XML e devolve como resultado outra secuencia de datos en XML.

doc(URI)

Por medio da función doc indicaremos cal é o documento do cal queremos extraer a información. Devolve o nodo raiz do documento que se pasa como parámetro e é o xeito máis empregado para recuperar datos de arquivos. A sintaxe é a seguinte:

collection()

Esta función devolve una secuencia de nodos referenciados por una URI, sin necesidade de que exista un nodo documento ou raíz.

collection("nomeColección")

Comentarios

Os comentarios en XQuery comenzan cunha paréntese, a continuación irán dous puntos e terminan cunha paréntese precedida de dous puntos.

(: exemplo de comentario en XQuery :)

Expresións XPath

Para obter unha información concreta dun documento XML teremos que indicar o documento do cal a queremos extraer, seguido pola expresión XPath que nos devolve esta información en concreto.

No noso exemplo:

  • Para obter información de todos os módulos do ciclo de ASIR:
 doc("/db/oferta/asir.xml")/ciclo/modulos/modulo
  • Para obter información dun módulo en concreto do cal sabemos o código:
 doc("/db/oferta/asir.xml")//modulo[@codigo="MP0373"]
  • Para amosar unha listaxe dos módulos dos que consta o ciclo:
 doc("/db/oferta/asir.xml")//modulo/nome/text()
  • Para obter os nomes dos módulos que teñen unidades formativas:
 doc("/db/oferta/asir.xml")//modulo/unidadeFormativa/../nome
  • Para amosar os nomes dos módulos de 1º curso:
 doc("/db/oferta/asir.xml") //modulo[curso="1"]/nome

Estrutura das consultas

Unha consulta en XQuery consta de:

  • Prólogo: onde se fan as declaracións (de espazos de nomes, variables, funcións, etc.) que crean o contorno necesario para o procesamento da consulta.
  • Corpo: a consulta propiamente dita

Variables

Unha variable permite conter unha expresión que pode ser reutilizada. As variables en XQuery comezan por $. Por exemplo: $variable As variables poden definirse no prólogo. Deste xeito estarán dispoñibles en calquera punto da consulta.

Creación de nodos

En XQuery existen dous xeitos de crear elementos:

  • Escribindo as etiquetas.
  • Usando constructores de elementos e atributos.

As expresións que se empregan para a creación de nodos irán entre chaves { } para que a expresión non se tome como un literal.

Así, como vemos no seguinte exemplo:

<exemplo>
	 <consulta> doc("/db/oferta/asir.xml")/ciclo/nome </consulta> 
	 <resultado> { doc("/db/oferta/asir.xml")/ciclo/nome/text() } </resultado>
</exemplo>

Unicamente se avalía a expresión se está pechada entre chaves O resultado sería:

<exemplo>
    <consulta> doc("/db/oferta/asir.xml")/ciclo/nome </consulta>
    <resultado>Administración de Sistemas Informáticos e redes</resultado>
</exemplo>

Construtores de elementos e atributos

Unha sintaxe alternativa sería empregar os seguintes construtores:

  • element nomeElemento { } → para crear elementos
  • attribute nomeAtributo { } → para crear atributos

Neste caso, se o elemento ten atributos, deben especificarse antes dos elementos fillos. Ademáis, os elementos deben ir separados por comas. Por exemplo:

element ciclo{
	 attribute numero {1},
	 element nome {doc("/db/oferta/asir.xml")/ciclo/nome/text()},
	 element modulos{doc("/db/oferta/asir.xml")/count(//modulo)}
}

Daría como resultado:

<ciclo numero="1">
    <nome>Administración de Sistemas Informáticos e redes</nome>
    <modulos>14</modulos>
</ciclo>

FLWOR

IMPORTANTE. Podes atopar máis información e exemplos no artigo de FLWOR.

A expresión FLWOR (pronunciado "flower") toma o seu nome dos cinco tipos de sentenzas das que pode constar:

  • FOR
  • LET
  • WHERE
  • ORDER BY
  • RETURN.

Así, do mesmo modo que en SQL usamos SELECT-FROM-WHERE, en XQuery usaremos FOR-WHERE-RETURN con algunhas inclusións adicionais.

Unha expresion FLOWR comeza por unha ou máis cláusulas FOR ou LET e remata con RETURN. Ademáis, pode filtrar a información a mostrar empregando a cláusula WHERE e ordear a información de salida con ORDER BY.

A sintaxe desta expresión sería a seguinte:

[for $var in expression]
[let $var:=expresión]
[where predicado]
[order by expresión]
return cláusula

For – in – return

A palabra clave for indica que se vai realizar unha iteración nunca árbore XML. Con in indícase sobre qué árbore en concreta se vai facer esta iteración. A palabra clave return construe o resultado.

Por exemplo, se queremos devolver todos os nomes dos módulos poderiamos facer:

for $a in doc("/db/oferta/asir.xml") //modulo/nome
return $a

Poderiamos incluir o resultado da consulta entre etiquetas poñendo a expresión entre chaves. Por exemplo:

<modulos>
{
for $a in doc("/db/oferta/asir.xml") //modulo/nome
return $a
}
</modulos>

A continuación vemos un exemplo de For con máis dunha variable, onde a segunda variable vai tomar os dous valores que lle indicamos entre paréntese:

<modulos>
{
  for $a in doc("/db/oferta/asir.xml")//modulo[@curso="1"]/nome, $b in (4,7)
  return <modulo> {<num> {$b} </num>,$a} </modulo>
}
</modulos>

Con dúas variables devolve todas as combinacións posibles. Así o resultado sería o seguinte:

<modulos>
    <modulo>
        <num>4</num>
        <nome>Implantación de sistemas operativos</nome>
    </modulo>
    <modulo>
        <num>7</num>
        <nome>Implantación de sistemas operativos</nome>
    </modulo>
    <modulo>
        <num>4</num>
        <nome>Planificación e administración de redes</nome>
    </modulo>
    <modulo>
        <num>7</num>
        <nome>Planificación e administración de redes</nome>
    </modulo>
    <modulo>
        <num>4</num>
        <nome>Fundamentos Hardware</nome>
    </modulo>
    <modulo>
        <num>7</num>
        <nome>Fundamentos Hardware</nome>
    </modulo>
    <modulo>
        <num>4</num>
        <nome>Xestión de bases de datos</nome>
    </modulo>
    <modulo>
        <num>7</num>
        <nome>Xestión de bases de datos</nome>
    </modulo>
    <modulo>
        <num>4</num>
        <nome>Linguaxes de marcas e sistemas de xestión de información
       </nome>
    </modulo>
    <modulo>
        <num>7</num>
        <nome>Linguaxes de marcas e sistemas de xestión de información
        </nome>
 </modulo>
    <modulo>
        <num>4</num>
        <nome>Formación e orientación laboral</nome>
    </modulo>
    <modulo>
        <num>7</num>
        <nome>Formación e orientación laboral</nome>
    </modulo>
</modulos>

Coa cláusula for tamén podemos indicar unha variable posicional para identificar a posición dun elemento na secuencia de tuplas. Esta especifícase con at. A sintaxe é:

for $a at $p
onde a variable $p conten a posición do elemento $a tratado en cada momento. Por exemplo:
for $a at $p in //modulo[@curso="1"]
return <mod
		nome="{$a/nome}"
		posicion="{$p}"/>

Daría como resultado:

<mod nome="Implantación de sistemas operativos" posicion="1"/>
<mod nome="Planificación e administración de redes" posicion="2"/>
<mod nome="Fundamentos Hardware" posicion="3"/>
<mod nome="Xestión de bases de datos" posicion="4"/>
<mod nome="Linguaxes de marcas e sistemas de xestión de información" posicion="5"/>
<mod nome="Formación e orientación laboral" posicion="6"/>

Let

O obxetivo da clausula let é o mesmo de for, isto é, obter unha secuencia de tuplas, pero o xeito no que o fai é diferente: con for obtemos unha tupla por cada elemento de entrada, mentres que con let obtemos unha soa tupla. Imos ver isto cun exemplo:

PENDENTE

Se usamos conxuntamente as cláusulas for e let, para cada tupla devolta pola sentencia for evaluarase a cláusula let. Por exemplo:

for $a in doc("/db/oferta/asir.xml") //modulo[@curso="1"]/nome
let $b:=$a/../unidadeFormativa
return <mod>{$a,$b}</mod>

Daría como resultado:

<mod>
    <nome>Implantación de sistemas operativos</nome>
    <unidadeFormativa codigo="MP0369_13" horas="71">
Instalación e configuración de sistemas operativos</unidadeFormativa>
    <unidadeFormativa codigo="MP0369_23" horas="90">
Xestión de dominios</unidadeFormativa>
    <unidadeFormativa codigo="MP0369_33" horas="52">
Monitorización e mantemento de sistemas operativos</unidadeFormativa>
</mod>
<mod>
    <nome>Planificación e administración de redes</nome>
</mod>
<mod>
    <nome>Fundamentos Hardware</nome>
</mod>
<mod>
    <nome>Xestión de bases de datos</nome>
</mod>
<mod>
    <nome>Linguaxes de marcas e sistemas de xestión de información</nome>
</mod>
<mod>
    <nome>Formación e orientación laboral</nome>
    <unidadeFormativa codigo="MP0380_12" horas="45">
Prevención de riscos laborais</unidadeFormativa>
    <unidadeFormativa codigo="MP0380_22" horas="62">
Equipos de traballo, dereito do traballo e da seguridade social, e
procura de emprego</unidadeFormativa>
</mod>

Where

A clausula where permite filtrar o resultado obtido establecendo una condición sobre as variables definidas nas clausulas for e let. Por exemplo:

for $a in doc("/db/oferta/asir.xml")//modulo[@curso="1"]/nome
let $b:=$a/../unidadeFormativa
where substring($a,1,4) ='Impl'
return <mod>{$a,$b}</mod>

Filtraría a información devolta pola consulta anterior para obter unicamente información dos módulos que comezan por a cade indicada, co cal o resultado sería:

<mod>
    <nome>Implantación de sistemas operativos</nome>
    <unidadeFormativa codigo="MP0369_13" horas="71">
Instalación e configuración de sistemas operativos</unidadeFormativa>
    <unidadeFormativa codigo="MP0369_23" horas="90">
Xestión de dominios</unidadeFormativa>
    <unidadeFormativa codigo="MP0369_33" horas="52">
Monitorización e mantemento de sistemas operativos</unidadeFormativa>
</mod>

Order by

A instrucción order by permite amosar o resultado en orde alfabético ou numérico. Avalíase despois do where e antes do return, e permite indicar se o resultado se ten que visualizar en orde ascendente ou descendente empregando as palabras clave ascending ou descending respectivamente.

O seguinte exemplo amosa información dos módulos cunha duración menor-igual a 120 horas ordeados por nome descendentemente.

for $a in doc("/db/oferta/asir.xml") //modulo
where $a/horas<=120
order by $a/nome descending
return <mod> {$a/nome,$a/horas,$a/sesionsSemanais}</mod>

Expresións cuantificadas: some e every

As expresións cuantificadas usanse para comprobar se nunha condición existe polo menos un item que a satisfaga (some) ou se todos os items involucrados satisfacen a condición (every). Isto permite obter o que se busca sen crear un contador.

Por exemplo, se queremos obter o código e nome dos módulos de sistemas operativos poderiamos como se ve a continuación:

<modulo codigo="MP0369">
    <nome>Implantación de sistemas operativos</nome>
</modulo>
<modulo codigo="MP0374">
    <nome>Administración de sistemas operativos</nome>
</modulo>

Poderiamos facer:

for $m in doc("/db/oferta/asir.xml")/ciclo/modulos/modulo
where some $a in $m/nome
satisfies (contains($a,"sistemas operativos"))
return <modulo  codigo="{$m/@codigo}">{$m/nome}</modulo>

Expresións condicionais

Ao igual que outras linguaxes, XQuery permite o uso de expresións condicionais a través das cláusulas if-then-else. A sintaxe é a seguinte:

if (expresión1)
then expresión2
else expresion3

A claúsula else é obligatoria. Se non se quere facer nada nese caso basta con indicalo poñendo paréntese baleiros: else().

A continuación vemos un exemplo do uso da expresión condicional:

for $a in doc("/db/oferta/asir.xml")//modulo
return if ($a/@curso="1")
then <primeiro>{data($a/nome)}</primeiro>
else <segundo>{data($a/nome)}</segundo>

O que etiqueta os módulos en función do curso ao que pertencen:

<primeiro>Implantación de sistemas operativos</primeiro>
<primeiro>Planificación e administración de redes</primeiro>
<primeiro>Fundamentos Hardware</primeiro>
<primeiro>Xestión de bases de datos</primero>
<primeiro>Linguaxes de marcas e sistemas de xestión de información</primeiro>
<segundo>Administración de sistemas operativos</segundo>
<segundo>Servizos de rede e internet</segundo>
<segundo>Implantación de aplicacións web</segundo>
<segundo>Adminstración de sistemas xestores de bases de datos</segundo>
<segundo>Seguridade e alta dispoñibildade</segundo>
<segundo>Proxecto de administración de sistemas informáticos en rede</segundo>
<primeiro>Formación e orientación laboral</primeiro>
<segundo>Empresa e iniciativa emprendedora</segundo>

A continuación vemos un exemplo onde creamos un elemento módulo para os módulos de 1º curso, e no caso de que teñan unidades formativas crearase tamén un elemento indicando o número de unidades formativas que ten:

<modulos>
{
for $m in doc("/db/oferta/asir.xml")/ciclo/modulos/modulo[@curso="1"]
return < modulo> 
		{$m/@codigo,$m/nome} 
		{ if ($m/count(unidadeFormativa)>= 1)
		  then <UFs>{$m/count(unidadeFormativa)}</UFs>  
	      else() } 
	  </modulo>
}
</modulos>

O resultado de executar esta consulta sería:

<modulos>
    <modulo codigo="MP0369">
       <nome>Implantación de sistemas operativos</nome>
       <UFs>3</UFs>
    </modulo>
    <modulo codigo="MP0370">
       <nome>Planificación e administración de redes</nome>
    </modulo>
    <modulo codigo="MP0371">
       <nome>Fundamentos Hardware</nome>
    </modulo>
    <modulo codigo="MP0372">
       <nome>Xestión de bases de datos</nome>
    </modulo>
    <modulo codigo="MP0373">
       <nome>Linguaxes de marcas e sistemas de xestión de información</nome>
    </modulo>
    <modulo codigo="MP0380">
       <nome>Formación e orientación laboral</nome>
        <UFs>2</UFs>
    </modulo>
</modulos>

Operadores e funcións

XQuery 1.0 soporta as mesmas funcións e operadores que XPath 2.0., e ademáis permite definir funcións propias e dependentes do entorno de execución do motor de XQuery. A continuación vemos os operadores e funcións máis importantes:

  • Matemáticas: +, -, *, div, idiv (divisións onde se ignora o resto), mod
  • Comparación xeral: =, !=, <, >, <=, >=, not()
  • Secuencia: union, except, intersect
  • Redondeo: floor(), ceiling(), round().
  • Funcións de agrupamento: count(), min(), max(), avg(), sum().
  • Funcións de cadea: concat(), string-length(), startswith(), ends-with(), substring(), upper-case(), lower-case(), string()
  • Uso xeral: distinct-values(), empty(), exits()
  • Información do nodo: data()

Operadores de secuencia

Union

O operador union permite obter a agrupación de dúas árbores que poden proceder de dous documentos distintos. Pódese escribir unha barra vertical | de xeito abreviado. Por exemplo: se temos outro documento dam.xml coa mesma estrutura que asir.xml para o ciclo de "Desenvolvemento de aplicacións informáticas" poderiamos obter unha listaxe dos módulos dos dous ciclos como se mostra a continuación:

doc("/db/oferta/asir.xml")/ciclo/modulos/modulo/nome union
doc("/db/oferta/dam.xml")/ciclo/modulos/modulo/nome

A continuación vemos unha consulta que usa o operador unión para obter unha listaxe ordenada de módulos e unidades formativas:

for $l in distinct-values(doc("/db/oferta/asir.xml")//(modulo/nome union unidadeFormativa))
order by $l
return <x>{ $l }</x>

Except

O operador de sustracción, recibe dúas árbores de nodos e devolve os elementos da primeira árbore que non se atopan na segunda árbore.

Por exemplo, se queremos obter os nodos correspondentes aos módulos que non teñen sesións semanais, poderiamos restar os que cumplen esta condición (ter sesións semanais) do total de nodos:

doc("/db/oferta/asir.xml")/ciclo/modulos/modulo except
doc("/db/oferta/asir.xml")/ciclo/modulos/modulo[@sesionsSemanais]

O seguinte exemplo, mostra como usar o operador except cando queremos obter o mesmo árbol de nodos que tiñamos pero eliminando determinados nodos fillos. Neste caso, queremos obter os módulos pero eliminando a información correspondente as unidades formativas:

for $b in doc("/db/oferta/asir.xml")//modulo
return
<modulo>
{ $b/@* }
{ $b/* except $b/unidadeFormativa }
</modulo>

Intersect

O operador de intersección de dous conxuntos de nodos, da como resultado unha árbore que conten os nodos que son comúns a ambalas dúas árbores. No seguinte exemplo obteranse os módulos de segundo curso que non teñen sesións semanais empregando o operador intersect

doc("/db/oferta/asir.xml")/ciclo/modulos/modulo[@curso="2"] intersect
doc("/db/oferta/asir.xml")/ciclo/modulos/modulo[not(sesionsSemanais)]

Funcións de uso xeral

distinct-values

Esta función elimina os duplicados do conxunto de nodos resultado. No exemplo seguinte obtemos unha listaxe dos distintos módulos que se imparten nos ciclos formativos de DAM e ASIR:

for $m in distinct-values(doc("/db/oferta/asir.xml")//modulo/nome union
     doc("/db/oferta/dam.xml")//modulo/nome)
order by $m
return <modulo> {$m} </modulo>

empty

A función empty() devolve certo cando a expresión escrita entre paréntese non devolve nada. O seguinte exemplo devolvería os nodos módulo que están divididos en unidades formativas:

for $m in doc("/db/oferta/asir.xml")//modulo
where not(empty($m/unidadeFormativa))
return $m

exists

A función exists() devolve certo cando a expresión escrita entre paréntese devolve polo menos un elemento. Así, outro xeito de obter os módulos que teñen unidades formativas sería:

for $m in doc("/db/oferta/asir.xml")//modulo
where exists($m/unidadeFormativa)
return $m

Información do nodo

data

Esta función acepta unha secuencia de elementos e devolve os valores con tipo. Para valores atómicos, isto significa simplemente devolver o valor en sí, sen cambios. Para os nodos, isto significa extraer o valor con tipo do nodo. Por exemplo, sobre o documento asir.xml:

data(doc("/db/oferta/asir.xml")//modulo[1]/nome)

  ao non ter subelementos é equivalente a

doc("/db/oferta/asir.xml")//modulo[1]/nome/text() → O nome do 1º módulo

data(doc("/db/oferta/asir.xml") //modulo[1]/@codigo) → O codigo do 1º módulo

data(doc("/db/oferta/asir.xml") //modulo/@codigo) → Os codigos dos módulo

O uso máis común desta función é nos constructores de elementos XQuery, xa que non está permitido obter o valor dun atributo directamente.

Combinar información de varios documentos

Pódese combinar a información de varios documentos empregando os elementos común. Por exemplo, supoñamos que queremos combinar información relativa ás familias as que pertencen os ciclos de asir e dam, que se encontra no documento familias.xml.

Poderíamos facer unha consulta que nos devolva o nome da familia profesional do ciclo de Administración de sistemas informáticos combinando ambos documentos como se amosa a continuación:

for $f in  doc("/db/oferta/familias.xml")//familia
let $m:=doc("/db/oferta/asir.xml")/ciclo
where $m/@familia=$f/@codigo
return $f/nome

Isto daría como saída:

<nome>Informática e comunicacións</nome>

Tamén se poden especificar varias fontes de datos ligándoas a distintas variables. Como resultado obtense un conxunto de tuplas composto polo producto cartesiano de todas as tuplas de cada variable ligada ao conxunto de datos (similar á sentenza SELECT de SQL que fai unha consulta sobre varias táboas). Para facer o JOIN haberá que especificar a condición que relacióna ambas fontes.

O seguinte exemplo especifíca dúas fontes de datos e establece a condición para realizar a combinación de ámbas. Así, en $f temos todas as familias e en $c todos os ciclos, e aquí establecemos a condición para indicar que a familia do noso ciclo debe coincidir co código do documento familias.

Deste xeito, no resultado só aparecen as familias que cumplen a condición:

for $f in  doc("/db/oferta/familias.xml")//familia,
    $c in doc("/db/oferta/asir.xml")/ciclo[@familia=$f/@codigo]
return
<familia>{$f/nome}
	<ciclo>{$c/nome,$c/horas}
	</ciclo>
</familia>


Obtense o seguinte resultado:

<familia>
    <nome>Informática e comunicacións</nome>
    <ciclo>
        <nome>Administración de Sistemas Informáticos e redes</nome>
        <horas>2000</horas>
    </ciclo>
</familia>


Resumo de XQuery

  • Utiliza sentenzas FLWOR
  • É o equivalente a SQL nas bases de datos relacionais.

Referencias