[dna-commits] DNA SVN: r1298 - in trunk: dna-graph/src/main/java/org/jboss/dna/graph/query/optimize and 4 other directories.

dna-commits at lists.jboss.org dna-commits at lists.jboss.org
Wed Oct 21 13:01:36 EDT 2009


Author: rhauch
Date: 2009-10-21 13:01:35 -0400 (Wed, 21 Oct 2009)
New Revision: 1298

Added:
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/AbstractMergeSelectNodes.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/MergeSetCriteria.java
Modified:
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/AllNodes.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/And.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/BindVariableName.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/ChildNode.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/ChildNodeJoinCondition.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Column.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Comparison.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/DescendantNode.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/DescendantNodeJoinCondition.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/EquiJoinCondition.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/FullTextSearch.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/FullTextSearchScore.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Join.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Length.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Limit.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Literal.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/LowerCase.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/NamedSelector.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/NodeDepth.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/NodeLocalName.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/NodeName.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/NodePath.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Not.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Or.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Ordering.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/PropertyExistence.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/PropertyValue.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Query.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/SameNode.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/SameNodeJoinCondition.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Selector.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/SetCriteria.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/SetQuery.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/UpperCase.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/PushProjects.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/PushSelectCriteria.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/ReplaceViews.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/RewriteIdentityJoins.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/RuleBasedOptimizer.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/CanonicalPlanner.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/PlanNode.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/PlanUtil.java
   trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/ImmutableSchemata.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/ReplaceViewsTest.java
   trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/RuleBasedOptimizerTest.java
   trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/xpath/XPathToQueryTranslatorTest.java
Log:
DNA-467 Added another optimization rule to merge set criteria, and added hashCode() methods to all of the AQM classes.

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/AllNodes.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/AllNodes.java	2009-10-20 16:14:27 UTC (rev 1297)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/AllNodes.java	2009-10-21 17:01:35 UTC (rev 1298)
@@ -60,6 +60,16 @@
     /**
      * {@inheritDoc}
      * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return getName().hashCode();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @see java.lang.Object#equals(java.lang.Object)
      */
     @Override

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/And.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/And.java	2009-10-20 16:14:27 UTC (rev 1297)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/And.java	2009-10-21 17:01:35 UTC (rev 1298)
@@ -25,6 +25,7 @@
 
 import net.jcip.annotations.Immutable;
 import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.common.util.HashCode;
 
 /**
  * A constraint that evaluates to true when <i>both</i> of the other constraints evaluate to true.
@@ -34,6 +35,7 @@
 
     private final Constraint left;
     private final Constraint right;
+    private final int hc;
 
     public And( Constraint left,
                 Constraint right ) {
@@ -41,6 +43,7 @@
         CheckArg.isNotNull(right, "right");
         this.left = left;
         this.right = right;
+        this.hc = HashCode.compute(this.left, this.right);
     }
 
     /**
@@ -70,6 +73,16 @@
     /**
      * {@inheritDoc}
      * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return hc;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @see java.lang.Object#equals(java.lang.Object)
      */
     @Override
@@ -77,6 +90,7 @@
         if (obj == this) return true;
         if (obj instanceof And) {
             And that = (And)obj;
+            if (this.hc != that.hc) return false;
             return left.equals(that.left) && right.equals(that.right);
         }
         return false;

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/BindVariableName.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/BindVariableName.java	2009-10-20 16:14:27 UTC (rev 1297)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/BindVariableName.java	2009-10-21 17:01:35 UTC (rev 1298)
@@ -59,6 +59,16 @@
     /**
      * {@inheritDoc}
      * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return variableName.hashCode();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @see java.lang.Object#equals(java.lang.Object)
      */
     @Override

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/ChildNode.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/ChildNode.java	2009-10-20 16:14:27 UTC (rev 1297)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/ChildNode.java	2009-10-21 17:01:35 UTC (rev 1298)
@@ -25,6 +25,7 @@
 
 import net.jcip.annotations.Immutable;
 import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.common.util.HashCode;
 import org.jboss.dna.graph.property.Path;
 
 /**
@@ -34,6 +35,7 @@
 public class ChildNode extends Constraint {
     private final SelectorName selectorName;
     private final Path parentPath;
+    private final int hc;
 
     /**
      * Create a constraint requiring that the node identified by the selector is a child of the node reachable by the supplied
@@ -48,6 +50,7 @@
         CheckArg.isNotNull(parentPath, "parentPath");
         this.selectorName = selectorName;
         this.parentPath = parentPath;
+        this.hc = HashCode.compute(this.selectorName, this.parentPath);
     }
 
     /**
@@ -77,6 +80,16 @@
     /**
      * {@inheritDoc}
      * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return hc;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @see java.lang.Object#equals(java.lang.Object)
      */
     @Override
@@ -84,6 +97,7 @@
         if (obj == this) return true;
         if (obj instanceof ChildNode) {
             ChildNode that = (ChildNode)obj;
+            if (this.hc != that.hc) return false;
             if (!this.selectorName.equals(that.selectorName)) return false;
             if (!this.parentPath.equals(that.parentPath)) return false;
             return true;

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/ChildNodeJoinCondition.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/ChildNodeJoinCondition.java	2009-10-20 16:14:27 UTC (rev 1297)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/ChildNodeJoinCondition.java	2009-10-21 17:01:35 UTC (rev 1298)
@@ -25,6 +25,7 @@
 
 import net.jcip.annotations.Immutable;
 import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.common.util.HashCode;
 
 /**
  * A join condition that evaluates to true only when the named child node is indeed a child of the named parent node.
@@ -33,6 +34,7 @@
 public class ChildNodeJoinCondition extends JoinCondition {
     private final SelectorName childSelectorName;
     private final SelectorName parentSelectorName;
+    private final int hc;
 
     /**
      * Create a join condition that determines whether the node identified by the child selector is a child of the node identified
@@ -47,6 +49,7 @@
         CheckArg.isNotNull(parentSelectorName, "parentSelectorName");
         this.childSelectorName = childSelectorName;
         this.parentSelectorName = parentSelectorName;
+        this.hc = HashCode.compute(this.childSelectorName, this.parentSelectorName);
     }
 
     /**
@@ -76,6 +79,16 @@
     /**
      * {@inheritDoc}
      * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return hc;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @see java.lang.Object#equals(java.lang.Object)
      */
     @Override
@@ -83,6 +96,7 @@
         if (obj == this) return true;
         if (obj instanceof ChildNodeJoinCondition) {
             ChildNodeJoinCondition that = (ChildNodeJoinCondition)obj;
+            if (this.hc != that.hc) return false;
             if (!this.childSelectorName.equals(that.childSelectorName)) return false;
             if (!this.parentSelectorName.equals(that.parentSelectorName)) return false;
             return true;

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Column.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Column.java	2009-10-20 16:14:27 UTC (rev 1297)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Column.java	2009-10-21 17:01:35 UTC (rev 1298)
@@ -102,6 +102,16 @@
     /**
      * {@inheritDoc}
      * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return getSelectorName().hashCode();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @see java.lang.Object#equals(java.lang.Object)
      */
     @Override

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Comparison.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Comparison.java	2009-10-20 16:14:27 UTC (rev 1297)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Comparison.java	2009-10-21 17:01:35 UTC (rev 1298)
@@ -25,6 +25,7 @@
 
 import net.jcip.annotations.Immutable;
 import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.common.util.HashCode;
 
 /**
  * A constraint that evaluates to true when the defined operation evaluates to true.
@@ -35,6 +36,7 @@
     private final DynamicOperand operand1;
     private final StaticOperand operand2;
     private final Operator operator;
+    private final int hc;
 
     public Comparison( DynamicOperand operand1,
                        Operator operator,
@@ -45,6 +47,7 @@
         this.operand1 = operand1;
         this.operand2 = operand2;
         this.operator = operator;
+        this.hc = HashCode.compute(this.operand1, this.operand2, this.operator);
     }
 
     /**
@@ -81,6 +84,16 @@
     /**
      * {@inheritDoc}
      * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return hc;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @see java.lang.Object#equals(java.lang.Object)
      */
     @Override
@@ -88,6 +101,7 @@
         if (obj == this) return true;
         if (obj instanceof Comparison) {
             Comparison that = (Comparison)obj;
+            if (this.hc != that.hc) return false;
             if (!this.operator.equals(that.operator)) return false;
             if (!this.operand1.equals(that.operand1)) return false;
             if (!this.operand2.equals(that.operand2)) return false;

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/DescendantNode.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/DescendantNode.java	2009-10-20 16:14:27 UTC (rev 1297)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/DescendantNode.java	2009-10-21 17:01:35 UTC (rev 1298)
@@ -77,6 +77,16 @@
     /**
      * {@inheritDoc}
      * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return getSelectorName().hashCode();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @see java.lang.Object#equals(java.lang.Object)
      */
     @Override

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/DescendantNodeJoinCondition.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/DescendantNodeJoinCondition.java	2009-10-20 16:14:27 UTC (rev 1297)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/DescendantNodeJoinCondition.java	2009-10-21 17:01:35 UTC (rev 1298)
@@ -25,6 +25,7 @@
 
 import net.jcip.annotations.Immutable;
 import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.common.util.HashCode;
 
 /**
  * A join condition that evaluates to true only when the named node is a descendant of another named node.
@@ -33,6 +34,7 @@
 public class DescendantNodeJoinCondition extends JoinCondition {
     private final SelectorName descendantSelectorName;
     private final SelectorName ancestorSelectorName;
+    private final int hc;
 
     /**
      * Create a join condition that determines whether the node identified by the descendant selector is indeed a descendant of
@@ -47,6 +49,7 @@
         CheckArg.isNotNull(ancestorSelectorName, "ancestorSelectorName");
         this.descendantSelectorName = descendantSelectorName;
         this.ancestorSelectorName = ancestorSelectorName;
+        this.hc = HashCode.compute(this.descendantSelectorName, this.ancestorSelectorName);
     }
 
     /**
@@ -76,6 +79,16 @@
     /**
      * {@inheritDoc}
      * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return hc;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @see java.lang.Object#equals(java.lang.Object)
      */
     @Override
@@ -83,6 +96,7 @@
         if (obj == this) return true;
         if (obj instanceof DescendantNodeJoinCondition) {
             DescendantNodeJoinCondition that = (DescendantNodeJoinCondition)obj;
+            if (this.hc != that.hc) return false;
             if (!this.descendantSelectorName.equals(that.descendantSelectorName)) return false;
             if (!this.ancestorSelectorName.equals(that.ancestorSelectorName)) return false;
             return true;

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/EquiJoinCondition.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/EquiJoinCondition.java	2009-10-20 16:14:27 UTC (rev 1297)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/EquiJoinCondition.java	2009-10-21 17:01:35 UTC (rev 1298)
@@ -25,6 +25,7 @@
 
 import net.jcip.annotations.Immutable;
 import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.common.util.HashCode;
 import org.jboss.dna.graph.property.Name;
 
 /**
@@ -42,6 +43,7 @@
     private final Name property1Name;
     private final SelectorName selector2Name;
     private final Name property2Name;
+    private final int hc;
 
     public EquiJoinCondition( SelectorName selector1Name,
                               Name property1Name,
@@ -55,6 +57,7 @@
         this.property1Name = property1Name;
         this.selector2Name = selector2Name;
         this.property2Name = property2Name;
+        this.hc = HashCode.compute(this.selector1Name, this.property1Name, this.selector2Name, this.property2Name);
     }
 
     public EquiJoinCondition( Column column1,
@@ -103,6 +106,16 @@
     /**
      * {@inheritDoc}
      * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return hc;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @see java.lang.Object#equals(java.lang.Object)
      */
     @Override
@@ -110,6 +123,7 @@
         if (obj == this) return true;
         if (obj instanceof EquiJoinCondition) {
             EquiJoinCondition that = (EquiJoinCondition)obj;
+            if (this.hc != that.hc) return false;
             if (!this.selector1Name.equals(that.selector1Name)) return false;
             if (!this.selector2Name.equals(that.selector2Name)) return false;
             if (!this.property1Name.equals(that.property1Name)) return false;

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/FullTextSearch.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/FullTextSearch.java	2009-10-20 16:14:27 UTC (rev 1297)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/FullTextSearch.java	2009-10-21 17:01:35 UTC (rev 1298)
@@ -28,6 +28,7 @@
 import net.jcip.annotations.Immutable;
 import org.jboss.dna.common.text.ParsingException;
 import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.common.util.HashCode;
 import org.jboss.dna.common.util.ObjectUtil;
 import org.jboss.dna.graph.property.Name;
 import org.jboss.dna.graph.query.parse.FullTextSearchParser;
@@ -42,6 +43,7 @@
     private final Name propertyName;
     private final String fullTextSearchExpression;
     private Term term;
+    private final int hc;
 
     /**
      * Create a constraint defining a full-text search against the property values on node within the search scope.
@@ -60,6 +62,7 @@
         this.selectorName = selectorName;
         this.propertyName = propertyName;
         this.fullTextSearchExpression = fullTextSearchExpression;
+        this.hc = HashCode.compute(this.selectorName, this.propertyName, this.fullTextSearchExpression);
     }
 
     /**
@@ -78,6 +81,7 @@
         this.propertyName = propertyName;
         this.fullTextSearchExpression = fullTextSearchExpression;
         this.term = null;
+        this.hc = HashCode.compute(this.selectorName, this.propertyName, this.fullTextSearchExpression);
     }
 
     /**
@@ -93,6 +97,7 @@
         this.selectorName = selectorName;
         this.propertyName = null;
         this.fullTextSearchExpression = fullTextSearchExpression;
+        this.hc = HashCode.compute(this.selectorName, this.propertyName, this.fullTextSearchExpression);
     }
 
     /**
@@ -143,6 +148,16 @@
     /**
      * {@inheritDoc}
      * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return hc;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @see java.lang.Object#equals(java.lang.Object)
      */
     @Override
@@ -150,6 +165,7 @@
         if (obj == this) return true;
         if (obj instanceof FullTextSearch) {
             FullTextSearch that = (FullTextSearch)obj;
+            if (this.hc != that.hc) return false;
             if (!this.selectorName.equals(that.selectorName)) return false;
             if (!ObjectUtil.isEqualWithNulls(this.propertyName, that.propertyName)) return false;
             if (!this.fullTextSearchExpression.equals(that.fullTextSearchExpression)) return false;

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/FullTextSearchScore.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/FullTextSearchScore.java	2009-10-20 16:14:27 UTC (rev 1297)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/FullTextSearchScore.java	2009-10-21 17:01:35 UTC (rev 1298)
@@ -67,6 +67,16 @@
     /**
      * {@inheritDoc}
      * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return getSelectorName().hashCode();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @see java.lang.Object#equals(java.lang.Object)
      */
     @Override

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Join.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Join.java	2009-10-20 16:14:27 UTC (rev 1297)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Join.java	2009-10-21 17:01:35 UTC (rev 1298)
@@ -25,6 +25,7 @@
 
 import net.jcip.annotations.Immutable;
 import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.common.util.HashCode;
 
 /**
  * 
@@ -36,6 +37,7 @@
     private final Source right;
     private final JoinType type;
     private final JoinCondition joinCondition;
+    private final int hc;
 
     /**
      * Create a join of the left and right sources, using the supplied join condition. The outputs of the left and right sources
@@ -58,6 +60,7 @@
         this.right = right;
         this.type = type;
         this.joinCondition = joinCondition;
+        this.hc = HashCode.compute(this.left, this.right, this.type, this.joinCondition);
     }
 
     /**
@@ -101,6 +104,16 @@
     /**
      * {@inheritDoc}
      * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return hc;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @see java.lang.Object#equals(java.lang.Object)
      */
     @Override
@@ -108,6 +121,7 @@
         if (obj == this) return true;
         if (obj instanceof Join) {
             Join that = (Join)obj;
+            if (this.hc != that.hc) return false;
             if (!this.type.equals(that.type)) return false;
             if (!this.left.equals(that.left)) return false;
             if (!this.right.equals(that.right)) return false;

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Length.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Length.java	2009-10-20 16:14:27 UTC (rev 1297)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Length.java	2009-10-21 17:01:35 UTC (rev 1298)
@@ -73,6 +73,16 @@
     /**
      * {@inheritDoc}
      * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return getPropertyValue().hashCode();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @see java.lang.Object#equals(java.lang.Object)
      */
     @Override

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Limit.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Limit.java	2009-10-20 16:14:27 UTC (rev 1297)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Limit.java	2009-10-21 17:01:35 UTC (rev 1298)
@@ -107,6 +107,16 @@
     /**
      * {@inheritDoc}
      * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return rowLimit;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @see java.lang.Object#equals(java.lang.Object)
      */
     @Override

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Literal.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Literal.java	2009-10-20 16:14:27 UTC (rev 1297)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Literal.java	2009-10-21 17:01:35 UTC (rev 1298)
@@ -59,6 +59,16 @@
     /**
      * {@inheritDoc}
      * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return getValue().hashCode();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @see java.lang.Object#equals(java.lang.Object)
      */
     @Override

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/LowerCase.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/LowerCase.java	2009-10-20 16:14:27 UTC (rev 1297)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/LowerCase.java	2009-10-21 17:01:35 UTC (rev 1298)
@@ -74,6 +74,16 @@
     /**
      * {@inheritDoc}
      * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return operand.hashCode();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @see java.lang.Object#equals(java.lang.Object)
      */
     @Override

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/NamedSelector.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/NamedSelector.java	2009-10-20 16:14:27 UTC (rev 1297)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/NamedSelector.java	2009-10-21 17:01:35 UTC (rev 1298)
@@ -67,6 +67,16 @@
     /**
      * {@inheritDoc}
      * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return getName().hashCode();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @see java.lang.Object#equals(java.lang.Object)
      */
     @Override

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/NodeDepth.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/NodeDepth.java	2009-10-20 16:14:27 UTC (rev 1297)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/NodeDepth.java	2009-10-21 17:01:35 UTC (rev 1298)
@@ -67,6 +67,16 @@
     /**
      * {@inheritDoc}
      * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return getSelectorName().hashCode();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @see java.lang.Object#equals(java.lang.Object)
      */
     @Override

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/NodeLocalName.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/NodeLocalName.java	2009-10-20 16:14:27 UTC (rev 1297)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/NodeLocalName.java	2009-10-21 17:01:35 UTC (rev 1298)
@@ -66,6 +66,16 @@
     /**
      * {@inheritDoc}
      * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return getSelectorName().hashCode();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @see java.lang.Object#equals(java.lang.Object)
      */
     @Override

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/NodeName.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/NodeName.java	2009-10-20 16:14:27 UTC (rev 1297)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/NodeName.java	2009-10-21 17:01:35 UTC (rev 1298)
@@ -66,6 +66,16 @@
     /**
      * {@inheritDoc}
      * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return getSelectorName().hashCode();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @see java.lang.Object#equals(java.lang.Object)
      */
     @Override

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/NodePath.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/NodePath.java	2009-10-20 16:14:27 UTC (rev 1297)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/NodePath.java	2009-10-21 17:01:35 UTC (rev 1298)
@@ -67,6 +67,16 @@
     /**
      * {@inheritDoc}
      * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return getSelectorName().hashCode();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @see java.lang.Object#equals(java.lang.Object)
      */
     @Override

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Not.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Not.java	2009-10-20 16:14:27 UTC (rev 1297)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Not.java	2009-10-21 17:01:35 UTC (rev 1298)
@@ -67,6 +67,16 @@
     /**
      * {@inheritDoc}
      * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return getConstraint().hashCode();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @see java.lang.Object#equals(java.lang.Object)
      */
     @Override

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Or.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Or.java	2009-10-20 16:14:27 UTC (rev 1297)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Or.java	2009-10-21 17:01:35 UTC (rev 1298)
@@ -25,6 +25,7 @@
 
 import net.jcip.annotations.Immutable;
 import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.common.util.HashCode;
 
 /**
  * A constraint that evaluates to true when either of the other constraints evaluates to true.
@@ -34,6 +35,7 @@
 
     private final Constraint left;
     private final Constraint right;
+    private final int hc;
 
     /**
      * Create a constraint that evaluates to true if either of the two supplied constraints evaluates to true.
@@ -48,6 +50,7 @@
         CheckArg.isNotNull(right, "right");
         this.left = left;
         this.right = right;
+        this.hc = HashCode.compute(this.left, this.right);
     }
 
     /**
@@ -81,6 +84,16 @@
     /**
      * {@inheritDoc}
      * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return hc;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @see java.lang.Object#equals(java.lang.Object)
      */
     @Override
@@ -88,6 +101,7 @@
         if (obj == this) return true;
         if (obj instanceof Or) {
             Or that = (Or)obj;
+            if (this.hc != that.hc) return false;
             return this.left.equals(that.left) && this.right.equals(that.right);
         }
         return false;

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Ordering.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Ordering.java	2009-10-20 16:14:27 UTC (rev 1297)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Ordering.java	2009-10-21 17:01:35 UTC (rev 1298)
@@ -81,6 +81,16 @@
     /**
      * {@inheritDoc}
      * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return operand.hashCode();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @see java.lang.Object#equals(java.lang.Object)
      */
     @Override

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/PropertyExistence.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/PropertyExistence.java	2009-10-20 16:14:27 UTC (rev 1297)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/PropertyExistence.java	2009-10-21 17:01:35 UTC (rev 1298)
@@ -25,6 +25,7 @@
 
 import net.jcip.annotations.Immutable;
 import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.common.util.HashCode;
 import org.jboss.dna.graph.property.Name;
 
 /**
@@ -34,6 +35,7 @@
 public class PropertyExistence extends Constraint {
     private final SelectorName selectorName;
     private final Name propertyName;
+    private final int hc;
 
     /**
      * Create a constraint requiring that a property exist on a node.
@@ -47,6 +49,7 @@
         CheckArg.isNotNull(propertyName, "propertyName");
         this.selectorName = selectorName;
         this.propertyName = propertyName;
+        this.hc = HashCode.compute(this.selectorName, this.propertyName);
     }
 
     /**
@@ -80,6 +83,16 @@
     /**
      * {@inheritDoc}
      * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return hc;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @see java.lang.Object#equals(java.lang.Object)
      */
     @Override
@@ -87,6 +100,7 @@
         if (obj == this) return true;
         if (obj instanceof PropertyExistence) {
             PropertyExistence that = (PropertyExistence)obj;
+            if (this.hc != that.hc) return false;
             return this.selectorName.equals(that.selectorName) && this.propertyName.equals(that.propertyName);
         }
         return false;

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/PropertyValue.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/PropertyValue.java	2009-10-20 16:14:27 UTC (rev 1297)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/PropertyValue.java	2009-10-21 17:01:35 UTC (rev 1298)
@@ -25,6 +25,7 @@
 
 import net.jcip.annotations.Immutable;
 import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.common.util.HashCode;
 import org.jboss.dna.graph.property.Name;
 
 /**
@@ -34,6 +35,7 @@
 public class PropertyValue extends DynamicOperand {
     private final SelectorName selectorName;
     private final Name propertyName;
+    private final int hc;
 
     /**
      * Create a dynamic operand that evaluates to the property values of the node identified by the selector.
@@ -48,6 +50,7 @@
         CheckArg.isNotNull(propertyName, "propertyName");
         this.selectorName = selectorName;
         this.propertyName = propertyName;
+        this.hc = HashCode.compute(this.selectorName, this.propertyName);
     }
 
     /**
@@ -82,6 +85,16 @@
     /**
      * {@inheritDoc}
      * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return hc;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @see java.lang.Object#equals(java.lang.Object)
      */
     @Override
@@ -89,6 +102,7 @@
         if (obj == this) return true;
         if (obj instanceof PropertyValue) {
             PropertyValue that = (PropertyValue)obj;
+            if (this.hc != that.hc) return false;
             return this.selectorName.equals(that.selectorName) && this.propertyName.equals(that.propertyName);
         }
         return false;

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Query.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Query.java	2009-10-20 16:14:27 UTC (rev 1297)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Query.java	2009-10-21 17:01:35 UTC (rev 1298)
@@ -29,6 +29,7 @@
 import java.util.List;
 import net.jcip.annotations.Immutable;
 import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.common.util.HashCode;
 import org.jboss.dna.common.util.ObjectUtil;
 
 /**
@@ -43,6 +44,7 @@
     private final Constraint constraint;
     private final List<Column> columns;
     private final boolean distinct;
+    private final int hc;
 
     /**
      * Create a new query that uses the supplied source.
@@ -57,6 +59,7 @@
         this.constraint = null;
         this.columns = Collections.<Column>emptyList();
         this.distinct = IS_DISTINCT_DEFAULT;
+        this.hc = HashCode.compute(this.source, this.constraint, this.columns, this.distinct);
     }
 
     /**
@@ -84,6 +87,7 @@
         this.constraint = constraint;
         this.columns = columns != null ? columns : Collections.<Column>emptyList();
         this.distinct = isDistinct;
+        this.hc = HashCode.compute(this.source, this.constraint, this.columns, this.distinct);
     }
 
     /**
@@ -189,6 +193,16 @@
     /**
      * {@inheritDoc}
      * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return hc;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @see java.lang.Object#equals(java.lang.Object)
      */
     @Override
@@ -196,6 +210,7 @@
         if (obj == this) return true;
         if (obj instanceof Query) {
             Query that = (Query)obj;
+            if (this.hc != that.hc) return false;
             if (this.distinct != that.distinct) return false;
             if (!this.source.equals(that.source)) return false;
             if (!ObjectUtil.isEqualWithNulls(this.getLimits(), that.getLimits())) return false;

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/SameNode.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/SameNode.java	2009-10-20 16:14:27 UTC (rev 1297)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/SameNode.java	2009-10-21 17:01:35 UTC (rev 1298)
@@ -25,6 +25,7 @@
 
 import net.jcip.annotations.Immutable;
 import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.common.util.HashCode;
 import org.jboss.dna.graph.property.Path;
 
 /**
@@ -34,6 +35,7 @@
 public class SameNode extends Constraint {
     private final SelectorName selectorName;
     private final Path path;
+    private final int hc;
 
     /**
      * Create a constraint requiring that the node identified by the selector is reachable by the supplied absolute path.
@@ -48,6 +50,7 @@
         CheckArg.isNotNull(path, "path");
         this.selectorName = selectorName;
         this.path = path;
+        this.hc = HashCode.compute(this.selectorName, this.path);
     }
 
     /**
@@ -81,6 +84,16 @@
     /**
      * {@inheritDoc}
      * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return hc;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @see java.lang.Object#equals(java.lang.Object)
      */
     @Override
@@ -88,6 +101,7 @@
         if (obj == this) return true;
         if (obj instanceof SameNode) {
             SameNode that = (SameNode)obj;
+            if (this.hc != that.hc) return false;
             if (!this.selectorName.equals(that.selectorName)) return false;
             if (!this.path.equals(that.path)) return false;
             return true;

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/SameNodeJoinCondition.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/SameNodeJoinCondition.java	2009-10-20 16:14:27 UTC (rev 1297)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/SameNodeJoinCondition.java	2009-10-21 17:01:35 UTC (rev 1298)
@@ -25,6 +25,7 @@
 
 import net.jcip.annotations.Immutable;
 import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.common.util.HashCode;
 import org.jboss.dna.common.util.ObjectUtil;
 import org.jboss.dna.graph.property.Path;
 
@@ -37,6 +38,7 @@
     private final SelectorName selector1Name;
     private final SelectorName selector2Name;
     private final Path selector2Path;
+    private final int hc;
 
     /**
      * Create a join condition that determines whether the node identified by the first selector is the same as the node at the
@@ -56,6 +58,7 @@
         this.selector1Name = selector1Name;
         this.selector2Name = selector2Name;
         this.selector2Path = selector2Path;
+        this.hc = HashCode.compute(this.selector1Name, this.selector2Name, this.selector2Path);
     }
 
     /**
@@ -73,6 +76,7 @@
         this.selector1Name = selector1Name;
         this.selector2Name = selector2Name;
         this.selector2Path = null;
+        this.hc = HashCode.compute(this.selector1Name, this.selector2Name, this.selector2Path);
     }
 
     /**
@@ -115,6 +119,16 @@
     /**
      * {@inheritDoc}
      * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return hc;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @see java.lang.Object#equals(java.lang.Object)
      */
     @Override
@@ -122,6 +136,7 @@
         if (obj == this) return true;
         if (obj instanceof SameNodeJoinCondition) {
             SameNodeJoinCondition that = (SameNodeJoinCondition)obj;
+            if (this.hc != that.hc) return false;
             if (!this.selector1Name.equals(that.selector1Name)) return false;
             if (!this.selector2Name.equals(that.selector2Name)) return false;
             if (!ObjectUtil.isEqualWithNulls(this.selector2Path, that.selector2Path)) return false;

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Selector.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Selector.java	2009-10-20 16:14:27 UTC (rev 1297)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/Selector.java	2009-10-21 17:01:35 UTC (rev 1298)
@@ -96,5 +96,4 @@
     public boolean hasAlias() {
         return alias != null;
     }
-
 }

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/SetCriteria.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/SetCriteria.java	2009-10-20 16:14:27 UTC (rev 1297)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/SetCriteria.java	2009-10-21 17:01:35 UTC (rev 1298)
@@ -23,7 +23,9 @@
  */
 package org.jboss.dna.graph.query.model;
 
+import java.util.Arrays;
 import java.util.Collection;
+import java.util.Collections;
 import net.jcip.annotations.Immutable;
 import org.jboss.dna.common.util.CheckArg;
 
@@ -44,6 +46,14 @@
         this.setOperands = setOperands;
     }
 
+    public SetCriteria( DynamicOperand left,
+                        StaticOperand... setOperands ) {
+        CheckArg.isNotNull(left, "left");
+        CheckArg.isNotNull(setOperands, "setOperands");
+        this.left = left;
+        this.setOperands = Collections.unmodifiableList(Arrays.asList(setOperands));
+    }
+
     /**
      * @return operand1
      */
@@ -71,6 +81,16 @@
     /**
      * {@inheritDoc}
      * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return left.hashCode();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @see java.lang.Object#equals(java.lang.Object)
      */
     @Override

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/SetQuery.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/SetQuery.java	2009-10-20 16:14:27 UTC (rev 1297)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/SetQuery.java	2009-10-21 17:01:35 UTC (rev 1298)
@@ -31,6 +31,7 @@
 import java.util.Map;
 import net.jcip.annotations.Immutable;
 import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.common.util.HashCode;
 import org.jboss.dna.common.util.ObjectUtil;
 import org.jboss.dna.graph.ExecutionContext;
 
@@ -101,6 +102,7 @@
     private final QueryCommand right;
     private final Operation operation;
     private final boolean all;
+    private final int hc;
 
     public SetQuery( QueryCommand left,
                      Operation operation,
@@ -114,6 +116,7 @@
         this.right = right;
         this.operation = operation;
         this.all = all;
+        this.hc = HashCode.compute(this.left, this.right, this.operation);
     }
 
     public SetQuery( QueryCommand left,
@@ -130,6 +133,7 @@
         this.right = right;
         this.operation = operation;
         this.all = all;
+        this.hc = HashCode.compute(this.left, this.right, this.operation);
     }
 
     /**
@@ -181,6 +185,16 @@
     /**
      * {@inheritDoc}
      * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return hc;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @see java.lang.Object#equals(java.lang.Object)
      */
     @Override
@@ -188,6 +202,7 @@
         if (obj == this) return true;
         if (obj instanceof SetQuery) {
             SetQuery that = (SetQuery)obj;
+            if (this.hc != that.hc) return false;
             if (this.operation != that.operation) return false;
             if (!this.left.equals(that.left)) return false;
             if (!this.right.equals(that.right)) return false;

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/UpperCase.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/UpperCase.java	2009-10-20 16:14:27 UTC (rev 1297)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/model/UpperCase.java	2009-10-21 17:01:35 UTC (rev 1298)
@@ -76,6 +76,16 @@
     /**
      * {@inheritDoc}
      * 
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return getOperand().hashCode();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
      * @see java.lang.Object#equals(java.lang.Object)
      */
     @Override

Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/AbstractMergeSelectNodes.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/AbstractMergeSelectNodes.java	                        (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/AbstractMergeSelectNodes.java	2009-10-21 17:01:35 UTC (rev 1298)
@@ -0,0 +1,113 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ * 
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.graph.query.optimize;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.Map;
+import net.jcip.annotations.Immutable;
+import org.jboss.dna.graph.query.QueryContext;
+import org.jboss.dna.graph.query.model.Constraint;
+import org.jboss.dna.graph.query.model.SetCriteria;
+import org.jboss.dna.graph.query.plan.PlanNode;
+import org.jboss.dna.graph.query.plan.PlanNode.Property;
+import org.jboss.dna.graph.query.plan.PlanNode.Type;
+
+/**
+ * An {@link OptimizerRule optimizer rule} that merges SELECT nodes that have {@link SetCriteria} applied to the same column on
+ * the same selector.
+ * 
+ * @param <SimilarityType> the type used to compare and identify similar nodes
+ * @param <ConstraintType> the concrete type of Constraint objects that are being merged
+ */
+ at Immutable
+public abstract class AbstractMergeSelectNodes<SimilarityType, ConstraintType extends Constraint> implements OptimizerRule {
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.query.optimize.OptimizerRule#execute(org.jboss.dna.graph.query.QueryContext,
+     *      org.jboss.dna.graph.query.plan.PlanNode, java.util.LinkedList)
+     */
+    @SuppressWarnings( "unchecked" )
+    public PlanNode execute( QueryContext context,
+                             PlanNode plan,
+                             LinkedList<OptimizerRule> ruleStack ) {
+        Map<SimilarityType, LinkedList<PlanNode>> mergeables = new HashMap<SimilarityType, LinkedList<PlanNode>>();
+        for (PlanNode source : plan.findAllAtOrBelow(Type.SOURCE)) {
+            // Walk up from the SOURCE and look for all SELECT nodes ...
+            PlanNode node = source.getParent();
+            while (node != null && node.is(Type.SELECT)) {
+                Constraint constraint = node.getProperty(Property.SELECT_CRITERIA, Constraint.class);
+                SimilarityType operand = getSimilarityKey(constraint);
+                if (operand != null) {
+                    LinkedList<PlanNode> nodes = mergeables.get(operand);
+                    if (nodes == null) {
+                        nodes = new LinkedList<PlanNode>();
+                        mergeables.put(operand, nodes);
+                    }
+                    nodes.addFirst(node); // So that the list is in the downward order they appear in the plan
+                }
+                node = node.getParent();
+            }
+
+            // Merge all mergeable SELECT nodes ...
+            for (LinkedList<PlanNode> mergeable : mergeables.values()) {
+                if (mergeable.size() > 1) {
+                    Iterator<PlanNode> iter = mergeable.iterator();
+                    PlanNode firstSelect = iter.next();
+                    Constraint constraint = firstSelect.getProperty(Property.SELECT_CRITERIA, Constraint.class);
+                    while (iter.hasNext()) {
+                        PlanNode nextSelect = iter.next();
+                        Constraint nextConstraint = nextSelect.getProperty(Property.SELECT_CRITERIA, Constraint.class);
+                        constraint = merge((ConstraintType)constraint, (ConstraintType)nextConstraint);
+                        nextSelect.extractFromParent();
+                    }
+                    firstSelect.setProperty(Property.SELECT_CRITERIA, constraint);
+                }
+            }
+            mergeables.clear();
+        }
+        return plan;
+    }
+
+    /**
+     * Obtain the similarity key that will be used to determine whether two SELECT nodes should be merged.
+     * 
+     * @param constraint the constraint from the SELECT node
+     * @return the similarity key, or null if the constraint should not be merged
+     */
+    protected abstract SimilarityType getSimilarityKey( Constraint constraint );
+
+    /**
+     * Merge the two constraints.
+     * 
+     * @param firstConstraint the first constraint
+     * @param secondConstraint the second constraint
+     * @return the merged constraint
+     */
+    protected abstract Constraint merge( ConstraintType firstConstraint,
+                                         ConstraintType secondConstraint );
+}


Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/AbstractMergeSelectNodes.java
___________________________________________________________________
Name: svn:keywords
   + Id Revision
Name: svn:eol-style
   + LF

Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/MergeSetCriteria.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/MergeSetCriteria.java	                        (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/MergeSetCriteria.java	2009-10-21 17:01:35 UTC (rev 1298)
@@ -0,0 +1,89 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership.  Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of 
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ * 
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.graph.query.optimize;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import net.jcip.annotations.Immutable;
+import org.jboss.dna.graph.query.model.Constraint;
+import org.jboss.dna.graph.query.model.DynamicOperand;
+import org.jboss.dna.graph.query.model.SetCriteria;
+import org.jboss.dna.graph.query.model.StaticOperand;
+
+/**
+ * An {@link OptimizerRule optimizer rule} that merges SELECT nodes that have {@link SetCriteria} applied to the same column on
+ * the same selector.
+ */
+ at Immutable
+public class MergeSetCriteria extends AbstractMergeSelectNodes<DynamicOperand, SetCriteria> {
+
+    public static final MergeSetCriteria INSTANCE = new MergeSetCriteria();
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.query.optimize.AbstractMergeSelectNodes#getSimilarityKey(org.jboss.dna.graph.query.model.Constraint)
+     */
+    @Override
+    protected DynamicOperand getSimilarityKey( Constraint constraint ) {
+        if (constraint instanceof SetCriteria) {
+            // Look for the list of plan nodes for this operand ...
+            return ((SetCriteria)constraint).getLeftOperand();
+        }
+        return null;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.graph.query.optimize.AbstractMergeSelectNodes#merge(org.jboss.dna.graph.query.model.Constraint,
+     *      org.jboss.dna.graph.query.model.Constraint)
+     */
+    @Override
+    protected Constraint merge( SetCriteria firstConstraint,
+                                SetCriteria secondConstraint ) {
+        assert firstConstraint.getLeftOperand().equals(secondConstraint.getLeftOperand());
+        Set<StaticOperand> allOperands = new HashSet<StaticOperand>();
+        List<StaticOperand> orderedOperands = new ArrayList<StaticOperand>(firstConstraint.getRightOperands());
+        allOperands.addAll(firstConstraint.getRightOperands());
+        for (StaticOperand second : secondConstraint.getRightOperands()) {
+            if (allOperands.add(second)) {
+                orderedOperands.add(second);
+            }
+        }
+        return new SetCriteria(firstConstraint.getLeftOperand(), orderedOperands);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        return getClass().getSimpleName();
+    }
+}


Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/MergeSetCriteria.java
___________________________________________________________________
Name: svn:keywords
   + Id Revision
Name: svn:eol-style
   + LF

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/PushProjects.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/PushProjects.java	2009-10-20 16:14:27 UTC (rev 1297)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/PushProjects.java	2009-10-21 17:01:35 UTC (rev 1298)
@@ -65,7 +65,16 @@
             // Is there already a PROJECT here ?
             assert parentOfProject.getChildCount() == 1; // should only have one child ...
             PlanNode child = parentOfProject.getFirstChild(); // should only have one child ...
-            if (child.is(Type.PROJECT)) continue;
+            if (child.is(Type.PROJECT)) {
+                // Check to see if there is a PROJECT above the access node ...
+                PlanNode accessParent = access.getParent();
+                if (accessParent == null || accessParent.isNot(Type.PROJECT)) continue;
+                // Otherwise, the parent is a PROJECT, but there is another PROJECT above the ACCESS node.
+                // Remove the lower PROJECT so the next code block moves the top PROJECT down below the ACCESS node ...
+                assert accessParent.is(Type.PROJECT);
+                child.extractFromParent();
+                child = parentOfProject.getFirstChild();
+            }
 
             // If the parent of the ACCESS node is a PROJECT, then we can simply move it to here ...
             PlanNode accessParent = access.getParent();

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/PushSelectCriteria.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/PushSelectCriteria.java	2009-10-20 16:14:27 UTC (rev 1297)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/PushSelectCriteria.java	2009-10-21 17:01:35 UTC (rev 1298)
@@ -297,6 +297,7 @@
 
         // first look for originating nodes that exactly match the required selectors ...
         for (PlanNode originatingNode : originatingNodes) {
+            if (!criteriaNode.isAbove(originatingNode)) continue;
             if (originatingNode.getSelectors().equals(requiredSelectors)) return originatingNode;
         }
 

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/ReplaceViews.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/ReplaceViews.java	2009-10-20 16:14:27 UTC (rev 1297)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/ReplaceViews.java	2009-10-21 17:01:35 UTC (rev 1298)
@@ -99,6 +99,7 @@
                         // The PROJECT from the plan may actually not be needed if there is another PROJECT above it ...
                         PlanNode node = viewPlan.getParent();
                         while (node != null) {
+                            if (node.isOneOf(Type.JOIN)) break;
                             if (node.is(Type.PROJECT) && viewPlan.getSelectors().containsAll(node.getSelectors())) {
                                 viewPlan.extractFromParent();
                                 break;

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/RewriteIdentityJoins.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/RewriteIdentityJoins.java	2009-10-20 16:14:27 UTC (rev 1297)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/RewriteIdentityJoins.java	2009-10-21 17:01:35 UTC (rev 1298)
@@ -25,13 +25,16 @@
 
 import java.util.HashMap;
 import java.util.LinkedList;
+import java.util.List;
 import java.util.Map;
 import net.jcip.annotations.Immutable;
 import org.jboss.dna.graph.GraphI18n;
 import org.jboss.dna.graph.property.ValueFactory;
 import org.jboss.dna.graph.query.QueryContext;
+import org.jboss.dna.graph.query.model.Column;
 import org.jboss.dna.graph.query.model.EquiJoinCondition;
 import org.jboss.dna.graph.query.model.JoinCondition;
+import org.jboss.dna.graph.query.model.SameNodeJoinCondition;
 import org.jboss.dna.graph.query.model.SelectorName;
 import org.jboss.dna.graph.query.plan.PlanNode;
 import org.jboss.dna.graph.query.plan.PlanUtil;
@@ -82,55 +85,68 @@
     public PlanNode execute( QueryContext context,
                              PlanNode plan,
                              LinkedList<OptimizerRule> ruleStack ) {
+        if (!context.getHints().hasJoin) return plan;
+
         // For each of the JOIN nodes ...
         Map<SelectorName, SelectorName> rewrittenSelectors = null;
+        int rewrittenJoins = 0;
+        int numJoins = 0;
         for (PlanNode joinNode : plan.findAllAtOrBelow(Type.JOIN)) {
+            ++numJoins;
             JoinCondition condition = joinNode.getProperty(Property.JOIN_CONDITION, JoinCondition.class);
             if (condition instanceof EquiJoinCondition) {
-                PlanNode leftNode = joinNode.getFirstChild();
-                PlanNode rightNode = joinNode.getLastChild();
+                PlanNode leftNode = joinNode.getFirstChild().findAtOrBelow(Type.SOURCE);
+                PlanNode rightNode = joinNode.getLastChild().findAtOrBelow(Type.SOURCE);
                 assert leftNode != null;
                 assert rightNode != null;
-                if (leftNode.getType() == Type.SOURCE && rightNode.getType() == Type.SOURCE) {
-                    EquiJoinCondition equiJoin = (EquiJoinCondition)condition;
-                    // Find the names (or aliases) of the tables ...
-                    Schemata schemata = context.getSchemata();
-                    assert schemata != null;
-                    SelectorName leftTableName = leftNode.getProperty(Property.SOURCE_NAME, SelectorName.class);
-                    SelectorName rightTableName = rightNode.getProperty(Property.SOURCE_NAME, SelectorName.class);
-                    assert leftTableName != null;
-                    assert rightTableName != null;
-                    // Presumably the join condition is using at least one alias, but we only care about the actual name ...
-                    if (!leftTableName.equals(rightTableName)) {
-                        // The join is not joining the same table, so this doesn't meet the condition ...
-                        continue;
-                    }
-                    // Find the schemata columns referenced by the join condition ...
-                    Table table = schemata.getTable(leftTableName);
-                    if (table == null) {
-                        context.getProblems().addError(GraphI18n.tableDoesNotExist, leftTableName);
-                        continue;
-                    }
-                    ValueFactory<String> stringFactory = context.getExecutionContext().getValueFactories().getStringFactory();
-                    String leftColumnName = stringFactory.create(equiJoin.getProperty1Name());
-                    String rightColumnName = stringFactory.create(equiJoin.getProperty2Name());
-                    Schemata.Column leftColumn = table.getColumn(leftColumnName);
-                    Schemata.Column rightColumn = table.getColumn(rightColumnName);
-                    if (leftColumn == null) {
-                        context.getProblems().addError(GraphI18n.columnDoesNotExistOnTable, leftColumnName, leftTableName);
-                        continue;
-                    }
-                    if (rightColumn == null) {
-                        context.getProblems().addError(GraphI18n.columnDoesNotExistOnTable, rightColumnName, leftTableName);
-                        continue;
-                    }
-                    // Are the join columns (on both sides) keys?
-                    if (table.hasKey(leftColumn) && (rightColumn == leftColumn || table.hasKey(rightColumn))) {
-                        // It meets all the criteria, so rewrite this join node ...
-                        if (rewrittenSelectors == null) rewrittenSelectors = new HashMap<SelectorName, SelectorName>();
-                        rewriteJoinNode(context, joinNode, equiJoin, rewrittenSelectors);
-                    }
+                EquiJoinCondition equiJoin = (EquiJoinCondition)condition;
+                // Find the names (or aliases) of the tables ...
+                Schemata schemata = context.getSchemata();
+                assert schemata != null;
+                SelectorName leftTableName = leftNode.getProperty(Property.SOURCE_NAME, SelectorName.class);
+                SelectorName rightTableName = rightNode.getProperty(Property.SOURCE_NAME, SelectorName.class);
+                assert leftTableName != null;
+                assert rightTableName != null;
+                // Presumably the join condition is using at least one alias, but we only care about the actual name ...
+                if (!leftTableName.equals(rightTableName)) {
+                    // The join is not joining the same table, so this doesn't meet the condition ...
+                    continue;
                 }
+                // Find the schemata columns referenced by the join condition ...
+                Table table = schemata.getTable(leftTableName);
+                if (table == null) {
+                    context.getProblems().addError(GraphI18n.tableDoesNotExist, leftTableName);
+                    continue;
+                }
+                ValueFactory<String> stringFactory = context.getExecutionContext().getValueFactories().getStringFactory();
+                String leftColumnName = stringFactory.create(equiJoin.getProperty1Name());
+                String rightColumnName = stringFactory.create(equiJoin.getProperty2Name());
+                Schemata.Column leftColumn = table.getColumn(leftColumnName);
+                Schemata.Column rightColumn = table.getColumn(rightColumnName);
+                if (leftColumn == null) {
+                    context.getProblems().addError(GraphI18n.columnDoesNotExistOnTable, leftColumnName, leftTableName);
+                    continue;
+                }
+                if (rightColumn == null) {
+                    context.getProblems().addError(GraphI18n.columnDoesNotExistOnTable, rightColumnName, leftTableName);
+                    continue;
+                }
+                // Are the join columns (on both sides) keys?
+                if (table.hasKey(leftColumn) && (rightColumn == leftColumn || table.hasKey(rightColumn))) {
+                    // It meets all the criteria, so rewrite this join node ...
+                    if (rewrittenSelectors == null) rewrittenSelectors = new HashMap<SelectorName, SelectorName>();
+                    rewriteJoinNode(context, joinNode, rewrittenSelectors);
+                    ++rewrittenJoins;
+                }
+            } else if (condition instanceof SameNodeJoinCondition) {
+                SameNodeJoinCondition sameNodeCondition = (SameNodeJoinCondition)condition;
+                if (sameNodeCondition.getSelector1Name().equals(sameNodeCondition.getSelector2Name())
+                    && sameNodeCondition.getSelector2Path() == null) {
+                    // It meets all the criteria, so rewrite this join node ...
+                    if (rewrittenSelectors == null) rewrittenSelectors = new HashMap<SelectorName, SelectorName>();
+                    rewriteJoinNode(context, joinNode, rewrittenSelectors);
+                    ++rewrittenJoins;
+                }
             }
         }
 
@@ -140,28 +156,72 @@
             // this criteria, so we need to re-run this rule...
             ruleStack.addFirst(this);
 
+            // After this rule is done as is no longer needed, we need to try to push SELECTs and PROJECTs again ...
+            if (!(ruleStack.peekFirst() instanceof PushSelectCriteria)) {
+                // We haven't already added these, so add them now ...
+                ruleStack.addFirst(PushProjects.INSTANCE);
+                if (context.getHints().hasCriteria) {
+                    ruleStack.addFirst(PushSelectCriteria.INSTANCE);
+                }
+            }
+
             // Now rewrite the various portions of the plan that make use of the now-removed selectors ...
             PlanUtil.replaceReferencesToRemovedSource(context, plan, rewrittenSelectors);
-        } else {
-            // There are no-untouched JOIN nodes, which means the sole JOIN node was rewritten as a single SOURCE node
-            assert plan.findAllAtOrBelow(Type.JOIN).isEmpty();
-            context.getHints().hasJoin = false;
+
+            assert rewrittenJoins > 0;
+            if (rewrittenJoins == numJoins) {
+                assert plan.findAllAtOrBelow(Type.JOIN).isEmpty();
+                context.getHints().hasJoin = false;
+            }
         }
         return plan;
     }
 
     protected void rewriteJoinNode( QueryContext context,
                                     PlanNode joinNode,
-                                    EquiJoinCondition joinCondition,
                                     Map<SelectorName, SelectorName> rewrittenSelectors ) {
         // Remove the right source node from the join node ...
-        PlanNode rightSource = joinNode.getLastChild();
-        rightSource.removeFromParent();
+        PlanNode rightChild = joinNode.getLastChild();
+        rightChild.removeFromParent();
+        PlanNode rightSource = rightChild.findAtOrBelow(Type.SOURCE);
 
         // Replace the join node with the left source node ...
-        PlanNode leftSource = joinNode.getFirstChild();
+        PlanNode leftChild = joinNode.getFirstChild();
         joinNode.extractFromParent();
+        PlanNode leftSource = leftChild.findAtOrBelow(Type.SOURCE);
 
+        // Combine the right PROJECT node with that on the left ...
+        PlanNode rightProject = rightChild.findAtOrBelow(Type.PROJECT);
+        if (rightProject != null) {
+            PlanNode leftProject = leftChild.findAtOrBelow(Type.PROJECT);
+            if (leftProject != null) {
+                List<Column> leftColumns = leftProject.getPropertyAsList(Property.PROJECT_COLUMNS, Column.class);
+                for (Column rightColumn : rightProject.getPropertyAsList(Property.PROJECT_COLUMNS, Column.class)) {
+                    if (!leftColumns.contains(rightColumn)) leftColumns.add(rightColumn);
+                }
+            } else {
+                // Just create a project on the left side ...
+                leftProject = new PlanNode(Type.PROJECT);
+                leftProject.setProperty(Property.PROJECT_COLUMNS, rightProject.getProperty(Property.PROJECT_COLUMNS));
+                leftChild.getFirstChild().insertAsParent(leftProject);
+            }
+        }
+
+        // Accumulate any SELECT nodes from the right side and add to the left ...
+        PlanNode topRightSelect = rightChild.findAtOrBelow(Type.SELECT);
+        if (topRightSelect != null) {
+            PlanNode bottomRightSelect = topRightSelect;
+            while (true) {
+                if (bottomRightSelect.getFirstChild().isNot(Type.SELECT)) break;
+                bottomRightSelect = bottomRightSelect.getFirstChild();
+            }
+            topRightSelect.setParent(null);
+            bottomRightSelect.removeAllChildren();
+            // Place just above the left source ...
+            leftSource.getParent().addLastChild(topRightSelect);
+            leftSource.setParent(bottomRightSelect);
+        }
+
         // Now record that references to the right selector name should be removed ...
         SelectorName rightTableName = rightSource.getProperty(Property.SOURCE_NAME, SelectorName.class);
         SelectorName rightTableAlias = rightSource.getProperty(Property.SOURCE_ALIAS, SelectorName.class);

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/RuleBasedOptimizer.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/RuleBasedOptimizer.java	2009-10-20 16:14:27 UTC (rev 1297)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/optimize/RuleBasedOptimizer.java	2009-10-21 17:01:35 UTC (rev 1298)
@@ -69,8 +69,10 @@
      */
     protected void populateRuleStack( LinkedList<OptimizerRule> ruleStack,
                                       PlanHints hints ) {
+        ruleStack.addFirst(MergeSetCriteria.INSTANCE);
         if (hints.hasJoin) {
             ruleStack.addFirst(ChooseJoinAlgorithm.USE_ONLY_NESTED_JOIN_ALGORITHM);
+            ruleStack.addFirst(RewriteIdentityJoins.INSTANCE);
         }
         ruleStack.addFirst(PushProjects.INSTANCE);
         if (hints.hasCriteria) {

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/CanonicalPlanner.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/CanonicalPlanner.java	2009-10-20 16:14:27 UTC (rev 1297)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/CanonicalPlanner.java	2009-10-21 17:01:35 UTC (rev 1298)
@@ -70,6 +70,8 @@
  *         |
  *      PROJECT      with the list of columns being SELECTed
  *         |
+ *       GROUP       if 'GROUP BY' is used
+ *         |
  *      SELECT1
  *         |         One or more SELECT plan nodes that each have
  *      SELECT2      a single non-join constraint that are then all AND-ed
@@ -127,6 +129,9 @@
         // Attach criteria (on top) ...
         plan = attachCriteria(context, plan, query.getConstraint());
 
+        // Attach groupbys (on top) ...
+        // plan = attachGrouping(context,plan,query.getGroupBy());
+
         // Attach the project ...
         plan = attachProject(context, plan, query.getColumns(), usedSources);
 

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/PlanNode.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/PlanNode.java	2009-10-20 16:14:27 UTC (rev 1297)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/PlanNode.java	2009-10-21 17:01:35 UTC (rev 1298)
@@ -173,6 +173,12 @@
         PROJECT_COLUMNS,
 
         /**
+         * For GROUP nodes, the ordered collection of columns used to group the result tuples. Value is a Collection of
+         * {@link Column} objects.
+         */
+        GROUP_COLUMNS,
+
+        /**
          * For SET_OPERATION nodes, the list of orderings for the results. Value is either a Collection of {@link Ordering}
          * objects or a collection of {@link SelectorName} objects (if the sorting is being done as an input to a merge-join).
          */
@@ -363,6 +369,31 @@
     }
 
     /**
+     * Determine if the supplied node is an ancestor of this node.
+     * 
+     * @param possibleAncestor the node that is to be determined if it is an ancestor
+     * @return true if the supplied node is indeed an ancestor, or false if it is not an ancestor
+     */
+    public boolean isBelow( PlanNode possibleAncestor ) {
+        PlanNode node = this;
+        while (node != null) {
+            if (node == possibleAncestor) return true;
+            node = node.getParent();
+        }
+        return false;
+    }
+
+    /**
+     * Determine if the supplied node is a descendant of this node.
+     * 
+     * @param possibleDescendant the node that is to be determined if it is a descendant
+     * @return true if the supplied node is indeed an ancestor, or false if it is not an ancestor
+     */
+    public boolean isAbove( PlanNode possibleDescendant ) {
+        return possibleDescendant != null && possibleDescendant.isBelow(this);
+    }
+
+    /**
      * Get the parent of this node.
      * 
      * @return the parent node, or null if this node has no parent

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/PlanUtil.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/PlanUtil.java	2009-10-20 16:14:27 UTC (rev 1297)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/plan/PlanUtil.java	2009-10-21 17:01:35 UTC (rev 1298)
@@ -23,6 +23,7 @@
  */
 package org.jboss.dna.graph.query.plan;
 
+import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
@@ -489,11 +490,10 @@
         List<PlanNode> potentiallyRemovableSources = new LinkedList<PlanNode>();
         do {
             // Remove the view from the selectors ...
-            if (node.getSelectors().contains(viewName)) {
+            if (node.getSelectors().remove(viewName)) {
                 switch (node.getType()) {
                     case PROJECT:
                         // Adjust the columns ...
-                        node.getSelectors().remove(viewName);
                         List<Column> columns = node.getPropertyAsList(Property.PROJECT_COLUMNS, Column.class);
                         if (columns != null) {
                             for (int i = 0; i != columns.size(); ++i) {
@@ -529,19 +529,41 @@
                             node.setProperty(Property.SELECT_CRITERIA, newConstraint);
                         }
                         break;
-                    case SET_OPERATION:
-                        break;
                     case SOURCE:
                         SelectorName sourceName = node.getProperty(Property.SOURCE_NAME, SelectorName.class);
                         assert sourceName.equals(sourceName); // selector name already matches
                         potentiallyRemovableSources.add(node);
                         break;
-                    default:
+                    case JOIN:
+                        JoinCondition joinCondition = node.getProperty(Property.JOIN_CONDITION, JoinCondition.class);
+                        JoinCondition newJoinCondition = replaceViewReferences(context, joinCondition, mappings, node);
+                        node.getSelectors().clear();
+                        node.setProperty(Property.JOIN_CONDITION, newJoinCondition);
+                        node.addSelectors(Visitors.getSelectorsReferencedBy(newJoinCondition));
+                        List<Constraint> joinConstraints = node.getPropertyAsList(Property.JOIN_CONSTRAINTS, Constraint.class);
+                        if (joinConstraints != null && !joinConstraints.isEmpty()) {
+                            List<Constraint> newConstraints = new ArrayList<Constraint>(joinConstraints.size());
+                            for (Constraint joinConstraint : joinConstraints) {
+                                newConstraint = replaceReferences(context, joinConstraint, mappings, node);
+                                newConstraints.add(newConstraint);
+                                node.addSelectors(Visitors.getSelectorsReferencedBy(newConstraint));
+                            }
+                            node.setProperty(Property.JOIN_CONSTRAINTS, newConstraints);
+                        }
                         break;
+                    case ACCESS:
+                        // Nothing to do here, as the selector names are fixed elsewhere
+                        break;
+                    case SORT:
+                        break;
+                    case GROUP:
+                        // Don't yet use GROUP BY
+                    case SET_OPERATION:
+                    case DUP_REMOVE:
+                    case LIMIT:
+                    case NULL:
+                        break;
                 }
-                if (!node.getSelectors().contains(viewName)) {
-                    // Used to reference view, so it needs to at least reference
-                }
             }
             // Move to the parent ...
             node = node.getParent();
@@ -704,10 +726,10 @@
         return operand;
     }
 
-    public static JoinCondition replaceReferences( QueryContext context,
-                                                   JoinCondition joinCondition,
-                                                   ColumnMapping mapping,
-                                                   PlanNode node ) {
+    public static JoinCondition replaceViewReferences( QueryContext context,
+                                                       JoinCondition joinCondition,
+                                                       ColumnMapping mapping,
+                                                       PlanNode node ) {
         if (joinCondition instanceof EquiJoinCondition) {
             EquiJoinCondition condition = (EquiJoinCondition)joinCondition;
             SelectorName replacement1 = condition.getSelector1Name();
@@ -749,6 +771,7 @@
             if (replacement2.equals(viewName)) replacement2 = sourceName;
             if (replacement1 == condition.getSelector1Name() && replacement2 == condition.getSelector2Name()) return condition;
             node.addSelector(replacement1, replacement2);
+            if (condition.getSelector2Path() == null) return new SameNodeJoinCondition(replacement1, replacement2);
             return new SameNodeJoinCondition(replacement1, replacement2, condition.getSelector2Path());
         }
         if (joinCondition instanceof ChildNodeJoinCondition) {

Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/ImmutableSchemata.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/ImmutableSchemata.java	2009-10-20 16:14:27 UTC (rev 1297)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/query/validate/ImmutableSchemata.java	2009-10-21 17:01:35 UTC (rev 1298)
@@ -356,7 +356,7 @@
                                String... columnNames ) {
             CheckArg.isNotEmpty(tableName, "tableName");
             CheckArg.isNotEmpty(columnNames, "columnNames");
-            ImmutableTable existing = tables.get(tableName);
+            ImmutableTable existing = tables.get(new SelectorName(tableName));
             if (existing == null) {
                 throw new IllegalArgumentException(GraphI18n.tableDoesNotExist.text(tableName));
             }

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/ReplaceViewsTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/ReplaceViewsTest.java	2009-10-20 16:14:27 UTC (rev 1297)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/ReplaceViewsTest.java	2009-10-21 17:01:35 UTC (rev 1298)
@@ -24,7 +24,6 @@
 package org.jboss.dna.graph.query.optimize;
 
 import static org.hamcrest.core.Is.is;
-import static org.hamcrest.core.IsSame.sameInstance;
 import static org.junit.Assert.assertThat;
 import java.util.Arrays;
 import java.util.LinkedList;
@@ -133,15 +132,12 @@
 
         // Execute the rule ...
         PlanNode result = rule.execute(context, project, new LinkedList<OptimizerRule>());
+        System.out.println(project);
         System.out.println(result);
-        assertThat(result, is(sameInstance(project)));
+        assertThat(result.isSameAs(project), is(true));
         assertChildren(project, select1);
         assertChildren(select1, select2);
         assertChildren(select2, select3);
-        assertChildren(select3, source);
-        assertSameChildren(source, viewSelect);
-        assertSameChildren(source.getFirstChild(), viewSource);
-        assertSameChildren(source.getFirstChild().getFirstChild());
     }
 
     protected List<Column> columns( Column... columns ) {

Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/RuleBasedOptimizerTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/RuleBasedOptimizerTest.java	2009-10-20 16:14:27 UTC (rev 1297)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/query/optimize/RuleBasedOptimizerTest.java	2009-10-21 17:01:35 UTC (rev 1298)
@@ -38,12 +38,14 @@
 import org.jboss.dna.graph.query.model.Column;
 import org.jboss.dna.graph.query.model.Comparison;
 import org.jboss.dna.graph.query.model.EquiJoinCondition;
+import org.jboss.dna.graph.query.model.FullTextSearch;
 import org.jboss.dna.graph.query.model.JoinType;
 import org.jboss.dna.graph.query.model.Literal;
 import org.jboss.dna.graph.query.model.Operator;
 import org.jboss.dna.graph.query.model.PropertyValue;
 import org.jboss.dna.graph.query.model.QueryCommand;
 import org.jboss.dna.graph.query.model.SelectorName;
+import org.jboss.dna.graph.query.model.SetCriteria;
 import org.jboss.dna.graph.query.parse.SqlQueryParser;
 import org.jboss.dna.graph.query.plan.CanonicalPlanner;
 import org.jboss.dna.graph.query.plan.JoinAlgorithm;
@@ -66,6 +68,7 @@
     private List<Integer> ruleExecutionOrder;
     private QueryContext context;
     private PlanNode node;
+    private boolean print = false;
 
     @Before
     public void beforeEach() {
@@ -73,8 +76,15 @@
         ImmutableSchemata.Builder builder = ImmutableSchemata.createBuilder(execContext);
         builder.addTable("t1", "c11", "c12", "c13");
         builder.addTable("t2", "c21", "c22", "c23");
+        builder.addTable("all", "a1", "a2", "a3", "a4", "primaryType", "mixins");
+        builder.addKey("all", "a1");
+        builder.addKey("all", "a3");
         builder.addView("v1", "SELECT c11, c12 AS c2 FROM t1 WHERE c13 < CAST('3' AS LONG)");
         builder.addView("v2", "SELECT t1.c11, t1.c12, t2.c23 FROM t1 JOIN t2 ON t1.c11 = t2.c21");
+        builder.addView("type1",
+                        "SELECT all.a1, all.a2 FROM all WHERE all.primaryType IN ('t1','t0') AND all.mixins IN ('t3','t4')");
+        builder.addView("type2",
+                        "SELECT all.a3, all.a4 FROM all WHERE all.primaryType IN ('t2','t0') AND all.mixins IN ('t4','t5')");
         Schemata schemata = builder.build();
         context = new QueryContext(execContext, new PlanHints(), schemata);
 
@@ -282,6 +292,136 @@
         assertThat(node.isSameAs(project), is(true));
     }
 
+    @Test
+    public void shouldOptimizePlanForQueryUsingTypeView() {
+        node = optimize("SELECT type1.a1 AS a, type1.a2 AS b FROM type1 WHERE CONTAINS(type1.a2,'something')");
+
+        // Create the expected plan ...
+        PlanNode access = new PlanNode(Type.ACCESS, selector("all"));
+        PlanNode project = new PlanNode(Type.PROJECT, access, selector("all"));
+        project.setProperty(Property.PROJECT_COLUMNS, columns(column("all", "a1", "a"), column("all", "a2", "b")));
+        PlanNode select1 = new PlanNode(Type.SELECT, project, selector("all"));
+        select1.setProperty(Property.SELECT_CRITERIA, new FullTextSearch(selector("all"), name("a2"), "something"));
+        PlanNode select2 = new PlanNode(Type.SELECT, select1, selector("all"));
+        select2.setProperty(Property.SELECT_CRITERIA, new SetCriteria(new PropertyValue(selector("all"), name("primaryType")),
+                                                                      new Literal("t1"), new Literal("t0")));
+        PlanNode select3 = new PlanNode(Type.SELECT, select2, selector("all"));
+        select3.setProperty(Property.SELECT_CRITERIA, new SetCriteria(new PropertyValue(selector("all"), name("mixins")),
+                                                                      new Literal("t3"), new Literal("t4")));
+        PlanNode source = new PlanNode(Type.SOURCE, select3, selector("all"));
+        source.setProperty(Property.SOURCE_NAME, selector("all"));
+        source.setProperty(Property.SOURCE_COLUMNS, context.getSchemata().getTable(selector("all")).getColumns());
+
+        // Compare the expected and actual plan ...
+        assertThat(node.isSameAs(access), is(true));
+    }
+
+    @Test
+    public void shouldOptimizePlanForQueryJoiningMultipleTypeViewsUsingIdentityEquiJoin() {
+        node = optimize("SELECT type1.a1 AS a, type1.a2 AS b, type2.a3 as c, type2.a4 as d "
+                        + "FROM type1 JOIN type2 ON type1.a1 = type2.a3 WHERE CONTAINS(type1.a2,'something')");
+
+        // Create the expected plan ...
+        PlanNode access = new PlanNode(Type.ACCESS, selector("all"));
+        PlanNode project = new PlanNode(Type.PROJECT, access, selector("all"));
+        project.setProperty(Property.PROJECT_COLUMNS, columns(column("all", "a1", "a"),
+                                                              column("all", "a2", "b"),
+                                                              column("all", "a3", "c"),
+                                                              column("all", "a4", "d")));
+        PlanNode select1 = new PlanNode(Type.SELECT, project, selector("all"));
+        select1.setProperty(Property.SELECT_CRITERIA, new FullTextSearch(selector("all"), name("a2"), "something"));
+        PlanNode select2 = new PlanNode(Type.SELECT, select1, selector("all"));
+        select2.setProperty(Property.SELECT_CRITERIA, new SetCriteria(new PropertyValue(selector("all"), name("primaryType")),
+                                                                      new Literal("t1"), new Literal("t0"), new Literal("t2")));
+        PlanNode select3 = new PlanNode(Type.SELECT, select2, selector("all"));
+        select3.setProperty(Property.SELECT_CRITERIA, new SetCriteria(new PropertyValue(selector("all"), name("mixins")),
+                                                                      new Literal("t3"), new Literal("t4"), new Literal("t5")));
+        PlanNode source = new PlanNode(Type.SOURCE, select3, selector("all"));
+        source.setProperty(Property.SOURCE_NAME, selector("all"));
+        source.setProperty(Property.SOURCE_COLUMNS, context.getSchemata().getTable(selector("all")).getColumns());
+
+        // Compare the expected and actual plan ...
+        assertThat(node.isSameAs(access), is(true));
+    }
+
+    @Test
+    public void shouldOptimizePlanForQueryJoiningMultipleTypeViewsUsingNonIdentityEquiJoin() {
+        node = optimize("SELECT type1.a1 AS a, type1.a2 AS b, type2.a3 as c, type2.a4 as d "
+                        + "FROM type1 JOIN type2 ON type1.a2 = type2.a3 WHERE CONTAINS(type1.a1,'something')");
+
+        // Create the expected plan ...
+        PlanNode project = new PlanNode(Type.PROJECT, selector("all"));
+        project.setProperty(Property.PROJECT_COLUMNS, columns(column("all", "a1", "a"),
+                                                              column("all", "a2", "b"),
+                                                              column("all", "a3", "c"),
+                                                              column("all", "a4", "d")));
+        PlanNode select1 = new PlanNode(Type.SELECT, project, selector("all"));
+        select1.setProperty(Property.SELECT_CRITERIA, new FullTextSearch(selector("all"), name("a1"), "something"));
+        PlanNode join = new PlanNode(Type.JOIN, select1, selector("all"));
+        join.setProperty(Property.JOIN_ALGORITHM, JoinAlgorithm.NESTED_LOOP);
+        join.setProperty(Property.JOIN_TYPE, JoinType.INNER);
+        join.setProperty(Property.JOIN_CONDITION, new EquiJoinCondition(selector("all"), name("a2"), selector("all"), name("a3")));
+
+        PlanNode leftAccess = new PlanNode(Type.ACCESS, join, selector("all"));
+        PlanNode leftProject = new PlanNode(Type.PROJECT, leftAccess, selector("all"));
+        leftProject.setProperty(Property.PROJECT_COLUMNS, columns(column("all", "a1"), column("all", "a2")));
+        PlanNode leftSelect1 = new PlanNode(Type.SELECT, leftProject, selector("all"));
+        leftSelect1.setProperty(Property.SELECT_CRITERIA,
+                                new SetCriteria(new PropertyValue(selector("all"), name("primaryType")), new Literal("t1"),
+                                                new Literal("t0")));
+        PlanNode leftSelect2 = new PlanNode(Type.SELECT, leftSelect1, selector("all"));
+        leftSelect2.setProperty(Property.SELECT_CRITERIA, new SetCriteria(new PropertyValue(selector("all"), name("mixins")),
+                                                                          new Literal("t3"), new Literal("t4")));
+        PlanNode leftSource = new PlanNode(Type.SOURCE, leftSelect2, selector("all"));
+        leftSource.setProperty(Property.SOURCE_NAME, selector("all"));
+        leftSource.setProperty(Property.SOURCE_COLUMNS, context.getSchemata().getTable(selector("all")).getColumns());
+
+        PlanNode rightAccess = new PlanNode(Type.ACCESS, join, selector("all"));
+        PlanNode rightProject = new PlanNode(Type.PROJECT, rightAccess, selector("all"));
+        rightProject.setProperty(Property.PROJECT_COLUMNS, columns(column("all", "a3"), column("all", "a4")));
+        PlanNode rightSelect1 = new PlanNode(Type.SELECT, rightProject, selector("all"));
+        rightSelect1.setProperty(Property.SELECT_CRITERIA,
+                                 new SetCriteria(new PropertyValue(selector("all"), name("primaryType")), new Literal("t2"),
+                                                 new Literal("t0")));
+        PlanNode rightSelect2 = new PlanNode(Type.SELECT, rightSelect1, selector("all"));
+        rightSelect2.setProperty(Property.SELECT_CRITERIA, new SetCriteria(new PropertyValue(selector("all"), name("mixins")),
+                                                                           new Literal("t4"), new Literal("t5")));
+        PlanNode rightSource = new PlanNode(Type.SOURCE, rightSelect2, selector("all"));
+        rightSource.setProperty(Property.SOURCE_NAME, selector("all"));
+        rightSource.setProperty(Property.SOURCE_COLUMNS, context.getSchemata().getTable(selector("all")).getColumns());
+
+        // Compare the expected and actual plan ...
+        assertThat(node.isSameAs(project), is(true));
+    }
+
+    @Test
+    public void shouldOptimizePlanForQueryJoiningMultipleTypeViewsUsingSameNodeJoin() {
+        node = optimize("SELECT type1.a1 AS a, type1.a2 AS b, type2.a3 as c, type2.a4 as d "
+                        + "FROM type1 JOIN type2 ON ISSAMENODE(type1,type2) WHERE CONTAINS(type1.a2,'something')");
+
+        // Create the expected plan ...
+        PlanNode access = new PlanNode(Type.ACCESS, selector("all"));
+        PlanNode project = new PlanNode(Type.PROJECT, access, selector("all"));
+        project.setProperty(Property.PROJECT_COLUMNS, columns(column("all", "a1", "a"),
+                                                              column("all", "a2", "b"),
+                                                              column("all", "a3", "c"),
+                                                              column("all", "a4", "d")));
+        PlanNode select1 = new PlanNode(Type.SELECT, project, selector("all"));
+        select1.setProperty(Property.SELECT_CRITERIA, new FullTextSearch(selector("all"), name("a2"), "something"));
+        PlanNode select2 = new PlanNode(Type.SELECT, select1, selector("all"));
+        select2.setProperty(Property.SELECT_CRITERIA, new SetCriteria(new PropertyValue(selector("all"), name("primaryType")),
+                                                                      new Literal("t1"), new Literal("t0"), new Literal("t2")));
+        PlanNode select3 = new PlanNode(Type.SELECT, select2, selector("all"));
+        select3.setProperty(Property.SELECT_CRITERIA, new SetCriteria(new PropertyValue(selector("all"), name("mixins")),
+                                                                      new Literal("t3"), new Literal("t4"), new Literal("t5")));
+        PlanNode source = new PlanNode(Type.SOURCE, select3, selector("all"));
+        source.setProperty(Property.SOURCE_NAME, selector("all"));
+        source.setProperty(Property.SOURCE_COLUMNS, context.getSchemata().getTable(selector("all")).getColumns());
+
+        // Compare the expected and actual plan ...
+        assertThat(node.isSameAs(access), is(true));
+    }
+
     // ----------------------------------------------------------------------------------------------------------------
     // Utility methods ...
     // ----------------------------------------------------------------------------------------------------------------
@@ -313,9 +453,11 @@
         assertThat("Problems planning query: " + sql + "\n" + problems, problems.hasErrors(), is(false));
         PlanNode optimized = new RuleBasedOptimizer().optimize(context, plan);
         assertThat("Problems optimizing query: " + sql + "\n" + problems, problems.hasErrors(), is(false));
-        System.out.println();
-        System.out.println(sql);
-        System.out.println(optimized);
+        if (print) {
+            System.out.println();
+            System.out.println(sql);
+            System.out.println(optimized);
+        }
         return optimized;
     }
 }

Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/xpath/XPathToQueryTranslatorTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/xpath/XPathToQueryTranslatorTest.java	2009-10-20 16:14:27 UTC (rev 1297)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/xpath/XPathToQueryTranslatorTest.java	2009-10-21 17:01:35 UTC (rev 1298)
@@ -73,7 +73,7 @@
     @Test
     public void shouldTranslateFromXPathContainingExplicitPath() {
         assertThat(xpath("/jcr:root/a"),
-                   isSql("SELECT * FROM __ALLNODES__ AS nodeSet1 WHERE NAME(nodeSet1) = 'a' AND DEPTH(nodeSet1) = 1"));
+                   isSql("SELECT * FROM __ALLNODES__ AS nodeSet1 WHERE NAME(nodeSet1) = 'a' AND DEPTH(nodeSet1) = CAST(1 AS LONG)"));
         assertThat(xpath("/jcr:root/a/b"), isSql("SELECT * FROM __ALLNODES__ AS nodeSet1 WHERE PATH(nodeSet1) = '/a/b'"));
         assertThat(xpath("/jcr:root/a/b/c"), isSql("SELECT * FROM __ALLNODES__ AS nodeSet1 WHERE PATH(nodeSet1) = '/a/b/c'"));
         assertThat(xpath("/jcr:root/a/b/c/d"), isSql("SELECT * FROM __ALLNODES__ AS nodeSet1 WHERE PATH(nodeSet1) = '/a/b/c/d'"));
@@ -218,13 +218,13 @@
     @Test
     public void shouldTranslateFromXPathOfNodeWithNameUnderRoot() {
         assertThat(xpath("/jcr:root/element(nodeName,*)"),
-                   isSql("SELECT * FROM __ALLNODES__ AS nodeSet1 WHERE NAME(nodeSet1) = 'nodeName' AND DEPTH(nodeSet1) = 1"));
+                   isSql("SELECT * FROM __ALLNODES__ AS nodeSet1 WHERE NAME(nodeSet1) = 'nodeName' AND DEPTH(nodeSet1) = CAST(1 AS LONG)"));
 
         assertThat(xpath("/jcr:root/nodeName"),
-                   isSql("SELECT * FROM __ALLNODES__ AS nodeSet1 WHERE NAME(nodeSet1) = 'nodeName' AND DEPTH(nodeSet1) = 1"));
+                   isSql("SELECT * FROM __ALLNODES__ AS nodeSet1 WHERE NAME(nodeSet1) = 'nodeName' AND DEPTH(nodeSet1) = CAST(1 AS LONG)"));
 
         assertThat(xpath("nodeName"),
-                   isSql("SELECT * FROM __ALLNODES__ AS nodeSet1 WHERE NAME(nodeSet1) = 'nodeName' AND DEPTH(nodeSet1) = 1"));
+                   isSql("SELECT * FROM __ALLNODES__ AS nodeSet1 WHERE NAME(nodeSet1) = 'nodeName' AND DEPTH(nodeSet1) = CAST(1 AS LONG)"));
     }
 
     @Test



More information about the dna-commits mailing list