Author: rhauch
Date: 2009-12-17 20:54:27 -0500 (Thu, 17 Dec 2009)
New Revision: 1455
Modified:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/QueryEngine.java
trunk/dna-integration-tests/src/test/resources/log4j.properties
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrLexicon.java
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrQueryManager.java
trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckTest.java
Log:
DNA-468 Completed more of the integration, including the addition of the mandatory
'jcr:path' and 'jcr:score' so-called 'pseudo-columns' to the
results for XPath queries. However, there still are issues with the TCK query-related
tests, so they are not enabled at the moment.
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/QueryEngine.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/QueryEngine.java 2009-12-18
01:53:48 UTC (rev 1454)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/QueryEngine.java 2009-12-18
01:54:27 UTC (rev 1455)
@@ -33,6 +33,7 @@
import org.jboss.dna.graph.query.optimize.Optimizer;
import org.jboss.dna.graph.query.optimize.RuleBasedOptimizer;
import org.jboss.dna.graph.query.plan.CanonicalPlanner;
+import org.jboss.dna.graph.query.plan.PlanHints;
import org.jboss.dna.graph.query.plan.PlanNode;
import org.jboss.dna.graph.query.plan.Planner;
import org.jboss.dna.graph.query.plan.PlanNode.Property;
@@ -99,7 +100,7 @@
// Find the query result columns ...
start = System.nanoTime();
- resultColumns = determineQueryResultColumns(optimizedPlan);
+ resultColumns = determineQueryResultColumns(optimizedPlan,
context.getHints());
duration = System.nanoTime() - start;
stats = stats.withOptimizationTime(duration);
@@ -118,18 +119,21 @@
return new org.jboss.dna.graph.query.process.QueryResults(resultColumns, stats,
context.getProblems());
}
- protected QueryResultColumns determineQueryResultColumns( PlanNode optimizedPlan ) {
+ protected QueryResultColumns determineQueryResultColumns( PlanNode optimizedPlan,
+ PlanHints hints ) {
// Look for which columns to include in the results; this will be defined by the
highest PROJECT node ...
PlanNode project = optimizedPlan.findAtOrBelow(Traversal.LEVEL_ORDER,
Type.PROJECT);
if (project != null) {
List<Column> columns =
project.getPropertyAsList(Property.PROJECT_COLUMNS, Column.class);
// Determine whether to include the full-text search scores in the results
...
- boolean includeFullTextSearchScores = false;
- for (PlanNode select : optimizedPlan.findAllAtOrBelow(Type.SELECT)) {
- Constraint constraint = select.getProperty(Property.SELECT_CRITERIA,
Constraint.class);
- if (QueryResultColumns.includeFullTextScores(constraint)) {
- includeFullTextSearchScores = true;
- break;
+ boolean includeFullTextSearchScores = hints.hasFullTextSearch;
+ if (!includeFullTextSearchScores) {
+ for (PlanNode select : optimizedPlan.findAllAtOrBelow(Type.SELECT)) {
+ Constraint constraint = select.getProperty(Property.SELECT_CRITERIA,
Constraint.class);
+ if (QueryResultColumns.includeFullTextScores(constraint)) {
+ includeFullTextSearchScores = true;
+ break;
+ }
}
}
return new QueryResultColumns(columns, includeFullTextSearchScores);
Modified: trunk/dna-integration-tests/src/test/resources/log4j.properties
===================================================================
--- trunk/dna-integration-tests/src/test/resources/log4j.properties 2009-12-18 01:53:48
UTC (rev 1454)
+++ trunk/dna-integration-tests/src/test/resources/log4j.properties 2009-12-18 01:54:27
UTC (rev 1455)
@@ -18,7 +18,6 @@
# Set up the default logging to be INFO level, then override specific units
log4j.logger.org.jboss.dna=WARN
log4j.logger.org.junit=DEBUG
-#
log4j.logger.org.jboss.dna.tests.integration.jackrabbit.JackrabbitMySqlStressTest=DEBUG
log4j.logger.org.hibernate=WARN
log4j.logger.org.hibernate.pretty.Printer=WARN
log4j.logger.org.hibernate.engine.Collections=WARN
@@ -31,10 +30,4 @@
log4j.logger.org.hibernate.hql.ast=WARN
log4j.logger.org.hibernate.engine.loading.CollectionLoadContext=WARN
-
-# Jackrabbit logging
-log4j.logger.org.apache.jackrabbit=WARN, stdout
-log4j.logger.org.apache.derby=INFO, stdout
-
-
log4j.logger.org.jboss.dna.connector.store=DEBUG
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrLexicon.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrLexicon.java 2009-12-18 01:53:48 UTC
(rev 1454)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrLexicon.java 2009-12-18 01:54:27 UTC
(rev 1455)
@@ -49,9 +49,13 @@
public static final Name LOCK_OWNER = new BasicName(Namespace.URI,
"lockOwner");
public static final Name MERGE_FAILED = new BasicName(Namespace.URI,
"mergeFailed");
public static final Name NODE_TYPES = new BasicName(Namespace.URI,
"nodeTypes");
+ /** The "jcr:path" pseudo-column used in queries */
+ public static final Name PATH = new BasicName(Namespace.URI, "path");
public static final Name PREDECESSORS = new BasicName(Namespace.URI,
"predecessors");
public static final Name ROOT = new BasicName(Namespace.URI, "root");
public static final Name ROOT_VERSION = new BasicName(Namespace.URI,
"rootVersion");
+ /** The "jcr:score" pseudo-column used in queries */
+ public static final Name SCORE = new BasicName(Namespace.URI, "score");
public static final Name STATEMENT = new BasicName(Namespace.URI,
"statement");
public static final Name SUCCESSORS = new BasicName(Namespace.URI,
"successors");
public static final Name SYSTEM = new BasicName(Namespace.URI, "system");
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrQueryManager.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrQueryManager.java 2009-12-18 01:53:48
UTC (rev 1454)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrQueryManager.java 2009-12-18 01:54:27
UTC (rev 1455)
@@ -24,7 +24,9 @@
package org.jboss.dna.jcr;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.Iterator;
+import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -32,6 +34,7 @@
import javax.jcr.Node;
import javax.jcr.NodeIterator;
import javax.jcr.PathNotFoundException;
+import javax.jcr.PropertyType;
import javax.jcr.RepositoryException;
import javax.jcr.Value;
import javax.jcr.nodetype.ConstraintViolationException;
@@ -48,6 +51,7 @@
import org.jboss.dna.graph.Location;
import org.jboss.dna.graph.property.NamespaceRegistry;
import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.graph.property.ValueFactories;
import org.jboss.dna.graph.query.QueryResults;
import org.jboss.dna.graph.query.QueryResults.Columns;
import org.jboss.dna.graph.query.model.QueryCommand;
@@ -110,7 +114,12 @@
try {
// Parsing must be done now ...
QueryCommand command = parser.parseQuery(expression, typeSystem);
- return new JcrQuery(this.session, expression, parser.getLanguage(), command,
storedAtPath);
+ PlanHints hints = new PlanHints();
+ // If using XPath, we need to add a few hints ...
+ if (Query.XPATH.equals(language)) {
+ hints.hasFullTextSearch = true; // requires 'jcr:score' to exist
+ }
+ return new JcrQuery(this.session, expression, parser.getLanguage(), command,
hints, storedAtPath);
} catch (ParsingException e) {
// The query is not well-formed and cannot be parsed ...
String reason = e.getMessage();
@@ -268,17 +277,19 @@
* @param statement the original statement as supplied by the client; may not be
null
* @param language the language obtained from the {@link QueryParser}; may not be
null
* @param query the parsed query representation; may not be null
+ * @param hints any hints that are to be used; may be null if there are no hints
* @param storedAtPath the path at which this query was stored, or null if this
is not a stored query
*/
protected JcrQuery( JcrSession session,
String statement,
String language,
QueryCommand query,
+ PlanHints hints,
Path storedAtPath ) {
super(session, statement, language, storedAtPath);
assert query != null;
this.query = query;
- this.hints = null;
+ this.hints = hints;
this.variables = null;
}
@@ -295,6 +306,9 @@
schemata,
hints,
variables);
+ if (Query.XPATH.equals(language)) {
+ return new XPathQueryResult(session, result);
+ }
return new JcrQueryResult(session, result);
}
@@ -336,14 +350,17 @@
}
}
+ protected static final String JCR_SCORE_COLUMN_NAME = "jcr:score";
+ protected static final String JCR_PATH_COLUMN_NAME = "jcr:path";
+
/**
* The results of a query. This is not thread-safe because it relies upon JcrSession,
which is not thread-safe. Also, although
* the results of a query never change, the objects returned by the iterators may
vary if the session information changes.
*/
@NotThreadSafe
protected static class JcrQueryResult implements QueryResult {
- private final JcrSession session;
- private final QueryResults results;
+ protected final JcrSession session;
+ protected final QueryResults results;
protected JcrQueryResult( JcrSession session,
QueryResults graphResults ) {
@@ -357,14 +374,18 @@
return results;
}
+ public List<String> getColumnNameList() {
+ return results.getColumns().getColumnNames();
+ }
+
/**
* {@inheritDoc}
*
* @see javax.jcr.query.QueryResult#getColumnNames()
*/
public String[] getColumnNames() /*throws RepositoryException*/{
- List<String> names = results.getColumns().getColumnNames();
- return names.toArray(new String[names.size()]);
+ List<String> names = getColumnNameList();
+ return names.toArray(new String[names.size()]); // make a defensive copy ...
}
/**
@@ -491,9 +512,10 @@
*/
@NotThreadSafe
protected static class QueryResultRowIterator implements RowIterator {
- protected final List<String> propertyNames;
+ protected final List<String> columnNames;
private final Iterator<Object[]> tuples;
protected final int locationIndex;
+ protected final int scoreIndex;
protected final JcrSession session;
private long position = 0L;
private final long numRows;
@@ -503,9 +525,10 @@
Iterator<Object[]> tuples,
long numRows ) {
this.tuples = tuples;
- this.propertyNames = columns.getColumnNames();
+ this.columnNames = columns.getColumnNames();
String selectorName = columns.getSelectorNames().get(0);
this.locationIndex = columns.getLocationIndex(selectorName);
+ this.scoreIndex = columns.getFullTextSearchScoreIndexFor(selectorName);
this.session = session;
this.numRows = numRows;
}
@@ -518,49 +541,11 @@
public Row nextRow() {
final Object[] tuple = tuples.next();
++position;
- return new Row() {
- private Node node = null;
- private Value[] values = null;
+ return createRow(tuple);
+ }
- /**
- * {@inheritDoc}
- *
- * @see javax.jcr.query.Row#getValue(java.lang.String)
- */
- public Value getValue( String propertyName ) throws
ItemNotFoundException, RepositoryException {
- return node().getProperty(propertyName).getValue();
- }
-
- /**
- * {@inheritDoc}
- *
- * @see javax.jcr.query.Row#getValues()
- */
- public Value[] getValues() throws RepositoryException {
- if (values == null) {
- int i = 0;
- for (String propertyName : propertyNames) {
- values[i++] = node().getProperty(propertyName).getValue();
- }
- }
- return values;
- }
-
- /**
- * Load the node. The properties are <i>always</i> fetched
from the session to ensure that any modifications to
- * the nodes within session are always used.
- *
- * @return the node
- * @throws RepositoryException if the node could not be found
- */
- protected final Node node() throws RepositoryException {
- if (node == null) {
- Location location = (Location)tuple[locationIndex];
- node = session.getNode(location.getPath());
- }
- return node;
- }
- };
+ protected Row createRow( final Object[] tuple ) {
+ return new QueryResultRow(this, tuple);
}
/**
@@ -620,4 +605,146 @@
throw new UnsupportedOperationException();
}
}
+
+ protected static class QueryResultRow implements Row {
+ protected final QueryResultRowIterator iterator;
+ protected final Object[] tuple;
+ private Node node = null;
+ private Value[] values = null;
+
+ protected QueryResultRow( QueryResultRowIterator iterator,
+ Object[] tuple ) {
+ this.iterator = iterator;
+ this.tuple = tuple;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see javax.jcr.query.Row#getValue(java.lang.String)
+ */
+ public Value getValue( String columnName ) throws ItemNotFoundException,
RepositoryException {
+ return node().getProperty(columnName).getValue();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see javax.jcr.query.Row#getValues()
+ */
+ public Value[] getValues() throws RepositoryException {
+ if (values == null) {
+ int i = 0;
+ values = new Value[iterator.columnNames.size()];
+ for (String columnName : iterator.columnNames) {
+ values[i++] = getValue(columnName);
+ }
+ }
+ return values;
+ }
+
+ /**
+ * Load the node. The properties are <i>always</i> fetched from the
session to ensure that any modifications to the nodes
+ * within session are always used.
+ *
+ * @return the node
+ * @throws RepositoryException if the node could not be found
+ */
+ protected final Node node() throws RepositoryException {
+ if (node == null) {
+ Location location = (Location)tuple[iterator.locationIndex];
+ node = iterator.session.getNode(location.getPath());
+ }
+ return node;
+ }
+ }
+
+ protected static class XPathQueryResult extends JcrQueryResult {
+ private final List<String> columnNames;
+
+ protected XPathQueryResult( JcrSession session,
+ QueryResults graphResults ) {
+ super(session, graphResults);
+ List<String> columnNames = new
LinkedList<String>(graphResults.getColumns().getColumnNames());
+ if (graphResults.getColumns().hasFullTextSearchScores() &&
!columnNames.contains(JCR_SCORE_COLUMN_NAME)) {
+ columnNames.add(0, JCR_SCORE_COLUMN_NAME);
+ }
+ columnNames.add(0, JCR_PATH_COLUMN_NAME);
+ this.columnNames = Collections.unmodifiableList(columnNames);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.jcr.JcrQueryManager.JcrQueryResult#getColumnNameList()
+ */
+ @Override
+ public List<String> getColumnNameList() {
+ return columnNames;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.jcr.JcrQueryManager.JcrQueryResult#getRows()
+ */
+ @Override
+ public RowIterator getRows() {
+ final int numRows = results.getRowCount();
+ final List<Object[]> tuples = results.getTuples();
+ return new XPathQueryResultRowIterator(session, results.getColumns(),
tuples.iterator(), numRows);
+ }
+ }
+
+ protected static class XPathQueryResultRowIterator extends QueryResultRowIterator {
+ private final ValueFactories factories;
+ private final SessionCache cache;
+
+ protected XPathQueryResultRowIterator( JcrSession session,
+ Columns columns,
+ Iterator<Object[]> tuples,
+ long numRows ) {
+ super(session, columns, tuples, numRows);
+ factories = session.executionContext.getValueFactories();
+ cache = session.cache();
+ }
+
+ @Override
+ protected Row createRow( final Object[] tuple ) {
+ return new XPathQueryResultRow(this, tuple);
+ }
+
+ protected Value jcrPath( Path path ) {
+ return new JcrValue(factories, cache, PropertyType.PATH, path);
+ }
+
+ protected Value jcrScore( Float score ) {
+ return new JcrValue(factories, cache, PropertyType.DOUBLE, score);
+ }
+ }
+
+ protected static class XPathQueryResultRow extends QueryResultRow {
+ protected XPathQueryResultRow( XPathQueryResultRowIterator iterator,
+ Object[] tuple ) {
+ super(iterator, tuple);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see javax.jcr.query.Row#getValue(java.lang.String)
+ */
+ @Override
+ public Value getValue( String columnName ) throws ItemNotFoundException,
RepositoryException {
+ if (JCR_PATH_COLUMN_NAME.equals(columnName)) {
+ Location location = (Location)tuple[iterator.locationIndex];
+ return
((XPathQueryResultRowIterator)iterator).jcrPath(location.getPath());
+ }
+ if (JCR_SCORE_COLUMN_NAME.equals(columnName)) {
+ Float score = (Float)tuple[iterator.scoreIndex];
+ return ((XPathQueryResultRowIterator)iterator).jcrScore(score);
+ }
+ return super.getValue(columnName);
+ }
+ }
}
Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckTest.java 2009-12-18 01:53:48 UTC
(rev 1454)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrTckTest.java 2009-12-18 01:54:27 UTC
(rev 1455)
@@ -180,7 +180,7 @@
//
addTestSuite(org.apache.jackrabbit.test.api.query.XPathPosIndexTest.class);
//
addTestSuite(org.apache.jackrabbit.test.api.query.XPathDocOrderTest.class);
//
addTestSuite(org.apache.jackrabbit.test.api.query.XPathOrderByTest.class);
- //
addTestSuite(org.apache.jackrabbit.test.api.query.XPathJcrPathTest.class);
+ addTestSuite(org.apache.jackrabbit.test.api.query.XPathJcrPathTest.class);
addTestSuite(org.apache.jackrabbit.test.api.query.DerefQueryLevel1Test.class);
addTestSuite(org.apache.jackrabbit.test.api.query.GetLanguageTest.class);
addTestSuite(org.apache.jackrabbit.test.api.query.GetPersistentQueryPathLevel1Test.class);