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/10/22 17:09:01 UTC

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

Author: jukka
Date: Fri Oct 22 15:09:00 2010
New Revision: 1026361

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

Extend LuceneQueryFactory to handle more QOM constraints.
Simplify handling of bind variables.
Improve OperandEvaluator, including its javadocs.

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/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/QueryObjectModelImpl.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/constraint/ConstraintBuilder.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/join/OperandEvaluator.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=1026361&r1=1026360&r2=1026361&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 Fri Oct 22 15:09:00 2010
@@ -16,7 +16,7 @@
  */
 package org.apache.jackrabbit.core.query;
 
-import org.apache.jackrabbit.spi.Name;
+import java.util.Map;
 
 import javax.jcr.RepositoryException;
 import javax.jcr.Value;
@@ -45,17 +45,16 @@ public interface ExecutableQuery {
      *
      * @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.
+     * @throws RepositoryException if <code>varName</code> is not a
+     *                             valid variable in this query.
      */
-    void bindValue(Name varName, Value value)
-        throws IllegalArgumentException, RepositoryException;
+    void bindValue(String varName, Value value) throws RepositoryException;
 
     /**
-     * @return the names of the bind variables in this query.
+     * Returns the bind variables of this query.
      *
-     * @throws RepositoryException if an error occurs.
+     * @return bind variables
      */
-    Name[] getBindVariableNames() throws RepositoryException;
+    Map<String, Value> getBindVariables();
+
 }

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=1026361&r1=1026360&r2=1026361&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 Fri Oct 22 15:09:00 2010
@@ -21,6 +21,7 @@ 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;
@@ -37,7 +38,6 @@ import javax.jcr.version.VersionExceptio
 
 import org.apache.jackrabbit.core.session.SessionContext;
 import org.apache.jackrabbit.core.session.SessionOperation;
-import org.apache.jackrabbit.spi.Name;
 import org.apache.jackrabbit.spi.Path;
 import org.apache.jackrabbit.spi.commons.conversion.NameException;
 import org.slf4j.Logger;
@@ -211,12 +211,8 @@ public class QueryImpl extends AbstractQ
      * {@inheritDoc}
      */
     public String[] getBindVariableNames() throws RepositoryException {
-        Name[] names = query.getBindVariableNames();
-        String[] strNames = new String[names.length];
-        for (int i = 0; i < names.length; i++) {
-            strNames[i] = sessionContext.getJCRName(names[i]);
-        }
-        return strNames;
+        Set<String> names = query.getBindVariables().keySet();
+        return names.toArray(new String[names.size()]);
     }
 
     /**
@@ -233,7 +229,7 @@ public class QueryImpl extends AbstractQ
             throws IllegalArgumentException, RepositoryException {
         checkInitialized();
         try {
-            query.bindValue(sessionContext.getQName(varName), value);
+            query.bindValue(varName, value);
         } catch (NameException e) {
             throw new RepositoryException(e.getMessage());
         }

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=1026361&r1=1026360&r2=1026361&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 Fri Oct 22 15:09:00 2010
@@ -51,8 +51,6 @@ public class QueryObjectModelImpl extend
      */
     protected QueryObjectModelTree qomTree;
 
-    private final Map<String, Value> variables = new HashMap<String, Value>();
-
     /**
      * {@inheritDoc}
      * @throws UnsupportedOperationException always.
@@ -91,19 +89,9 @@ public class QueryObjectModelImpl extend
         this.node = node;
         this.statement = QueryObjectModelBuilderRegistry.getQueryObjectModelBuilder(language).toString(this);
         this.query = handler.createExecutableQuery(sessionContext, qomTree);
-        for (Name name : query.getBindVariableNames()) {
-            variables.put(sessionContext.getJCRName(name), null);
-        }
         setInitialized();
     }
 
-    @Override
-    public void bindValue(String varName, Value value)
-            throws IllegalArgumentException, RepositoryException {
-        super.bindValue(varName, value);
-        variables.put(varName, value);
-    }
-
     public QueryResult execute() throws RepositoryException {
         Source source = getSource();
         if (source instanceof Join) {
@@ -111,7 +99,7 @@ public class QueryObjectModelImpl extend
             if (JCR_JOIN_TYPE_INNER.equals(join.getJoinType())
                     && join.getJoinCondition() instanceof EquiJoinCondition) {
                 QueryEngine engine =
-                    new QueryEngine(sessionContext.getSessionImpl(), variables);
+                    new QueryEngine(sessionContext.getSessionImpl(), query.getBindVariables());
                 return engine.execute(
                         getColumns(), getSource(), getConstraint(),
                         getOrderings(), offset, limit);

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=1026361&r1=1026360&r2=1026361&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 Fri Oct 22 15:09:00 2010
@@ -16,21 +16,16 @@
  */
 package org.apache.jackrabbit.core.query.lucene;
 
-import org.apache.jackrabbit.core.query.ExecutableQuery;
-import org.apache.jackrabbit.core.query.PropertyTypeRegistry;
-import org.apache.jackrabbit.core.session.SessionContext;
-import org.apache.jackrabbit.spi.Name;
+import java.util.Map;
 
-import javax.jcr.Value;
 import javax.jcr.RepositoryException;
+import javax.jcr.Value;
 import javax.jcr.Workspace;
 import javax.jcr.query.qom.QueryObjectModelFactory;
 
-import java.util.Set;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.HashMap;
-import java.util.Collections;
+import org.apache.jackrabbit.core.query.ExecutableQuery;
+import org.apache.jackrabbit.core.query.PropertyTypeRegistry;
+import org.apache.jackrabbit.core.session.SessionContext;
 
 /**
  * <code>AbstractQueryImpl</code> provides a base class for executable queries
@@ -59,15 +54,8 @@ public abstract class AbstractQueryImpl 
      */
     private boolean documentOrder = true;
 
-    /**
-     * Set&lt;Name>, where Name is a variable name in the query statement.
-     */
-    private final Set<Name> variableNames = new HashSet<Name>();
-
-    /**
-     * Binding of variable name to value. Maps {@link Name} to {@link Value}.
-     */
-    private final Map<Name, Value> bindValues = new HashMap<Name, Value>();
+    /** Bind variables of this query */
+    private final Map<String, Value> variables;
 
 
     /**
@@ -79,10 +67,11 @@ public abstract class AbstractQueryImpl 
      */
     public AbstractQueryImpl(
             SessionContext sessionContext, SearchIndex index,
-            PropertyTypeRegistry propReg) {
+            PropertyTypeRegistry propReg, Map<String, Value> variables) {
         this.sessionContext = sessionContext;
         this.index = index;
         this.propReg = propReg;
+        this.variables = variables;
     }
 
     /**
@@ -122,37 +111,21 @@ public abstract class AbstractQueryImpl 
      *                                  variable in this query.
      * @throws RepositoryException      if an error occurs.
      */
-    public void bindValue(Name varName, Value value)
+    public void bindValue(String varName, Value value)
             throws IllegalArgumentException, RepositoryException {
-        if (!variableNames.contains(varName)) {
-            throw new IllegalArgumentException("not a valid variable in this query");
+        if (variables.containsKey(varName)) {
+            variables.put(varName, value);
         } else {
-            bindValues.put(varName, value);
+            throw new IllegalArgumentException(
+                    varName + " is not a valid variable in this query");
         }
     }
 
     /**
      * {@inheritDoc}
      */
-    public Name[] getBindVariableNames() throws RepositoryException {
-        return variableNames.toArray(new Name[variableNames.size()]);
-    }
-
-    /**
-     * Adds a name to the set of variables.
-     *
-     * @param varName the name of the variable.
-     */
-    protected void addVariableName(Name varName) {
-        variableNames.add(varName);
-    }
-
-    /**
-     * @return an unmodifieable map, which contains the variable names and their
-     *         respective value.
-     */
-    protected Map<Name, Value> getBindVariableValues() {
-        return Collections.unmodifiableMap(bindValues);
+    public Map<String, Value> getBindVariables() {
+        return variables;
     }
 
     /**

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=1026361&r1=1026360&r2=1026361&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 Fri Oct 22 15:09:00 2010
@@ -16,42 +16,70 @@
  */
 package org.apache.jackrabbit.core.query.lucene;
 
+import static javax.jcr.PropertyType.DATE;
+import static javax.jcr.PropertyType.DOUBLE;
+import static javax.jcr.PropertyType.LONG;
+import static javax.jcr.PropertyType.NAME;
+import static javax.jcr.PropertyType.STRING;
+import static javax.jcr.PropertyType.UNDEFINED;
+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 org.apache.jackrabbit.core.query.lucene.FieldNames.PROPERTIES;
 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.SHOULD;
 
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 
+import javax.jcr.PropertyType;
 import javax.jcr.RepositoryException;
+import javax.jcr.UnsupportedRepositoryOperationException;
 import javax.jcr.Value;
 import javax.jcr.nodetype.NodeType;
 import javax.jcr.nodetype.NodeTypeIterator;
 import javax.jcr.nodetype.NodeTypeManager;
-import javax.jcr.query.InvalidQueryException;
-import javax.jcr.query.qom.Literal;
+import javax.jcr.nodetype.PropertyDefinition;
+import javax.jcr.query.qom.And;
+import javax.jcr.query.qom.ChildNode;
+import javax.jcr.query.qom.Comparison;
+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.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.StaticOperand;
 
-import org.apache.lucene.analysis.Analyzer;
-import org.apache.lucene.index.Term;
-import org.apache.lucene.queryParser.ParseException;
-import org.apache.lucene.queryParser.QueryParser;
-import org.apache.lucene.search.BooleanQuery;
-import org.apache.lucene.search.Query;
 import org.apache.jackrabbit.core.HierarchyManager;
 import org.apache.jackrabbit.core.SessionImpl;
+import org.apache.jackrabbit.core.query.lucene.join.OperandEvaluator;
 import org.apache.jackrabbit.spi.Name;
 import org.apache.jackrabbit.spi.commons.conversion.NamePathResolver;
-import org.apache.jackrabbit.spi.commons.query.qom.BindVariableValueImpl;
 import org.apache.jackrabbit.spi.commons.query.qom.DefaultQOMTreeVisitor;
-import org.apache.jackrabbit.spi.commons.query.qom.JoinConditionImpl;
-import org.apache.jackrabbit.spi.commons.query.qom.SelectorImpl;
 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.jackrabbit.spi.commons.query.qom.JoinImpl;
+import org.apache.lucene.analysis.Analyzer;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.queryParser.ParseException;
+import org.apache.lucene.queryParser.QueryParser;
+import org.apache.lucene.search.BooleanClause.Occur;
+import org.apache.lucene.search.BooleanQuery;
+import org.apache.lucene.search.Query;
 
 /**
  * Factory that creates Lucene queries from QOM elements.
@@ -98,10 +126,8 @@ public class LuceneQueryFactory {
      */
     private final IndexFormatVersion version;
 
-    /**
-     * The Bind Variable values.
-     */
-    private final Map<Name, Value> bindVariables;
+    /** Operand evaluator */
+    private final OperandEvaluator evaluator;
 
     private final String mixinTypesField;
 
@@ -123,7 +149,7 @@ public class LuceneQueryFactory {
             SessionImpl session, HierarchyManager hmgr,
             NamespaceMappings nsMappings, Analyzer analyzer,
             SynonymProvider synonymProvider, IndexFormatVersion version,
-            Map<Name, Value> bindVariables) throws RepositoryException {
+            Map<String, Value> bindVariables) throws RepositoryException {
         this.session = session;
         this.ntManager = session.getWorkspace().getNodeTypeManager();
         this.hmgr = hmgr;
@@ -132,7 +158,8 @@ public class LuceneQueryFactory {
         this.synonymProvider = synonymProvider;
         this.version = version;
         this.npResolver = NamePathResolverImpl.create(nsMappings);
-        this.bindVariables = bindVariables;
+        this.evaluator =
+            new OperandEvaluator(session.getValueFactory(), bindVariables);
         this.mixinTypesField = nsMappings.translateName(JCR_MIXINTYPES);
         this.primaryTypeField = nsMappings.translateName(JCR_PRIMARYTYPE);
     }
@@ -205,20 +232,7 @@ public class LuceneQueryFactory {
                 fieldname, analyzer, synonymProvider);
         try {
             StaticOperand expr = fts.getFullTextSearchExpression();
-            if (expr instanceof Literal) {
-                return parser.parse(
-                        ((Literal) expr).getLiteralValue().getString());
-            } else if (expr instanceof BindVariableValueImpl) {
-                Value value = this.bindVariables.get(
-                        ((BindVariableValueImpl) expr).getBindVariableQName());
-                if (value == null) {
-                    throw new InvalidQueryException("Bind variable not bound");
-                }
-                return parser.parse(value.getString());
-            } else {
-                throw new RepositoryException(
-                        "Unknown static operand type: " + expr);
-            }
+            return parser.parse(evaluator.getValue(expr).getString());
         } catch (ParseException e) {
             throw new RepositoryException(e);
         }
@@ -277,4 +291,154 @@ public class LuceneQueryFactory {
                 (JoinConditionImpl) join.getJoinCondition(), nsMappings, hmgr);
     }
 
+    public Query create(
+            Constraint constraint, Map<String, NodeType> selectorMap)
+            throws RepositoryException {
+        if (constraint instanceof And) {
+            return getAndQuery((And) constraint, selectorMap);
+        } else if (constraint instanceof Or) {
+            return getOrQuery((Or) constraint, selectorMap);
+        } else if (constraint instanceof Not) {
+            return getNotQuery((Not) constraint, selectorMap);
+        } else if (constraint instanceof PropertyExistence) {
+            return getPropertyExistenceQuery((PropertyExistence) constraint);
+        } else if (constraint instanceof Comparison) {
+            return getComparisonQuery((Comparison) constraint, selectorMap);
+        } else if (constraint instanceof FullTextSearch) {
+            return null; // FIXME
+        } else if (constraint instanceof SameNode) {
+            return null; // FIXME
+        } else if (constraint instanceof ChildNode) {
+            return null; // FIXME
+        } else if (constraint instanceof DescendantNode) {
+            return null; // FIXME
+        } else {
+            throw new UnsupportedRepositoryOperationException(
+                    "Unknown constraint type: " + constraint);
+        }
+    }
+
+    private BooleanQuery getAndQuery(
+            And and, Map<String, NodeType> selectorMap)
+            throws RepositoryException {
+        BooleanQuery query = new BooleanQuery();
+        addBooleanConstraint(query, and.getConstraint1(), MUST, selectorMap);
+        addBooleanConstraint(query, and.getConstraint2(), MUST, selectorMap);
+        return query;
+    }
+
+    private BooleanQuery getOrQuery(Or or, Map<String, NodeType> selectorMap)
+            throws RepositoryException {
+        BooleanQuery query = new BooleanQuery();
+        addBooleanConstraint(query, or.getConstraint1(), SHOULD, selectorMap);
+        addBooleanConstraint(query, or.getConstraint2(), SHOULD, selectorMap);
+        return query;
+    }
+
+    private void addBooleanConstraint(
+            BooleanQuery query, Constraint constraint, Occur occur,
+            Map<String, NodeType> selectorMap) throws RepositoryException {
+        if (occur == MUST && constraint instanceof And) {
+            And and = (And) constraint;
+            addBooleanConstraint(query, and.getConstraint1(), occur, selectorMap);
+            addBooleanConstraint(query, and.getConstraint2(), occur, selectorMap);
+        } else if (occur == SHOULD && constraint instanceof Or) {
+            Or or = (Or) constraint;
+            addBooleanConstraint(query, or.getConstraint1(), occur, selectorMap);
+            addBooleanConstraint(query, or.getConstraint2(), occur, selectorMap);
+        } else {
+            query.add(create(constraint, selectorMap), occur);
+        }
+    }
+
+    private NotQuery getNotQuery(Not not, Map<String, NodeType> selectorMap)
+            throws RepositoryException {
+        return new NotQuery(create(not.getConstraint(), selectorMap));
+    }
+
+    private Query getPropertyExistenceQuery(PropertyExistence property)
+            throws RepositoryException {
+        String name = npResolver.getJCRName(session.getQName(
+                property.getPropertyName()));
+        return Util.createMatchAllQuery(name, version);
+    }
+
+    private Query getComparisonQuery(
+            Comparison comparison, Map<String, NodeType> selectorMap)
+            throws RepositoryException {
+        DynamicOperand operand = comparison.getOperand1();
+        if (operand instanceof PropertyValue) {
+            PropertyValue property = (PropertyValue) operand;
+            String field = npResolver.getJCRName(session.getQName(
+                    property.getPropertyName()));
+            int type = PropertyType.UNDEFINED;
+            NodeType nt = selectorMap.get(property.getSelectorName());
+            if (nt != null) {
+                for (PropertyDefinition pd : nt.getPropertyDefinitions()) {
+                    if (pd.getName().equals(property.getPropertyName())) {
+                        type = pd.getRequiredType();
+                    }
+                }
+            }
+            return getPropertyValueQuery(
+                    field, comparison.getOperator(),
+                    evaluator.getValue(comparison.getOperand2()), type);
+        } else {
+            throw new UnsupportedRepositoryOperationException(); // FIXME
+        }
+    }
+
+    private Query getPropertyValueQuery(
+            String field, String operator, Value value, int type)
+            throws RepositoryException {
+        Term term = getTerm(field, getValueString(value, type));
+        if (JCR_OPERATOR_EQUAL_TO.equals(operator)) {
+            return new JackrabbitTermQuery(term);
+        } else if (JCR_OPERATOR_GREATER_THAN.equals(operator)) {
+            return new RangeQuery(term, getTerm(field, "\uFFFF"), false);
+        } else if (JCR_OPERATOR_GREATER_THAN_OR_EQUAL_TO.equals(operator)) {
+            return new RangeQuery(term, getTerm(field, "\uFFFF"), true);
+        } else if (JCR_OPERATOR_LESS_THAN.equals(operator)) {
+            return new RangeQuery(getTerm(field, ""), term, false);
+        } else if (JCR_OPERATOR_LESS_THAN_OR_EQUAL_TO.equals(operator)) {
+            return new RangeQuery(getTerm(field, ""), term, true);
+        } 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
+        } else {
+            throw new UnsupportedRepositoryOperationException(); // FIXME
+        }
+    }
+
+    private Term getTerm(String field, String value) {
+        return new Term(PROPERTIES, FieldNames.createNamedValue(field, value));
+    }
+
+    private String getValueString(Value value, int type)
+            throws RepositoryException {
+        switch (value.getType()) {
+        case DATE:
+            return DateField.dateToString(value.getDate().getTime());
+        case DOUBLE:
+            return DoubleField.doubleToString(value.getDouble());
+        case LONG:
+            return LongField.longToString(value.getLong());
+        case NAME:
+            return npResolver.getJCRName(session.getQName(value.getString()));
+        default:
+            String string = value.getString();
+            if (type != UNDEFINED && type != STRING) {
+                return getValueString(
+                        session.getValueFactory().createValue(string, type),
+                        UNDEFINED);
+            } else {
+                return 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=1026361&r1=1026360&r2=1026361&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 Fri Oct 22 15:09:00 2010
@@ -16,10 +16,12 @@
  */
 package org.apache.jackrabbit.core.query.lucene;
 
+import java.util.Collections;
 import java.util.LinkedHashMap;
 import java.util.Map;
 
 import javax.jcr.RepositoryException;
+import javax.jcr.Value;
 import javax.jcr.Workspace;
 import javax.jcr.nodetype.PropertyDefinition;
 import javax.jcr.query.InvalidQueryException;
@@ -86,7 +88,8 @@ public class QueryImpl extends AbstractQ
             SessionContext sessionContext, SearchIndex index,
             PropertyTypeRegistry propReg, String statement, String language,
             QueryNodeFactory factory) throws InvalidQueryException {
-        super(sessionContext, index, propReg);
+        super(sessionContext, index, propReg,
+                Collections.<String, Value>emptyMap());
         // 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/QueryObjectModelImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/QueryObjectModelImpl.java?rev=1026361&r1=1026360&r2=1026361&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/QueryObjectModelImpl.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/QueryObjectModelImpl.java Fri Oct 22 15:09:00 2010
@@ -17,9 +17,12 @@
 package org.apache.jackrabbit.core.query.lucene;
 
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 import javax.jcr.RepositoryException;
+import javax.jcr.Value;
 import javax.jcr.nodetype.PropertyDefinition;
 import javax.jcr.query.InvalidQueryException;
 import javax.jcr.query.QueryResult;
@@ -63,10 +66,9 @@ public class QueryObjectModelImpl extend
             SessionContext sessionContext, SearchIndex index,
             PropertyTypeRegistry propReg, QueryObjectModelTree qomTree)
             throws InvalidQueryException {
-        super(sessionContext, index, propReg);
+        super(sessionContext, index, propReg, extractBindVariables(qomTree));
         this.qomTree = qomTree;
         checkNodeTypes();
-        extractBindVariableNames();
     }
 
     /**
@@ -98,13 +100,13 @@ public class QueryObjectModelImpl extend
                 session, index.getContext().getHierarchyManager(),
                 index.getNamespaceMappings(), index.getTextAnalyzer(),
                 index.getSynonymProvider(), index.getIndexFormatVersion(),
-                getBindVariableValues());
+                getBindVariables());
 
         MultiColumnQuery query = factory.create(qomTree.getSource());
 
         if (qomTree.getConstraint() != null) {
             Constraint c = ConstraintBuilder.create(qomTree.getConstraint(),
-                    getBindVariableValues(), qomTree.getSource().getSelectors(),
+                    getBindVariables(), qomTree.getSource().getSelectors(),
                     factory, session.getValueFactory());
             query = new FilterMultiColumnQuery(query, c);
         }
@@ -145,17 +147,20 @@ public class QueryObjectModelImpl extend
      * Extracts all {@link BindVariableValueImpl} from the {@link #qomTree}
      * and adds it to the set of known variable names.
      */
-    private void extractBindVariableNames() {
+    private static Map<String, Value> extractBindVariables(
+            QueryObjectModelTree qomTree) {
+        final Map<String, Value> variables = new HashMap<String, Value>();
         try {
             qomTree.accept(new DefaultTraversingQOMTreeVisitor() {
                 public Object visit(BindVariableValueImpl node, Object data) {
-                    addVariableName(node.getBindVariableQName());
+                    variables.put(node.getBindVariableName(), null);
                     return data;
                 }
             }, null);
         } catch (Exception e) {
             // will never happen
         }
+        return variables;
     }
 
     /**

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/constraint/ConstraintBuilder.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/constraint/ConstraintBuilder.java?rev=1026361&r1=1026360&r2=1026361&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/constraint/ConstraintBuilder.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/constraint/ConstraintBuilder.java Fri Oct 22 15:09:00 2010
@@ -73,7 +73,7 @@ public class ConstraintBuilder {
      *                             constraint.
      */
     public static Constraint create(ConstraintImpl constraint,
-                                    Map<Name, Value> bindVariableValues,
+                                    Map<String, Value> bindVariableValues,
                                     SelectorImpl[] selectors,
                                     LuceneQueryFactory factory,
                                     ValueFactory vf)
@@ -96,7 +96,7 @@ public class ConstraintBuilder {
         /**
          * The bind variables and their values.
          */
-        private final Map<Name, Value> bindVariableValues;
+        private final Map<String, Value> bindVariableValues;
 
         /**
          * The selectors of the query.
@@ -121,7 +121,7 @@ public class ConstraintBuilder {
          * @param factory            the lucene query factory.
          * @param vf                 the value factory of the current session.
          */
-        Visitor(Map<Name, Value> bindVariableValues,
+        Visitor(Map<String, Value> bindVariableValues,
                 SelectorImpl[] selectors,
                 LuceneQueryFactory factory,
                 ValueFactory vf) {
@@ -140,7 +140,14 @@ public class ConstraintBuilder {
 
         public Object visit(BindVariableValueImpl node, Object data)
                 throws Exception {
-            return bindVariableValues.get(node.getBindVariableQName());
+            String name = node.getBindVariableName();
+            Value value = bindVariableValues.get(name);
+            if (value != null) {
+                return value;
+            } else {
+                throw new RepositoryException(
+                        "No value specified for bind variable " + name);
+            }
         }
 
         public Object visit(ChildNodeImpl node, Object data) throws Exception {

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=1026361&r1=1026360&r2=1026361&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 Fri Oct 22 15:09:00 2010
@@ -38,28 +38,79 @@ import javax.jcr.query.qom.NodeLocalName
 import javax.jcr.query.qom.NodeName;
 import javax.jcr.query.qom.Operand;
 import javax.jcr.query.qom.PropertyValue;
+import javax.jcr.query.qom.StaticOperand;
 import javax.jcr.query.qom.UpperCase;
 
-class OperandEvaluator {
+/**
+ * Evaluator of QOM {@link Operand operands}. This class evaluates operands
+ * in the context of a {@link ValueFactory value factory}, a set of bind
+ * variables and possibly a query result row.
+ */
+public class OperandEvaluator {
 
+    /** Value factory */
     private final ValueFactory factory;
 
+    /** Bind variables */
     private final Map<String, Value> variables;
 
+    /**
+     * Creates an operand evaluator for the given value factory and set of
+     * bind variables.
+     *
+     * @param factory value factory
+     * @param variables bind variables
+     */
     public OperandEvaluator(
             ValueFactory factory, Map<String, Value> variables) {
         this.factory = factory;
         this.variables = variables;
     }
 
+    /**
+     * Returns the value of the given static operand
+     * ({@link Literal literal} or {@link BindVariableValue bind variable}).
+     *
+     * @param operand static operand to be evaluated
+     * @return evaluated value
+     * @throws RepositoryException if a named bind variable is not found,
+     *                             or if the operand type is unknown
+     */
+    public Value getValue(StaticOperand operand) throws RepositoryException {
+        if (operand instanceof Literal) {
+            Literal literal = (Literal) operand;
+            return literal.getLiteralValue();
+        } else if (operand instanceof BindVariableValue) {
+            BindVariableValue bvv = (BindVariableValue) operand;
+            Value value = variables.get(bvv.getBindVariableName());
+            if (value != null) {
+                return value;
+            } else {
+                throw new RepositoryException(
+                        "Unknown bind variable: " + bvv.getBindVariableName());
+            }
+        } else {
+            throw new UnsupportedRepositoryOperationException(
+                    "Unknown static operand type: " + operand);
+        }
+    }
+
+    /**
+     * Returns the value of the given operand in the context of the given row.
+     * This is a convenience method that uses a somewhat lossy best-effort
+     * mapping to evaluate multi-valued operands to a single value. Use the
+     * {@link #getValues(Operand, Row)} method for more accurate results.
+     *
+     * @param operand operand to be evaluated
+     * @param row query result row
+     * @return evaluated value
+     * @throws RepositoryException
+     */
     public Value getValue(Operand operand, Row row) throws RepositoryException {
         Value[] values = getValues(operand, row);
-        switch (values.length) {
-        case 0:
-            return factory.createValue("");
-        case 1:
+        if (values.length == 1) {
             return values[0];
-        default:
+        } else {
             StringBuilder builder = new StringBuilder();
             for (int i = 0; i < values.length; i++) {
                 if (i > 0) {
@@ -72,34 +123,36 @@ class OperandEvaluator {
     }
 
     /**
-     * Evaluates the given operand against the given row.
+     * Evaluates the given operand in the context of the given row.
      *
-     * @param operand operand
-     * @param row row
+     * @param operand operand to be evaluated
+     * @param row query result row
      * @return values of the operand at the given row
      * @throws RepositoryException if the operand can't be evaluated
      */
     public Value[] getValues(Operand operand, Row row)
             throws RepositoryException {
-        if (operand instanceof BindVariableValue) {
-            return getBindVariableValues((BindVariableValue) operand);
+        if (operand instanceof StaticOperand) {
+            StaticOperand so = (StaticOperand) operand;
+            return new Value[] { getValue(so) };
         } else if (operand instanceof FullTextSearchScore) {
-            return getFullTextSearchScoreValues(
-                    (FullTextSearchScore) operand, row);
+            FullTextSearchScore ftss = (FullTextSearchScore) operand;
+            double score = row.getScore(ftss.getSelectorName());
+            return new Value[] { factory.createValue(score) };
+        } else if (operand instanceof NodeName) {
+            NodeName nn = (NodeName) operand;
+            Node node = row.getNode(nn.getSelectorName());
+            return new Value[] { factory.createValue(node.getName(), NAME) };
         } else if (operand instanceof Length) {
             return getLengthValues((Length) operand, row);
-        } else if (operand instanceof Literal) {
-            return getLiteralValues((Literal) operand);
         } else if (operand instanceof LowerCase) {
             return getLowerCaseValues((LowerCase) operand, row);
+        } else if (operand instanceof UpperCase) {
+            return getUpperCaseValues((UpperCase) operand, row);
         } else if (operand instanceof NodeLocalName) {
             return getNodeLocalNameValues((NodeLocalName) operand, row);
-        } else if (operand instanceof NodeName) {
-            return getNodeNameValues((NodeName) operand, row);
         } else if (operand instanceof PropertyValue) {
             return getPropertyValues((PropertyValue) operand, row);
-        } else if (operand instanceof UpperCase) {
-            return getUpperCaseValues((UpperCase) operand, row);
         } else {
             throw new UnsupportedRepositoryOperationException(
                     "Unknown operand type: " + operand);
@@ -107,35 +160,6 @@ class OperandEvaluator {
     }
 
     /**
-     * Returns the value of the given variable value operand at the given row.
-     *
-     * @param operand variable value operand
-     * @return value of the operand at the given row
-     */
-    private Value[] getBindVariableValues(BindVariableValue operand) {
-        Value value = variables.get(operand.getBindVariableName());
-        if (value != null) {
-            return new Value[] { value };
-        } else {
-            return new Value[0];
-        }
-    }
-
-    /**
-     * Returns the value of the given search score operand at the given row.
-     *
-     * @param operand search score operand
-     * @param row row
-     * @return value of the operand at the given row
-     * @throws RepositoryException if the operand can't be evaluated
-     */
-    private Value[] getFullTextSearchScoreValues(
-            FullTextSearchScore operand, Row row) throws RepositoryException {
-        double score = row.getScore(operand.getSelectorName());
-        return new Value[] { factory.createValue(score) };
-    }
-
-    /**
      * Returns the values of the given value length operand at the given row.
      *
      * @see #getProperty(PropertyValue, Row)
@@ -163,16 +187,6 @@ class OperandEvaluator {
     }
 
     /**
-     * Returns the value of the given literal value operand.
-     *
-     * @param operand literal value operand
-     * @return value of the operand
-     */
-    private Value[] getLiteralValues(Literal operand) {
-        return new Value[] { operand.getLiteralValue() };
-    }
-
-    /**
      * Returns the values of the given lower case operand at the given row.
      *
      * @param operand lower case operand
@@ -194,35 +208,42 @@ class OperandEvaluator {
     }
 
     /**
-     * Returns the value of the given local name operand at the given row.
+     * Returns the values of the given upper case operand at the given row.
      *
-     * @param operand local name operand
+     * @param operand upper case operand
      * @param row row
-     * @return value of the operand at the given row
+     * @return values of the operand at the given row
      * @throws RepositoryException if the operand can't be evaluated
      */
-    private Value[] getNodeLocalNameValues(NodeLocalName operand, Row row)
+    private Value[] getUpperCaseValues(UpperCase operand, Row row)
             throws RepositoryException {
-        String name = row.getNode(operand.getSelectorName()).getName();
-        int colon = name.indexOf(':');
-        if (colon != -1) {
-            name = name.substring(colon + 1);
+        Value[] values = getValues(operand.getOperand(), row);
+        for (int i = 0; i < values.length; i++) {
+            String value = values[i].getString();
+            String upper = value.toUpperCase(ENGLISH);
+            if (!value.equals(upper)) {
+                values[i] = factory.createValue(upper);
+            }
         }
-        return new Value[] { factory.createValue(name, NAME) };
+        return values;
     }
 
     /**
-     * Returns the value of the given node name operand at the given row.
+     * Returns the value of the given local name operand at the given row.
      *
-     * @param operand node name operand
+     * @param operand local name operand
      * @param row row
      * @return value of the operand at the given row
      * @throws RepositoryException if the operand can't be evaluated
      */
-    private Value[] getNodeNameValues(NodeName operand, Row row)
+    private Value[] getNodeLocalNameValues(NodeLocalName operand, Row row)
             throws RepositoryException {
-        Node node = row.getNode(operand.getSelectorName());
-        return new Value[] { factory.createValue(node.getName(), NAME) };
+        String name = row.getNode(operand.getSelectorName()).getName();
+        int colon = name.indexOf(':');
+        if (colon != -1) {
+            name = name.substring(colon + 1);
+        }
+        return new Value[] { factory.createValue(name, NAME) };
     }
 
     /**
@@ -247,27 +268,6 @@ class OperandEvaluator {
     }
 
     /**
-     * Returns the values of the given upper case operand at the given row.
-     *
-     * @param operand upper case operand
-     * @param row row
-     * @return values of the operand at the given row
-     * @throws RepositoryException if the operand can't be evaluated
-     */
-    private Value[] getUpperCaseValues(UpperCase operand, Row row)
-            throws RepositoryException {
-        Value[] values = getValues(operand.getOperand(), row);
-        for (int i = 0; i < values.length; i++) {
-            String value = values[i].getString();
-            String upper = value.toLowerCase(ENGLISH);
-            if (!value.equals(upper)) {
-                values[i] = factory.createValue(upper);
-            }
-        }
-        return values;
-    }
-
-    /**
      * Returns the identified property from the given row. This method
      * is used by both the {@link #getValue(Length, Row)} and the
      * {@link #getValue(PropertyValue, Row)} methods to access properties.