O Query Builder oferta uma maneira fácil de consultar o repositório de conteúdo do AEM. A funcionalidade é exposta por meio de uma API Java e REST. Este documento descreve essas APIs.
O construtor de query do lado do servidor (QueryBuilder
) aceitará uma descrição de query, criará e executará um query XPath, filtrará opcionalmente o conjunto de resultados e também extrairá aspectos, se desejado.
A descrição do query é simplesmente um conjunto de predicados (Predicate
). Os exemplos incluem um predicado de texto completo, que corresponde à função jcr:contains()
no XPath.
Para cada tipo de predicado, há um componente avaliador (PredicateEvaluator
) que sabe como lidar com esse predicado específico para XPath, filtragem e extração de aspecto. É muito fácil criar avaliadores personalizados, que são conectados por meio do tempo de execução do componente OSGi.
A REST API fornece acesso aos mesmos recursos por meio de HTTP, com respostas sendo enviadas em JSON.
A API do QueryBuilder é criada usando a API JCR. Você também pode query o JCR AEM usando a API JCR de dentro de um pacote OSGi. Para obter informações, consulte Consultando dados da Adobe Experience Manager usando a API JCR.
AEM Gemis faz uma série de mergulhos técnicos no Adobe Experience Manager, fornecidos por especialistas em Adobe.
Você pode revisar a sessão dedicada ao construtor de query para obter uma visão geral e o uso da ferramenta.
Essas amostras são fornecidas na notação de estilo de propriedades Java. Para usá-los com a API Java, use um Java HashMap
como na amostra de API a seguir.
Para o QueryBuilder
Servlet JSON, cada exemplo inclui um link de amostra para uma instalação AEM (no local padrão, http://<host>:<port>
). Observe que é necessário fazer logon na instância AEM antes de usar esses links.
Por padrão, o servlet JSON do construtor de query exibe no máximo 10 ocorrências.
A adição do seguinte parâmetro permite que o servlet exiba todos os resultados do query:
p.limit=-1
Para visualização dos dados JSON retornados no seu navegador, você pode usar um plug-in como JSONView para Firefox.
O query a seguir retornará dez resultados (ou para ser preciso no máximo dez), mas informará o número de ocorrências: que estão 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
O mesmo query (com o parâmetro p.limit=-1
) retornará todos os resultados (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
A finalidade do parâmetro p.guessTotal
é retornar o número apropriado de resultados que podem ser mostrados combinando os valores mínimos viáveis p.offset
e p.limit
. A vantagem de usar esse parâmetro é melhorar o desempenho com grandes conjuntos de resultados. Isso evita calcular o total completo (por exemplo, chamar result.getSize()
) e ler todo o conjunto de resultados, otimizado até o mecanismo e índice OAK. Essa pode ser uma diferença significativa quando há centenas de milhares de resultados, tanto no tempo de execução quanto no uso da 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
para que ele sempre leia até 1000, para que você obtenha totais exatos para conjuntos de resultados menores, mas se for mais que isso, você só poderá mostrar "e mais".
Adicione p.guessTotal=true
ao query 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
O query retornará o padrão p.limit
dos resultados 10
com um deslocamento 0
:
"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 o mesmo query como 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 no 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,
Por padrão, o Construtor de Query também fornece o número de ocorrências. Dependendo do tamanho do resultado, isso pode levar muito tempo, já que determinar a contagem precisa envolve verificar todos os resultados para o controle de acesso. A maior parte do total é usada para implementar a paginação para a interface do usuário final. Como determinar a contagem exata pode ser lenta, é recomendável usar o recurso de chuteTotal para implementar a paginação.
Por exemplo, a interface do usuário pode adaptar a seguinte abordagem:
Obter e exibir a contagem precisa do número total de ocorrências (SearchResult.getTotalMatches() ou total na resposta querybuilder.json
) são menores ou iguais a 100;
Defina guessTotal
como 100 ao fazer a chamada para o Construtor de Query.
A resposta pode ter os seguintes resultados:
total=43
, more=false
- Indica que o número total de ocorrências é 43. A interface do usuário pode exibir 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 que 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. Você também pode usar isso 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 Query aumentariam o limite de guessTotal
e também dos parâmetros offset
e limit
.guessTotal
também deve ser usado nos casos em que a interface do usuário precisa usar a rolagem infinita, para evitar que o Construtor de Query determine a contagem exata de ocorrências.
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
http://<host>:<port>/bin/querybuilder.json?type=cq:Page&orderby=@jcr:content/cq:lastModified
type=cq:Page
orderby=@jcr:content/cq:lastModified
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
http://<host>:<port>/bin/querybuilder.json?fulltext=Management&orderby=@jcr:score&orderby.sort=desc
fulltext=Management
orderby=@jcr:score
orderby.sort=desc
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 predicado tagid
como no exemplo se você souber a ID explícita da tag.
Use o predicado tag
para o caminho do título da tag (sem espaços).
Como, no exemplo anterior, você está procurando por páginas (cq:Page
nós), é necessário usar o caminho relativo desse nó para o predicado tagid.property
, que é jcr:content/cq:tags
. Por padrão, tagid.property
seria simplesmente cq:tags
.
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
Este query usa um grupo (chamado group
), que atua para delimitar subexpressões dentro de um query, assim como os parênteses fazem em classificações mais padrão. Por exemplo, o query anterior pode ser expresso em um estilo mais familiar como:
"Experience" and ("/content/wknd/us/en/magazine?lang=pt-BR" or "/content/wknd/us/en/adventures?lang=pt-BR")
No grupo do exemplo, o predicado path
é usado várias vezes. Para diferenciar e ordenar as duas instâncias do predicado (a ordem é necessária para alguns predicados), você deve prefixar os predicados com N_
onde N
é o índice de ordenação. No exemplo anterior, os predicados resultantes são 1_path
e 2_path
.
O p
em p.or
é um delimitador especial que indica que o que se segue (neste caso, um or
) é um parâmetro do grupo, em vez de um subpredicado do grupo, como 1_path
.
Se nenhum p.or
for fornecido, todos os predicados serão ANDed juntos, ou seja, cada resultado deverá satisfazer todos os predicados.
Não é possível usar o mesmo prefixo numérico em um único query, mesmo para predicados diferentes.
Aqui você está procurando por todas as páginas de um determinado modelo, usando a propriedade cq:template
:
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 os nós jcr:content
das páginas, e 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
Ao usar o predicado de 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
Para evitar grandes grupos quando você deseja pesquisar por vários valores de uma propriedade ("A" or "B" or "C"
), é possível fornecer vários valores ao predicado property
:
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 de 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
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, é possível fazer o seguinte:
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
Uso
p.hits=selective
e especifique as propriedades que deseja obter
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 Query. Para fazer isso, é necessário especificar
p.nodedepth=n
em que n
é o número de níveis que você deseja que o query 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
Para obter mais predicados, consulte a página Referência de previsão do construtor de Query.
Você também pode verificar Javadoc para as classes PredicateEvaluator
. O Javadoc para essas classes contém a lista de propriedades que você pode usar.
O prefixo do nome da classe (por exemplo, similar
em SimilarityPredicateEvaluator
) é a propriedade principal da classe. Essa propriedade também é o nome do predicado a ser usado no query (em minúsculas).
Para essas propriedades principais, você pode encurtar o query 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.
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 );
}
O mesmo query executado por HTTP usando o servlet Query Builder (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
Os query podem ser armazenados no repositório para que você possa usá-los posteriormente. O QueryBuilder
fornece o método storeQuery
com a seguinte assinatura:
void storeQuery(Query query, String path, boolean createFile, Session session) throws RepositoryException, IOException;
Ao usar o método QueryBuilder#storeQuery
, o Query
especificado é armazenado no repositório como um arquivo ou uma propriedade de acordo com o valor do argumento createFile
. O exemplo a seguir mostra como salvar um Query
no caminho /mypath/getfiles
como um arquivo:
builder.storeQuery(query, "/mypath/getfiles", true, session);
Qualquer query armazenado anteriormente pode ser carregado do repositório usando o método QueryBuilder#loadQuery
:
Query loadQuery(String path, Session session) throws RepositoryException, IOException
Por exemplo, um Query
armazenado no caminho /mypath/getfiles
pode ser carregado pelo seguinte snippet:
Query loadedQuery = builder.loadQuery("/mypath/getfiles", session);
Para reproduzir e depurar query do Construtor de Query, você pode usar o console do depurador do Construtor de Query em
http://<host>:<port>/libs/cq/search/content/querydebug.html
ou, alternativamente, o servlet JSON do Construtor de Query em
http://<host>:<port>/bin/querybuilder.json?path=/tmp
path=/tmp
é apenas um exemplo.
Explique os query all durante o ciclo de desenvolvimento em relação ao conjunto de índices de públicos alvos.
https://<host>:<port>/system/console/slinglog
. Crie um novo agente de log para com.day.cq.search.impl.builder.QueryImpl
em DEBUG.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"))]
Use o depurador do Construtor de Query AEM para gerar um query XPath explicável.
Query que não sejam do Construtor de Query (XPath, JCR-SQL2) podem ser fornecidos diretamente para Explicar o Query.
A configuração dos registradores está descrita no documento Registro.
A saída de log (nível INFO) da implementação do construtor de query ao executar o query descrito 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 um query usando avaliadores de predicado que filtram ou usam um pedido personalizado por comparador, isso também será observado no query:
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 | API básica do Construtor de Query e do Query |
com.day.cq.search.result | API de resultado |
com.day.cq.search.facets | Aspectos |
com.day.cq.search.facets.buckets | Grupos (contidos em facetas) |
com.day.cq.search.eval | Avaliadores Predicados |
com.day.cq.search.facets.extrators | Extratores de Aspectos (para avaliadores) |
com.day.cq.search.writer | Gravador de Ocorrência de Resultado JSON para o servlet do Construtor de Query (/bin/querybuilder.json ) |