You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by ju...@apache.org on 2010/11/28 16:03:57 UTC

svn commit: r1039888 - in /jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query: ./ lucene/ lucene/join/

Author: jukka
Date: Sun Nov 28 15:03:57 2010
New Revision: 1039888

URL: http://svn.apache.org/viewvc?rev=1039888&view=rev
Log:
JCR-2715: Improved join query performance

Fix all remaining TCK test failures with the new QOM/SQL2 implementation
and enable it for all QOM/SQL2 queries

Removed:
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/QueryObjectModelImpl.java
Modified:
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/ExecutableQuery.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/QueryHandler.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/QueryImpl.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/QueryObjectModelImpl.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/AbstractQueryImpl.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/LuceneQueryFactory.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/QueryImpl.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/SearchIndex.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/join/JoinMerger.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/join/OperandEvaluator.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/join/QueryEngine.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/join/SameNodeJoinMerger.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/join/ValueComparator.java

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/ExecutableQuery.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/ExecutableQuery.java?rev=1039888&r1=1039887&r2=1039888&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/ExecutableQuery.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/ExecutableQuery.java Sun Nov 28 15:03:57 2010
@@ -16,10 +16,7 @@
  */
 package org.apache.jackrabbit.core.query;
 
-import java.util.Map;
-
 import javax.jcr.RepositoryException;
-import javax.jcr.Value;
 import javax.jcr.query.QueryResult;
 
 /**
@@ -39,22 +36,4 @@ public interface ExecutableQuery {
      */
     QueryResult execute(long offset, long limit) throws RepositoryException;
 
-    /**
-     * Binds the given <code>value</code> to the variable named
-     * <code>varName</code>.
-     *
-     * @param varName name of variable in query
-     * @param value   value to bind
-     * @throws RepositoryException if <code>varName</code> is not a
-     *                             valid variable in this query.
-     */
-    void bindValue(String varName, Value value) throws RepositoryException;
-
-    /**
-     * Returns the bind variables of this query.
-     *
-     * @return bind variables
-     */
-    Map<String, Value> getBindVariables();
-
 }

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/QueryHandler.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/QueryHandler.java?rev=1039888&r1=1039887&r2=1039888&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/QueryHandler.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/QueryHandler.java Sun Nov 28 15:03:57 2010
@@ -26,7 +26,6 @@ import org.apache.jackrabbit.core.fs.Fil
 import org.apache.jackrabbit.core.id.NodeId;
 import org.apache.jackrabbit.core.session.SessionContext;
 import org.apache.jackrabbit.core.state.NodeState;
-import org.apache.jackrabbit.spi.commons.query.qom.QueryObjectModelTree;
 
 /**
  * Defines an interface for the actual node indexing and query execution.
@@ -112,20 +111,6 @@ public interface QueryHandler {
             throws InvalidQueryException;
 
     /**
-     * Creates a new query by specifying the query object model. If the query
-     * object model is considered invalid for the implementing class, an
-     * InvalidQueryException is thrown.
-     *
-     * @param sessionContext component context of the current session
-     * @param qomTree query query object model tree.
-     * @return A <code>Query</code> object.
-     * @throws InvalidQueryException if the query object model tree is invalid.
-     */
-    ExecutableQuery createExecutableQuery(
-            SessionContext sessionContext, QueryObjectModelTree qomTree)
-            throws InvalidQueryException;
-    
-    /**
      * @return the name of the query class to use.
      */
     String getQueryClass();

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/QueryImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/QueryImpl.java?rev=1039888&r1=1039887&r2=1039888&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/QueryImpl.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/QueryImpl.java Sun Nov 28 15:03:57 2010
@@ -21,7 +21,6 @@ import static org.apache.jackrabbit.spi.
 import static org.apache.jackrabbit.spi.commons.name.NameConstants.NT_QUERY;
 
 import java.text.NumberFormat;
-import java.util.Set;
 
 import javax.jcr.ItemExistsException;
 import javax.jcr.ItemNotFoundException;
@@ -97,7 +96,7 @@ public class QueryImpl extends AbstractQ
     /**
      * The offset in the total result set
      */
-    protected long offset;
+    protected long offset = 0;
 
     /**
      * @inheritDoc
@@ -210,29 +209,19 @@ public class QueryImpl extends AbstractQ
     /**
      * {@inheritDoc}
      */
-    public String[] getBindVariableNames() throws RepositoryException {
-        Set<String> names = query.getBindVariables().keySet();
-        return names.toArray(new String[names.size()]);
+    public String[] getBindVariableNames() {
+        return new String[0];
     }
 
     /**
-     * Binds the given <code>value</code> to the variable named
-     * <code>varName</code>.
+     * Throws an {@link IllegalArgumentException} as XPath and SQL1 queries
+     * have no bind variables.
      *
-     * @param varName name of variable in query
-     * @param value   value to bind
-     * @throws IllegalArgumentException      if <code>varName</code> is not a
-     *                                       valid variable in this query.
-     * @throws javax.jcr.RepositoryException if an error occurs.
+     * @throws IllegalArgumentException always thrown
      */
     public void bindValue(String varName, Value value)
-            throws IllegalArgumentException, RepositoryException {
-        checkInitialized();
-        try {
-            query.bindValue(varName, value);
-        } catch (NameException e) {
-            throw new RepositoryException(e.getMessage());
-        }
+            throws IllegalArgumentException {
+        throw new IllegalArgumentException("No such bind variable: " + varName);
     }
 
     /**

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/QueryObjectModelImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/QueryObjectModelImpl.java?rev=1039888&r1=1039887&r2=1039888&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/QueryObjectModelImpl.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/QueryObjectModelImpl.java Sun Nov 28 15:03:57 2010
@@ -16,8 +16,6 @@
  */
 package org.apache.jackrabbit.core.query;
 
-import static javax.jcr.query.qom.QueryObjectModelConstants.JCR_JOIN_TYPE_INNER;
-
 import java.util.HashMap;
 import java.util.Map;
 
@@ -28,17 +26,17 @@ import javax.jcr.query.InvalidQueryExcep
 import javax.jcr.query.QueryResult;
 import javax.jcr.query.qom.Column;
 import javax.jcr.query.qom.Constraint;
-import javax.jcr.query.qom.EquiJoinCondition;
-import javax.jcr.query.qom.Join;
 import javax.jcr.query.qom.Ordering;
 import javax.jcr.query.qom.QueryObjectModel;
-import javax.jcr.query.qom.QueryObjectModelConstants;
 import javax.jcr.query.qom.Source;
 
 import org.apache.jackrabbit.commons.query.QueryObjectModelBuilderRegistry;
+import org.apache.jackrabbit.core.query.lucene.LuceneQueryFactory;
+import org.apache.jackrabbit.core.query.lucene.SearchIndex;
 import org.apache.jackrabbit.core.query.lucene.join.QueryEngine;
 import org.apache.jackrabbit.core.session.SessionContext;
-import org.apache.jackrabbit.spi.Name;
+import org.apache.jackrabbit.spi.commons.query.qom.BindVariableValueImpl;
+import org.apache.jackrabbit.spi.commons.query.qom.DefaultTraversingQOMTreeVisitor;
 import org.apache.jackrabbit.spi.commons.query.qom.QueryObjectModelTree;
 
 /**
@@ -51,6 +49,11 @@ public class QueryObjectModelImpl extend
      */
     protected QueryObjectModelTree qomTree;
 
+    /** Bind variables */
+    private final Map<String, Value> variables = new HashMap<String, Value>();
+
+    private LuceneQueryFactory lqf;
+
     /**
      * {@inheritDoc}
      * @throws UnsupportedOperationException always.
@@ -88,24 +91,46 @@ public class QueryObjectModelImpl extend
         this.qomTree = qomTree;
         this.node = node;
         this.statement = QueryObjectModelBuilderRegistry.getQueryObjectModelBuilder(language).toString(this);
-        this.query = handler.createExecutableQuery(sessionContext, qomTree);
+
+        try {
+            qomTree.accept(new DefaultTraversingQOMTreeVisitor() {
+                @Override
+                public Object visit(BindVariableValueImpl node, Object data) {
+                    variables.put(node.getBindVariableName(), null);
+                    return data;
+                }
+            }, null);
+        } catch (Exception ignore) {
+        }
+        this.lqf = new LuceneQueryFactory(
+                sessionContext.getSessionImpl(), (SearchIndex) handler,
+                variables);
         setInitialized();
     }
 
     public QueryResult execute() throws RepositoryException {
-        Source source = getSource();
-        if (source instanceof Join) {
-            Join join = (Join) source;
-            if (JCR_JOIN_TYPE_INNER.equals(join.getJoinType())
-                    && join.getJoinCondition() instanceof EquiJoinCondition) {
-                QueryEngine engine =
-                    new QueryEngine(sessionContext.getSessionImpl(), query.getBindVariables());
-                return engine.execute(
-                        getColumns(), getSource(), getConstraint(),
-                        getOrderings(), offset, limit);
-            }
+        QueryEngine engine = new QueryEngine(
+                sessionContext.getSessionImpl(), lqf, variables);
+        return engine.execute(
+                getColumns(), getSource(), getConstraint(),
+                getOrderings(), offset, limit);
+    }
+
+
+    @Override
+    public String[] getBindVariableNames() {
+        return variables.keySet().toArray(new String[variables.size()]);
+    }
+
+    @Override
+    public void bindValue(String varName, Value value)
+            throws IllegalArgumentException {
+        if (variables.containsKey(varName)) {
+            variables.put(varName, value);
+        } else {
+            throw new IllegalArgumentException(
+                    "No such bind variable: " + varName);
         }
-        return super.execute();
     }
 
     //-------------------------< QueryObjectModel >-----------------------------

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/AbstractQueryImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/AbstractQueryImpl.java?rev=1039888&r1=1039887&r2=1039888&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/AbstractQueryImpl.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/AbstractQueryImpl.java Sun Nov 28 15:03:57 2010
@@ -16,10 +16,7 @@
  */
 package org.apache.jackrabbit.core.query.lucene;
 
-import java.util.Map;
-
 import javax.jcr.RepositoryException;
-import javax.jcr.Value;
 import javax.jcr.Workspace;
 import javax.jcr.query.qom.QueryObjectModelFactory;
 
@@ -54,10 +51,6 @@ public abstract class AbstractQueryImpl 
      */
     private boolean documentOrder = true;
 
-    /** Bind variables of this query */
-    private final Map<String, Value> variables;
-
-
     /**
      * Creates a new query instance from a query string.
      *
@@ -67,11 +60,10 @@ public abstract class AbstractQueryImpl 
      */
     public AbstractQueryImpl(
             SessionContext sessionContext, SearchIndex index,
-            PropertyTypeRegistry propReg, Map<String, Value> variables) {
+            PropertyTypeRegistry propReg) {
         this.sessionContext = sessionContext;
         this.index = index;
         this.propReg = propReg;
-        this.variables = variables;
     }
 
     /**
@@ -102,33 +94,6 @@ public abstract class AbstractQueryImpl 
     }
 
     /**
-     * Binds the given <code>value</code> to the variable named
-     * <code>varName</code>.
-     *
-     * @param varName name of variable in query
-     * @param value   value to bind
-     * @throws IllegalArgumentException if <code>varName</code> is not a valid
-     *                                  variable in this query.
-     * @throws RepositoryException      if an error occurs.
-     */
-    public void bindValue(String varName, Value value)
-            throws IllegalArgumentException, RepositoryException {
-        if (variables.containsKey(varName)) {
-            variables.put(varName, value);
-        } else {
-            throw new IllegalArgumentException(
-                    varName + " is not a valid variable in this query");
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public Map<String, Value> getBindVariables() {
-        return variables;
-    }
-
-    /**
      * @return the query object model factory.
      * @throws RepositoryException if an error occurs.
      */

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/LuceneQueryFactory.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/LuceneQueryFactory.java?rev=1039888&r1=1039887&r2=1039888&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/LuceneQueryFactory.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/LuceneQueryFactory.java Sun Nov 28 15:03:57 2010
@@ -17,9 +17,11 @@
 package org.apache.jackrabbit.core.query.lucene;
 
 import static javax.jcr.PropertyType.DATE;
+import static javax.jcr.PropertyType.DECIMAL;
 import static javax.jcr.PropertyType.DOUBLE;
 import static javax.jcr.PropertyType.LONG;
 import static javax.jcr.PropertyType.NAME;
+import static javax.jcr.PropertyType.PATH;
 import static javax.jcr.PropertyType.STRING;
 import static javax.jcr.PropertyType.UNDEFINED;
 import static javax.jcr.query.qom.QueryObjectModelConstants.JCR_OPERATOR_EQUAL_TO;
@@ -29,24 +31,41 @@ import static javax.jcr.query.qom.QueryO
 import static javax.jcr.query.qom.QueryObjectModelConstants.JCR_OPERATOR_LESS_THAN_OR_EQUAL_TO;
 import static javax.jcr.query.qom.QueryObjectModelConstants.JCR_OPERATOR_LIKE;
 import static javax.jcr.query.qom.QueryObjectModelConstants.JCR_OPERATOR_NOT_EQUAL_TO;
+import static org.apache.jackrabbit.core.query.lucene.FieldNames.LOCAL_NAME;
+import static org.apache.jackrabbit.core.query.lucene.FieldNames.MVP;
+import static org.apache.jackrabbit.core.query.lucene.FieldNames.NAMESPACE_URI;
+import static org.apache.jackrabbit.core.query.lucene.FieldNames.PARENT;
 import static org.apache.jackrabbit.core.query.lucene.FieldNames.PROPERTIES;
+import static org.apache.jackrabbit.core.query.lucene.FieldNames.UUID;
+import static org.apache.jackrabbit.core.query.lucene.TransformConstants.TRANSFORM_LOWER_CASE;
+import static org.apache.jackrabbit.core.query.lucene.TransformConstants.TRANSFORM_NONE;
+import static org.apache.jackrabbit.core.query.lucene.TransformConstants.TRANSFORM_UPPER_CASE;
 import static org.apache.jackrabbit.spi.commons.name.NameConstants.JCR_MIXINTYPES;
 import static org.apache.jackrabbit.spi.commons.name.NameConstants.JCR_PRIMARYTYPE;
 import static org.apache.lucene.search.BooleanClause.Occur.MUST;
+import static org.apache.lucene.search.BooleanClause.Occur.MUST_NOT;
 import static org.apache.lucene.search.BooleanClause.Occur.SHOULD;
 
+import java.io.IOException;
 import java.util.ArrayList;
+import java.util.Collections;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 
+import javax.jcr.ItemNotFoundException;
+import javax.jcr.PathNotFoundException;
 import javax.jcr.PropertyType;
 import javax.jcr.RepositoryException;
 import javax.jcr.UnsupportedRepositoryOperationException;
 import javax.jcr.Value;
+import javax.jcr.ValueFormatException;
 import javax.jcr.nodetype.NodeType;
 import javax.jcr.nodetype.NodeTypeIterator;
 import javax.jcr.nodetype.NodeTypeManager;
 import javax.jcr.nodetype.PropertyDefinition;
+import javax.jcr.query.InvalidQueryException;
+import javax.jcr.query.Row;
 import javax.jcr.query.qom.And;
 import javax.jcr.query.qom.ChildNode;
 import javax.jcr.query.qom.Comparison;
@@ -54,26 +73,35 @@ import javax.jcr.query.qom.Constraint;
 import javax.jcr.query.qom.DescendantNode;
 import javax.jcr.query.qom.DynamicOperand;
 import javax.jcr.query.qom.FullTextSearch;
+import javax.jcr.query.qom.FullTextSearchScore;
+import javax.jcr.query.qom.Length;
+import javax.jcr.query.qom.LowerCase;
+import javax.jcr.query.qom.NodeLocalName;
+import javax.jcr.query.qom.NodeName;
 import javax.jcr.query.qom.Not;
 import javax.jcr.query.qom.Or;
 import javax.jcr.query.qom.PropertyExistence;
 import javax.jcr.query.qom.PropertyValue;
 import javax.jcr.query.qom.SameNode;
+import javax.jcr.query.qom.Selector;
 import javax.jcr.query.qom.StaticOperand;
+import javax.jcr.query.qom.UpperCase;
 
-import org.apache.jackrabbit.core.HierarchyManager;
+import org.apache.jackrabbit.commons.predicate.Predicate;
+import org.apache.jackrabbit.commons.predicate.Predicates;
+import org.apache.jackrabbit.commons.predicate.RowPredicate;
+import org.apache.jackrabbit.core.NodeImpl;
 import org.apache.jackrabbit.core.SessionImpl;
+import org.apache.jackrabbit.core.id.NodeId;
 import org.apache.jackrabbit.core.query.lucene.join.OperandEvaluator;
+import org.apache.jackrabbit.core.query.lucene.join.SelectorRow;
+import org.apache.jackrabbit.core.query.lucene.join.ValueComparator;
 import org.apache.jackrabbit.spi.Name;
+import org.apache.jackrabbit.spi.commons.conversion.IllegalNameException;
 import org.apache.jackrabbit.spi.commons.conversion.NamePathResolver;
-import org.apache.jackrabbit.spi.commons.query.qom.DefaultQOMTreeVisitor;
 import org.apache.jackrabbit.spi.commons.query.qom.FullTextSearchImpl;
-import org.apache.jackrabbit.spi.commons.query.qom.JoinConditionImpl;
-import org.apache.jackrabbit.spi.commons.query.qom.JoinImpl;
 import org.apache.jackrabbit.spi.commons.query.qom.PropertyExistenceImpl;
-import org.apache.jackrabbit.spi.commons.query.qom.SelectorImpl;
-import org.apache.jackrabbit.spi.commons.query.qom.SourceImpl;
-import org.apache.lucene.analysis.Analyzer;
+import org.apache.lucene.index.IndexReader;
 import org.apache.lucene.index.Term;
 import org.apache.lucene.queryParser.ParseException;
 import org.apache.lucene.queryParser.QueryParser;
@@ -96,10 +124,8 @@ public class LuceneQueryFactory {
      */
     private final NodeTypeManager ntManager;
 
-    /**
-     * The hierarchy manager.
-     */
-    private final HierarchyManager hmgr;
+    /** Lucene search index */
+    private final SearchIndex index;
 
     /**
      * Namespace mappings to internal prefixes
@@ -111,21 +137,6 @@ public class LuceneQueryFactory {
      */
     private final NamePathResolver npResolver;
 
-    /**
-     * The analyzer instance to use for contains function query parsing
-     */
-    private final Analyzer analyzer;
-
-    /**
-     * The synonym provider or <code>null</code> if none is configured.
-     */
-    private final SynonymProvider synonymProvider;
-
-    /**
-     * The index format version.
-     */
-    private final IndexFormatVersion version;
-
     /** Operand evaluator */
     private final OperandEvaluator evaluator;
 
@@ -137,26 +148,16 @@ public class LuceneQueryFactory {
      * Creates a new lucene query factory.
      *
      * @param session         the session that executes the query.
-     * @param scs             the sort comparator source of the index.
-     * @param hmgr            the hierarchy manager of the workspace.
-     * @param nsMappings      the index internal namespace mappings.
-     * @param analyzer        the analyzer of the index.
-     * @param synonymProvider the synonym provider of the index.
-     * @param version         the version of the index format.
+     * @param index           the search index
      * @param bindVariables   the bind variable values of the query
      */
     public LuceneQueryFactory(
-            SessionImpl session, HierarchyManager hmgr,
-            NamespaceMappings nsMappings, Analyzer analyzer,
-            SynonymProvider synonymProvider, IndexFormatVersion version,
+            SessionImpl session, SearchIndex index,
             Map<String, Value> bindVariables) throws RepositoryException {
         this.session = session;
         this.ntManager = session.getWorkspace().getNodeTypeManager();
-        this.hmgr = hmgr;
-        this.nsMappings = nsMappings;
-        this.analyzer = analyzer;
-        this.synonymProvider = synonymProvider;
-        this.version = version;
+        this.index = index;
+        this.nsMappings = index.getNamespaceMappings();
         this.npResolver = NamePathResolverImpl.create(nsMappings);
         this.evaluator =
             new OperandEvaluator(session.getValueFactory(), bindVariables);
@@ -164,6 +165,51 @@ public class LuceneQueryFactory {
         this.primaryTypeField = nsMappings.translateName(JCR_PRIMARYTYPE);
     }
 
+    public List<Row> execute(
+            Map<String, PropertyValue> columns, Selector selector,
+            Constraint constraint) throws RepositoryException, IOException {
+        final IndexReader reader = index.getIndexReader(true);
+        try {
+            JackrabbitIndexSearcher searcher = new JackrabbitIndexSearcher(
+                    session, reader, index.getContext().getItemStateManager());
+            searcher.setSimilarity(index.getSimilarity());
+
+            Predicate filter = Predicate.TRUE;
+            BooleanQuery query = new BooleanQuery();
+            query.add(create(selector), MUST);
+            if (constraint != null) {
+                String name = selector.getSelectorName();
+                NodeType type =
+                    ntManager.getNodeType(selector.getNodeTypeName());
+                filter = mapConstraintToQueryAndFilter(
+                        query, constraint, Collections.singletonMap(name, type),
+                        searcher, reader);
+            }
+
+            List<Row> rows = new ArrayList<Row>();
+            QueryHits hits = searcher.evaluate(query);
+            ScoreNode node = hits.nextScoreNode();
+            while (node != null) {
+                try {
+                    Row row = new SelectorRow(
+                            columns, evaluator, selector.getSelectorName(),
+                            session.getNodeById(node.getNodeId()),
+                            node.getScore());
+                    if (filter.evaluate(row)) {
+                        rows.add(row);
+                    }
+                } catch (ItemNotFoundException e) {
+                    // skip the node
+                }
+                node = hits.nextScoreNode();
+            }
+            return rows;
+        } finally {
+            PerQueryCache.getInstance().dispose();
+            Util.closeOrRelease(reader);
+        }
+    }
+
     /**
      * Creates a lucene query for the given QOM selector.
      *
@@ -171,7 +217,7 @@ public class LuceneQueryFactory {
      * @return a lucene query for the given selector.
      * @throws RepositoryException if an error occurs while creating the query.
      */
-    public Query create(SelectorImpl selector) throws RepositoryException {
+    public Query create(Selector selector) throws RepositoryException {
         List<Term> terms = new ArrayList<Term>();
 
         String name = selector.getNodeTypeName();
@@ -229,7 +275,7 @@ public class LuceneQueryFactory {
             fieldname = tmp.toString();
         }
         QueryParser parser = new JackrabbitQueryParser(
-                fieldname, analyzer, synonymProvider);
+                fieldname, index.getTextAnalyzer(), index.getSynonymProvider());
         try {
             StaticOperand expr = fts.getFullTextSearchExpression();
             return parser.parse(evaluator.getValue(expr).getString());
@@ -247,168 +293,377 @@ public class LuceneQueryFactory {
      */
     public Query create(PropertyExistenceImpl prop) throws RepositoryException {
         String propName = npResolver.getJCRName(prop.getPropertyQName());
-        return Util.createMatchAllQuery(propName, version);
+        return Util.createMatchAllQuery(propName, index.getIndexFormatVersion());
     }
 
-    /**
-     * Creates a multi column query for the given QOM source.
-     *
-     * @param source the QOM source.
-     * @return a multi column query for the given source.
-     * @throws RepositoryException if an error occurs while creating the query.
-     */
-    public MultiColumnQuery create(SourceImpl source) throws RepositoryException {
-        // source is either selector or join
-        try {
-            return (MultiColumnQuery) source.accept(new DefaultQOMTreeVisitor() {
-                public Object visit(JoinImpl node, Object data) throws Exception {
-                    return create(node);
-                }
-
-                public Object visit(SelectorImpl node, Object data) throws Exception {
-                    return MultiColumnQueryAdapter.adapt(
-                            create(node), node.getSelectorQName());
+    private Predicate mapConstraintToQueryAndFilter(
+            BooleanQuery query, Constraint constraint,
+            Map<String, NodeType> selectorMap,
+            JackrabbitIndexSearcher searcher, IndexReader reader)
+            throws RepositoryException, IOException {
+        Predicate filter = Predicate.TRUE;
+        if (constraint instanceof And) {
+            And and = (And) constraint;
+            filter = mapConstraintToQueryAndFilter(
+                    query, and.getConstraint1(), selectorMap, searcher, reader);
+            Predicate other = mapConstraintToQueryAndFilter(
+                    query, and.getConstraint2(), selectorMap, searcher, reader);
+            if (filter == Predicate.TRUE) {
+                filter = other;
+            } else if (other != Predicate.TRUE) {
+                filter = Predicates.and(filter, other);
+            }
+        } else if (constraint instanceof Comparison) {
+            Comparison c = (Comparison) constraint;
+            Transform transform = new Transform(c.getOperand1());
+            DynamicOperand left = transform.operand;
+            final String operator = c.getOperator();
+            StaticOperand right = c.getOperand2();
+            if (left instanceof Length
+                    || left instanceof FullTextSearchScore
+                    || ((!JCR_OPERATOR_EQUAL_TO.equals(operator)
+                            || transform.transform != TRANSFORM_NONE)
+                            && (left instanceof NodeName
+                                    || left instanceof NodeLocalName))) {
+                try {
+                    int type = PropertyType.UNDEFINED;
+                    if (left instanceof Length) {
+                        type = PropertyType.LONG;
+                    } else if (left instanceof FullTextSearchScore) {
+                        type = PropertyType.DOUBLE;
+                    }
+                    final DynamicOperand operand = c.getOperand1();
+                    final Value value = evaluator.getValue(right, type);
+                    filter = new RowPredicate() {
+                        @Override
+                        protected boolean evaluate(Row row)
+                                throws RepositoryException {
+                            return new ValueComparator().evaluate(
+                                    operator,
+                                    evaluator.getValue(operand, row), value);
+                        }
+                    };
+                } catch (ValueFormatException e) {
+                    throw new InvalidQueryException(e);
                 }
-            }, null);
-        } catch (RepositoryException e) {
-            throw e;
-        } catch (Exception e) {
-            throw new RepositoryException(e);
+            } else {
+                Query cq = getComparisonQuery(
+                        left, transform.transform, operator, right, selectorMap);
+                query.add(cq, MUST);
+            }
+        } else {
+            query.add(create(constraint, selectorMap, searcher), MUST);
         }
+        return filter;
     }
 
-    /**
-     * Creates a multi column query for the given QOM join.
-     *
-     * @param join the QOM join.
-     * @return the multi column query for the given join.
-     * @throws RepositoryException if an error occurs while creating the query.
-     */
-    public MultiColumnQuery create(JoinImpl join) throws RepositoryException {
-        MultiColumnQuery left = create((SourceImpl) join.getLeft());
-        MultiColumnQuery right = create((SourceImpl) join.getRight());
-        return new JoinQuery(left, right, join.getJoinTypeInstance(),
-                (JoinConditionImpl) join.getJoinCondition(), nsMappings, hmgr);
-    }
-
-    public Query create(
-            Constraint constraint, Map<String, NodeType> selectorMap)
-            throws RepositoryException {
+    private Query create(
+            Constraint constraint, Map<String, NodeType> selectorMap,
+            JackrabbitIndexSearcher searcher)
+            throws RepositoryException, IOException {
         if (constraint instanceof And) {
-            return getAndQuery((And) constraint, selectorMap);
+            return getAndQuery((And) constraint, selectorMap, searcher);
         } else if (constraint instanceof Or) {
-            return getOrQuery((Or) constraint, selectorMap);
+            return getOrQuery((Or) constraint, selectorMap, searcher);
         } else if (constraint instanceof Not) {
-            return getNotQuery((Not) constraint, selectorMap);
+            return getNotQuery((Not) constraint, selectorMap, searcher);
         } else if (constraint instanceof PropertyExistence) {
             return getPropertyExistenceQuery((PropertyExistence) constraint);
         } else if (constraint instanceof Comparison) {
-            return getComparisonQuery((Comparison) constraint, selectorMap);
+            Comparison c = (Comparison) constraint;
+            Transform left = new Transform(c.getOperand1());
+            return getComparisonQuery(
+                    left.operand, left.transform, c.getOperator(),
+                    c.getOperand2(), selectorMap);
         } else if (constraint instanceof FullTextSearch) {
-            return null; // FIXME
+            return getFullTextSearchQuery((FullTextSearch) constraint);
         } else if (constraint instanceof SameNode) {
-            return null; // FIXME
+            SameNode sn = (SameNode) constraint;
+            return getNodeIdQuery(UUID, sn.getPath());
         } else if (constraint instanceof ChildNode) {
-            return null; // FIXME
+            ChildNode cn = (ChildNode) constraint;
+            return getNodeIdQuery(PARENT, cn.getParentPath());
         } else if (constraint instanceof DescendantNode) {
-            return null; // FIXME
+            DescendantNode dn = (DescendantNode) constraint;
+            return getDescendantNodeQuery(dn, searcher);
         } else {
             throw new UnsupportedRepositoryOperationException(
                     "Unknown constraint type: " + constraint);
         }
     }
 
-    private BooleanQuery getAndQuery(
-            And and, Map<String, NodeType> selectorMap)
-            throws RepositoryException {
+    private Query getDescendantNodeQuery(
+            DescendantNode dn, JackrabbitIndexSearcher searcher)
+            throws RepositoryException, IOException {
         BooleanQuery query = new BooleanQuery();
-        addBooleanConstraint(query, and.getConstraint1(), MUST, selectorMap);
-        addBooleanConstraint(query, and.getConstraint2(), MUST, selectorMap);
+
+        try {
+            LinkedList<NodeId> ids = new LinkedList<NodeId>();
+            NodeImpl ancestor = (NodeImpl) session.getNode(dn.getAncestorPath());
+            ids.add(ancestor.getNodeId());
+            while (!ids.isEmpty()) {
+                String id = ids.removeFirst().toString();
+                Query q = new JackrabbitTermQuery(new Term(FieldNames.PARENT, id));
+                QueryHits hits = searcher.evaluate(q);
+                ScoreNode sn = hits.nextScoreNode();
+                if (sn != null) {
+                    query.add(q, SHOULD);
+                    do {
+                        ids.add(sn.getNodeId());
+                        sn = hits.nextScoreNode();
+                    } while (sn != null);
+                }
+            }
+        } catch (PathNotFoundException e) {
+            query.add(new JackrabbitTermQuery(new Term(
+                    FieldNames.UUID, "invalid-node-id")), // never matches
+                    SHOULD);
+        }
+
         return query;
     }
 
-    private BooleanQuery getOrQuery(Or or, Map<String, NodeType> selectorMap)
+    private Query getFullTextSearchQuery(FullTextSearch fts)
             throws RepositoryException {
+        String field = FieldNames.FULLTEXT;
+        String property = fts.getPropertyName();
+        if (property != null) {
+            Name name = session.getQName(property);
+            field = nsMappings.getPrefix(name.getNamespaceURI()) + ":"
+                + FieldNames.FULLTEXT_PREFIX + name.getLocalName();
+        }
+
+        StaticOperand expression = fts.getFullTextSearchExpression();
+        String query = evaluator.getValue(expression).getString();
+        try {
+            QueryParser parser = new JackrabbitQueryParser(
+                    field, index.getTextAnalyzer(), index.getSynonymProvider());
+            return parser.parse(query);
+        } catch (ParseException e) {
+            throw new RepositoryException(
+                    "Invalid full text search expression: " + query, e);
+        }
+    }
+
+    private BooleanQuery getAndQuery(
+            And and, Map<String, NodeType> selectorMap,
+            JackrabbitIndexSearcher searcher)
+            throws RepositoryException, IOException {
+        BooleanQuery query = new BooleanQuery();
+        addBooleanConstraint(
+                query, and.getConstraint1(), MUST, selectorMap, searcher);
+        addBooleanConstraint(
+                query, and.getConstraint2(), MUST, selectorMap, searcher);
+        return query;
+    }
+
+    private BooleanQuery getOrQuery(
+            Or or, Map<String, NodeType> selectorMap,
+            JackrabbitIndexSearcher searcher)
+            throws RepositoryException, IOException {
         BooleanQuery query = new BooleanQuery();
-        addBooleanConstraint(query, or.getConstraint1(), SHOULD, selectorMap);
-        addBooleanConstraint(query, or.getConstraint2(), SHOULD, selectorMap);
+        addBooleanConstraint(
+                query, or.getConstraint1(), SHOULD, selectorMap, searcher);
+        addBooleanConstraint(
+                query, or.getConstraint2(), SHOULD, selectorMap, searcher);
         return query;
     }
 
     private void addBooleanConstraint(
             BooleanQuery query, Constraint constraint, Occur occur,
-            Map<String, NodeType> selectorMap) throws RepositoryException {
+            Map<String, NodeType> selectorMap, JackrabbitIndexSearcher searcher)
+            throws RepositoryException, IOException {
         if (occur == MUST && constraint instanceof And) {
             And and = (And) constraint;
-            addBooleanConstraint(query, and.getConstraint1(), occur, selectorMap);
-            addBooleanConstraint(query, and.getConstraint2(), occur, selectorMap);
+            addBooleanConstraint(
+                    query, and.getConstraint1(), occur, selectorMap, searcher);
+            addBooleanConstraint(
+                    query, and.getConstraint2(), occur, selectorMap, searcher);
         } else if (occur == SHOULD && constraint instanceof Or) {
             Or or = (Or) constraint;
-            addBooleanConstraint(query, or.getConstraint1(), occur, selectorMap);
-            addBooleanConstraint(query, or.getConstraint2(), occur, selectorMap);
+            addBooleanConstraint(
+                    query, or.getConstraint1(), occur, selectorMap, searcher);
+            addBooleanConstraint(
+                    query, or.getConstraint2(), occur, selectorMap, searcher);
         } else {
-            query.add(create(constraint, selectorMap), occur);
+            query.add(create(constraint, selectorMap, searcher), occur);
         }
     }
 
-    private NotQuery getNotQuery(Not not, Map<String, NodeType> selectorMap)
-            throws RepositoryException {
-        return new NotQuery(create(not.getConstraint(), selectorMap));
+    private NotQuery getNotQuery(
+            Not not, Map<String, NodeType> selectorMap,
+            JackrabbitIndexSearcher searcher)
+            throws RepositoryException, IOException {
+        return new NotQuery(create(not.getConstraint(), selectorMap, searcher));
     }
 
     private Query getPropertyExistenceQuery(PropertyExistence property)
             throws RepositoryException {
         String name = npResolver.getJCRName(session.getQName(
                 property.getPropertyName()));
-        return Util.createMatchAllQuery(name, version);
+        return Util.createMatchAllQuery(name, index.getIndexFormatVersion());
+    }
+
+    private static class Transform {
+
+        private final DynamicOperand operand;
+
+        private final int transform;
+
+        public Transform(DynamicOperand operand) {
+            // Check the transformation type
+            if (operand instanceof UpperCase) {
+                this.transform = TRANSFORM_UPPER_CASE;
+            } else if (operand instanceof LowerCase) {
+                this.transform = TRANSFORM_LOWER_CASE;
+            } else {
+                this.transform = TRANSFORM_NONE;
+            }
+
+            // Unwrap any nested transformations
+            while (true) {
+                if (operand instanceof UpperCase) {
+                    operand = ((UpperCase) operand).getOperand();
+                } else if (operand instanceof LowerCase) {
+                    operand = ((LowerCase) operand).getOperand();
+                } else {
+                    break;
+                }
+            }
+            this.operand = operand;
+        }
     }
 
     private Query getComparisonQuery(
-            Comparison comparison, Map<String, NodeType> selectorMap)
+            DynamicOperand left, int transform, String operator,
+            StaticOperand rigth, Map<String, NodeType> selectorMap)
             throws RepositoryException {
-        DynamicOperand operand = comparison.getOperand1();
-        if (operand instanceof PropertyValue) {
-            PropertyValue property = (PropertyValue) operand;
+        if (left instanceof PropertyValue) {
+            PropertyValue pv = (PropertyValue) left;
             String field = npResolver.getJCRName(session.getQName(
-                    property.getPropertyName()));
+                    pv.getPropertyName()));
             int type = PropertyType.UNDEFINED;
-            NodeType nt = selectorMap.get(property.getSelectorName());
+            NodeType nt = selectorMap.get(pv.getSelectorName());
             if (nt != null) {
                 for (PropertyDefinition pd : nt.getPropertyDefinitions()) {
-                    if (pd.getName().equals(property.getPropertyName())) {
+                    if (pd.getName().equals(pv.getPropertyName())) {
                         type = pd.getRequiredType();
                     }
                 }
             }
             return getPropertyValueQuery(
-                    field, comparison.getOperator(),
-                    evaluator.getValue(comparison.getOperand2()), type);
+                    field, operator, evaluator.getValue(rigth), type, transform);
+        } else if (left instanceof NodeName) {
+            return getNodeNameQuery(transform, operator, rigth);
+        } else if (left instanceof NodeLocalName) {
+            return getNodeLocalNameQuery(transform, operator, rigth);
         } else {
-            throw new UnsupportedRepositoryOperationException(); // FIXME
+            throw new UnsupportedRepositoryOperationException(
+                    "Unknown operand type: " + left); // FIXME
         }
     }
 
-    private Query getPropertyValueQuery(
-            String field, String operator, Value value, int type)
+    private Query getNodeNameQuery(
+            int transform, String operator, StaticOperand right)
             throws RepositoryException {
-        Term term = getTerm(field, getValueString(value, type));
+        if (transform != TRANSFORM_NONE
+                || !JCR_OPERATOR_EQUAL_TO.equals(operator)) {
+            throw new UnsupportedRepositoryOperationException();
+        }
+
+        Value value = evaluator.getValue(right);
+        int type = value.getType();
+        String string = value.getString();
+        if (type == PropertyType.URI && string.startsWith("./")) {
+            string = string.substring("./".length());
+        } else if (type == PropertyType.DOUBLE
+                || type == PropertyType.DECIMAL
+                || type == PropertyType.LONG
+                || type == PropertyType.BOOLEAN
+                || type == PropertyType.REFERENCE
+                || type == PropertyType.WEAKREFERENCE) {
+            throw new InvalidQueryException("Invalid name value: " + string);
+        }
+
+        try {
+            Name name = session.getQName(string);
+            Term uri = new Term(NAMESPACE_URI, name.getNamespaceURI());
+            Term local = new Term(LOCAL_NAME, name.getLocalName());
+
+            BooleanQuery query = new BooleanQuery();
+            query.add(new JackrabbitTermQuery(uri), MUST);
+            query.add(new JackrabbitTermQuery(local), MUST);
+            return query;
+        } catch (IllegalNameException e) {
+            throw new InvalidQueryException("Illegal name: " + string, e);
+        }
+    }
+
+    private Query getNodeLocalNameQuery(
+            int transform, String operator, StaticOperand right)
+            throws RepositoryException {
+        if (transform != TRANSFORM_NONE
+                || !JCR_OPERATOR_EQUAL_TO.equals(operator)) {
+            throw new UnsupportedRepositoryOperationException();
+        }
+
+        String name = evaluator.getValue(right).getString();
+        return new JackrabbitTermQuery(new Term(LOCAL_NAME, name));
+    }
+
+    private Query getNodeIdQuery(String field, String path)
+            throws RepositoryException {
+        String value;
+        try {
+            NodeImpl node = (NodeImpl) session.getNode(path);
+            value = node.getNodeId().toString();
+        } catch (PathNotFoundException e) {
+            value = "invalid-node-id"; // can never match a node
+        }
+        return new JackrabbitTermQuery(new Term(field, value));
+    }
+
+    private Query getPropertyValueQuery(
+            String field, String operator, Value value,
+            int type, int transform) throws RepositoryException {
+        String string = getValueString(value, type);
+        if (JCR_OPERATOR_LIKE.equals(operator)) {
+            return new WildcardQuery(PROPERTIES, field, string, transform);
+        }
+
+        Term term = getTerm(field, string);
         if (JCR_OPERATOR_EQUAL_TO.equals(operator)) {
-            return new JackrabbitTermQuery(term);
+            switch (transform) {
+            case TRANSFORM_UPPER_CASE:
+                return new CaseTermQuery.Upper(term);
+            case TRANSFORM_LOWER_CASE:
+                return new CaseTermQuery.Lower(term);
+            default:
+                return new JackrabbitTermQuery(term);
+            }
         } else if (JCR_OPERATOR_GREATER_THAN.equals(operator)) {
-            return new RangeQuery(term, getTerm(field, "\uFFFF"), false);
+            return new RangeQuery(term, getTerm(field, "\uFFFF"), false, transform);
         } else if (JCR_OPERATOR_GREATER_THAN_OR_EQUAL_TO.equals(operator)) {
-            return new RangeQuery(term, getTerm(field, "\uFFFF"), true);
+            return new RangeQuery(term, getTerm(field, "\uFFFF"), true, transform);
         } else if (JCR_OPERATOR_LESS_THAN.equals(operator)) {
-            return new RangeQuery(getTerm(field, ""), term, false);
+            return new RangeQuery(getTerm(field, ""), term, false, transform);
         } else if (JCR_OPERATOR_LESS_THAN_OR_EQUAL_TO.equals(operator)) {
-            return new RangeQuery(getTerm(field, ""), term, true);
+            return new RangeQuery(getTerm(field, ""), term, true, transform);
         } else if (JCR_OPERATOR_NOT_EQUAL_TO.equals(operator)) {
-            BooleanQuery or = new BooleanQuery();
-            or.add(new RangeQuery(getTerm(field, ""), term, false), SHOULD);
-            or.add(new RangeQuery(term, getTerm(field, "\uFFFF"), false), SHOULD);
-            return or;
-        } else if (JCR_OPERATOR_LIKE.equals(operator)) {
-            throw new UnsupportedRepositoryOperationException(); // FIXME
+            BooleanQuery query = new BooleanQuery();
+            query.add(Util.createMatchAllQuery(
+                    field, index.getIndexFormatVersion()), SHOULD);
+            switch (transform) {
+            case TRANSFORM_UPPER_CASE:
+                query.add(new CaseTermQuery.Upper(term), MUST_NOT);
+            case TRANSFORM_LOWER_CASE:
+                query.add(new CaseTermQuery.Lower(term), MUST_NOT);
+            default:
+                query.add(new JackrabbitTermQuery(term), MUST_NOT);
+            }
+            // and exclude all nodes where 'field' is multi valued
+            query.add(new JackrabbitTermQuery(new Term(MVP, field)), MUST_NOT);
+            return query;
         } else {
             throw new UnsupportedRepositoryOperationException(); // FIXME
         }
@@ -427,8 +682,12 @@ public class LuceneQueryFactory {
             return DoubleField.doubleToString(value.getDouble());
         case LONG:
             return LongField.longToString(value.getLong());
+        case DECIMAL:
+            return DecimalField.decimalToString(value.getDecimal());
         case NAME:
             return npResolver.getJCRName(session.getQName(value.getString()));
+        case PATH:
+            return npResolver.getJCRPath(session.getQPath(value.getString()));
         default:
             String string = value.getString();
             if (type != UNDEFINED && type != STRING) {

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/QueryImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/QueryImpl.java?rev=1039888&r1=1039887&r2=1039888&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/QueryImpl.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/QueryImpl.java Sun Nov 28 15:03:57 2010
@@ -88,8 +88,7 @@ public class QueryImpl extends AbstractQ
             SessionContext sessionContext, SearchIndex index,
             PropertyTypeRegistry propReg, String statement, String language,
             QueryNodeFactory factory) throws InvalidQueryException {
-        super(sessionContext, index, propReg,
-                Collections.<String, Value>emptyMap());
+        super(sessionContext, index, propReg);
         // parse query according to language
         // build query tree using the passed factory
         this.root = QueryParser.parse(

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/SearchIndex.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/SearchIndex.java?rev=1039888&r1=1039887&r2=1039888&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/SearchIndex.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/SearchIndex.java Sun Nov 28 15:03:57 2010
@@ -16,10 +16,9 @@
  */
 package org.apache.jackrabbit.core.query.lucene;
 
-import java.io.InputStream;
-
 import java.io.File;
 import java.io.IOException;
+import java.io.InputStream;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -39,7 +38,6 @@ import javax.xml.parsers.DocumentBuilder
 import javax.xml.parsers.ParserConfigurationException;
 
 import org.apache.jackrabbit.core.HierarchyManager;
-import org.apache.jackrabbit.core.ItemManager;
 import org.apache.jackrabbit.core.SessionImpl;
 import org.apache.jackrabbit.core.fs.FileSystem;
 import org.apache.jackrabbit.core.fs.FileSystemException;
@@ -60,12 +58,10 @@ import org.apache.jackrabbit.core.state.
 import org.apache.jackrabbit.spi.Name;
 import org.apache.jackrabbit.spi.Path;
 import org.apache.jackrabbit.spi.PathFactory;
-import org.apache.jackrabbit.spi.commons.conversion.NamePathResolver;
 import org.apache.jackrabbit.spi.commons.name.NameConstants;
 import org.apache.jackrabbit.spi.commons.name.PathFactoryImpl;
 import org.apache.jackrabbit.spi.commons.query.DefaultQueryNodeFactory;
 import org.apache.jackrabbit.spi.commons.query.qom.OrderingImpl;
-import org.apache.jackrabbit.spi.commons.query.qom.QueryObjectModelTree;
 import org.apache.lucene.analysis.Analyzer;
 import org.apache.lucene.analysis.Token;
 import org.apache.lucene.document.Document;
@@ -683,28 +679,6 @@ public class SearchIndex extends Abstrac
     }
 
     /**
-     * Creates a new query by specifying the query object model. If the query
-     * object model is considered invalid for the implementing class, an
-     * InvalidQueryException is thrown.
-     *
-     * @param sessionContext component context of the current session
-     * @param qomTree query query object model tree.
-     * @return A <code>Query</code> object.
-     * @throws javax.jcr.query.InvalidQueryException
-     *          if the query object model tree is invalid.
-     * @see QueryHandler#createExecutableQuery(SessionImpl, ItemManager, QueryObjectModelTree)
-     */
-    public ExecutableQuery createExecutableQuery(
-            SessionContext sessionContext, QueryObjectModelTree qomTree)
-            throws InvalidQueryException {
-        QueryObjectModelImpl query = new QueryObjectModelImpl(
-                sessionContext, this, getContext().getPropertyTypeRegistry(),
-                qomTree);
-        query.setRespectDocumentOrder(documentOrder);
-        return query;
-    }
-
-    /**
      * {@inheritDoc}
      */
     public Iterable<NodeId> getWeaklyReferringNodes(NodeId id)

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/join/JoinMerger.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/join/JoinMerger.java?rev=1039888&r1=1039887&r2=1039888&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/join/JoinMerger.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/join/JoinMerger.java Sun Nov 28 15:03:57 2010
@@ -17,7 +17,6 @@
 package org.apache.jackrabbit.core.query.lucene.join;
 
 import static javax.jcr.query.qom.QueryObjectModelConstants.JCR_JOIN_TYPE_LEFT_OUTER;
-import static javax.jcr.query.qom.QueryObjectModelConstants.JCR_JOIN_TYPE_RIGHT_OUTER;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -163,93 +162,47 @@ abstract class JoinMerger {
     public QueryResult merge(RowIterator leftRows, RowIterator rightRows)
             throws RepositoryException {
         RowIterator joinRows;
-        if (JCR_JOIN_TYPE_RIGHT_OUTER.equals(type)) {
-            Map<String, List<Row>> map = new HashMap<String, List<Row>>();
-            for (Row row : new RowIterable(leftRows)) {
-                for (String value : getLeftValues(row)) {
-                    List<Row> rows = map.get(value);
-                    if (rows == null) {
-                        rows = new ArrayList<Row>();
-                        map.put(value, rows);
-                    }
-                    rows.add(row);
-                }
-            }
-            joinRows = mergeRight(map, rightRows);
-        } else {
-            Map<String, List<Row>> map = new HashMap<String, List<Row>>();
-            for (Row row : new RowIterable(rightRows)) {
-                for (String value : getRightValues(row)) {
-                    List<Row> rows = map.get(value);
-                    if (rows == null) {
-                        rows = new ArrayList<Row>();
-                        map.put(value, rows);
-                    }
-                    rows.add(row);
+
+        Map<String, List<Row>> map = new HashMap<String, List<Row>>();
+        for (Row row : new RowIterable(rightRows)) {
+            for (String value : getRightValues(row)) {
+                List<Row> rows = map.get(value);
+                if (rows == null) {
+                    rows = new ArrayList<Row>();
+                    map.put(value, rows);
                 }
+                rows.add(row);
             }
-            boolean outer = JCR_JOIN_TYPE_LEFT_OUTER.equals(type);
-            joinRows = mergeLeft(leftRows, map, outer);
         }
-        return new SimpleQueryResult(columnNames, selectorNames, joinRows);
-    }
 
-    private RowIterator mergeLeft(
-            RowIterator leftRows, Map<String, List<Row>> rightRowMap,
-            boolean outer) throws RepositoryException {
-        if (!rightRowMap.isEmpty()) {
+        if (!map.isEmpty()) {
             List<Row> rows = new ArrayList<Row>();
             for (Row leftRow : new RowIterable(leftRows)) {
                 for (String value : getLeftValues(leftRow)) {
-                    List<Row> rightRows = rightRowMap.get(value);
-                    if (rightRows != null) {
-                        for (Row rightRow : rightRows) {
+                    List<Row> matchingRows = map.get(value);
+                    if (matchingRows != null) {
+                        for (Row rightRow : matchingRows) {
                             rows.add(mergeRow(leftRow, rightRow));
                         }
-                    } else if (outer) {
+                    } else if (JCR_JOIN_TYPE_LEFT_OUTER.equals(type)) {
+                        // No matches in an outer join -> add a null row
                         rows.add(mergeRow(leftRow, null));
                     }
                 }
             }
-            return new RowIteratorAdapter(rows);
-        } else if (outer) {
-            return new RowIteratorAdapter(leftRows) {
+            joinRows = new RowIteratorAdapter(rows);
+        } else if (JCR_JOIN_TYPE_LEFT_OUTER.equals(type)) {
+            joinRows = new RowIteratorAdapter(leftRows) {
                 @Override
                 public Object next() {
                     return mergeRow((Row) super.next(), null);
                 }
             };
         } else {
-            return new RowIteratorAdapter(Collections.emptySet());
+            joinRows = new RowIteratorAdapter(Collections.emptySet());
         }
-    }
 
-    private RowIterator mergeRight(
-            Map<String, List<Row>> leftRowMap, RowIterator rightRows)
-            throws RepositoryException {
-        if (leftRowMap.isEmpty()) {
-            List<Row> rows = new ArrayList<Row>();
-            for (Row rightRow : new RowIterable(rightRows)) {
-                for (String value : getRightValues(rightRow)) {
-                    List<Row> leftRows = leftRowMap.get(value);
-                    if (leftRows != null) {
-                        for (Row leftRow : leftRows) {
-                            rows.add(mergeRow(leftRow, rightRow));
-                        }
-                    } else {
-                        rows.add(mergeRow(null, rightRow));
-                    }
-                }
-            }
-            return new RowIteratorAdapter(rows);
-        } else {
-            return new RowIteratorAdapter(rightRows) {
-                @Override
-                public Object next() {
-                    return mergeRow(null, (Row) super.next());
-                }
-            };
-        }
+        return new SimpleQueryResult(columnNames, selectorNames, joinRows);
     }
 
     /**

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/join/OperandEvaluator.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/join/OperandEvaluator.java?rev=1039888&r1=1039887&r2=1039888&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/join/OperandEvaluator.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/join/OperandEvaluator.java Sun Nov 28 15:03:57 2010
@@ -24,6 +24,7 @@ import java.util.Map;
 import javax.jcr.Node;
 import javax.jcr.PathNotFoundException;
 import javax.jcr.Property;
+import javax.jcr.PropertyType;
 import javax.jcr.RepositoryException;
 import javax.jcr.UnsupportedRepositoryOperationException;
 import javax.jcr.Value;
@@ -67,6 +68,21 @@ public class OperandEvaluator {
         this.variables = variables;
     }
 
+    public Value getValue(StaticOperand operand, int type) throws RepositoryException {
+        Value value = getValue(operand);
+        if (type == PropertyType.UNDEFINED || type == value.getType()) {
+            return value;
+        } if (type == PropertyType.LONG) {
+            return factory.createValue(value.getLong());
+        } if (type == PropertyType.DOUBLE) {
+            return factory.createValue(value.getDouble());
+        } if (type == PropertyType.DATE) {
+            return factory.createValue(value.getDate());
+        } else {
+            return factory.createValue(value.getString(), type);
+        }
+    }
+
     /**
      * Returns the value of the given static operand
      * ({@link Literal literal} or {@link BindVariableValue bind variable}).
@@ -280,11 +296,14 @@ public class OperandEvaluator {
      */
     private Property getProperty(PropertyValue operand, Row row)
             throws RepositoryException {
-        try {
-            String selector = operand.getSelectorName();
-            String property = operand.getPropertyName();
-            return row.getNode(selector).getProperty(property);
-        } catch (PathNotFoundException e) {
+        Node node = row.getNode(operand.getSelectorName());
+        if (node != null) {
+            try {
+                return node.getProperty(operand.getPropertyName());
+            } catch (PathNotFoundException e) {
+                return null;
+            }
+        } else {
             return null;
         }
     }

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/join/QueryEngine.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/join/QueryEngine.java?rev=1039888&r1=1039887&r2=1039888&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/join/QueryEngine.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/join/QueryEngine.java Sun Nov 28 15:03:57 2010
@@ -16,68 +16,45 @@
  */
 package org.apache.jackrabbit.core.query.lucene.join;
 
-import static javax.jcr.query.qom.QueryObjectModelConstants.JCR_OPERATOR_EQUAL_TO;
-import static javax.jcr.query.qom.QueryObjectModelConstants.JCR_OPERATOR_GREATER_THAN;
-import static javax.jcr.query.qom.QueryObjectModelConstants.JCR_OPERATOR_GREATER_THAN_OR_EQUAL_TO;
-import static javax.jcr.query.qom.QueryObjectModelConstants.JCR_OPERATOR_LESS_THAN;
-import static javax.jcr.query.qom.QueryObjectModelConstants.JCR_OPERATOR_LESS_THAN_OR_EQUAL_TO;
-import static javax.jcr.query.qom.QueryObjectModelConstants.JCR_OPERATOR_LIKE;
-import static javax.jcr.query.qom.QueryObjectModelConstants.JCR_OPERATOR_NOT_EQUAL_TO;
+import static javax.jcr.query.qom.QueryObjectModelConstants.JCR_JOIN_TYPE_LEFT_OUTER;
+import static javax.jcr.query.qom.QueryObjectModelConstants.JCR_JOIN_TYPE_RIGHT_OUTER;
 import static javax.jcr.query.qom.QueryObjectModelConstants.JCR_ORDER_DESCENDING;
 
+import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.LinkedHashMap;
 import java.util.List;
-import java.util.Locale;
 import java.util.Map;
 
-import javax.jcr.Node;
-import javax.jcr.NodeIterator;
-import javax.jcr.PropertyType;
-import javax.jcr.RangeIterator;
 import javax.jcr.RepositoryException;
 import javax.jcr.Session;
 import javax.jcr.UnsupportedRepositoryOperationException;
 import javax.jcr.Value;
 import javax.jcr.ValueFactory;
 import javax.jcr.Workspace;
+import javax.jcr.nodetype.NoSuchNodeTypeException;
 import javax.jcr.nodetype.NodeType;
 import javax.jcr.nodetype.NodeTypeManager;
 import javax.jcr.nodetype.PropertyDefinition;
-import javax.jcr.query.Query;
-import javax.jcr.query.QueryManager;
+import javax.jcr.query.InvalidQueryException;
 import javax.jcr.query.QueryResult;
 import javax.jcr.query.Row;
 import javax.jcr.query.RowIterator;
-import javax.jcr.query.qom.And;
-import javax.jcr.query.qom.BindVariableValue;
-import javax.jcr.query.qom.ChildNode;
 import javax.jcr.query.qom.Column;
-import javax.jcr.query.qom.Comparison;
 import javax.jcr.query.qom.Constraint;
-import javax.jcr.query.qom.DescendantNode;
-import javax.jcr.query.qom.FullTextSearch;
 import javax.jcr.query.qom.Join;
-import javax.jcr.query.qom.Literal;
-import javax.jcr.query.qom.LowerCase;
-import javax.jcr.query.qom.Not;
 import javax.jcr.query.qom.Operand;
-import javax.jcr.query.qom.Or;
 import javax.jcr.query.qom.Ordering;
-import javax.jcr.query.qom.PropertyExistence;
 import javax.jcr.query.qom.PropertyValue;
 import javax.jcr.query.qom.QueryObjectModelFactory;
-import javax.jcr.query.qom.SameNode;
 import javax.jcr.query.qom.Selector;
 import javax.jcr.query.qom.Source;
-import javax.jcr.query.qom.UpperCase;
 
 import org.apache.jackrabbit.commons.JcrUtils;
-import org.apache.jackrabbit.commons.iterator.NodeIteratorAdapter;
-import org.apache.jackrabbit.commons.iterator.RangeIteratorAdapter;
 import org.apache.jackrabbit.commons.iterator.RowIteratorAdapter;
+import org.apache.jackrabbit.core.query.lucene.LuceneQueryFactory;
 
 public class QueryEngine {
 
@@ -128,7 +105,7 @@ public class QueryEngine {
 
     }
 
-    private final Session session;
+    private final LuceneQueryFactory lqf;
 
     private final NodeTypeManager ntManager;
 
@@ -138,9 +115,10 @@ public class QueryEngine {
 
     private final OperandEvaluator evaluator;
 
-    public QueryEngine(Session session, Map<String, Value> variables)
-            throws RepositoryException {
-        this.session = session;
+    public QueryEngine(
+            Session session, LuceneQueryFactory lqf,
+            Map<String, Value> variables) throws RepositoryException {
+        this.lqf = lqf;
 
         Workspace workspace = session.getWorkspace();
         this.ntManager = workspace.getNodeTypeManager();
@@ -160,6 +138,12 @@ public class QueryEngine {
                     columns, selector, constraint, orderings, offset, limit);
         } else if (source instanceof Join) {
             Join join = (Join) source;
+            if (join.getJoinType() == JCR_JOIN_TYPE_RIGHT_OUTER) {
+                // Swap the join sources to normalize all outer joins to left
+                join = qomFactory.join(
+                        join.getRight(), join.getLeft(),
+                        JCR_JOIN_TYPE_LEFT_OUTER, join.getJoinCondition());
+            }
             return execute(
                     columns, join, constraint, orderings, offset, limit);
         } else {
@@ -221,155 +205,29 @@ public class QueryEngine {
         return sort(result, orderings, offset, limit);
     }
 
-    private String toSqlConstraint(Constraint constraint)
-            throws RepositoryException {
-        if (constraint instanceof And) {
-            And and = (And) constraint;
-            String c1 = toSqlConstraint(and.getConstraint1());
-            String c2 = toSqlConstraint(and.getConstraint2());
-            return "(" + c1 + ") AND (" + c2 + ")";
-        } else if (constraint instanceof Or) {
-            Or or = (Or) constraint;
-            String c1 = toSqlConstraint(or.getConstraint1());
-            String c2 = toSqlConstraint(or.getConstraint2());
-            return "(" + c1 + ") OR (" + c2 + ")";
-        } else if (constraint instanceof Not) {
-            Not or = (Not) constraint;
-            return "NOT (" + toSqlConstraint(or.getConstraint()) + ")";
-        } else if (constraint instanceof Comparison) {
-            Comparison c = (Comparison) constraint;
-            String left = toSqlOperand(c.getOperand1());
-            String right = toSqlOperand(c.getOperand2());
-            if (c.getOperator().equals(JCR_OPERATOR_EQUAL_TO)) {
-                return left + " = " + right;
-            } else if (c.getOperator().equals(JCR_OPERATOR_GREATER_THAN)) {
-                return left + " > " + right;
-            } else if (c.getOperator().equals(JCR_OPERATOR_GREATER_THAN_OR_EQUAL_TO)) {
-                return left + " >= " + right;
-            } else if (c.getOperator().equals(JCR_OPERATOR_LESS_THAN)) {
-                return left + " < " + right;
-            } else if (c.getOperator().equals(JCR_OPERATOR_LESS_THAN_OR_EQUAL_TO)) {
-                return left + " <= " + right;
-            } else if (c.getOperator().equals(JCR_OPERATOR_LIKE)) {
-                return left + " LIKE " + right;
-            } else if (c.getOperator().equals(JCR_OPERATOR_NOT_EQUAL_TO)) {
-                return left + " <> " + right;
-            } else {
-                throw new RepositoryException("Unsupported comparison: " + c);
-            }
-        } else if (constraint instanceof SameNode) {
-            SameNode sn = (SameNode) constraint;
-            return "jcr:path = '" + sn.getPath() + "'";
-        } else if (constraint instanceof ChildNode) {
-            ChildNode cn = (ChildNode) constraint;
-            return "jcr:path LIKE '" + cn.getParentPath() + "/%'";
-        } else if (constraint instanceof DescendantNode) {
-            DescendantNode dn = (DescendantNode) constraint;
-            return "jcr:path LIKE '" + dn.getAncestorPath() + "/%'";
-        } else if (constraint instanceof PropertyExistence) {
-            PropertyExistence pe = (PropertyExistence) constraint;
-            return pe.getPropertyName() + " IS NOT NULL";
-        } else if (constraint instanceof FullTextSearch) {
-            FullTextSearch fts = (FullTextSearch) constraint;
-            String expr = toSqlOperand(fts.getFullTextSearchExpression());
-            return "CONTAINS(" + fts.getPropertyName() + ", " + expr + ")";
-        } else  {
-            throw new RepositoryException("Unsupported constraint: " + constraint);
-        }
-    }
-
-    private static enum Transform {
-        NONE,
-        UPPER,
-        LOWER
-    }
-
-    private String toSqlOperand(Operand operand) throws RepositoryException {
-        return toSqlOperand(operand, Transform.NONE);
-    }
-
-    private String toSqlOperand(Operand operand, Transform transform)
-            throws RepositoryException {
-        if (operand instanceof PropertyValue) {
-            PropertyValue pv = (PropertyValue) operand;
-            switch (transform) {
-            case UPPER:
-                return "UPPER(" + pv.getPropertyName() + ")";
-            case LOWER:
-                return "LOWER(" + pv.getPropertyName() + ")";
-            default:
-                return pv.getPropertyName();
-            } 
-        } else if (operand instanceof LowerCase) {
-            LowerCase lc = (LowerCase) operand;
-            if (transform == Transform.NONE) {
-                transform = Transform.LOWER;
-            }
-            return toSqlOperand(lc.getOperand(), transform);
-        } else if (operand instanceof UpperCase) {
-            UpperCase uc = (UpperCase) operand;
-            if (transform == Transform.NONE) {
-                transform = Transform.UPPER;
-            }
-            return toSqlOperand(uc.getOperand(), transform);
-        } else if ((operand instanceof Literal)
-                || (operand instanceof BindVariableValue)) {
-            Value value = evaluator.getValue(operand, null);
-            int type = value.getType();
-            if (type == PropertyType.LONG || type == PropertyType.DOUBLE) {
-                return value.getString();
-            } else if (type == PropertyType.DATE && transform == Transform.NONE) {
-                return "TIMESTAMP '" + value.getString() + "'";
-            } else if (transform == Transform.UPPER) {
-                return "'" + value.getString().toUpperCase(Locale.ENGLISH) + "'";
-            } else if (transform == Transform.LOWER) {
-                return "'" + value.getString().toLowerCase(Locale.ENGLISH) + "'";
-            } else {
-                return "'" + value.getString() + "'";
-            }
-        } else {
-            throw new RepositoryException("Uknown operand type: " + operand);
-        }
-    }
-
     protected QueryResult execute(
             Column[] columns, Selector selector, Constraint constraint,
             Ordering[] orderings, long offset, long limit)
             throws RepositoryException {
-        StringBuilder builder = new StringBuilder();
-        builder.append("SELECT * FROM ");
-        builder.append(selector.getNodeTypeName());
-        if (constraint != null) {
-            builder.append(" WHERE ");
-            builder.append(toSqlConstraint(constraint));
-        }
-
-        QueryManager manager = session.getWorkspace().getQueryManager();
-        Query query = manager.createQuery(builder.toString(), Query.SQL);
-
         Map<String, NodeType> selectorMap = getSelectorNames(selector);
-        final String[] selectorNames =
+        String[] selectorNames =
             selectorMap.keySet().toArray(new String[selectorMap.size()]);
 
-        final Map<String, PropertyValue> columnMap =
+        Map<String, PropertyValue> columnMap =
             getColumnMap(columns, selectorMap);
-        final String[] columnNames =
+        String[] columnNames =
             columnMap.keySet().toArray(new String[columnMap.size()]);
 
-        NodeIterator nodes = query.execute().getNodes();
-        final String selectorName = selector.getSelectorName();
-        RowIterator rows = new RowIteratorAdapter(nodes) {
-            @Override
-            public Object next() {
-                Node node = (Node) super.next();
-                return new SelectorRow(
-                        columnMap, evaluator, selectorName, node, 1.0);
-            }
-        };
-
-        QueryResult result =
-            new SimpleQueryResult(columnNames, selectorNames, rows);
-        return sort(result, orderings, offset, limit);
+        try {
+            RowIterator rows = new RowIteratorAdapter(lqf.execute(
+                    columnMap, selector, constraint));
+            QueryResult result =
+                new SimpleQueryResult(columnNames, selectorNames, rows);
+            return sort(result, orderings, offset, limit);
+        } catch (IOException e) {
+            throw new RepositoryException(
+                    "Failed to access the query index", e);
+        }
     }
 
     private Map<String, PropertyValue> getColumnMap(
@@ -418,8 +276,7 @@ public class QueryEngine {
         if (source instanceof Selector) {
             Selector selector = (Selector) source;
             return Collections.singletonMap(
-                    selector.getSelectorName(),
-                    ntManager.getNodeType(selector.getNodeTypeName()));
+                    selector.getSelectorName(), getNodeType(selector));
         } else if (source instanceof Join) {
             Join join = (Join) source;
             Map<String, NodeType> map = new LinkedHashMap<String, NodeType>();
@@ -432,6 +289,15 @@ public class QueryEngine {
         }
     }
 
+    private NodeType getNodeType(Selector selector) throws RepositoryException {
+        try {
+            return ntManager.getNodeType(selector.getNodeTypeName());
+        } catch (NoSuchNodeTypeException e) {
+            throw new InvalidQueryException(
+                    "Selected node type does not exist: " + selector, e);
+        }
+    }
+
     /**
      * Sorts the given query results according to the given QOM orderings.
      * If one or more orderings have been specified, this method will iterate
@@ -461,13 +327,13 @@ public class QueryEngine {
                 Collections.sort(rows, new RowComparator(orderings));
             }
 
-            if (offset != 0 || limit >= 0) {
-                int from = (int) offset;
-                int to = rows.size();
-                if (limit >= 0 && offset + limit < to) {
-                    to = (int) (offset + limit);
-                }
-                rows = rows.subList(from, to);
+            if (offset > 0) {
+                int size = rows.size();
+                rows = rows.subList((int) Math.min(offset, size), size);
+            }
+            if (limit >= 0) {
+                int size = rows.size();
+                rows = rows.subList(0, (int) Math.min(limit, size));
             }
 
             return new SimpleQueryResult(

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/join/SameNodeJoinMerger.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/join/SameNodeJoinMerger.java?rev=1039888&r1=1039887&r2=1039888&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/join/SameNodeJoinMerger.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/join/SameNodeJoinMerger.java Sun Nov 28 15:03:57 2010
@@ -33,6 +33,8 @@ import javax.jcr.query.qom.PropertyValue
 import javax.jcr.query.qom.QueryObjectModelFactory;
 import javax.jcr.query.qom.SameNodeJoinCondition;
 
+import org.apache.jackrabbit.spi.commons.conversion.PathResolver;
+
 class SameNodeJoinMerger extends JoinMerger {
 
     private final String selector1;
@@ -91,10 +93,14 @@ class SameNodeJoinMerger extends JoinMer
             Node node = row.getNode(selector2);
             if (node != null) {
                 try {
-                    if (path != null) {
-                        node = node.getNode(path);
+                    String p = node.getPath();
+                    if (path != null && !".".equals(path)) {
+                        if (!"/".equals(p)) {
+                            p += "/";
+                        }
+                        p += path;
                     }
-                    return Collections.singleton(node.getPath());
+                    return Collections.singleton(p);
                 } catch (PathNotFoundException e) {
                     // fall through
                 }

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/join/ValueComparator.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/join/ValueComparator.java?rev=1039888&r1=1039887&r2=1039888&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/join/ValueComparator.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/join/ValueComparator.java Sun Nov 28 15:03:57 2010
@@ -26,12 +26,16 @@ import static javax.jcr.query.qom.QueryO
 import static javax.jcr.query.qom.QueryObjectModelConstants.JCR_OPERATOR_LESS_THAN;
 import static javax.jcr.query.qom.QueryObjectModelConstants.JCR_OPERATOR_LESS_THAN_OR_EQUAL_TO;
 import static javax.jcr.query.qom.QueryObjectModelConstants.JCR_OPERATOR_LIKE;
+import static javax.jcr.query.qom.QueryObjectModelConstants.JCR_OPERATOR_NOT_EQUAL_TO;
 
 import java.util.Comparator;
+import java.util.regex.Pattern;
 
 import javax.jcr.RepositoryException;
 import javax.jcr.Value;
 
+import org.apache.jackrabbit.core.query.lucene.Util;
+
 /**
  * Comparator for {@link Value} instances.
  */
@@ -78,10 +82,16 @@ public class ValueComparator implements 
             return compare(a, b) < 0;
         } else if (JCR_OPERATOR_LESS_THAN_OR_EQUAL_TO.equals(operator)) {
             return compare(a, b) <= 0;
+        } else if (JCR_OPERATOR_NOT_EQUAL_TO.equals(operator)) {
+            return compare(a, b) != 0;
         } else if (JCR_OPERATOR_LIKE.equals(operator)) {
-            // TODO: Implement LIKE support
-            throw new RuntimeException(
-                    "LIKE comparisions are not currently supported");
+            try {
+                Pattern pattern = Util.createRegexp(b.getString());
+                return pattern.matcher(a.getString()).matches();
+            } catch (RepositoryException e) {
+                throw new RuntimeException(
+                        "Unable to compare values " + a + " and " + b, e);
+            }
         } else {
             throw new IllegalArgumentException(
                     "Unknown comparison operator: " + operator);