API do Construtor de consulta

O Query Builder oferece uma maneira fácil de consultar o repositório de conteúdo do AEM. A funcionalidade é exposta por meio de uma API Java e uma API REST. Este documento descreve essas APIs.

O construtor de consultas do lado do servidor (QueryBuilder) aceitará uma descrição de consulta, criará e executará uma consulta XPath, filtrará opcionalmente o conjunto de resultados e também extrairá facetas, se desejar.

A descrição da consulta é simplesmente um conjunto de predicados (Predicate). Os exemplos incluem um predicado de texto completo, que corresponde à variável jcr:contains() no XPath.

Para cada tipo de predicado, há um componente de avaliador (PredicateEvaluator) que sabe como lidar com esse predicado específico para XPath, filtragem e extração de facetas. É muito fácil criar avaliadores personalizados, que são conectados por meio do tempo de execução do componente OSGi.

A API REST fornece acesso exatamente aos mesmos recursos por meio do HTTP, com respostas sendo enviadas em JSON.

OBSERVAÇÃO

A API do QueryBuilder é criada usando a API JCR. Você também pode consultar o JCR do AEM usando a API JCR de um pacote OSGi. Para obter informações, consulte Consulta de dados do Adobe Experience Manager usando a API JCR.

Sessão Gem

Gems AEM é uma série de aprofundamentos técnicos no Adobe Experience Manager oferecidos por especialistas do Adobe.

Você pode revisar a sessão dedicada ao construtor de consultas para obter uma visão geral e usar a ferramenta.

Exemplos de consulta

Essas amostras são fornecidas na notação de estilo das propriedades Java. Para usá-los com a API Java, use uma API Java HashMap como na amostra de API a seguir.

Para o QueryBuilder JSON Servlet, cada exemplo inclui um link de amostra para uma instalação do AEM (no local padrão, http://<host>:<port>). Observe que é necessário fazer logon na instância do AEM antes de usar esses links.

ATENÇÃO

Por padrão, o servlet JSON do construtor de consultas exibe no máximo 10 ocorrências.

Adicionar o seguinte parâmetro permite que o servlet exiba todos os resultados da consulta:

p.limit=-1

OBSERVAÇÃO

Para visualizar os dados JSON retornados em seu navegador, você pode usar um plug-in, como o JSONView para Firefox.

Retornando Todos os Resultados

A consulta a seguir retornar dez resultados (ou seja, um máximo de dez), mas informe você sobre a Número de ocorrências: que estão efetivamente disponíveis:

http://<host>:<port>/bin/querybuilder.json?path=/content&1_property=sling:resourceType&1_property.value=wknd/components/structure/page&1_property.operation=like&orderby=path

path=/content
1_property=sling:resourceType
1_property.value=wknd/components/structure/page
1_property.operation=like
orderby=path

A mesma consulta (com o parâmetro p.limit=-1) fará retornar todos os resultados (esse pode ser um número alto, dependendo da sua instância):

http://<host>:<port>/bin/querybuilder.json?path=/content&1_property=sling:resourceType&1_property.value=wknd/components/structure/page&1_property.operation=like&orderby=path&p.limit=-1

path=/content
1_property=sling:resourceType
1_property.value=wknd/components/structure/page
1_property.operation=like
p.limit=-1
orderby=path

Utilização de p.guessTotal para retornar os resultados

O objetivo da p.guessTotal parâmetro é retornar o número apropriado de resultados que podem ser mostrados combinando o mínimo viável p.offset e p.limit valores. A vantagem de usar esse parâmetro é o melhor desempenho com conjuntos de resultados grandes. Isso evita calcular o total completo (por exemplo, chamando result.getSize()) e a leitura de todo o conjunto de resultados, otimizado até o mecanismo e índice do OAK. Essa pode ser uma diferença significativa quando há centenas de milhares de resultados, tanto em tempo de execução quanto em uso de memória.

A desvantagem do parâmetro é que os usuários não veem o total exato. Mas você pode definir um número mínimo como p.guessTotal=1000 portanto, a leitura sempre chegará a 1000, portanto, você obterá totais exatos para conjuntos de resultados menores, mas se for maior que isso, você só poderá mostrar "e mais".

Adicionar p.guessTotal=true consulte a consulta abaixo para ver como funciona:

http://<host>:<port>/bin/querybuilder.json?path=/content&1_property=sling:resourceType&1_property.value=wknd/components/structure/page&1_property.operation=like&p.guessTotal=true&orderby=path

path=/content
1_property=sling:resourceType
1_property.value=wknd/components/structure/page
1_property.operation=like
p.guessTotal=true
orderby=path

A consulta retornará a variável p.limit padrão de 10 resultados com um 0 deslocamento:

"success": true,
"results": 10,
"total": 10,
"more": true,
"offset": 0,

Você também pode usar um valor numérico para contar até um número personalizado de resultados máximos. Use a mesma query acima, mas altere o valor de p.guessTotal para 50:

http://<host>:<port>/bin/querybuilder.json?path=/content&1_property=sling:resourceType&1_property.value=wknd/components/structure/page&1_property.operation=like&p.guessTotal=50&orderby=path

Ele retornará um número com o mesmo limite padrão de 10 resultados com um deslocamento 0, mas exibirá apenas um máximo de 50 resultados:

"success": true,
"results": 10,
"total": 50,
"more": true,
"offset": 0,

Implementar paginação

Por padrão, o Query Builder também forneceria o número de ocorrências. Dependendo do tamanho do resultado, isso pode levar muito tempo, já que a determinação da contagem precisa envolve a verificação de cada resultado por controle de acesso. Geralmente, o total é usado para implementar a paginação para a interface do usuário final. Como a determinação da contagem exata pode ser lenta, é recomendável usar o recurso guessTotal para implementar a paginação.

Por exemplo, a interface pode adaptar a seguinte abordagem:

  • Obtenha e exiba a contagem precisa do número total de ocorrências (SearchResult.getTotalMatches() ou total no querybuilder.json igual ou inferior a 100;

  • Definir guessTotal para 100 ao fazer a chamada para o Construtor de consultas.

  • A resposta pode ter o seguinte resultado:

    • total=43, more=false - Indica que o número total de ocorrências é 43. A interface do usuário pode mostrar até dez resultados como parte da primeira página e fornecer paginação para as próximas três páginas. Você também pode usar essa implementação para exibir um texto descritivo como "43 resultados encontrados".
    • total=100, more=true - Indica que o número total de ocorrências é maior que 100 e a contagem exata não é conhecida. A interface do usuário pode exibir até dez como parte da primeira página e fornecer paginação para as próximas dez páginas. Também é possível usar para exibir um texto como "mais de 100 resultados encontrados". Conforme o usuário vai para as próximas páginas, as chamadas feitas para o Construtor de consultas aumentariam o limite de guessTotal e também da offset e limit parâmetros.

guessTotal também devem ser usados nos casos em que a interface do usuário precisa usar rolagem infinita, para evitar que o Construtor de consultas determine a contagem exata de ocorrências.

Localizar arquivos jar e ordená-los, os mais recentes primeiro

http://<host>:<port>/bin/querybuilder.json?type=nt:file&nodename=*.jar&orderby=@jcr:content/jcr:lastModified&orderby.sort=desc

type=nt:file
nodename=*.jar
orderby=@jcr:content/jcr:lastModified
orderby.sort=desc

Localizar todas as páginas e ordená-las pela última modificação

http://<host>:<port>/bin/querybuilder.json?type=cq:Page&orderby=@jcr:content/cq:lastModified

type=cq:Page
orderby=@jcr:content/cq:lastModified

Localizar todas as páginas e ordená-las por Última modificação, em ordem decrescente

http://<host>:<port>/bin/querybuilder.json?type=cq:Page&orderby=@jcr:content/cq:lastModified&orderby.sort=desc

type=cq:Page
orderby=@jcr:content/cq:lastModified
orderby.sort=desc

Pesquisa de texto completo, ordenada por pontuação

http://<host>:<port>/bin/querybuilder.json?fulltext=Management&orderby=@jcr:score&orderby.sort=desc

fulltext=Management
orderby=@jcr:score
orderby.sort=desc

Pesquisar páginas com uma determinada tag

http://<host>:<port>/bin/querybuilder.json?type=cq:Page&tagid=wknd:activity/cycling&tagid.property=jcr:content/cq:tags

type=cq:Page
tagid=wknd:activity/cycling
tagid.property=jcr:content/cq:tags

Use o tagid predicado como no exemplo, se você souber a ID de tag explícita.

Use o tag para o caminho do título da tag (sem espaços).

Porque, no exemplo anterior, você está procurando por páginas (cq:Page nós), é necessário usar o caminho relativo desse nó para a variável tagid.property predicado, que é jcr:content/cq:tags. Por padrão, a variável tagid.property seria simplesmente cq:tags.

Pesquisar vários caminhos (usando grupos)

http://<host>:<port>/bin/querybuilder.json?fulltext=Experience&group.1_path=/content/wknd/us/en/magazine&group.2_path=/content/wknd/us/en/adventures&group.p.or=true

fulltext=Experience
group.p.or=true
group.1_path=/content/wknd/us/en/magazine
group.2_path=/content/wknd/us/en/adventures

Esta consulta usa um grupo (nomeado como group), que atua para delimitar subexpressões em um query, da mesma forma que os parênteses em notações mais padrão. Por exemplo, a consulta anterior pode ser expressa em um estilo mais familiar como:

"Experience" and ("/content/wknd/us/en/magazine" or "/content/wknd/us/en/adventures")

No grupo no exemplo, a variável path o predicado é usado várias vezes. Para diferenciar e ordenar as duas instâncias do predicado (a ordenação é necessária para alguns predicados), você deve adicionar prefixos aos predicados com N_ onde N é o índice de ordenação. No exemplo anterior, os predicados resultantes são 1_path e 2_path.

A variável p in p.or é um delimitador especial que indica que o que se segue (neste caso, um or) é um parâmetro do grupo, em oposição a um subpredicado do grupo, como 1_path.

Se não p.or é fornecido então todos os predicados são ANDed juntos, ou seja, cada resultado deve satisfazer todos os predicados.

OBSERVAÇÃO

Não é possível usar o mesmo prefixo numérico em uma única consulta, mesmo para predicados diferentes.

Pesquisar propriedades

Aqui você está pesquisando todas as páginas de um determinado modelo, usando o cq:template propriedade:

http://<host>:<port>/bin/querybuilder.json?property=cq%3atemplate&property.value=%2fconf%2fwknd%2fsettings%2fwcm%2ftemplates%2fadventure-page-template&type=cq%3aPageContent

type=cq:PageContent
property=cq:template
property.value=/conf/wknd/settings/wcm/templates/adventure-page-template

Isso tem a desvantagem de que a jcr:content Os nós das páginas, não as próprias páginas, são retornados. Para resolver isso, você pode pesquisar por caminho relativo:

http://<host>:<port>/bin/querybuilder.json?property=jcr%3acontent%2fcq%3atemplate&property.value=%2fconf%2fwknd%2fsettings%2fwcm%2ftemplates%2fadventure-page-template&type=cq%3aPage

type=cq:Page
property=jcr:content/cq:template
property.value=/conf/wknd/settings/wcm/templates/adventure-page-template

Pesquisar por várias propriedades

Ao usar o predicado da propriedade várias vezes, é necessário adicionar os prefixos de número novamente:

http://<host>:<port>/bin/querybuilder.json?1_property=jcr%3acontent%2fcq%3atemplate&1_property.value=%2fconf%2fwknd%2fsettings%2fwcm%2ftemplates%2fadventure-page-template&2_property=jcr%3acontent%2fjcr%3atitle&2_property.value=Cycling%20Tuscany&type=cq%3aPage

type=cq:Page
1_property=jcr:content/cq:template
1_property.value=/conf/wknd/settings/wcm/templates/adventure-page-template
2_property=jcr:content/jcr:title
2_property.value=Cycling Tuscany

Procurar Vários Valores de Propriedade

Para evitar grupos grandes quando quiser procurar vários valores de uma propriedade ("A" or "B" or "C"), você pode fornecer vários valores para a variável property predicado:

http://<host>:<port>/bin/querybuilder.json?property=jcr%3atitle&property.1_value=Cycling%20Tuscany&property.2_value=Ski%20Touring&property.3_value=Whistler%20Mountain%20Biking

property=jcr:title
property.1_value=Cycling Tuscany
property.2_value=Ski Touring
property.3_value=Whistler Mountain Biking

Para propriedades com vários valores, também é possível exigir que vários valores correspondam ("A" and "B" and "C"):

http://<host>:<port>/bin/querybuilder.json?property=jcr%3atitle&property.and=true&property.1_value=Cycling%20Tuscany&property.2_value=Ski%20Touring&property.3_value=Whistler%20Mountain%20Biking

property=jcr:title
property.and=true
property.1_value=Cycling Tuscany
property.2_value=Ski Touring
property.3_value=Whistler Mountain Biking

Refinando o que é Retornado

Por padrão, o Servlet JSON do QueryBuilder retornará um conjunto padrão de propriedades para cada nó no resultado da pesquisa (por exemplo, caminho, nome, título etc.). Para obter controle sobre quais propriedades são retornadas, você pode executar um dos seguintes procedimentos:

Especifique

p.hits=full

nesse caso, todas as propriedades serão incluídas para cada nó:

http://<host>:<port>/bin/querybuilder.json?p.hits=full&property=jcr%3atitle&property.value=Cycling%20Tuscany

property=jcr:title
property.value=Cycling Tuscany
p.hits=full

Utilização

p.hits=selective

e especifique as propriedades que deseja obter em

p.properties

separados por um espaço:

http://<host>:<port>/bin/querybuilder.json?p.hits=selective&p.properties=sling%3aresourceType%20jcr%3aprimaryType&property=jcr%3atitle&property.value=Cycling%20Tuscany

property=jcr:title
property.value=Cycling Tuscany
p.hits=selective
p.properties=sling:resourceType jcr:primaryType

Outra coisa que você pode fazer é incluir nós filhos na resposta do Construtor de consultas. Para fazer isso, é necessário especificar

p.nodedepth=n

onde n é o número de níveis que você deseja que a consulta retorne. Observe que para que um nó filho seja retornado, ele deve ser especificado pelo seletor de propriedades

p.hits=full

Exemplo:

http://<host>:<port>/bin/querybuilder.json?p.hits=full&p.nodedepth=5&property=jcr%3atitle&property.value=Cycling%20Tuscany

property=jcr:title
property.value=Cycling Tuscany
p.hits=full
p.nodedepth=5

Mais predicados

Para obter mais predicados, consulte a Página Referência de predicado do Query Builder.

Você também pode verificar o Javadoc para o PredicateEvaluator classes. O Javadoc dessas classes contém a lista de propriedades que podem ser usadas.

O prefixo do nome da classe (por exemplo, similar in SimilarityPredicateEvaluator) é o propriedade principal da classe. Essa propriedade também é o nome do predicado a ser usado na query (em minúsculas).

Para essas propriedades principais, é possível encurtar a consulta e usar similar=/content/en em vez da variante totalmente qualificada similar.similar=/content/en. O formulário totalmente qualificado deve ser usado para todas as propriedades não principais de uma classe.

Exemplo de uso da API do Query Builder

   String fulltextSearchTerm = "WKND";

    // create query description as hash map (simplest way, same as form post)
    Map<String, String> map = new HashMap<String, String>();

// create query description as hash map (simplest way, same as form post)
    map.put("path", "/content");
    map.put("type", "cq:Page");
    map.put("group.p.or", "true"); // combine this group with OR
    map.put("group.1_fulltext", fulltextSearchTerm);
    map.put("group.1_fulltext.relPath", "jcr:content");
    map.put("group.2_fulltext", fulltextSearchTerm);
    map.put("group.2_fulltext.relPath", "jcr:content/@cq:tags");

    // can be done in map or with Query methods
    map.put("p.offset", "0"); // same as query.setStart(0) below
    map.put("p.limit", "20"); // same as query.setHitsPerPage(20) below

    Query query = builder.createQuery(PredicateGroup.create(map), session);
    query.setStart(0);
    query.setHitsPerPage(20);

    SearchResult result = query.getResult();

    // paging metadata
    int hitsPerPage = result.getHits().size(); // 20 (set above) or lower
    long totalMatches = result.getTotalMatches();
    long offset = result.getStartIndex();
    long numberOfPages = totalMatches / 20;

    //Place the results in XML to return to client
    DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
    DocumentBuilder builder = factory.newDocumentBuilder();
    Document doc = builder.newDocument();

    //Start building the XML to pass back to the AEM client
    Element root = doc.createElement( "results" );
    doc.appendChild( root );

    // iterating over the results
    for (Hit hit : result.getHits()) {
       String path = hit.getPath();

      //Create a result element
      Element resultel = doc.createElement( "result" );
      root.appendChild( resultel );

      Element pathel = doc.createElement( "path" );
      pathel.appendChild( doc.createTextNode(path ) );
      resultel.appendChild( pathel );
    }

A mesma consulta executada por HTTP usando o Servlet do Construtor de consultas (JSON):

http://<host>:<port>/bin/querybuilder.json?path=/content&type=cq:Page&group.p.or=true&group.1_fulltext=WKND&group.1_fulltext.relPath=jcr:content&group.2_fulltext=WKND&group.2_fulltext.relPath=jcr:content/@cq:tags&p.offset=0&p.limit=20

Armazenamento e carregamento de consultas

As consultas podem ser armazenadas no repositório para que você possa usá-las posteriormente. A variável QueryBuilder fornece a storeQuery com a seguinte assinatura:

void storeQuery(Query query, String path, boolean createFile, Session session) throws RepositoryException, IOException;

Ao usar o QueryBuilder#storeQuery método, o dado Query é armazenado no repositório como um arquivo ou como uma propriedade de acordo com a variável createFile valor do argumento. O exemplo a seguir mostra como salvar um Query ao caminho /mypath/getfiles como um arquivo:

builder.storeQuery(query, "/mypath/getfiles", true, session);

Quaisquer consultas armazenadas anteriormente podem ser carregadas do repositório usando o QueryBuilder#loadQuery método:

Query loadQuery(String path, Session session) throws RepositoryException, IOException

Por exemplo, uma variável Query armazenado no caminho /mypath/getfiles pode ser carregado pelo seguinte trecho:

Query loadedQuery = builder.loadQuery("/mypath/getfiles", session);

Teste e depuração

Para reproduzir e depurar consultas do Construtor de consultas, você pode usar o console do Depurador do Construtor de consultas em

http://<host>:<port>/libs/cq/search/content/querydebug.html

ou, como alternativa, o servlet JSON do Construtor de consultas em

http://<host>:<port>/bin/querybuilder.json?path=/tmp

path=/tmp O é apenas um exemplo.

Depuração geral do Recommendations

Obter XPath explicável por meio de registro

Explicar all consultas durante o ciclo de desenvolvimento em relação ao índice de destino definido.

  1. Habilitar logs DEBUG para o QueryBuilder a fim de obter consulta XPath subjacente e explicável
    • Vá até https://<host>:<port>/system/console/slinglog. Criar um novo agente para com.day.cq.search.impl.builder.QueryImpl em DEPURAR.
  2. Depois que DEBUG for habilitado para a classe acima, os logs exibirão o XPath gerado pelo Construtor de consultas.
  3. Copie a consulta XPath da entrada de log da consulta associada do Construtor de Consultas. Por exemplo:
    • com.day.cq.search.impl.builder.QueryImpl XPath query: /jcr:root/content//element(*, cq:Page)[(jcr:contains(jcr:content, "WKND") or jcr:contains(jcr:content/@cq:tags, "WKND"))]
  4. Cole a consulta XPath na Explicar consulta como XPath para obter o plano de consulta.

Obter XPath explicável por meio do depurador do construtor de consultas

Use o depurador do Construtor de consultas AEM para gerar uma consulta XPath explicável.

Depurador do Construtor de consultas

  1. Fornecer a consulta do Construtor de consultas no Depurador do Construtor de consultas
  2. Executar a pesquisa
  3. Obter o XPath gerado
  4. Cole a consulta XPath na Explicar consulta como XPath para obter o plano de consulta
OBSERVAÇÃO

Consultas que não sejam do Construtor de consultas (XPath, JCR-SQL2) podem ser fornecidas diretamente para Explicar consulta.

Depuração de consultas com registro

OBSERVAÇÃO

A configuração dos registradores é descrita no documento Logs.

A saída de log (nível INFO) da implementação do construtor de consultas ao executar a consulta descrita na seção anterior Teste e depuração:

com.day.cq.search.impl.builder.QueryImpl executing query (predicate tree):
null=group: limit=20, offset=0[
    {group=group: or=true[
        {1_fulltext=fulltext: fulltext=WKND, relPath=jcr:content}
        {2_fulltext=fulltext: fulltext=WKND, relPath=jcr:content/@cq:tags}
    ]}
    {path=path: path=/content}
    {type=type: type=cq:Page}
]
com.day.cq.search.impl.builder.QueryImpl XPath query: /jcr:root/content//element(*, cq:Page)[(jcr:contains(jcr:content, "WKND") or jcr:contains(jcr:content/@cq:tags, "WKND"))]
com.day.cq.search.impl.builder.QueryImpl no filtering predicates
com.day.cq.search.impl.builder.QueryImpl query execution took 69 ms

Se você tiver uma consulta usando avaliadores de predicado que filtram ou que usam uma ordem personalizada por comparador, isso também será observado na consulta:

com.day.cq.search.impl.builder.QueryImpl executing query (predicate tree):
null=group: [
    {nodename=nodename: nodename=*.jar}
    {orderby=orderby: orderby=@jcr:content/jcr:lastModified}
    {type=type: type=nt:file}
]
com.day.cq.search.impl.builder.QueryImpl custom order by comparator: jcr:content/jcr:lastModified
com.day.cq.search.impl.builder.QueryImpl XPath query: //element(*, nt:file)
com.day.cq.search.impl.builder.QueryImpl filtering predicates: {nodename=nodename: nodename=*.jar}
com.day.cq.search.impl.builder.QueryImpl query execution took 272 ms
Javadoc Descrição
com.day.cq.search Construtor básico de consultas e API de consulta
com.day.cq.search.result API de resultado
com.day.cq.search.facets Facetas
com.day.cq.search.facets.buckets Compartimentos (contidos nas facetas)
com.day.cq.search.eval Avaliadores de predicado
com.day.cq.search.facets.extractors Extratores de facetas (para avaliadores)
com.day.cq.search.writer Gravador de ocorrência do resultado JSON para o servlet do Construtor de consulta (/bin/querybuilder.json)

Nesta página