2. DQL

A operação de seleccionar os elementos com os quais se quer fazer alguma coisa, ou aos quais se quer aplicar algum processamento, tem sido, desde há algum tempo, uma preocupação das pessoas que trabalham nesta área. Começou por surgir na transformação e na formatação: era preciso seleccionar os elementos que se queriam transformar, ou que se queriam mapear num ou mais objectos com características gráficas (formatação). Este esforço é visível no DSSSL [DSSSLa]; o primeiro elemento das suas regras é uma expressão de "query" que selecciona os elementos aos quais será aplicado o processamento especificado. Por último, esta necessidade surgiu ligada às linguagens de "query" para documentos estruturados, como as que foram propostas na última conferência dedicada a esse tópico [RLS98, DeR98, Tom98, Wid8, CCDFPT98].

Assim se chegou, rapidamente, à conclusão de que a operação de selecção necessária para a transformação ou formatação era muito semelhante à necessária nos sistemas de bases de dados documentais para a realização de "queries".

Depois de algum tempo de discussão (moderada pelo W3C - World Wide Web Consortium), começa a emergir algum consenso na utilização do XSLT [xslt], uma sublinguagem de padrões presente no XSL [xsl] - a proposta de normalização para a especificação de estilos a associar a documentos XML. O XSLT encontra-se já numa fase prévia ao lançamento de um standard e foi já alvo de um estudo formal por parte de Wadler [Wad99], apresentado na última conferência mundial da área ("Markup Technologies 99"), e onde ele define a linguagem usando semântica denotacional (formalismo de cariz funcional utilizado para especificar a sintaxe e a semântica de linguagens - [Paa95]).

Depois dum estudo de algumas destas linguagens (em particular todas as que já foram referidas), foi fácil constatar que o XSLT é um denominador comum de uma grande parte delas, aquelas que foram desenvolvidas a pensar em documentos estruturados, tratando-se portanto de uma linguagem específica. Houve, no entanto, uma linguagem que cativou a atenção do autor, pela sua simplicidade e recurso à teoria de conjuntos, a linguagem proposta por Tim Bray [Bray98] na QL'98 - The Query Languages Workshop designada por Element Sets. Um estudo mais atento da linguagem e do seu historial, revelou ser esta a especificação por detrás do conhecido motor de procura Pat comercializado pela OpenText e utilizado na maior parte dos primeiros portais da Internet.

Enquanto as linguagens do tipo XSLT assentam numa sintaxe concreta e específica, a Element Sets define uma notação abstracta baseada em cinco operadores da teoria de conjuntos: contido (within), contém (including), união (+), intersecção (^) e diferença (-). Bray argumenta ser capaz de especificar uma grande percentagem de queries que possam ser necessárias num sistema de arquivo documental à custa da combinação daqueles cinco operadores.

Numa primeira análise e a título comparativo, apresentam-se a seguir dois exemplos, uma query simples e uma mais complicada que irão ser especificadas respectivamente recorrendo a XSLT e a Element Sets.


Exemplo 1. Query simples

Pretende-se seleccionar todos os parágrafos (PARA) pertencentes à introdução (INTROD) que contenham uma ou mais notas de rodapé (FOOTNOTE) ou uma ou mais referências (REF) a outros elementos no documento.

Em Element Sets a query seria:

      set1 = Set('PARA') within Set('INTROD')
      set2 = set1 including Set('FOOTNOTE')
      set3 = set1 including Set('REF')
      set4 = set2 + set3

A função Set selecciona todos os elementos do tipo indicado no argumento. No fim, o resultado da query é dado pelo valor de set4.

Por outro lado, em XSLT teríamos:

      INTROD/PARA[FOOTNOTE $or$ REF]

A query tem duas partes, a primeira de selecção de contexto, INTROD/PARA, que selecciona todos os parágrafos dentro da introdução, e a segunda parte de restrição desse contexto, [FOOTNOTE $or$ REF], que restringe o conjunto de elementos seleccionadas áqueles que tenham pelo menos um elemento do tipo FOOTNOTE ou REF.


Depois desta comparação, num caso muito simples, veja-se uma outra situação de interrogação mais elaborada.


Exemplo 2. Query mais complexa

Pretende-se agora, seleccionar todos os parágrafos da introdução que contenham uma referência ou uma nota de rodapé mas não ambos.

Em Element Sets a query seria:

      set1 = Set('PARA') within Set('INTROD')
      set2 = set1 including Set('FOOTNOTE')
      set3 = set1 including Set('REF')
      set4 = (set2 + set3) - (set2 ^ set3)

Apesar de complexa, foi fácil especificar esta query. Bastou excluir (diferença de conjuntos) os elementos resultantes da query anterior que continham ambos os elementos (intersecção de conjuntos), REF e FOOTNOTE.

Temos agora, a especificação em XSLT:

INTROD/PARA[(FOOTNOTE $and$ $not$ REF)  $or$ (REF $and$ $not$ FOOTNOTE)]


Do estudo comparativo realizado entre os dois tipos de linguagem, e do qual os dois exemplos acima fazem parte, podemos concluir que, em termos da operação de selecção, são mais ou menos equivalentes, não se tendo encontrado nenhuma situação que uma solucionasse e a outra não. Vão diferir é no método como fazem a selecção: o XSLT usa a árvore documental e toda a operação de selecção é feita em função dessa estrutura; a Element Sets, por outro lado, não usa a árvore documental, manipula o documento como um conjunto de elementos usando uma sintaxe mais universal. Mas esta diferença existe apenas perante o utilizador que usa a linguagem porque em termos de implementação não se pode fugir às travessias da árvore documental.

Ao contrário do que o leitor poderia supor nesta altura, a escolha não recaiu sobre a Element Sets mas sim sobre uma linguagem do tipo XSLT, a XQL - XML Query Language [RLS98]. Os motivos por detrás desta escolha são muito simples. Apesar dos paradigmas, em termos de selecção, serem equivalentes, as linguagens do tipo XSLT vão além da selecção, permitem ter um segundo nível de selecção baseado em restrições sobre o conteúdo.

2.1. Escolha da linguagem

A linguagem XSLT fornece um método bastante simples para descrever a classe de nodos que se quer seleccionar. É declarativa em lugar de procedimental. Apenas é preciso especificar o tipo dos nodos a procurar usando um tipo de padrões simples baseado na notação de directorias dum sistema de ficheiros (a sua estrutura é equivalente à de uma árvore documental). Por exemplo, livro/autor, significa: seleccionar todos os elementos do tipo autor contidos em elementos livro.

A XQL é uma extensão do XSLT. Adiciona operadores para a especificação de filtros, operações lógicas sobre conteúdo, indexação em conjuntos de elementos, e restrições sobre o conteúdo dos elementos. Basicamente, é uma notação para a especificação de operações de extracção de informação de documentos estruturados.

Como já foi dito, vamos começar por descrever operadores relacionados com a selecção mas a linha divisória entre selecção e restrição irá sendo diluída ao longo do texto, confundindo-se até, para os casos em que a integração das duas é muito forte.

2.1.1. Padrões e Contexto

Uma expressão de selecção é sempre avaliada em função dum contexto de procura. Um contexto de procura é um conjunto de nodos a que uma expressão se pode aplicar de modo a calcular o resultado. Todos os nodos no contexto de procura são filhos do mesmo nodo pai; o contexto de procura é constituído por todos os nodos que são filhos deste nodo pai e respectivos atributos mais os atributos do nodo pai.

As expressões de selecção poderão ser absolutas (o contexto é seleccionado em função do nodo raíz - "/"), ou relativas (o contexto é seleccionado em função do contexto actual - "."). Na especificação do contexto pode ainda ser usado o operador "//" com o significado de descendência recursiva.


Exemplo 3. Selecção de contextos

  1. Seleccionar todos os elementos autor no contexto actual.

          ./autor

    Que é equivalente a:

          autor
  2. Seleccionar o elemento raíz (livro) deste documento.

          /livro
  3. Seleccionar todos os elementos autor em qualquer ponto do documento actual.

          //autor
  4. Seleccionar todos os elementos capítulo cujo atributo tema é igual ao atributo especialidade de livro.

          capítulo[/livro/@especialidade = @tema]
  5. Seleccionar todos os elementos título que estejam um ou mais níveis abaixo do contexto actual.

          .//título

2.1.2. Quantificador: todos

O operador "*" quando usado numa expressão de selecção selecciona todos os elementos nesse contexto.


Exemplo 4. Selecção com "*"

  1. Seleccionar todos os elementos filhos de autor.

          autor/*
  2. Seleccionar todos os elementos nome que sejam netos de livro.

          livro/*/nome
  3. Seleccionar todos os elementos netos do contexto actual.

          */*
  4. Seleccionar todos os elementos que tenham o atributo identificador.

          *[@identificador]

2.1.3. Atributos

Como já se pôde observar nalguns exemplos, o nome de atributos é precedido por "@". Os atributos são tratados como sub-elementos, imparcialmente, sempre que possível. De notar que os atributos não podem ter sub-elementos pelo que não poderão ter operadores de contexto aplicados ao seu conteúdo (tal resultaria numa situação de erro sintáctico). Os atributos também não têm conceito de ordem, são por natureza anárquicos pelo que nenhum operador de indexação deverá ser-lhes aplicado.


Exemplo 5. Selecção com atributos

  1. Seleccionar o atributo valor no contexto actual.

          @valor
  2. Seleccionar o atributo dólar de todos os elementos preço no contexto actual.

          preço/@dólar
  3. Seleccionar todos os elementos capítulo que tenham o atributo língua.

          capítulo[@língua]
  4. Seleccionar o atributo língua de todos os elementos capítulo.

          capítulo/@língua
  5. O próximo exemplo não é válido.

          preço/@dólar/total

2.1.4. Filtro - sub-query

O resultado duma query pode ser refinado através de uma sub-query (restrição aplicada ao resultado da query principal), indicada entre "[" e "]" (nos exemplos anteriores já apareceram várias sem nunca se ter explicado a sua sintaxe e semântica).

A sub-query é equivalente à cláusula SQL WHERE. O valor resultante da aplicação de uma sub-query é booleano e os elementos para os quais o valor final seja verdadeiro farão parte do resultado final.

Há operadores nas sub-queries que permitem testar o conteúdo de elementos e atributos.


Exemplo 6. Sub-query

  1. Seleccionar todos os elementos capítulo que contenham pelo menos um elemento excerto.

          capítulo[excerto]
  2. Seleccionar todos os elementos título pertencentes a elementos capítulo que tenham pelo menos um elemento excerto.

          capítulo[excerto]/titulo
  3. Seleccionar todos os elementos autor pertencentes a elementos artigo que tenham pelo menos um elemento excerto, e onde autor tenha email.

          artigo[excerto]/autor[email]
  4. Seleccionar todos os elementos artigo que contenham elementos autor com email.

          artigo[autor/email]
  5. Seleccionar todos os elementos artigo que tenham um autor e um título.

          artigo[autor][titulo]

Como se pode observar nalguns destes exemplos, algumas das restrições que pretendemos colocar sobre os documentos podem ser especificadas com os construtores e operadores já apresentados. A linha divisória entre a selecção e a restrição parece já um pouco diluída.

2.1.5. Expressões booleanas

As expressões booleanas podem ser usadas nas sub-queries e estas, já nos permitem especificar condições contextuais como a restrição de valores a um domínio. Uma expressão booleana tem a seguinte forma:

      val-esquerda $operador$ val-direita

Os operadores têm a forma $operador$ e são normalmente binários: tomam como argumentos um valor à esquerda e um valor à direita.

Com estes operadores e o agrupamento por parentesis podem especificar-se queries bastante complexas.


Exemplo 7. Operadores booleanos

  1. Seleccionar todos os elementos autor que tenham um email e um url.

          autor[email $and$ url]

    No universo das queries, o resultado seria o conjunto de autores que tivessem email e url.

  2. Seleccionar todos os elementos autor que tenham um email ou um url e pelo menos uma publicação.

          autor[(email $or$ url) $and$ publicação]
  3. Seleccionar todos os elementos autor que tenham um email e nenhuma publicação.

          autor[email $and$ $not$ publicação]
  4. Seleccionar todos os elementos autor que tenham pelo menos uma publicação e não tenham email nem url.

          autor[publicação $and$ $not$ (email $or$ url)]

2.1.6. Equivalência

A igualdade é notada por = e a desigualdade por !=. Alternativamente podem ser usados $eq$ e $neq$.

Podemos usar strings nas expressões desde que limitadas por aspas simples ou duplas.

Na comparação com o conteúdo de elementos o método value() está implícito. Por exemplo nome='Carlos' é uma abreviatura de nome!value()='Carlos'.


Exemplo 8. Equivalência

  1. Seleccionar todos os autores que têm o sub-elemento organização preenchido com o valor 'U.Minho'.

          autor[organização = 'U.Minho']
  2. Seleccionar todos os elementos que têm o atributo língua preenchido com o valor 'pt'.

          *[@língua = 'pt']

A linguagem possui todos os operadores relacionais habituais, cuja utilização não foi aqui exemplificada, porém, a sua semântica é bem conhecida e este enunciado já tem complexidade qb. Fica ao critério dos grupos de trabalho a sua implementação.