Author: rhauch
Date: 2009-11-17 18:21:38 -0500 (Tue, 17 Nov 2009)
New Revision: 1326
Modified:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/FullTextSearchParser.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/SqlQueryParser.java
Log:
DNA-467 Documented the SQL and full-text search grammars in the JavaDoc for each parser.
Modified:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/FullTextSearchParser.java
===================================================================
---
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/FullTextSearchParser.java 2009-11-17
21:09:49 UTC (rev 1325)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/FullTextSearchParser.java 2009-11-17
23:21:38 UTC (rev 1326)
@@ -38,6 +38,24 @@
/**
* A {@link QueryParser} implementation that parses a full-text search expression. This
grammar is based on the full-text search
* grammar as defined by the JCR 2.0 specification.
+ * <p>
+ * </p>
+ * <h3>Grammar</h3>
+ * <p>
+ * The grammar for the full-text expression is taken from the JCR 2.0 specification, and
is as follows:
+ * </p>
+ *
+ * <pre>
+ * 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 */
+ * </pre>
*/
public class FullTextSearchParser {
Modified:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/SqlQueryParser.java
===================================================================
---
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/SqlQueryParser.java 2009-11-17
21:09:49 UTC (rev 1325)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/parse/SqlQueryParser.java 2009-11-17
23:21:38 UTC (rev 1326)
@@ -97,13 +97,342 @@
/**
* A {@link QueryParser} implementation that parses a subset of SQL select and set
queries.
* <p>
- * This grammar is based on the SQL grammar as defined by the JCR 2.0 specification, with
some useful additions:
+ * This grammar is equivalent to the SQL grammar as defined by the JCR 2.0 specification,
with some useful additions:
* <ul>
- * <li>(UNION|INTERSECT|EXCEPT) [ALL]</li>
- * <li>SELECT DISTINCT</li>
- * <li>LIMIT clauses</li>
+ * <li>"<code>... (UNION|INTERSECT|EXCEPT) [ALL] ...</code>"
to combine and merge results from multiple queries</li>
+ * <li>"<code>SELECT DISTINCT ...</code>" to remove
duplicates</li>
+ * <li>"<code>LIMIT count [OFFSET number]</code>" clauses to
control the number of results returned as well as the number of rows
+ * that should be skipped</li>
+ * <li>Support for additional join types, including "<code>FULL OUTER
JOIN</code>" and "<code>CROSS JOIN</code>"</li>
+ * <li>Additional dynamic operands
"<code>DEPTH([<selectorName>])</code>" and
"<code>PATH([<selectorName>])</code>" that
+ * enables placing constraints on the node depth and path, respectively, and which can be
used in a manner similar to "
+ * <code>NAME([<selectorName>])</code>" and
"<code>LOCALNAME([<selectorName>])</code>. Note in each of
these cases, the
+ * selector name is optional if there is only one selector in the query. on the node
depth</li>
+ * <li>Support for the IN clause and NOT IN clause to more easily supply a list of
valid discrete static operands: "
+ * <code><dynamicOperand> [NOT] IN (<staticOperand> {,
<staticOperand>})</code>"</li>
+ * <li>Support for the BETWEEN clause: "<code><dynamicOperand>
[NOT] BETWEEN <lowerBoundStaticOperand> [EXCLUSIVE] AND
+ * <upperBoundStaticOperand> [EXCLUSIVE]</code>"</i>
* </ul>
* </p>
+ * <h3>SQL grammar</h3>
+ * <p>
+ * This section defines the complete grammar for the SQL dialect supported by this
parser.
+ * </p>
+ * <h4>Queries</h4>
+ *
+ * <pre>
+ * QueryCommand ::= Query | SetQuery
+ *
+ * SetQuery ::= Query ('UNION'|'INTERSECT'|'EXCEPT') [ALL] Query
+ * { ('UNION'|'INTERSECT'|'EXCEPT') [ALL] Query
}
+ *
+ * Query ::= 'SELECT' ['DISINCT'] columns
+ * 'FROM' Source
+ * ['WHERE' Constraint]
+ * ['ORDER BY' orderings]
+ * [Limit]
+ * </pre>
+ *
+ * <h4>Sources</h4>
+ *
+ * <pre>
+ * Source ::= Selector | Join
+ *
+ * Selector ::= nodeTypeName ['AS' selectorName]
+ *
+ * nodeTypeName ::= Name
+ * </pre>
+ *
+ * <h4>Joins</h4>
+ *
+ * <pre>
+ * 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
+ * </pre>
+ *
+ * <h5>Equi-join conditions</h5>
+ *
+ * <pre>
+ * EquiJoinCondition ::= selector1Name'.'property1Name '='
selector2Name'.'property2Name
+ *
+ * selector1Name ::= selectorName
+ * selector2Name ::= selectorName
+ * property1Name ::= propertyName
+ * property2Name ::= propertyName
+ * </pre>
+ *
+ * <h5>Same-node join condition</h5>
+ *
+ * <pre>
+ * SameNodeJoinCondition ::= 'ISSAMENODE(' selector1Name ','
selector2Name [',' selector2Path] ')'
+ *
+ * selector2Path ::= Path
+ * </pre>
+ *
+ * <h5>Child-node join condition</h5>
+ *
+ * <pre>
+ * ChildNodeJoinCondition ::= 'ISCHILDNODE(' childSelectorName ','
parentSelectorName ')'
+ *
+ * childSelectorName ::= selectorName
+ * parentSelectorName ::= selectorName
+ * </pre>
+ *
+ * <h5>Descendant-node join condition</h5>
+ *
+ * <pre>
+ * DescendantNodeJoinCondition ::= 'ISDESCENDANTNODE(' descendantSelectorName
',' ancestorSelectorName ')'
+ * descendantSelectorName ::= selectorName
+ * ancestorSelectorName ::= selectorName
+ * </pre>
+ *
+ * <h4>Constraints</h4>
+ *
+ * <pre>
+ * Constraint ::= ConstraintItem | '(' ConstraintItem ')'
+ *
+ * ConstraintItem ::= And | Or | Not | Comparison | Between | PropertyExistence |
SetConstraint | FullTextSearch |
+ * SameNode | ChildNode | DescendantNode
+ * </pre>
+ *
+ * <h5>And constraint</h5>
+ *
+ * <pre>
+ * And ::= constraint1 'AND' constraint2
+ *
+ * constraint1 ::= Constraint
+ * constraint2 ::= Constraint
+ * </pre>
+ *
+ * <h5>Or constraint</h5>
+ *
+ * <pre>
+ * Or ::= constraint1 'OR' constraint2
+ * </pre>
+ *
+ * <h5>Not constraint</h5>
+ *
+ * <pre>
+ * Not ::= 'NOT' Constraint
+ * </pre>
+ *
+ * <h5>Comparison constraint</h5>
+ *
+ * <pre>
+ * Comparison ::= DynamicOperand Operator StaticOperand
+ *
+ * Operator ::= '=' | '!=' | '<' | '<=' |
'>' | '>=' | 'LIKE'
+ * </pre>
+ *
+ * <h5>Between constraint</h5>
+ *
+ * <pre>
+ * Between ::= DynamicOperand ['NOT'] 'BETWEEN' lowerBound
['EXCLUSIVE'] 'AND' upperBound ['EXCLUSIVE']
+ *
+ * lowerBound ::= StaticOperand
+ * upperBound ::= StaticOperand
+ * </pre>
+ *
+ * <h5>Property existence constraint</h5>
+ *
+ * <pre>
+ * PropertyExistence ::= selectorName'.'propertyName 'IS' ['NOT']
'NULL' |
+ * propertyName 'IS' ['NOT'] 'NULL'
/* If only one selector exists in this query */
+ *
+ * </pre>
+ *
+ * <h5>Set constraint</h5>
+ *
+ * <pre>
+ * SetConstraint ::= selectorName'.'propertyName ['NOT'] 'IN' |
+ * propertyName ['NOT'] 'IN' /* If only one
selector exists in this query */
+ * '(' firstStaticOperand {','
additionalStaticOperand } ')'
+ * firstStaticOperand ::= StaticOperand
+ * additionalStaticOperand ::= StaticOperand
+ * </pre>
+ *
+ * <h5>Full-text search constraint</h5>
+ *
+ * <pre>
+ * 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 ::= /* a full-text search expression, see {@link
FullTextSearchParser} */
+ * </pre>
+ *
+ * <h5>Same-node constraint</h5>
+ *
+ * <pre>
+ * SameNode ::= 'ISSAMENODE(' [selectorName ','] Path ')'
+ * /* If only one selector exists in this query, explicit
specification of the selectorName
+ * preceding the propertyName is optional */
+ * </pre>
+ *
+ * <h5>Child-node constraint</h5>
+ *
+ * <pre>
+ * ChildNode ::= 'ISCHILDNODE(' [selectorName ','] Path ')'
+ * /* If only one selector exists in this query, explicit
specification of the selectorName
+ * preceding the propertyName is optional */
+ * </pre>
+ *
+ * <h5>Descendant-node constraint</h5>
+ *
+ * <pre>
+ * DescendantNode ::= 'ISDESCENDANTNODE(' [selectorName ','] Path
')'
+ * /* If only one selector exists in this query, explicit
specification of the selectorName
+ * preceding the propertyName is optional */
+ * </pre>
+ *
+ * <h5>Paths and names</h5>
+ *
+ * <pre>
+ *
+ * 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) */
+ * </pre>
+ *
+ * <h4>Static operands</h4>
+ *
+ * <pre>
+ * StaticOperand ::= Literal | BindVariableValue
+ * </pre>
+ *
+ * <h5>Literal</h5>
+ *
+ * <pre>
+ * 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
*/
+ * </pre>
+ *
+ * <h5>Bind variables</h5>
+ *
+ * <pre>
+ * BindVariableValue ::= '$'bindVariableName
+ *
+ * bindVariableName ::= /* A string that conforms to the JCR Name syntax, though the
prefix does not need to be
+ * a registered namespace prefix. */
+ * </pre>
+ *
+ * <h4>Dynamic operands</h4>
+ *
+ * <pre>
+ * DynamicOperand ::= PropertyValue | Length | NodeName | NodeLocalName | NodePath |
NodeDepth |
+ * FullTextSearchScore | LowerCase | UpperCase
+ * </pre>
+ * <h5>Property value</h5>
+ * <pre>
+ * PropertyValue ::= [selectorName'.'] propertyName
+ * /* If only one selector exists in this query, explicit
specification of the selectorName
+ * preceding the propertyName is optional */
+ * </pre>
+ * <h5>Property length</h5>
+ * <pre>
+ * Length ::= 'LENGTH(' PropertyValue ')'
+ * </pre>
+ * <h5>Node name</h5>
+ * <pre>
+ * NodeName ::= 'NAME(' [selectorName] ')'
+ * /* If only one selector exists in this query, explicit
specification of the selectorName
+ * is optional */
+ * </pre>
+ * <h5>Node local name</h5>
+ * <pre>
+ * NodeLocalName ::= 'LOCALNAME(' [selectorName] ')'
+ * /* If only one selector exists in this query, explicit
specification of the selectorName
+ * is optional */
+ * </pre>
+ * <h5>Node path</h5>
+ * <pre>
+ * NodePath ::= 'PATH(' [selectorName] ')'
+ * /* If only one selector exists in this query, explicit
specification of the selectorName
+ * is optional */
+ * </pre>
+ * <h5>Node depth</h5>
+ * <pre>
+ * NodeDepth ::= 'DEPTH(' [selectorName] ')'
+ * /* If only one selector exists in this query, explicit
specification of the selectorName
+ * is optional */
+ * </pre>
+ * <h5>Full-text search score</h5>
+ * <pre>
+ * FullTextSearchScore ::= 'SCORE(' [selectorName] ')'
+ * /* If only one selector exists in this query, explicit
specification of the selectorName
+ * is optional */
+ * </pre>
+ * <h5>Lowercase</h5>
+ * <pre>
+ * LowerCase ::= 'LOWER(' DynamicOperand ')'
+ * </pre>
+ * <h5>Uppercase</h5>
+ * <pre>
+ * UpperCase ::= 'UPPER(' DynamicOperand ')'
+ * </pre>
+ *
+ * <h4>Ordering</h4>
+ *
+ * <pre>
+ * orderings ::= Ordering {',' Ordering}
+ *
+ * Ordering ::= DynamicOperand [Order]
+ *
+ * Order ::= 'ASC' | 'DESC'
+ * </pre>
+ *
+ * <h4>Columns</h4>
+ *
+ * <pre>
+ * 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
+ * </pre>
+ *
+ * <h4>Limit</h4>
+ *
+ * <pre>
+ * Limit ::= 'LIMIT' count [ 'OFFSET' offset ]
+ * count ::= /* Positive integer value */
+ * offset ::= /* Non-negative integer value */
+ * </pre>
*/
public class SqlQueryParser implements QueryParser {
@@ -255,11 +584,11 @@
} else if (tokens.canConsume("OUTER", "JOIN") ||
tokens.canConsume("LEFT", "JOIN")
|| tokens.canConsume("LEFT", "OUTER",
"JOIN")) {
joinType = JoinType.LEFT_OUTER;
- } else if (tokens.canConsume("RIGHT", "OUTER",
"JOIN")) {
+ } else if (tokens.canConsume("RIGHT", "OUTER",
"JOIN") || tokens.canConsume("RIGHT", "OUTER")) {
joinType = JoinType.RIGHT_OUTER;
- } else if (tokens.canConsume("FULL", "OUTER",
"JOIN")) {
+ } else if (tokens.canConsume("FULL", "OUTER",
"JOIN") || tokens.canConsume("FULL", "OUTER")) {
joinType = JoinType.FULL_OUTER;
- } else if (tokens.canConsume("CROSS", "JOIN")) {
+ } else if (tokens.canConsume("CROSS", "JOIN") ||
tokens.canConsume("CROSS")) {
joinType = JoinType.CROSS;
}
if (joinType == null) break;