JCR Query API Producing Unexpected XPath
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:
- http://localhost:4502/bin/querybuilder.json?type=cq:Page&tagid=workflow:wcm/translation&tagid.property=jcr:content/cq:tags
- http://localhost:4502/bin/querybuilder.json?type=cq:Page&tagid=workflow:wcm&tagid.property=jcr:content/cq:tags
- http://localhost:4502/bin/querybuilder.json?type=cq:Page&group.p.or=false&group.1_tagid=workflow:wcm&group.1_tagid.property=jcr:content/cq:tags&group.2_tagid=workflow:wcm/translation&group.2_tagid.property=jcr:content/cq:tags
- http://localhost:4502/bin/querybuilder.json?type=cq:Page&group.p.or=false&group.1_tagid=workflow:dam&group.1_tagid.property=jcr:content/cq:tags&group.2_tagid=workflow:wcm&group.2_tagid.property=jcr:content/cq:tags
- http://localhost:4502/bin/querybuilder.json?type=cq:Page&group.p.or=false&group.1_tagid=workflow:wcm&group.1_tagid.property=jcr:content/cq:tags&group.2_tagid=workflow:dam&group.2_tagid.property=jcr:content/cq:tags
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?