3
votes

This seems like a rather simple query but I've been banging my head for hours on this. I've got a node structure similar to below:

food-group
    jcr:content
        nuts -> type=almonds
        meat -> beef=true
        fruit -> type=apples,oranges,bananas

There are three types of properties I need to gather from my child nodes: one that is a string, boolean and string array. I would think that the following sql2 query would work and get the properties of them but for whatever reason I'm getting an error:

QUERY

SELECT 
    parent.* 
FROM 
    [cq:PageContent] AS parent 
INNER JOIN 
    [nt:base] as child ON ISCHILDNODE(parent) 
WHERE 
    ISDESCENDANTNODE(parent, [/content/grocerystore/food/])"

ERROR:

Need to specify the selector name because the query contains more than one selector.

Any help is appreciated as I've been at this for the past few days.

2

2 Answers

7
votes

There are two places the ISCHILDNODE function can be used in a JCR-SQL2 query: in the WHERE clause and in the join criteria. Unfortunately, they take different parameters.

Your query is attempting to use the ISCHILDNODE in the join criteria, which requires two parameters: the selector name for the child nodes, and the selector name for the parent nodes.

Here's the query I think you want:

SELECT parent.* 
FROM [cq:PageContent] AS parent 
INNER JOIN [nt:base] as child ON ISCHILDNODE(child,parent) 
WHERE ISDESCENDANTNODE(parent, [/content/grocerystore/food/])

The only change is the parameters to the ISCHILDNODE function.

Unfortunately, the error message doesn't really make this clear. To understand why, it helps to know about the other form of ISCHILDNODE - the one used in the WHERE clause. That form also takes two parameters: the name of the selector that will represent the child nodes, and the literal path of the parent (under which the resulting nodes are children). Here's a contrived query that uses this form:

SELECT node.*
FROM [nt:base] AS node
WHERE ISCHILDNODE(node,[/content/grocerystore])

The query result would contain all nodes that are children of the /content/grocerystore node.

Now, if the query defines just one selector (e.g., a non-join), then there is only one selector name that can be passed to the ISCHILDNODE function. Strictly speaking, the selector is implicitly known, so JCR-SQL2 allows you to pass in just the path. Here's a query that is semantically identical to the previous contrived example:

SELECT node.*
FROM [nt:base] AS node
WHERE ISCHILDNODE([/content/grocerystore])

This is the only form of the ISCHILDNODE that takes a single parameter, and I think this is the form that I think CQ5 is expecting: the error says that the query defines more than one selector, so the selector must be supplied as the first parameter.

Of course, this is very misleading because you're actually using the form of the function that appears in the join criteria. A better error message would say that function requires the child selector name and the parent selector name.

1
votes

This exception will be thrown if you invoke getNodes() method on a QueryResult that have more than one selector (in your case it's parent and child). This method will also be used if you call findResources() on the ResourceResolver.

Use the QueryResult#getRows() method instead:

Session session = resourceResolver.adaptTo(Session.class);
QueryManager queryManager = session.getWorkspace().getQueryManager();
Query query = queryManager.createQuery("...", Query.JCR_SQL2);
QueryResult result = query.execute();
// consider using result.getColumnNames() here
RowIterator rows = result.getRows();
while (rows.hasNext()) {
    Row row = rows.nextRow();
    // use row.getValue() or row.getValues()
}

A few remarks:

  1. You need to fix the SQL query adding child as the first parameter to the ISCHILDNODE(parent).
  2. Pass properties list explicite in the SELECT clause rather than using parent.*, so you don't have to invoke result.getColumnNames().
  3. The query is quite complex and the way to invoke it is even more complex. Why don't you use Sling methods to iterate over the food space in your repository? It seems to be a perfect use-case for iterating over the tree.
    • You may be also interested in the SlingQuery project.