Expand my Community achievements bar.

Don’t miss the AEM Skill Exchange in SF on Nov 14—hear from industry leaders, learn best practices, and enhance your AEM strategy with practical tips.
SOLVED

JCR Query API Producing Unexpected XPath

Avatar

Former Community Member

We have built a REST API backed by the JCR Query API on AEM 5.6.1.  We're seeing a peculiar XPath expression being produced by the PredicateGroupEvaluator, when it uses the underlying JcrTagImpl.TagQueryExpressionBuilder via TagPredicateEvaluator.

Consider these Query Builder servlet request: 

  1. http://localhost:4502/bin/querybuilder.json?type=cq:Page&tagid=workflow:wcm/translation&tagid.proper...
  2. http://localhost:4502/bin/querybuilder.json?type=cq:Page&tagid=workflow:wcm&tagid.property=jcr:conte...
  3. http://localhost:4502/bin/querybuilder.json?type=cq:Page&group.p.or=false&group.1_tagid=workflow:wcm...
  4. http://localhost:4502/bin/querybuilder.json?type=cq:Page&group.p.or=false&group.1_tagid=workflow:dam...
  5. http://localhost:4502/bin/querybuilder.json?type=cq:Page&group.p.or=false&group.1_tagid=workflow:wcm...

Query #1 results in one hit (/etc/workflow/models/translation).  Query #2 filters for the more general workflow:wcm tag and results in four hits.  Query #3 uses a group to AND the previous two queries and should still result in only one hit, since the filter for workflow:wcm/translation still applies.  It actually returns the same four results as query #2.  This becomes even more obvious when you AND two disjoint tags, which should produce no hits, but will actually return the union of the two tags.

This becomes even more obvious when you AND two disjoint tags, which should produce no hits, but will actually return the union of the two tags: Queries #4 and #5 further demonstrate the incorrectness, as they should produce the same result, but they are different and both are incorrect.

 

 

Debugging the PredicateGroupEvaluator at line 104, we can verify that the group is formed correctly, but the multi-predicate expressions produced by the TagPredicateEvaluator get AND-ed without grouping parenthesis, producing the wrong query.

PredicateGroup group=group: or=false[ {1_tagid=tagid: tagid=workflow:wcm, property=jcr:content/cq:tags} {2_tagid=tagid: tagid=workflow:wcm/translation, property=jcr:content/cq:tags} ]
List<String> expressions = [     jcr:content/@cq:tags = 'workflow:wcm' or jcr:content/@cq:tags = '/etc/tags/workflow/wcm' or         jcr:like(jcr:content/@cq:tags, 'workflow:wcm/%') or jcr:like(jcr:content/@cq:tags, '/etc/tags/workflow/wcm/%'),     jcr:content/@cq:tags = 'workflow:wcm/translation' or jcr:content/@cq:tags = '/etc/tags/workflow/wcm/translation' or         jcr:like(jcr:content/@cq:tags, 'workflow:wcm/translation/%') or jcr:like(jcr:content/@cq:tags,         '/etc/tags/workflow/wcm/translation/%') ]
String xpath = "(jcr:content/@cq:tags = 'workflow:wcm' or jcr:content/@cq:tags = '/etc/tags/workflow/wcm' or jcr:like(jcr:content/@cq:tags, 'workflow:wcm/%') or jcr:like(jcr:content/@cq:tags, '/etc/tags/workflow/wcm/%') and jcr:content/@cq:tags = 'workflow:wcm/translation' or jcr:content/@cq:tags = '/etc/tags/workflow/wcm/translation' or jcr:like(jcr:content/@cq:tags, 'workflow:wcm/translation/%') or jcr:like(jcr:content/@cq:tags, '/etc/tags/workflow/wcm/translation/%'))";

Note the improper grouping of the expressions when they are conjoined in the xpath string.  The correct xpath should be:

String xpath = "((jcr:content/@cq:tags = 'workflow:wcm' or jcr:content/@cq:tags = '/etc/tags/workflow/wcm' or jcr:like(jcr:content/@cq:tags, 'workflow:wcm/%') or jcr:like(jcr:content/@cq:tags, '/etc/tags/workflow/wcm/%')) and (jcr:content/@cq:tags = 'workflow:wcm/translation' or jcr:content/@cq:tags = '/etc/tags/workflow/wcm/translation' or jcr:like(jcr:content/@cq:tags, 'workflow:wcm/translation/%') or jcr:like(jcr:content/@cq:tags, '/etc/tags/workflow/wcm/translation/%)))";

Query #3 can also be produced in Java as:

PredicateGroup group = new PredicateGroup(); predicateGroup.setAllRequired(true); predicateGroup.add(new Predicate("tagid") .set("property", "cq:tags") .set("tagid", "example/a")); predicateGroup.add(new Predicate("tagid") .set("property", "cq:tags") .set("tagid", "example/b")); QueryBuilderImpl.createQuery(PredicateGroup, Session);

 

Is this a known issue?  Is there a patch for it?  Fixed in AEM 6?

We have worked around it by wrapping all tagid predicates in their own group, which then gets added to the outer predicateGroup.  The correct solution, however, is probably along the lines of:

// PredicateGroupEvaluator /*  94 */         xpath.append((String)exIter.next()); // Should be /*  -- */         xpath.append(getOpeningBracket()); /*  94 */         xpath.append((String)exIter.next()); /*  -- */         xpath.append(getClosingBracket());

Can you recommend a cleaner solution?

1 Accepted Solution

Avatar

Correct answer by
Former Community Member

We built this custom implementation over months of deciphering the poorly documented CQ Search API.  It is similar to the QueryBuilder servlet, but with better Predicate configuration and query language, and some custom domain logic.

I have raised the DayCare ticket #68923.  Is there a better channel to raise the issue on?

View solution in original post

3 Replies

Avatar

Level 10

This may be an unknown bug. I would recommend opening a ticket for this issue. 

Also - have you followed an online doc or article for this task or is this your own custom implementation?

Avatar

Correct answer by
Former Community Member

We built this custom implementation over months of deciphering the poorly documented CQ Search API.  It is similar to the QueryBuilder servlet, but with better Predicate configuration and query language, and some custom domain logic.

I have raised the DayCare ticket #68923.  Is there a better channel to raise the issue on?

Avatar

Level 10

I agree with the fact that we need to improve the Search API functionality content.  I will pass this information to the AEM docs team.

Also the community team will look at helping too by creating content and using an Ask the Experts session around this subject. 

Yeah - you used the correct channel to log the bug. 

Also - I'd recommend opening a bug against the AEM docs too. I would state that more information for Search API/examples is required. 

In meantime - i'd use your workaround. 

The community team will produce a community article around custom Predicates. Still a lot of community members do now know how to do this. 

The following has evaluated to null or missing: ==> liqladmin("SELECT id, value FROM metrics WHERE id = 'net_accepted_solutions' and user.id = '${acceptedAnswer.author.id}'").data.items [in template "analytics-container" at line 83, column 41] ---- Tip: It's the step after the last dot that caused this error, not those before it. ---- Tip: If the failing expression is known to be legally refer to something that's sometimes null or missing, either specify a default value like myOptionalVar!myDefault, or use <#if myOptionalVar??>when-present<#else>when-missing. (These only cover the last step of the expression; to cover the whole expression, use parenthesis: (myOptionalVar.foo)!myDefault, (myOptionalVar.foo)?? ---- ---- FTL stack trace ("~" means nesting-related): - Failed at: #assign answerAuthorNetSolutions = li... [in template "analytics-container" at line 83, column 5] ----