Introduction
OOTB Apache OAK allow to create queries on SQL(deprecated), XPath(deprecated), JCR-JQOM and JCR-SQL2. It’s obvious that there is not a good point to explain deprecated SQL and XPath. This is a little How-To article which try to cower JCR-SQL2 queries for Apache Jackrabbit/OAK. Apache OAK has a more strict JCR-SQL2 syntax therefore some queries which are working correct on Apache Jackrabbit wouldn’t be executed on Apache OAK so we should always hold this in mind. Anyway let’s deal with JCR-SQL2 queries.
There is two ways how JCR-SQL2 query can be executed from Java code:
- JCR API:
Session session = ... ; Workspace workspace = session.getWorkspace(); QueryManager queryManager = workspace.getQueryManager(); Query query = queryManager.createQuery("{JCR-SQL2 query}", Query.JCR_SQL2); QueryResult result = query.execute();
- Sling API
ResourceResolver resolver = ...; Iterator<Resource> result = resolver.findResources("{JCR-SQL2 query}", Query.JCR_SQL2);
Note: If you obtaining some data from the user side for JCR-SQL2 query it must be escaped with
import org.apache.jackrabbit.oak.query.SQL2Parser; ... SQL2Parser.escapeStringLiteral("String for escape"); ...
SQL2Parcer can be added to maven as below dependency:
<dependency> <groupId>org.apache.jackrabbit</groupId> <artifactId>oak-core</artifactId> </dependency>
JCR-SQL2 Queries examples
Find pages
QUERY |
SELECT p.* FROM [cq:Page] AS p |
---|---|
DESCRIPTION | Selects all “cq:Page” nodes and set p as selector in query. You can querying any type of nodes which registered in JCR. |
Cast property value
By default we are using strings within queries constraints, but as JCR properties can hold not only values of String type we can cast string to required property type. We can cast to this property types: STRING, BINARY, DATE, LONG, DOUBLE, DECIMAL, BOOLEAN, NAME, PATH, REFERENCE, WEAKREFERENCE, URI.
QUERY |
SELECT p.* FROM [cq:Page] AS p WHERE p.[jcr:created] > CAST('2015-09-22T12:33:01.042Z' AS DATE) |
---|---|
DESCRIPTION | Selects all “cq:Page” nodes which was created after 2015-09-22 12:33:01.042. Also it’s possible to specify time with Time Zone as 2015-09-22T12:33:01.042+02:00 |
Find pages constrained by path
QUERY |
SELECT p.* FROM [cq:Page] AS p WHERE ISDESCENDANTNODE(p, [/content/geometrixx/en]) |
---|---|
DESCRIPTION | Selects all “cq:Page” nodes under the “/content/geometrixx/en” path |
Find pages with constraint by path and name
QUERY |
SELECT p.* FROM [cq:Page] AS p WHERE ISDESCENDANTNODE(p, [/content/geometrixx/en]) AND NAME(p)='geometrixx_user_grou' |
---|---|
DESCRIPTION | Selects all “cq:Page” nodes under the “/content/geometrixx/en” path and geometrixx_user_grou node name |
Find pages with inner nodes properties constrain
QUERY |
SELECT p.* FROM [cq:Page] AS p WHERE ISDESCENDANTNODE(p, [/content/geometrixx/en]) AND p.[jcr:content/author] = 'Henry Ford' |
---|---|
DESCRIPTION | Selects all “cq:Page” nodes under the “/content/geometrixx/en” path with author property within jcr:content node constrained to Henry Ford value. Note: jcr:content/author path can be replaced with any kind of nodes depth |
Search with LIKE
LIKE – is a predicate for regular expression search. Basically it works like “=” but supports additional regular expressions:
Wildcard | Description |
---|---|
% | A substitute for zero or more characters |
_ | A substitute for a single character |
QUERY |
SELECT p.* FROM [cq:PageContent] AS p WHERE p.[author] LIKE 'Henry Ford%' |
---|---|
DESCRIPTION | Selects all “cq:PageContent” nodes with author property starting with Henry Ford |
QUERY |
SELECT p.* FROM [cq:PageContent] AS p WHERE p.[author] LIKE '%Henry Ford%' |
---|---|
DESCRIPTION | Selects all “cq:PageContent” nodes with author property containing the pattern with Henry Ford |
QUERY |
SELECT p.* FROM [cq:PageContent] AS p WHERE p.[author] LIKE 'H_n_y Ford' |
---|---|
DESCRIPTION | Selects all “cq:PageContent” nodes with author property starting with H, followed by any character, followed by “n“, followed by any character, followed by “y Ford“ |
Full-text search with CONTAINS
CONTAINS – is a predicate for full-text search which allow to specify additional expression within search expression:
WILDCARD | DESCRIPTION |
---|---|
* | A substitute for zero or more characters |
OR | Add condition to the (UPPERCASE) |
{ } | For separate words within full-text expression. This is optional and can be omitted if you do not want to have a readable code |
QUERY |
SELECT p.* FROM [cq:PageContent] AS p WHERE CONTAINS(p.[author], '{Henry Ford}') |
---|---|
DESCRIPTION | Selects all “cq:PageContent” nodes with author property contains Henry Ford value |
QUERY |
SELECT p.* FROM [cq:PageContent] AS p WHERE CONTAINS(p.[author], '{Hen*}') |
---|---|
DESCRIPTION | Selects all “cq:PageContent” nodes with author property which contains Hen word followed by any characters |
QUERY |
SELECT p.* FROM [cq:PageContent] AS p WHERE CONTAINS(p.[author], '{Henry} OR {Josephine} OR {John Doe}') |
---|---|
DESCRIPTION | Selects all “cq:PageContent” nodes with author property which contains Henry or Josephine, or John Doe words |
If you are using full-text queries you should keep in mind that if content wasn’t indexed or indexes are corrupted – full-text queries returns nothing or some nodes will be skipped from a result.
JOIN the results
Apache OAK supports INNER JOIN, LEFT OUTER JOIN and RIGHT OUTER JOIN.
Just to refresh in memory:
- JOIN clause is used to combine rows from two or more tables, based on a common field between them
- INNER JOIN – returns all rows when there is at least one match in BOTH tables
- LEFT OUTER JOIN – return all rows from the left table, and the matched rows from the right table
- RIGHT OUTER JOIN – return all rows from the right table, and the matched rows from the left table
QUERY |
SELECT parent.* FROM [cq:Page] AS parent INNER JOIN [cq:PageContent] AS child ON ISCHILDNODE(child,parent) WHERE ISDESCENDANTNODE(parent, [/content/geometrixx-outdoors/en]) AND child.[cq:template] = '/libs/social/blog/templates/page' |
---|---|
DESCRIPTION | Selects all “cq:Page” nodes under the “/content/geometrixx/en” path with author property within cq:PageContent child node containing cq:template value matched to /libs/social/blog/templates/page. |
QUERY |
SELECT meta.* FROM [nt:base] AS meta INNER JOIN [cq:Taggable] AS taggable ON ISSAMENODE(meta,taggable) WHERE ISDESCENDANTNODE(meta, [/content/dam/geometrixx/banners]) |
---|---|
DESCRIPTION | Selects all “nt:base” nodes under the “/content/dam/geometrixx/banners” path which has cq:Taggable mixin. |
QUERY |
SELECT parent.* FROM [cq:Page] AS parent RIGHT OUTER JOIN [cq:PageContent] AS child ON ISCHILDNODE(child,parent) WHERE ISDESCENDANTNODE(parent, [/content]) |
---|---|
DESCRIPTION | Selects all “cq:Page” nodes which doesn’t has cq:PageContent node (this is search of corrupted nodes). |
UNION the results
The UNION operator is used to combine the result-set of two or more SELECT statements. Therefore below notice is mandatory.
Notice:
- each SELECT statement within the UNION must have the same number of columns.Data types of the columns should be compatible
- This easy rules allow to use UNION operator with fun.
QUERY |
SELECT parent.* FROM [cq:Page] AS parent RIGHT OUTER JOIN [cq:PageContent] AS child ON ISCHILDNODE(child,parent) WHERE ISDESCENDANTNODE(parent, [/content/site1/en/blog]) UNION SELECT parent.* FROM [cq:Page] AS parent RIGHT OUTER JOIN [cq:PageContent] AS child ON ISCHILDNODE(child,parent) WHERE ISDESCENDANTNODE(parent, [/content/site2/en/blog]) |
---|---|
DESCRIPTION | Selects all “cq:Page” nodes both on site1 and site2 which doesn’t has cq:PageContent child node (this is search of corrupted Pages). |