ModeShape includes full support for the complete JCR-SQL2 query language. However, ModeShape adds several extensions to make it even more powerful:
-
Support for the "FULL OUTER JOIN" and "CROSS JOIN" join types, in addition to the "LEFT OUTER JOIN", "RIGHT OUTER JOIN" and "INNER JOIN" types defined by JCR-SQL2. Note that "JOIN" is a shorthand for "INNER JOIN". For detail, see the grammar for joins.
-
Support for the UNION, INTERSECT, and EXCEPT set operations on multiple result sets to form a single result set. As with standard SQL, the result sets being combined must have the same columns. The UNION operator combines the rows from two result sets, the INTERSECT operator returns the difference between two result sets, and the EXCEPT operator returns the rows that are common to two result sets. Duplicate rows are removed unless the operator is followed by the ALL keyword. For detail, see the grammar for set queries.
-
Removal of duplicate rows in the results, using "SELECT DISTINCT ...". For detail, see the grammar for queries.
-
Limiting the number of rows in the result set with the "LIMIT count" clause, where count is the maximum number of rows that should be returned. This clause may optionally be followed by the "OFFSET number" clause to specify the number of initial rows that should be skipped. For detail, see the grammar for limits and offsets.
-
Additional dynamic operands "DEPTH(<selectorName>)" and "PATH(<selectorName>))" that enable placing constraints on the node depth and path, respectively. These dynamic operands can be used in a manner similar to "NAME(<selectorName>)" and "LOCALNAME(<selectorName>)" that are defined by JCR-SQL2. Note in each of these cases, the selector name is optional if there is only one selector in the query. For detail, see the grammar for dynamic operands.
-
Additional dynamic operand "REFERENCE(<selectorName>.<propertyName>)" and "REFERENCE(<propertyName>)" that enables placing constraints on one or any of the reference properties, respectively, and which can be used in a manner similar to " PropertyValue(<propertyName>)". Note in each of these cases, the selector name is optional if there is only one selector in the query, and that the property name can be excluded if the constraint should apply to all reference properties. For detail, see the grammar for dynamic operands.
-
Support for the IN and NOT IN clauses to more easily and concisely supply multiple of discrete static operands. For example, "WHERE ... [my:type].[prop1] IN (3,5,7,10,11,50) ...". For detail, see the grammar for set constraints.
-
Support for the BETWEEN clause to more easily and concisely supply a range of discrete operands. For example, "WHERE ... [my:type].[prop1] BETWEEN 3 EXCLUSIVE AND 10 ...". For detail, see the grammar for between constraints.
-
Support for simple arithmetic in numeric-based criteria and order-by clauses. For example, "... WHERESCORE(type1) +SCORE(type2) > 1.0" or "... ORDER BY(SCORE(type1) * SCORE(type2)) ASC,LENGTH(type2.property1) DESC". For detail, see the grammar for order-by clauses.
-
Support for (non-correlated) subqueries in the WHERE clause, wherever a static operand can be used. Subqueries can even be used within another subquery. All subqueries must return a single column, and each row's single value will be treated as a literal value. If the subquery is used in a clause that expects a single value (e.g., in a comparison), only the subquery's first row will be used. If the subquery is used in a clause that allows multiple values (e.g., IN (...)), then all of the subquery's rows will be used. For example, this query "WHERE ... [my:type].[prop1] IN (SELECT [my:prop2] FROM [my:type2] WHERE [my:prop3] < '1000' ) AND ..." will use the results of the subquery as the literal values in the IN clause.
-
Support for several pseudo-columns ("jcr:path", "jcr:score", "jcr:name", "mode:localName", and "mode:depth") that can be used in the SELECT, equijoin, and WHERE clauses. These pseudo-columns make it possible to return location-related and score information within the QueryResult's rows. They also make queries look more like SQL, and thus may be more friendly and easier to use in existing SQL-aware client applications. See the detailed description for more information.
The grammar for the JCR-SQL2 query language is actually a superset of that defined by the JCR 2.0 specification, and as such the complete grammar is included here.
Grammar
Queries
QueryCommand ::= Query | SetQuery
SetQuery ::= Query ('UNION'|'INTERSECT'|'EXCEPT') ['ALL'] Query
{ ('UNION'|'INTERSECT'|'EXCEPT') ['ALL'] Query }
Query ::= 'SELECT' ['DISTINCT'] columns
'FROM' Source
['WHERE' Constraint]
['ORDER BY' orderings]
[Limit]
Sources
Source ::= Selector | Join
Selector ::= nodeTypeName ['AS' selectorName]
nodeTypeName ::= Name
Joins
Join ::= left [JoinType] 'JOIN' right 'ON' JoinCondition
// If JoinType is omitted INNER is assumed.
left ::= Source
right ::= Source
JoinType ::= Inner | LeftOuter | RightOuter | FullOuter | Cross
Inner ::= 'INNER' ['JOIN']
LeftOuter ::= 'LEFT JOIN' | 'OUTER JOIN' | 'LEFT OUTER JOIN'
RightOuter ::= 'RIGHT OUTER' ['JOIN']
RightOuter ::= 'FULL OUTER' ['JOIN']
RightOuter ::= 'CROSS' ['JOIN']
JoinCondition ::= EquiJoinCondition | SameNodeJoinCondition |
ChildNodeJoinCondition | DescendantNodeJoinCondition
Equi-Join Conditions
EquiJoinCondition ::= selector1Name'.'property1Name '=' selector2Name'.'property2Name
selector1Name ::= selectorName
selector2Name ::= selectorName
property1Name ::= propertyName
property2Name ::= propertyName
Same-Node Join Conditions
SameNodeJoinCondition ::= 'ISSAMENODE(' selector1Name ',' selector2Name [',' selector2Path] ')'
selector2Path ::= Path
Child-Node Join Conditions
ChildNodeJoinCondition ::= 'ISCHILDNODE(' childSelectorName ',' parentSelectorName ')'
childSelectorName ::= selectorName
parentSelectorName ::= selectorName
Descendant-Node Join Conditions
DescendantNodeJoinCondition ::= 'ISDESCENDANTNODE(' descendantSelectorName
',' ancestorSelectorName ')'
descendantSelectorName ::= selectorName
ancestorSelectorName ::= selectorName
Constraints
Constraint ::= ConstraintItem | '(' ConstraintItem ')'
ConstraintItem ::= And | Or | Not | Comparison | Between | PropertyExistence |
SetConstraint | FullTextSearch | SameNode | ChildNode | DescendantNode
And Constraints
And ::= constraint1 'AND' constraint2
constraint1 ::= Constraint
constraint2 ::= Constraint
Or Constraints
Or ::= constraint1 'OR' constraint2
Comparison Constraints
Comparison ::= DynamicOperand Operator StaticOperand
Operator ::= '=' | '!=' | '<' | '<=' | '>' | '>=' | 'LIKE'
Between Constraints
Between ::= DynamicOperand ['NOT'] 'BETWEEN' lowerBound ['EXCLUSIVE']
'AND' upperBound ['EXCLUSIVE']
lowerBound ::= StaticOperand
upperBound ::= StaticOperand
Property Existence Constraints
PropertyExistence ::= selectorName'.'propertyName 'IS' ['NOT'] 'NULL' |
propertyName 'IS' ['NOT'] 'NULL' /* If only one selector exists in this query */
Set Constraints
SetConstraint ::= selectorName'.'propertyName ['NOT'] 'IN' |
propertyName ['NOT'] 'IN' /* If only one selector exists in this query */
'(' firstStaticOperand {',' additionalStaticOperand } ')'
firstStaticOperand ::= StaticOperand
additionalStaticOperand ::= StaticOperand
Full-text Search Constraints
FullTextSearch ::= 'CONTAINS(' ([selectorName'.']propertyName | selectorName'.*')
',' ''' fullTextSearchExpression''' ')'
/* If only one selector exists in this query, explicit specification of the selectorName
preceding the propertyName is optional */
fullTextSearchExpression ::= FulltextSearch
where FulltextSearch is defined by the following, and is the same as the full-text search language supported by ModeShape:
FulltextSearch ::= Disjunct {Space 'OR' Space Disjunct}
Disjunct ::= Term {Space Term}
Term ::= ['-'] SimpleTerm
SimpleTerm ::= Word | '"' Word {Space Word} '"'
Word ::= NonSpaceChar {NonSpaceChar}
Space ::= SpaceChar {SpaceChar}
NonSpaceChar ::= Char - SpaceChar /* Any Char except SpaceChar */
SpaceChar ::= ' '
Char ::= /* Any character */
Same-Node Constraint
SameNode ::= 'ISSAMENODE(' [selectorName ','] Path ')'
/* If only one selector exists in this query, explicit specification of the selectorName
preceding the path is optional */
Child-Node Constraints
ChildNode ::= 'ISCHILDNODE(' [selectorName ','] Path ')'
/* If only one selector exists in this query, explicit specification of the selectorName
preceding the path is optional */
Descendant-Node Constraints
DescendantNode ::= 'ISDESCENDANTNODE(' [selectorName ','] Path ')'
/* If only one selector exists in this query, explicit specification of the selectorName
preceding the propertyName is optional */
Paths and Names
Name ::= '[' quotedName ']' | '[' simpleName ']' | simpleName
quotedName ::= /* A JCR Name (see the JCR specification) */
simpleName ::= /* A JCR Name that contains only SQL-legal
characters (namely letters, digits, and underscore) */
Path ::= '[' quotedPath ']' | '[' simplePath ']' | simplePath
quotedPath ::= /* A JCR Path that contains non-SQL-legal characters */
simplePath ::= /* A JCR Path (rather Name) that contains only SQL-legal
characters (namely letters, digits, and underscore) */
Static Operands
StaticOperand ::= Literal | BindVariableValue | Subquery
Literal
Literal ::= CastLiteral | UncastLiteral
CastLiteral ::= 'CAST(' UncastLiteral ' AS ' PropertyType ')'
PropertyType ::= 'STRING' | 'BINARY' | 'DATE' | 'LONG' | 'DOUBLE' | 'DECIMAL' |
'BOOLEAN' | 'NAME' | 'PATH' | 'REFERENCE' | 'WEAKREFERENCE' | 'URI'
/* 'WEAKREFERENCE' is not currently supported in JCR 1.0 */
UncastLiteral ::= UnquotedLiteral | ''' UnquotedLiteral ''' | '"' UnquotedLiteral '"'
UnquotedLiteral ::= /* String form of a JCR Value, as defined in the JCR specification */
Bind Variables
BindVariableValue ::= '$'bindVariableName
bindVariableName ::= /* A string that conforms to the JCR Name syntax, though the prefix
does not need to be a registered namespace prefix. */
Subqueries
Subquery ::= '(' QueryCommand ')' |
QueryCommand
Dynamic Operands
DynamicOperand ::= PropertyValue | ReferenceValue | Length | NodeName | NodeLocalName | NodePath |
NodeDepth | FullTextSearchScore | LowerCase | UpperCase | Arithmetic |
'(' DynamicOperand ')'
PropertyValue ::= [selectorName'.'] propertyName
/* If only one selector exists in this query, explicit specification of the selectorName
preceding the propertyName is optional */
ReferenceValue ::= 'REFERENCE(' selectorName '.' propertyName ')' |
'REFERENCE(' selectorName ')' |
'REFERENCE()' |
/* If only one selector exists in this query, explicit specification of the selectorName
preceding the propertyName is optional. Also, the property name may be excluded
if the constraint should apply to any reference property. */
Length ::= 'LENGTH(' PropertyValue ')'
NodeName ::= 'NAME(' [selectorName] ')'
/* If only one selector exists in this query, explicit specification of the selectorName
is optional */
NodeLocalName ::= 'LOCALNAME(' [selectorName] ')'
/* If only one selector exists in this query, explicit specification of the selectorName
is optional */
NodePath ::= 'PATH(' [selectorName] ')'
/* If only one selector exists in this query, explicit specification of the selectorName
is optional */
NodeDepth ::= 'DEPTH(' [selectorName] ')'
/* If only one selector exists in this query, explicit specification of the selectorName
is optional */
FullTextSearchScore ::= 'SCORE(' [selectorName] ')'
/* If only one selector exists in this query, explicit specification of the selectorName
is optional */
LowerCase ::= 'LOWER(' DynamicOperand ')'
UpperCase ::= 'UPPER(' DynamicOperand ')'
Arithmetic ::= DynamicOperand ('+'|'-'|'*'|'/') DynamicOperand
Ordering
orderings ::= Ordering {',' Ordering}
Ordering ::= DynamicOperand [Order]
Order ::= 'ASC' | 'DESC'
Columns
columns ::= (Column ',' {Column}) | '*'
Column ::= ([selectorName'.']propertyName ['AS' columnName]) | (selectorName'.*')
/* If only one selector exists in this query, explicit specification of the selectorName
preceding the propertyName is optional */
selectorName ::= Name
propertyName ::= Name
columnName ::= Name
Limit and Offset
Limit ::= 'LIMIT' count [ 'OFFSET' offset ]
count ::= /* Positive integer value */
offset ::= /* Non-negative integer value */
Pseudo-columns
The design of the JCR-SQL2 query language makes fairly heavy use of functions, including SCORE(), NAME(), and LOCALNAME(). ModeShape adds several more useful functions, including PATH() and DEPTH(), that follow the same patterns.
However, there are several disadvantages of these functions. First, they make the JCR-SQL2 language less "SQL-like", since SQL-92 and -99 don't define these kinds of functions. (There are aggregate functions, like COUNT, SUM, etc., but they are not terribly analogous.) This means that applications that use SQL and SQL-like query languages are less likely to be able to build and issue JCR-SQL2 queries.
A second disadvantage of these functions is that JCR-SQL2 does not allow them to be used within the SELECT clause. As a result, the location-related and score information cannot be included as columns of values in the QueryResult rows. Instead, a client can only access this information by obtaining the Node object(s) for each row. Relying upon both the result set and additional Java objects makes it difficult to use.
For example, ModeShape's JDBC driver is designed to enable JDBC-aware applications to query repository content using JCR-SQL2 queries. The standard JDBC API cannot expose the Node objects, so the only way to return the path-related and score information is through additional columns in the result. While such columns could "magically" appear in the result set, doing this is not compatible with JDBC applications that dynamically build queries based upon database metadata. Such applications require the columns to be properly described in database metadata, and the columns need to be used within queries.
ModeShape attempts to solve these issues by directly supporting a number of "pseudo-columns" within JCR-SQL2 queries, wherever columns can be used. These "pseudo-columns" include:
-
jcr:score is a column of type DOUBLE that represents the full-text search score of the node, which is a measure of the node's relevance to the full-text search expression. ModeShape does compute the scores for all queries, though the score for rows in queries that do not include a full-text search criteria may not be reliable.
-
jcr:path is a column of type PATH that represents the normalized path of a node, including same-name siblings. This is the same as what would be returned by the getPath() method of Node. Examples of paths include "/jcr:system" and "/foo/bar[3]".
-
jcr:name is a column of type NAME that represents the node name in its namespace-qualified form using namespace prefixes and excluding same-name-sibling indexes. Examples of node names include "jcr:system", "jcr:content", "ex:UserData", and "bar".
-
mode:localName is a column of type STRING that represents the local name of the node, which excludes the namespace prefix and same-name-sibling index. As an example, the local name of the "jcr:system" node is "system", while the local name of the "ex:UserData[3]" node is "UserData".
-
mode:depth is a column of type LONG that represents the depth of a node, which corresponds exactly to the number of path segments within the path. For example, the depth of the root node is 0, whereas the depth of the "/jcr:system/jcr:nodeTypes" node is 2.
All of these pseudo-columns can be used in the SELECT clause of any JCR-SQL2 query, and their use defines whether such columns appear in the result set. In fact, all of these pseudo-columns will be included when "SELECT *" clauses in JCR-SQL2 queries are expanded by the query engine. This means that every node type (even mixin node types that have no properties and are essentially markers) are represented by a queryable table.
Like any other column, all of these pseudo-columns can be also be used in the WHERE clause of any JCR-SQL2 query, even if they are not included in the SELECT clause. They can be used anywhere that a regular column can be used, including within constraints and dynamic operands. ModeShape will automatically rewrite queries that use pseudo-columns in the dynamic operands to use the corresponding function, such as SCORE(), PATH(), NAME(), LOCALNAME(), and DEPTH(). Additionally, any property existence constraint using these pseudo-columns will always evaluate to 'true' (and will thus be removed by the optimizer).
The jcr:path pseudo-column may also be used on both sides of an equijoin constraint clause. For example:
... selector1.[jcr:path] = selector2.[jcr:path] ...
Equijoins of this form will be automatically rewritten by the optimizer to the following form:
... ISSAMENODE(selector1,selector2) ...
As with regular columns, the pseudo-columns must be qualified with the selector name if the query contains more than one selector.
Note that the jcr:path and jcr:score pseudo-columns are consistent with the pseudo-columns of the same names used in JCR-SQL query language. However, unlike in JCR-SQL, in JCR-SQL2 these columns are not automatically included in the results unless explicitly included in the SELECT clause or implicitly included via "SELECT *"
Example JCR-SQL2 queries
One of the simplest JCR-SQL2 queries finds all nodes in the current workspace of the repository:
This query will return a result set containing the "jcr:primaryType" column, since the nt:base defines only one single-valued property called "jcr:primaryType". (As allowed by the JCR 2.0 Specification, ModeShape does not currently support returning multi-valued columns in result sets, and thus the above query does not include a column for the jcr:mixinTypes multi-valued property.)
Since our query used "SELECT *", ModeShape also includes the non-standard pseudo-columns mentioned above: "jcr:path", "jcr:score", "jcr:name", "mode:localName", and "mode:depth". These columns are very convenient to have in the results, but also make certain criteria much easier than with the corresponding standard or ModeShape-specific functions.
Queries can explicitly specify the columns that are to be returned in the results. The following query is very similar to the previous query and will return the same rows, but the result set will have only a single column:
SELECT [jcr:primaryType] FROM [nt:base]
The following query will return the same rows as in the previous two queries, but the SELECT clause explicitly includes only two of the pseudo-columns for the path and depth (which are computed from the nodes' locations):
SELECT [jcr:primaryType], [jcr:path], [mode:depth] FROM [nt:base]
In JCR-SQL2, a table representing a particular node type will have a column for each of the node type's property definitions, including those inherited from supertypes. For example, the nt:file node type, its nt:hierarchyNode supertype, and the mix:created mixin type are defined using the CND notation as follows:
[mix:created] mixin
- jcr:created (date) protected
- jcr:createdBy (string) protected
[nt:hierarchyNode] > mix:created abstract
[nt:file] > nt:hierarchyNode
+ jcr:content (nt:base) primary mandatory
Therefore, the table representing the nt:file node type will have two three columns: the jcr:created and jcr:createdBy columns inherited from the mix:created mixin node type (via the nt:hierarchyNode node type), and the jcr:primaryType column inherited from the nt:base node type, which is the implicit supertype of the nt:hierarchyNode.
ModeShape adheres to this behavior with the exception that a "SELECT *" will result in the additional pseudo-columns. Thus, this next query:
is equivalent to this query:
SELECT [jcr:primaryType], [jcr:created], [jcr:createdBy],
[jcr:path], [jcr:name], [jcr:score], [mode:localName], [mode:depth]
FROM [nt:file]
Here is an example query that selects some of the available columns from the nt:file table and uses a constraint to ensure the resulting file nodes have names that end in '.txt':
SELECT [jcr:primaryType], [jcr:created], [jcr:createdBy], [jcr:path] FROM [nt:file]
WHERE LOCALNAME() LIKE '%.txt'
This query is JCR-SQL2, but ModeShape supports placing criteria against the mode:localName pseudo-column instead of using the LOCALNAME() function. Such a query is equivalent to the previous query and will produce the exact same results:
SELECT [jcr:primaryType], [jcr:created], [jcr:createdBy], [jcr:path] FROM [nt:file]
WHERE [mode:localName] LIKE '%.txt'
Although this query looks much more like SQL, the use of the '[' and ']' characters to quote the identifiers is not typical of a SQL dialect. ModeShape actually supports the using double-quote characters and square braces interchangeably around identifiers (although they must match around any single identifier). Again, this next query, which looks remarkably like any SQL-92 or -99 dialect, is functionally identical to the previous two queries:
SELECT "jcr:primaryType", "jcr:created", "jcr:createdBy", "jcr:path" FROM "nt:file"
WHERE "mode:localName" LIKE '%.txt'
In JCR-SQL2, a node will appear as a row in each table that corresponds to the node types defined by that node's primary type or mixin types, or any supertypes of these node types. In other words, a node will appear in the table corresponding to each node type for which Node.isNodeType(...) returns true.
For example, consider a node that has a primary type of nt:file but has an explicit mixin of mix:referenceable. This node will appear as a row in the all of these tables: nt:file, mix:referenceable, nt:hierarchyNode, mix:created, and nt:base. However, the columns in each of these tables will differ. The nt:file node type has the nt:hierarchyNode, mix:created, and nt:base for supertypes, and therefore the table for nt:file contains columns for the property definitions on all of these types. But because mix:referenceable is not a supertype of nt:file, the table for nt:file will not contain a jcr:uuid column. To obtain a single result set that contains columns for all the properties of our node, we need to perform an identity join. The next query shows how this is done to return all properties for nt:file nodes that are also mix:referenceable:
SELECT file.*, ref.* FROM [nt:file] AS file JOIN [mix:referenceable] AS ref
JOIN ON ISSAMENODE(file,ref)
ModeShape expands the SELECT clause to include the columns for all (explicit and inherited) property definitions of each type plus pseudo-columns for each type:
SELECT file.[jcr:primaryType], file.[jcr:created], file.[jcr:createdBy], ref.[jcr:uuid],
file.[jcr:path], file.[jcr:name], file.[jcr:score], file.[mode:localName], file.[mode:depth],
ref.[jcr:path], ref.[jcr:name], ref.[jcr:score], ref.[mode:localName], ref.[mode:depth]
FROM [nt:file] AS file JOIN [mix:referenceable] AS ref
ON ISSAMENODE(file,ref)
Note because we are using an identity join, the "file.[jcr:path]" column will contain the same value as the "ref.[jcr:path]". Therefore, as is typical with standard SQL and relational databases, with JCR-SQL2 you will probably want to fully expand the SELECT clause to be exactly what you want. Here is a query that does this by eliminating columns with duplicate values and using aliases that are simpler than the namespace-qualified names:
SELECT file.[jcr:primaryType] AS primaryType,
file.[jcr:created] AS created,
file.[jcr:createdBy] AS createdBy,
ref.[jcr:uuid] AS uuid,
file.[jcr:path] AS path,
file.[jcr:name] AS name,
file.[jcr:score] AS score,
file.[mode:localName] AS localName,
file.[mode:depth] AS depth
FROM [nt:file] AS file JOIN [mix:referenceable] AS ref
ON ISSAMENODE(file,ref)
Although this query looks much more like SQL, JCR-SQL2's use of the '[' and ']' characters to quote the identifiers is not typical of a SQL dialect. ModeShape actually supports the using double-quote characters and square braces interchangeably around identifiers (although they must match around any single identifier). This makes it easier for existing SQL-oriented tools and applications to work more readily with ModeShape, including applications that use ModeShape's JDBC driver to query a ModeShape JCR repository.
Again, this next query, which looks remarkably like any SQL-92 or -99 dialect, is functionally identical to the previous query, except that it uses double quotes and a pseudo-column identity constraint on "jcr:path" (which is identical in semantics and performance as the "ISSAMENODE(...)" constraint):
SELECT file."jcr:primaryType" AS primaryType,
file."jcr:created" AS created,
file."jcr:createdBy" AS createdBy,
ref."jcr:uuid" AS uuid,
file."jcr:path" AS path,
file."jcr:name" AS name,
file."jcr:score" AS score,
file."mode:localName" AS localName,
file."mode:depth" AS depth
FROM "nt:file" AS file JOIN "mix:referenceable" AS ref
ON file."jcr:path" = ref."jcr:path"
These are examples of two-way inner joins, but ModeShape supports joining multiple tables together in a single query. ModeShape also supports a variety of joins, including INNER JOIN (or just JOIN), LEFT OUTER JOIN, RIGHT OUTER JOIN, FULL OUTER JOIN, and CROSS JOIN.
ModeShape also supports several other query features beyond JCR-SQL2. One of these is support for UNION, INTERSECT, EXCEPT, UNION ALL, INTERSECT ALL and EXCEPT ALL. Here is an example of a union:
SELECT [jcr:primaryType], [jcr:created], [jcr:createdBy], [jcr:path] FROM [nt:file]
UNION
SELECT [jcr:primaryType], [jcr:created], [jcr:createdBy], [jcr:path] FROM [nt:folder]
ModeShape also supports using (non-correlated) subqueries within the WHERE clause, wherever a static operand can be used. Subqueries can even be used within another subquery. All subqueries, though, should return a single column (all other columns will be ignored), and each row's single value will be treated as a literal value. If the subquery is used in a clause that expects a single value (e.g., in a comparison), only the subquery's first row will be used.
Subqueries in ModeShape are a powerful and easy way to use more complex criteria that is a function of the content in the repository, without having to resort to multiple queries (take the results of one query and dynamically generate the criteria of another query).
Here's an example of a query that finds all nt:file nodes in the repository whose paths are referenced in the value of the vdb:originalFile property of the vdb:virtualDatabase nodes. (This query also uses the "$maxVersion" bind variables in the subquery.)
SELECT [jcr:primaryType], [jcr:created], [jcr:createdBy], [jcr:path] FROM [nt:file]
WHERE PATH() IN (
SELECT [vdb:originalFile] FROM [vdb:virtualDatabase]
WHERE [vdb:version] <= $maxVersion AND CONTAINS([vdb:description],'xml OR xml maybe')
)
Without subqueries, this query would need to be broken into two separate queries: the first would find all of the paths referenced by the vdb:virtualDatabase nodes matching the version and description criteria, followed by one (or more) subsequent queries to find the nt:file nodes with the paths expressed as literal values (or bind variables). Using a subquery is not only easier to implement and understand, it is actually more efficient.
The examples shown in this section hopefully begin to show the power and flexibility of JCR-SQL2 and the ModeShape extensions.