Guia de aula laboratorial

O guia da 2º aula prática está disponível online. Os tópicos abordados incluem conceitos relacionados com as linguagens XPath e XQuery.

XQuery

A linguagem XQuery está para o XML como o SQL está para as bases de dados relacionais. Trata-se de uma linguagem desenhada para executar interrogações sobre documentos XML. Apesar de ter sido desenhada a pensar em bases de dados de documentos XML, algumas pessoas usam a XQuery para manipular documentos XML isolados. Neste papel, a XQuery compete directamente com a XSLT.

Nos exemplos que se seguem, iremos usar um processador de XML e XSLT open source denominado Saxon ( http://saxon.sourceforge.net/ ). Na sua versão mais recente, esta ferramenta implementa os standards XQuery 1.0 e XPath 2.0. Para utilizar, o ficheiro saxon8.jar deve ser colocado no "classpath" do Java. As interrogações XQuery podem ser criadas usando um qualquer editor de texto, sendo que na linha de comandos poderão ser executadas através da instrução java -cp saxon8.jar net.sf.saxon.Query ficheiro-query.txt

O ficheiro xquery.zip ( http://web.tagus.ist.utl.pt/~bruno.martins/materials/class-materials/labs-gti/xquery/xquery.zip ) inclui a ferramenta saxon assim como alguns ficheiros de teste.

Os exemplos que se seguem utilizam o seguinte ficheiro XML como fonte de dados. O ficheiro define informações diversas sobre filmes, actores e realizadores.

<?xml version="1.0" encoding="UTF-8"?>

<result>

<actors>
<actor id="00000015">Anderson, Jeff</actor>
<actor id="00000030">Bishop, Kevin</actor>
<actor id="0000000f">Bonet, Lisa</actor>
<actor id="00000024">Bowz, Eddie</actor>
<actor id="0000002d">Curry, Tim</actor>
<actor id="00000033">Dullea, Keir</actor>
<actor id="00000042">Fisher, Carrie</actor>
<actor id="00000006">Ford, Harrison</actor>
<actor id="00000045">Foster, Jodie</actor>
<actor id="00000018">Ghigliotti, Marilyn</actor>
<actor id="0000000c">Hackman, Gene</actor>
<actor id="0000003f">Hamill, Mark</actor>
<actor id="00000027">Heames, Darin</actor>
<actor id="0000001e">Heche, Anne</actor>
<actor id="00000003">Jones, Tommy Lee</actor>
<actor id="00000039">Lockwood, Gary</actor>
<actor id="00000048">McConaughey, Matthew</actor>
<actor id="0000003c">Richter, Daniel</actor>
<actor id="00000021">Schwimmer, David</actor>
<actor id="00000009">Smith, Will</actor>
<actor id="0000001b">Spoonhauer, Lisa</actor>
<actor id="00000036">Sylvester, William</actor>
<actor id="0000002a">Todd, Antonio</actor>
<actor id="00000012">Voight, John</actor>
<actor id="0000004b">Woods, James</actor>
<actor id="1784027567">Lewis, Daniel</actor>
<actor id="2096814035">Manesse, Gaspard</actor>
<actor id="325442748">Feito, Raphael</actor>
<actor id="4231919377">Morier, Philippe</actor>
<actor id="2142583927">Racette, Francine</actor>
<actor id="916503204">Beatles, The</actor>
<actor id="916503205">Neeson, Liam</actor>
<actor id="916503206">Pinocchio, Mr.</actor>
<actor id="916503207">Parillaud, Anne</actor>
<actor id="916503208">Pitt, Brad</actor>
<actor id="916503209">Freeman, Morgan</actor>
<actor id="916503211">Domingo, Placido</actor>
<actor id="916503210">Sharif, Omar</actor>
</actors>
<videos>
<video id="id1235AA0">
<title>The Fugitive</title>
<genre>action</genre>
<rating>PG-13</rating>
<summary>Tommy Lee Jones and Harrison Ford are the hunter and the hunted in this fast-paced story of a falsely
convicted man who escapes to find his wife's true killer.</summary>
<details>Harrison Ford and Tommy Lee Jones race through the breathless manhunt movie based on the classic TV series.
Ford is prison escapee Dr. Richard Kimble, a Chicago surgeon falsely convicted of killing his wife and determined
to prove his innocence by leading his pursuers to the one-armed man who actually commited the crime.</details>
<year>1997</year>
<director>Andrew Davis</director>
<studio>Warner</studio>
<user_rating>4</user_rating>
<runtime>110</runtime>
<actorRef>00000003</actorRef>
<actorRef>00000006</actorRef>
<vhs>13.99</vhs>
<vhs_stock>206</vhs_stock>
<dvd>14.99</dvd>
<dvd_stock>125</dvd_stock>
<beta>1.03</beta>
<beta_stock>12</beta_stock>
<LaserDisk>12.00</LaserDisk>
<LaserDisk_stock>10</LaserDisk_stock>
</video>
<video id="id1244100">
<title>Enemy of the State</title>
<genre>action</genre>
<rating>R</rating>
<summary>After a chance meeting with an old pal, Robert Deal finds himself in posession of a disk that contains evidence of an
assassination plot by the NSA.</summary>
<details>It's not paranoia if they're really after you. Will Smith (Men in Black) stars as attorney Robert Clayton Dean,
a man at the center of a high-stakes pursuit when he inadvertently comes into possession of secret information
implicating corrupt officials within the government.</details>
<year>1999</year>
<director>Tony Scott</director>
<studio>Buena vista</studio>
<user_rating>5</user_rating>
<runtime>113</runtime>
<actorRef>00000009</actorRef>
<actorRef>0000000c</actorRef>
<actorRef>0000000f</actorRef>
<actorRef>00000012</actorRef>
<vhs>16.99</vhs>
<vhs_stock>188</vhs_stock>
<dvd>29.99</dvd>
<dvd_stock>353</dvd_stock>
<LaserDisk>12.57</LaserDisk>
<LaserDisk_stock>203</LaserDisk_stock>
</video>
<video id="id124E230">
<title>Clerks</title>
<genre>comedy</genre>
<rating>R</rating>
<summary>Spend a day at work with two friends and their quirky customers at a video shop and convenience store.</summary>
<details>A day-in-the-life of two men that work as cleks in adjacent video and convenience stores. The story follows the
two through an agonizing day at work, a surprise hit at the 1994 Cannes film festival.</details>
<year>1999</year>
<director>Kevin Smith</director>
<studio>Buena vista</studio>
<user_rating>3</user_rating>
<runtime>97</runtime>
<actorRef>00000015</actorRef>
<actorRef>00000018</actorRef>
<actorRef>0000001b</actorRef>
<beta>23.97</beta>
<beta_stock>419</beta_stock>
</video>

<!-- o ficheiro incluido em xquery.zip inclui descrições para um maior número de filmes -->
</videos>
</result>
Acedendo a documentos XML através de XQuery

A linguagem XQuery permite aceder a um ficheiro XML directamente através da sua localização, expressa por um URI passado como argumento a uma função doc(). O seguinte exemplo mostra um documento XML que se encontre no disco rígido na localização c:/Exemplos/videos.xml.

doc('file:///c:/Exemplos/videos.xml')

A mesma função pode ser usada para solicitar um documento que se encontre online:


doc('
http://web.tagus.ist.utl.pt/~bruno.martins/materials/class-materials/labs-gti/xquery/videos.xml ')

O ficheiro dado atrás como exemplo contem várias secções, sendo que uma delas é um elemento <actores>. Este elemento pode ser seleccionado através da seguinte expressão XPath:


.//actors

O resultado desta interrogação seria:


<actors>
<actor id="00000015">Anderson, Jeff</actor>
<actor id="00000030">Bishop, Kevin</actor>
<actor id="0000000f">Bonet, Lisa</actor>
...etc...
</actors>

A linguagem XQuery está desenhada por forma a que uma qualquer expressão XPath válida seja também uma interrogação XQuery. Por exemplo a expressão:


.//actors/actor[ends-with(., 'Lisa')]

iria produzir o resultado:


<actor id="0000000f">Bonet, Lisa</actor>
<actor id="0000001b">Spoonhauer, Lisa</actor>

A expressão XPath usada na query tem duas partes. A parte .//actors/actor indica quais os elementos nos quais estamos interessados, e o predicado [ends-with(., 'Lisa')] indica um teste feito com a função ends-with() que os elementos devem satisfazer. O predicado é avaliado uma vez para cada elemento seleccionado, sendo que a expressão "." (dot) se refere ao elemento que o predicado está a testar (neste caso o actor seleccionado). A expressão "/" no path informalmente significa "descer um nível." A expressão "//" significa "descer um número arbitrário de níveis". É possível fazer construções como "/.." por forma a "subir um nível" ou "/@id" por forma a seleccionar um atributo.

Os exemplos que se seguem ilustram as possibilidades da linguagem XPath:

  • result permite seleccionar todos os nós filho do elemento result.
  • /result permite seleccionar o elemento "root" result.
  • /result/videos/video permite seleccionar os elementos video que são filhos de result/videos.
  • count( /result/videos/video) permite contar o número de videos
  • //video permite seleccionar todos os elementos video independentemente de onde se encontrem no documento.
  • result//video permite seleccionar os elementos video que sejam descendentes do elemento result, independentemente de serem filhos directos ou não.
  • //@id permite seleccionar todos os atríbutos de nome "id".
  • //video/* permite seleccionar todos os elementos filho do elemento video.
  • //* permite seleccionar todos os elementos no documento.
  • //video[@*] permite seleccionar todos os elementos video que tenham algum atributo.
  • //video/title | //video/summary permite seleccionar os elementos title ou summary que são filhos de um elemento video.
  • /result/videos/video[1] permite seleccionar o primeiro elemento video que é filho de result/videos.
  • /result/videos/video[last()] permite seleccionar o último elemento video que é filho de result/videos.
  • /result/videos/video[position()<3] permite seleccionar os primeiros dois elementos video que são filho de result/videos.
  • //video[@id] permite seleccionar todos os elementos video que tenham um atríbuto id.
  • //video[@id='id1235AA0'] permite seleccionar todos os elementos video que tenham um atrubuto id com valor igual a id1235AA0.
  • //video[user_rating>4] permite seleccionar todos os elementos video que tenham um elemento filho user_rating com valor superior a 4.
  • //video[user_rating>4]/title permite seleccionar todos os elementos title que sejam filhos de um elemento video que tenham um elemento filho user_rating com valor superior a 4.

A linguagem XPath permite expressar selecções bastante poderosas. Suponhamos que queremos encontrar os títulos de todos os vídeos onde participa um actor cujo primeiro nome é Lisa. Cada vídeo no ficheiro de exemplo é representado por um elemento <video> tal como o que se segue:

<video id="647599251">
<studio></studio>
<director>Francesco Rosi</director>
<actorRef>916503211</actorRef>
<actorRef>916503212</actorRef>
<title>Carmen</title>
<dvd>18</dvd>
<laserdisk></laserdisk>
<laserdisk_stock></laserdisk_stock>
<genre>musical</genre>
<rating>PG</rating>
<runtime>125</runtime>
<user_rating>4</user_rating>
<summary>A fine screen adaptation of Bizet's popular opera.</summary>
<details>Placido Domingo does it again, this time in Bizet's popular opera.</details>
<vhs>15</vhs>
<beta_stock></beta_stock>
<year>1984</year>
<vhs_stock>88</vhs_stock>
<dvd_stock>22</dvd_stock>
<beta></beta>
</video>

Podemos escrever a query pretendida da seguinte forma:

//video[actorRef=//actors/actor[ends-with(., 'Lisa')]/@id]/title

Mais uma vez, esta query é apenas uma expressão XPath. Pode ser lida da esquerda para a direita da seguinte forma:

  • Começar no documento seleccionado implicitamente.
  • Escolher todos os elementos <video> sob qualquer nível.
  • Escolher os elementos que têm como filho um elemento actorRef cujo valor é igual ao valor dado através da seguinte selecção:
    • Escolher os elementos <actors> sob qualquer nível.
    • Escolher os seus elementos filho <actor> .
    • Escolher o elemento apenas se o seu valor termina com a palavra 'Lisa'.
    • Escolher o valor do atributo id.
  • Escolher os elementos <title> filhos dos elementos <video> seleccionados.

O resultado da query é o seguinte:


<title>Enemy of the State</title>
<title>Clerks</title>

A expressão XPath anterior atinge já um nível de complexidade considerável. Para este tipo de interrogações, a linguagem XQuery oferece mecanismos mais sofisticados.


Expressões XQuery FLWOR

Quem já usou SQL anteriormente é provável que tenha reconhecido o exemplo anterior como um join entre duas tabelas (a tabela vídeos e a tabela actores). As diferenças no mundo XML relacionam-se com o facto de os dados serem hierárquicos em lugar de tabulares, mas a linguagem XQuery permite a expressão de joins de uma forma semelhante á que é usada em SQL. O equivalente a uma expressão SELECT em SQL é denominado FLWOR em XPath. O nome deriva das suas 5 clausulas: for, let, where, order by, return.

O último exemplo, dado como uma expressão FLWOR, seria:

let $doc := .
for $v in $doc//video,
$a in $doc//actors/actor
where ends-with($a, 'Lisa') and $v/actorRef = $a/@id
return $v/title

E obviamente, o resultado da execução desta query seria o mesmo.

  • A clausula let simplesmente declara uma variável. Poderia ter sido inicializada com a localização do documento, e.g. ('videos.xml'), ou com o resultado de uma query XPath específica.
  • A clausula for define duas outras variáveis: uma percorre todos os videos e a outra percorre todos os actores. Desta forma, a query está a processar todas os pares possíveis de actores e vídeos.
  • A clausula where selecciona apenas os pares nos quais estamos interessados.
  • Finalmente, a clausula return indica qual a informação que estamos interessados em obter como resultado.

Este exemplo recorre a um pequeno truque XPath, relacionado com o facto de a maioria dos vídeos envolver mais do que um actor. A expressão $v/actorRef selecciona portanto vários elementos. As regras para o operador = em XPath (e portanto também em XQuery) dizem que tudo o que está à esquerda do operador é comparado com tudo o que está à direita e o resultado é verdade apenas se existe pelo menos um "match". Como resultado, a query está implicitamente a fazer um join. De uma forma mais tradicional, a mesma query podia ser expressa como:

let $doc := .
for $v in $doc//video,
$va in $v/actorRef,
$a in $doc//actors/actor
where ends-with($a, 'Lisa') and $va eq $a/@id
return $v/title

Desta feita é usado o operador eq, o qual compara estritamente um valor à esquerda com um valor à direita.

Para ordenar os resultados da query anterior, poderíamos fazer algo como:

let $doc := .
for $v in $doc//video,
$a in $doc//actors/actor
where ends-with($a, 'Lisa') and $v/actorRef = $a/@id
order by $v/year
return $v/title

Quem está familiarizado com a linguagem SQL poderá interrogar-se sobre quais são os equivalentes XPath às clausulas DISTINCT ou GROUP BY. Infelizmente, não existem equivalentes directos, podendo-se no entanto obter funcionalidades semelhantes através da função distinct-values(). Em baixo apresenta-se um exemplo que permite agrupar os videos de acordo com quem os realizou.

<movies>
{for $d in distinct-values(//director) return
<director name="{$d}">
{ for $v in //video[director = $d] return
<title>{$v/title}</title>
}
</director>
}
</movies>

Finalmente, a XQuery inclui uma clausula if-then-else que permite a direccionar a execução de acordo com expressões condicionais. A sua sintaxe é como se apresenta abaixo, sendo que as clausulas if-then-else se podem combinar tanto com clausulas where como com clausulas return.

  if  ( test-expression ) then result-expression1 
  
else result-expression2
Mais informações sobre as expressões FLWOR podem ser obtidas em tutoriais específico ( http://www.w3schools.com/xquery/xquery_flwor.asp , http://www.yukonxml.com/articles/xquery/ ).

Gerando output XML através de XQuery


As querys que vimos até agora seleccionam elementos num documento de origem, mostrando como resultado os elementos obtidos pela query. Numa aplicação real, é desejável ter um maior controlo sobre a forma do documento que é produzido como resultado, o qual pode vir a ser fornecido como input a uma outra aplicação. A linguagem XQuery permite que a estrutura do documento de resultado seja definida usando uma notação específica. O exemplo que se segue mostra como isso pode ser feito:

<videos featuring="Lisa">
{
let $doc := .
for $v in $doc//video,
$a in $doc//actors/actor
where ends-with($a, 'Lisa') and $v/actorRef = $a/@id
order by $v/year
return
<video year="{$v/year}">
{$v/title}
</video>
}
</videos>

O resultado obtido seria agora o seguinte:


<videos featuring="Lisa">
<video year="1999">
<title>Enemy of the State</title>
</video>
<video year="1999">
<title>Clerks</title>
</video>
</videos>

Bases de dados XML


Começamos por dizer que a principal função da linguagem XQuery se relacionada com a extracção de dados a partir de bases de dados XML. Contudo, os exemplos que vimos até agora usaram apenas um único documento XML como input. Temos ainda que uma ferramenta como o saxon se adequa mais ao processamento de documentos XML isolados, não incluindo todas as funcionalidades de um sistema de gestão de bases de dados (e.g. indexação, acesso concorrente segundo um modelo cliente-servidor, etc.)

Numa base de dados XML, em lugar de usarmos a função doc() function (ou simplesmente ".") para seleccionar um documento, seria mais provável usarmos a função collection() para se abrir uma ligação a uma base de dados, ou para seleccionar uma colecção de documentos específica dentro de uma base de dados. A forma como esta operação se processa irá depender do sistema que está a ser utilizado. O resultado da função collection() é um conjunto de documentos (mais especificamente, uma sequência de documentos) que podem ser processados usando expressões XPath ou FLWOR da mesma forma que nos exemplos vistos atrás.

Para mais informações deve ser consultada a documentação específica do sistema de base dados XML a ser utilizado. Dois exemplos de sistemas open source existentes são:


De referir por último que o saxon tem algum suporte básico para a manipulação de colecções de documentos XML. Um ficheiro semelhante ao que se apresenta abaixo pode ser utilizado para definir uma colecção de documentos:

<collection>
<doc href="videos1.xml"/>
<doc href="videos2.xml"/>
<doc href="videos3.xml"/>
</collection>


Posteriormente, numa expressão XQuery, poderia ser usada uma chamada à função collection() que tomasse como argumento o nome do ficheiro XML definindo a colecção.

Links