My favorites | Português | Sign in

Consultas e índices

Cada consulta ao armazenamento de dados utiliza um índice, uma tabela que contém os resultados da consulta na ordem desejada. Um aplicativo do Google App Engine define seus índices em um arquivo de configuração denominado datastore-indexes.xml. O servidor da web para desenvolvimento pode gerar sugestões para este arquivo automaticamente, ao encontrar consultas que ainda não têm índices configurados.

O mecanismo de consulta baseado em índice suporta a maioria dos tipos de consulta comuns, mas não suporta alguns tipos de consulta que você pode ter o costume de fazer em outras tecnologias de bancos de dados. As restrições de consultas e suas explicações são descritas abaixo.

Introdução às consultas

Uma consulta recupera entidades do armazenamento de dados que atendam a um conjunto de condições. A consulta especifica um tipo de entidade, zero ou mais condições com base em valores de propriedade de entidade (às vezes denominados "filtros") e zero ou mais descrições de ordem de classificação. Quando a consulta é executada, ela obtém todas as entidades de um tipo determinado que atendam a todas as condições dadas, classificadas na ordem descrita.

A JDO pode realizar consultas em entidades que atendam a certos critérios. Você também pode usar uma JDO Extent para representar a coleção de todas as entidades de um tipo (todos os objetos armazenados de uma classe).

Consultas com JDOQL

A JDO inclui uma linguagem de consulta para recuperar objetos que atendem a um conjunto de critérios. Essa linguagem, chamada JDOQL, refere-se diretamente às classes e campos de dados JDO, e inclui a verificação de tipos para parâmetros e resultados de consultas. A JDOQL é semelhante à SQL, mas é mais adequada para bancos de dados orientados a objetos, como o armazenamento de dados do Google App Engine (o armazenamento de dados do Google App Engine não oferece suporte às consultas SQL com a interface JDO).

A API de consultas suporta vários estilos de chamada. É possível especificar uma consulta completa em uma string usando a sintaxe de string da JDOQL. Você também pode especificar algumas ou todas as partes da consulta ao chamar métodos no objeto da consulta.

Aqui está um exemplo simples de uma consulta usando o estilo de método de chamada, com um filtro e uma ordem de classificação, usando a substituição de parâmetros para o valor usado no filtro. O método execute() do objeto Query é chamado com os valores a serem substituídos na consulta, na ordem em que são declarados.

import java.util.List;
import javax.jdo.Query;

// ...

    Query query = pm.newQuery(Employee.class);
    query.setFilter("lastName == lastNameParam");
    query.setOrdering("hireDate desc");
    query.declareParameters("String lastNameParam");

    try {
        List<Employee> results = (List<Employee>) query.execute("Smith");
        if (results.iterator().hasNext()) {
            for (Employee e : results) {
                // ...
            }
        } else {
            // ... no results ...
        }
    } finally {
        query.closeAll();
    }

Aqui está a mesma consulta usando a sintaxe da string:

    Query query = pm.newQuery("select from Employee " +
                              "where lastName == lastNameParam " +
                              "order by hireDate desc " +
                              "parameters String lastNameParam")

    List<Employee> results = (List<Employee>) query.execute("Smith");

Você pode misturar os estilos de definição da consulta. Por exemplo:

    Query query = pm.newQuery(Employee.class,
                              "lastName == lastNameParam order by hireDate desc");
    query.declareParameters("String lastNameParam");

    List<Employee> results = (List<Employee>) query.execute("Smith");

Você pode reutilizar uma única instância Query com valores diferentes substituídos para os parâmetros chamando o método execute() várias vezes. Cada chamada realiza a consulta e retorna os resultados como uma coleção.

A sintaxe de string da JDOQL suporta literais de valores dentro da string para valores de string e valores numéricos. Coloque as strings entre aspas simples (') ou duplas ("). Todos os outros tipos de valor devem usar a substituição de parâmetros. Aqui está um exemplo usando um valor de literal da string:

    Query query = pm.newQuery(Employee.class,
                              "lastName == 'Smith' order by hireDate desc");

Filtros da consulta

Um filtro especifica um nome de campo, um operador e um valor. O valor deve ser fornecido pelo aplicativo e não pode se referir a outra propriedade ou ser calculado nos termos de outras propriedades. O operador pode ser qualquer um destes: < <= == >= >

Observação: A interface de armazenamento de dados em Java não suporta os operadores de filtro != e IN que estão implementados na interface de armazenamento de dados em Python. Na interface em Python, esses operadores são implementados nas bibliotecas cliente como várias consultas do armazenamento de dados. Eles não são recursos do armazenamento de dados em si.

O sujeito de um filtro pode ser qualquer campo de objeto, incluindo a chave principal e o pai do grupo de entidades (consulte Transações).

Uma entidade deve corresponder a todos os filtros para ser um resultado. Na sintaxe de string da LDOQL, os vários filtros são especificados separadamente por && ("e" lógico). Outras combinações lógicas de filtros ("ou" e "não" lógicos) não são suportadas.

Devido à maneira como o armazenamento de dados do Google App Engine realiza as consultas, uma única consulta não pode usar filtros de desigualdade (< <= >= >) em mais de uma propriedade. Vários filtros de desigualdade na mesma propriedade (como consultar um intervalo de valores) são permitidos. Consulte Restrições às consultas.

    query.setFilter("lastName == 'Smith' && hireDate > hireDateMinimum");
    query.declareParameters("Date hireDateMinimum");

Ordens de classificação das consultas

Uma ordem de classificação especifica uma propriedade e uma direção, crescente ou decrescente. Os resultados são retornados classificados de acordo com as ordens fornecidas, na ordem em que foram especificados. Se nenhuma ordem de classificação foi especificada para a consulta, os resultados serão ordenados pelas suas chaves de entidade.

Devido à maneira como o armazenamento de dados do Google App Engine realiza as consultas, se uma consulta especifica filtros de desigualdade em uma propriedade e ordens de classificação em outras, a propriedade usada para os filtros de desigualdade deve ser ordenada antes das outras propriedades. Consulte Restrições às consultas.

    query.setOrdering("hireDate desc, firstName asc");

Intervalos de consulta

Uma consulta pode especificar um intervalo de resultados a serem retornados para o aplicativo. O intervalo especifica qual resultado no conjunto completo de resultados deve ser o primeiro a ser retornado e qual deve ser o último, usando índices numéricos, começando com 0 para o primeiro resultado. Por exemplo, um intervalo de 5, 10 retorna o 6°, 7°, 8°, 9° e 10° resultados.

O deslocamento inicial tem implicações no desempenho: o armazenamento de dados deve recuperar e descartar em seguida todos do resultados anteriores ao deslocamento inicial. Por exemplo, uma consulta com um intervalo de 5, 10 obtém dez resultados do armazenamento de dados, descarta os cinco primeiros e retorna os cinco restantes para o aplicativo.

    query.setRange(5, 10);

Extents

Um JDO Extent representa todos os objetos no armazenamento de dados de uma determinada classe.

Você começa um Extent usando o método getExtent() do PersistenceManager, passando-o para a classe de dados. A classe Extent implementa a interface Iterable para acessar os resultados. Ao terminar de acessá-los, chame o método closeAll().

O exemplo a seguir acessa todos os objetos Employee no armazenamento de dados:

import java.util.Iterator;
import javax.jdo.Extent;

// ...

    Extent extent = pm.getExtent(Employee.class, false);
    for (Employee e : extent) {
        // ...
    }
    extent.closeAll();

Um Extent recupera resultados em lotes e pode exceder o limite de 1.000 resultados aplicável às consultas.

Introdução aos índices

O armazenamento de dados do Google App Engine mantém um índice para cada consulta que um aplicativo pretende realizar. À medida que o aplicativo faz alterações nas entidades do armazenamento de dados, este atualiza os índices com os resultados corretos. Quando o aplicativo executa uma consulta, o armazenamento de dados obtém os resultados diretamente do índice correspondente.

Um aplicativo tem um índice para cada combinação de tipo, propriedade de filtro e operador, e ordem de classificação usados em uma consulta. Considere o exemplo de consulta, declarada em JDOQL:

select from Person where lastName == "Smith"
                      && height < 72
                order by height desc

O índice desta consulta é uma tabela de chaves de entidades do tipo Person, com colunas para os valores das propriedades height e lastName. O índice é classificado por height em ordem decrescente.

Duas consultas do mesmo formulário mas com valores de filtros diferentes utilizam o mesmo índice. Por exemplo, a consulta abaixo utiliza o mesmo índice que a consulta acima:

select from Person where lastName == "Jones"
                      && height < 64
                order by height desc

O armazenamento de dados executa uma consulta utilizando as etapas abaixo:

  1. O armazenamento de dados identifica o índice correspondente ao tipo, propriedades de filtro, operadores de filtro e ordens de classificação da consulta.
  2. O armazenamento de dados começa a percorrer o índice na primeira entidade que atende a todas as condições de filtro, usando os valores de filtro da consulta.
  3. O armazenamento de dados continua a percorrer o índice, retornando cada entidade, até encontrar a próxima entidade que não atende às condições do filtro, até chegar ao final do índice ou até ter coletado o número máximo de resultados solicitado pela consulta.

Uma tabela de índice contém colunas para cada propriedade usada em um filtro ou ordem de classificação. As linhas são classificadas pelos aspectos abaixo, em ordem:

  • ancestrais
  • valores de propriedade usados em filtros de igualdade
  • valores de propriedade usados em filtros de desigualdade
  • valores de propriedade usados em ordens de classificação

Isso coloca todos os resultados de cada consulta possível que utilize este índice em linhas consecutivas da tabela.

Esse mecanismo suporta uma ampla variedade de consultas e é adequado para a maioria dos aplicativos. Entretanto, ele não suporta alguns tipos de consultas que você pode ter o costume de fazer em outras tecnologias de banco de dados.

Entidades sem uma propriedade filtrada nunca são retornadas por uma consulta

Um índice contém somente as entidades que tiverem todas as propriedades mencionadas no índice. Se uma entidade não tiver uma propriedade mencionada em um índice, a entidade não aparecerá no índice e nunca será um resultado da consulta que utiliza o índice.

O armazenamento de dados do Google App Engine diferencia uma entidade que não possui uma propriedade de uma entidade que possui a propriedade com um valor nulo (null). Se você quiser que toda entidade de um certo tipo seja um possível resultado para uma consulta, poderá usar uma classe de dados JDO ou JPA, que sempre atribui um valor a toda propriedade que corresponde a um campo na classe.

Valores Text e Blob não são indexados

Propriedades com valores do tipo de valor de texto longo (Text) ou do tipo de valor de binário longo (Blob) não são incluídos nos índices e, por isso, não podem ser encontrados pelas consultas.

Como consequência da não indexação desses valores de propriedade, uma consulta com filtro ou ordem de classificação de uma propriedade nunca encontrará uma entidade cujo valor da propriedade seja Text ou Blob. As propriedades com tais valores comportam-se como se a propriedade não fosse definida em relação aos filtros de consulta e ordens de classificação.

Valores de propriedade de tipos mistos são classificados por tipo

Quando duas entidades têm propriedades com o mesmo nome, mas com tipos de valores diferentes, um índice da propriedade classifica as entidades primeiro pelo tipo do valor e depois pela ordem apropriada ao tipo. Por exemplo, se duas entidades cada tiverem uma propriedade chamada "idade", uma com um valor de número inteiro e outra com um valor de string, a entidade com o valor de número inteiro sempre aparecerá antes da entidade com o valor de string quando forem classificadas pela propriedade "Idade", independentemente dos valores em si.

É importante observar isso, especialmente no caso de números inteiros e números de ponto flutuante, que são tratados como tipos separados pelo armazenamento de dados. Uma propriedade com o valor de número inteiro 38 é classificada antes de uma propriedade com o valor de ponto flutuante 37.5, pois todos os inteiros são classificados antes dos flutuantes.

Se você estiver usando JDO ou JPA, essa situação não aparecerá a menos que você modifique o tipo de um campo sem atualizar as entidades existentes no armazenamento de dados ou use a API de armazenamento de dados de nível inferior ou uma API que não seja Java.

Definição de índices com configuração

Como padrão, o Google App Engine constrói índices para diversas consultas simples. Para outras consultas, o aplicativo deve especificar os índices de que necessita em um arquivo de configuração denominado datastore-indexes.xml. Se o aplicativo sendo executado no Google App Engine tentar realizar uma consulta para a qual não exista um índice correspondente (fornecido como padrão ou descrito em datastore-indexes.xml), a consulta irá falhar.

O Google App Engine fornece índices automáticos para os seguintes formulários de consulta:

  • consultas que utilizam somente filtros de igualdade e de ancestral;
  • consultas que utilizam somente filtros de desigualdade (que podem ser de apenas uma propriedade);
  • consultas com apenas uma ordem de classificação em uma propriedade, crescente ou decrescente.

Para outros formulários de consulta, é necessário especificar o índice em datastore-indexes.xml, incluindo:

  • consultas com diversas ordens de classificação;
  • consultas com uma ordem de classificação sobre chaves em ordem decrescente;
  • consultas com um ou mais filtros de desigualdade em uma propriedade e um ou mais filtros de igualdade em outras propriedades;
  • consultas com filtros de desigualdade e filtros de ancestral.

O servidor da web para desenvolvimento facilita o gerenciamento da configuração de índice: em vez de não executar uma consulta que exige um índice, mas não o tem, o servidor da web para desenvolvimento pode gerar a configuração de um índice que permitiria o êxito da consulta. Se os testes locais do seu aplicativo chamarem todas as consultas possíveis que o aplicativo irá fazer (cada combinação de tipo, ancestral, filtro e ordem de classificação), as entradas geradas representarão um conjunto completo de índices. Se os seus testes não exercitarem todos os formulários de consulta possíveis, você poderá verificar e ajustar a configuração do índice antes de enviar o aplicativo.

Os índices podem ser definidos manualmente usando um arquivo de configuração chamado datastore-indexes.xml no diretório WEB-INF/ do WAR do seu aplicativo. Se a configuração automática do índice estiver ativada (veja abaixo), o servidor de desenvolvimento cria uma configuração do índice em um arquivo chamado datastore-indexes-auto.xml em um diretório chamado WEB-INF/appengine-generated/, e usa os dois arquivos para determinar o conjunto completo de índices.

Veja novamente o exemplo de consulta abaixo:

select from Person where lastName = 'Smith'
                      && height < 72
                   order by height desc

A configuração do índice exigida por essa consulta apareceria em datastore-indexes.xml, como mostrado a seguir:

<?xml version="1.0" encoding="utf-8"?>
<datastore-indexes
  xmlns="http://appengine.google.com/ns/datastore-indexes/1.0"
  autoGenerate="true">
    <datastore-index kind="Person" ancestor="false">
        <property name="lastName" direction="asc" />
        <property name="height" direction="desc" />
    </datastore-index>
</datastore-indexes>

Se o elemento <datastore-indexes> em datastore-indexes.xml tem o atributo autoGenerate="true" (como acima) ou se o aplicativo não tem um arquivo datastore-indexes.xml, a configuração automática do índice está ativada. Com a configuração automática do índice ativada, se o aplicativo realizar essa consulta no servidor de desenvolvimento e não existir nenhuma configuração do índice, o servidor adicionará essa configuração do índice ao arquivo datastore-indexes-auto.xml.

Para obter mais informações sobre datastore-indexes.xml e datastore-indexes-auto.xml, consulte a Configuração de índices do armazenamento de dados em Java.

Consultas em chaves

As chaves de entidade podem ser o assunto de um filtro de consulta ou de uma ordem de classificação. Na JDO, você se refere à chave de entidade na consulta usando o campo da chave principal do objeto. O armazenamento de dados considera o valor da chave completa para essas consultas, incluindo o caminho do pai, o tipo e a string com o nome da chave atribuído pelo aplicativo ou um ID numérico atribuído pelo sistema da entidade.

Como uma chave de entidade é exclusiva em todas as entidades no sistema, as consultas à chave facilitam a recuperação em lote de entidades de um determinado tipo, como para um depósito de lote do conteúdo do armazenamento de dados. Diferentemente dos intervalos JDOQL, isso funciona de maneira eficaz em qualquer número de entidades.

As chaves são classificadas primeiro pelo caminho pai, em seguida pelo tipo e, por fim, pelo nome de chave ou ID. Os tipos e nomes de chave são strings e são classificados pelo valor de byte. IDs são números inteiros e são classificados numericamente. Se entidades com o mesmo pai e tipo usarem uma mistura de strings de nome de chave e IDs numéricos, as entidades com IDs numéricos serão consideradas inferiores às entidades com strings de nome de chave. Os elementos do caminho pai são comparados de forma parecida: pelo tipo (string) e depois pelo nome de chave (string) ou ID (número).

As consultas que envolvem chaves usam índices da mesma forma que as consultas que envolvem propriedades, com uma pequena diferença: ao contrário do que ocorre com uma propriedade, uma consulta com um filtro de igualdade na chave que também tem filtros adicionais deve usar um índice personalizado definido no arquivo de configuração de índice do aplicativo. Assim como faz com todas as consultas, o servidor da web para desenvolvimento cria entradas de configuração apropriadas nesse arquivo quando uma consulta que precisa de um índice personalizado é testada.

Restrições às consultas

A natureza do mecanismo de consulta do índice impõe algumas restrições sobre o que uma consulta pode fazer.

A filtragem ou classificação sobre uma propriedade requer que a propriedade exista

Uma condição de filtro de consulta ou ordem de classificação para uma propriedade também implica a condição de que a entidade tenha um valor para a propriedade.

Uma entidade do armazenamento de dados não precisa ter um valor para uma propriedade presente em outras entidades do mesmo tipo. Um filtro de uma propriedade irá comparar somente a entidade que tiver um valor para a propriedade. As entidades que não tiverem um valor para uma propriedade usada em um filtro ou ordem de classificação são omitidas do índice gerado para a consulta.

Nenhum filtro correspondente a entidades que não possuem uma propriedade

Não é possível realizar uma consulta por entidades que não tenham uma determinada propriedade. Uma alternativa é criar uma propriedade fixa (modelada) com valor padrão de null e criar um filtro para entidades com null como o valor da propriedade.

Os filtros de desigualdade são permitidos somente em uma propriedade

Uma consulta somente pode usar os filtros de desigualdade (<, <=, >=, >) em uma propriedade.

Por exemplo, esta consulta é permitida:

select from Person where birthYear >= minBirthYearParam
                      && birthYear <= maxBirthYearParam

Entretanto, esta consulta não é permitida, porque utiliza filtros de desigualdade em duas propriedades diferentes na mesma consulta:

select from Person where birthYear >= minBirthYearParam
                      && height >= minHeightParam   // ERROR

Os filtros podem combinar comparações de igualdade (==) para diferentes propriedades na mesma consulta, incluindo consultas com uma ou mais condições de desigualdade em uma propriedade. Isto é permitido:

select from Person where lastName == lastNameParam
                      && city == cityParam
                      && birthYear >= minBirthYearParam

O mecanismo de consulta depende de todos os resultados de uma consulta serem adjacentes na tabela de índice, para evitar percorrer a tabela inteira em busca de resultados. Uma única tabela de índice não pode representar diversos filtros de desigualdade em diversas propriedades e manter todos os resultados de forma consecutiva na tabela.

As propriedades dos filtros de desigualdade devem ser classificadas antes das outras ordens de classificação

Se uma consulta tiver um filtro com uma comparação de desigualdade e uma ou mais ordens de classificação, ela deve incluir uma ordem de classificação para a propriedade usada na desigualdade e a ordem de classificação deve aparecer antes das ordens das outras propriedades.

Esta consulta não é válida, pois utiliza um filtro de desigualdade e não classifica segundo a propriedade filtrada:

select from Person where birthYear >= minBirthYearParam
                order by lastName                    // ERROR

De modo semelhante, esta consulta não é válida, pois não classifica segundo a propriedade filtrada antes de classificar pelas outras propriedades:

select from Person where birthYear > minBirthYearParam
                order by lastName, birthYear         // ERROR

Esta consulta é válida:

select from Person where birthYear >= minBirthYearParam
                order by birthYear, lastName

Para obter todos os resultados que correspondam a um filtro de desigualdade, uma consulta percorre a tabela de índice até encontrar a primeira linha correspondente e retorna todos os resultados consecutivos até encontrar uma linha que não corresponda. Para que as linhas consecutivas representem o conjunto completo de resultados, elas devem ser classificadas pelo filtro de desigualdade antes das outras ordens de classificação.

Ordens de classificação e propriedades com diversos valores

Devido à maneira com a qual as propriedades com múltiplos valores são indexadas, a ordem de classificação dessas propriedades é incomum:

  • Se as entidades forem classificadas de acordo com uma propriedade com valores múltiplos na ordem crescente, o valor usado para a classificação será o menor valor.
  • Se as entidades forem classificadas de acordo com uma propriedade com valores múltiplos na ordem decrescente, o valor usado para a classificação será o maior valor.
  • Outros valores não afetam a ordem de classificação, nem o número de valores.
  • No caso de um empate, a chave da entidade é usada para desempatar.

Esta ordem de classificação tem a conseqüência incomum de fazer [1, 9] vir antes de [4, 5, 6, 7] nas ordens crescente e decrescente.

É importante lembrar das consultas com filtro de igualdade e ordem de classificação em uma propriedade com múltiplos valores. Nessas consultas, a ordem de classificação é ignorada. Para as propriedades com valores únicos, esta é uma simples otimização. Todos os resultados teriam o mesmo valor para a propriedade, não sendo necessário classificá-los.

Entretanto, as propriedades com múltiplos valores podem ter valores adicionais. Como a ordem de classificação é ignorada, os resultados da consulta podem ser retornados em uma ordem diferente do que se a ordem de classificação fosse aplicada. (Restaurar a ordem de classificação ignorada poderia ser dispendioso e exigir índices extra. Como este caso de uso é raro, ele é deixado de lado pelo planejador de consultas).

Entidades grandes e índices explosivos

Como foi descrito acima, cada propriedade (que não tenha um valor Text ou Blob) de cada entidade é adicionada a pelo menos uma tabela de índice, incluindo um índice simples fornecido por padrão e qualquer outro índice descrito no arquivo do aplicativo datastore-indexes.xml que menciona a propriedade. Para que uma entidade tenha um valor para cada propriedade, o Google App Engine armazena um valor de propriedade uma única vez em seu índice simples e uma vez para cada vez que a propriedade é mencionada em um índice personalizado. Cada uma dessas entradas de índice deve ser atualizada sempre que o valor da propriedade for alterado. Assim, quanto mais índices fizerem referência à propriedade, mais tempo será necessário para atualizar a propriedade.

Para evitar que a atualização de uma entidade demore muito, o armazenamento de dados limita o número de entradas de índice que podem existir para uma única propriedade. O limite é grande e a maioria dos aplicativos sequer irá notar. Entretanto, você pode atingir o limite em algumas circunstâncias. Por exemplo, uma entidade com muitas propriedades de valor único pode exceder o limite de entradas de índice.

As propriedades com múltiplos valores armazenam cada valor como uma entrada separada em um índice. Uma entidade com uma única propriedade com muitos valores pode exceder o limite de entradas de índice.

Os índices personalizados que fazem referência a diversas propriedades com diversos valores podem ficar muito grandes com apenas alguns valores. Para registrar completamente essas propriedades, a tabela de índice deve incluir uma linha para cada permutação dos valores de cada propriedade do índice.

Por exemplo, o índice a seguir (descrito em sintaxe datastore-indexes.xml) inclui as propriedades x e y para entidades do tipo MyModel:

<?xml version="1.0" encoding="utf-8"?>
<datastore-indexes>
    <datastore-index kind="MyModel">
        <property name="x" direction="asc" />
        <property name="y" direction="asc" />
    </datastore-index>
</datastore-indexes>

O código a seguir cria uma entidade com dois valores para a propriedade x e dois valores para a propriedade y:

        MyModel m = new MyModel();

        m.setX(Arrays.asList("one", "two"));
        m.setY(Arrays.asList("three", "four"));

        pm.makePersistent(m);

Para representar esses valores com exatidão, o índice deve armazenar 12 valores de propriedade: dois de cada para os índices embutidos em x e y e dois para cada uma das quatro permutações de x e y no índice personalizado. Com muitos valores de propriedades de múltiplos valores, isto pode significar que um índice precisa armazenar muitas entradas de índice para uma única entidade. Um índice que faz referência a diversas propriedades com diversos valores pode ser chamado de "índice explosivo", pois pode tornar-se muito grande com apenas alguns valores.

Se um put() resultar em um número de entradas de índice que exceda o limite, a chamada falhará com uma exceção. Se você criar um índice novo em que algumas das entradas de índice excedam o limite de qualquer entidade quando gerado, as consultas realizadas ao índice falharão e este aparecerá em estado de "Erro" no Console administrativo.

Para evitar os índices explosivos, evite consultas que exijam o uso de uma propriedade de lista em um índice personalizado. Conforme descrito acima, isso inclui consultas com diversas ordens de classificação, uma mistura de filtros de igualdade e desigualdade e filtros de ancestral.