Index: jackrabbit-core/src/test/java/org/apache/jackrabbit/core/query/ParentNodeTest.java =================================================================== --- jackrabbit-core/src/test/java/org/apache/jackrabbit/core/query/ParentNodeTest.java (revision 0) +++ jackrabbit-core/src/test/java/org/apache/jackrabbit/core/query/ParentNodeTest.java (revision 0) @@ -0,0 +1,137 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.jackrabbit.core.query; + +import javax.jcr.Node; +import javax.jcr.RepositoryException; +import javax.jcr.query.Query; +import javax.jcr.query.QueryManager; +import javax.jcr.query.QueryResult; + +/** + * QueryResultTest tests various methods on the + * NodeIterator returned by a QueryResult. + */ +public class ParentNodeTest extends AbstractQueryTest { + + protected void setUp() throws Exception { + super.setUp(); + + Node base = testRootNode.addNode("base"); + base.setProperty("foo1", "bar1"); + Node child = base.addNode("child"); + child.setProperty("foo2", "bar2"); + + child = base.addNode("child2"); + child.setProperty("foo2", "bar2"); + + child = base.addNode("child3"); + child.setProperty("foo2", "bar2"); + + testRootNode.save(); + } + + public void testParentInPath() throws RepositoryException { + QueryManager qm = superuser.getWorkspace().getQueryManager(); + String stmt = testPath + "//child/..[@foo1]"; + QueryResult result = qm.createQuery(stmt, Query.XPATH).execute(); + assertEquals("Wrong size of NodeIterator in result", + 1, result.getNodes().getSize()); + + assertEquals("base", result.getNodes().nextNode().getName()); + + // remove node for the next iteration + testRootNode.getNode("base").remove(); + testRootNode.save(); + } + + public void testParentInAttribute1() throws RepositoryException { + QueryManager qm = superuser.getWorkspace().getQueryManager(); + String stmt = testPath + "//child[../@foo1]"; + QueryResult result = qm.createQuery(stmt, Query.XPATH).execute(); + assertTrue("Wrong size of NodeIterator in result", result.getNodes().getSize() > 0); + + assertEquals("child", result.getNodes().nextNode().getName()); + + // remove node for the next iteration + testRootNode.getNode("base").remove(); + testRootNode.save(); + } + + public void testParentInAttribute2() throws RepositoryException { + QueryManager qm = superuser.getWorkspace().getQueryManager(); + String stmt = testPath + "//child[../child/@foo2]"; + QueryResult result = qm.createQuery(stmt, Query.XPATH).execute(); + assertTrue("Wrong size of NodeIterator in result", result.getNodes().getSize() > 0); + + assertEquals("child", result.getNodes().nextNode().getName()); + + // remove node for the next iteration + testRootNode.getNode("base").remove(); + testRootNode.save(); + } + + public void testParentInAttribute3() throws RepositoryException { + QueryManager qm = superuser.getWorkspace().getQueryManager(); + String stmt = testPath + "//child[../../base/@foo1]"; + QueryResult result = qm.createQuery(stmt, Query.XPATH).execute(); + assertTrue("Wrong size of NodeIterator in result", result.getNodes().getSize() > 0); + + assertEquals("child", result.getNodes().nextNode().getName()); + + // remove node for the next iteration + testRootNode.getNode("base").remove(); + testRootNode.save(); + } + + public void testParentInAttribute4() throws RepositoryException { + QueryManager qm = superuser.getWorkspace().getQueryManager(); + String stmt = testPath + "//child[../@foo1 = 'bar1']"; + QueryResult result = qm.createQuery(stmt, Query.XPATH).execute(); + assertTrue("Wrong size of NodeIterator in result", result.getNodes().getSize() > 0); + + assertEquals("child", result.getNodes().nextNode().getName()); + + // remove node for the next iteration + testRootNode.getNode("base").remove(); + testRootNode.save(); + } + + public void testParentAttributeWithDescendant() throws RepositoryException { + QueryManager qm = superuser.getWorkspace().getQueryManager(); + String stmt = testPath + "//base[../base/@foo1 = 'bar1']/child"; + QueryResult result = qm.createQuery(stmt, Query.XPATH).execute(); + assertTrue("Wrong size of NodeIterator in result", result.getNodes().getSize() > 0); + + assertEquals("child", result.getNodes().nextNode().getName()); + + // remove node for the next iteration + testRootNode.getNode("base").remove(); + testRootNode.save(); + } + + public void testParentWithAnd() throws RepositoryException { + QueryManager qm = superuser.getWorkspace().getQueryManager(); + String stmt = testPath + "//child[../@foo1 = 'bar1 and @foo2']"; + QueryResult result = qm.createQuery(stmt, Query.XPATH).execute(); + assertEquals("Wrong size of NodeIterator in result", 0, result.getNodes().getSize()); + + // remove node for the next iteration + testRootNode.getNode("base").remove(); + testRootNode.save(); + } +} Property changes on: jackrabbit-core/src/test/java/org/apache/jackrabbit/core/query/ParentNodeTest.java ___________________________________________________________________ Added: svn:executable + * Added: svn:keywords + Author Date Id Revision Added: svn:eol-style + native Index: jackrabbit-core/src/test/java/org/apache/jackrabbit/core/query/AbstractQueryTest.java =================================================================== --- jackrabbit-core/src/test/java/org/apache/jackrabbit/core/query/AbstractQueryTest.java (revision 790368) +++ jackrabbit-core/src/test/java/org/apache/jackrabbit/core/query/AbstractQueryTest.java (working copy) @@ -202,8 +202,11 @@ } Set resultPaths = new HashSet(); while (result.hasNext()) { - resultPaths.add(result.nextNode().getPath()); + Node node = result.nextNode(); + resultPaths.add(node.getPath()); } + + System.out.println("Got " + resultPaths); // check if all expected are in result for (Iterator it = expectedPaths.iterator(); it.hasNext();) { String path = (String) it.next(); Index: jackrabbit-core/src/test/java/org/apache/jackrabbit/core/query/DerefTest.java =================================================================== --- jackrabbit-core/src/test/java/org/apache/jackrabbit/core/query/DerefTest.java (revision 790368) +++ jackrabbit-core/src/test/java/org/apache/jackrabbit/core/query/DerefTest.java (working copy) @@ -39,6 +39,8 @@ */ private Node sun, microsoft, ibm; + private Node people; + /** * Sets up the following structure: *
@@ -58,7 +60,7 @@
     protected void setUp() throws Exception {
         super.setUp();
 
-        Node people = testRootNode.addNode("people");
+        people = testRootNode.addNode("people");
         Node company = testRootNode.addNode("company");
 
         sun = company.addNode("sun");
@@ -137,6 +139,17 @@
                 new Node[]{microsoft});
     }
 
+    public void testDerefInPredicate() throws RepositoryException {
+        executeXPathQuery(testPath + "/people//*[jcr:deref(@worksfor, '*')/@ceo='McNealy']",
+                new Node[]{andrew, eric});
+
+        executeXPathQuery("//*[people/jcr:deref(@worksfor, '*')/@ceo='McNealy']",
+                new Node[]{testRootNode});
+
+//        executeXPathQuery("//*[jcr:contains(people/jcr:deref(@worksfor, '*'),'ballmer')]",
+//                new Node[]{testRootNode});
+    }
+
     /**
      * Checks if jcr:deref works when dereferencing into the version storage.
      */
Index: jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/PredicateDerefQuery.java
===================================================================
--- jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/PredicateDerefQuery.java	(revision 0)
+++ jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/PredicateDerefQuery.java	(revision 0)
@@ -0,0 +1,375 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.core.query.lucene;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.jackrabbit.spi.Name;
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.index.TermDocs;
+import org.apache.lucene.search.Explanation;
+import org.apache.lucene.search.HitCollector;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.search.Scorer;
+import org.apache.lucene.search.Searcher;
+import org.apache.lucene.search.Similarity;
+import org.apache.lucene.search.Weight;
+
+/**
+ * Implements a Lucene Query which returns the nodes which have a 
+ * reference property which matches the nodes of the subquery.
+ */
+class PredicateDerefQuery extends Query {
+
+    /**
+     * The context query
+     */
+    private final Query subQuery;
+
+    /**
+     * The name of the reference property.
+     */
+    private final String refProperty;
+
+    /**
+     * The nameTest to apply on target node, or null if all
+     * target nodes should be selected.
+     */
+    private final Name nameTest;
+
+    /**
+     * The index format version.
+     */
+    private final IndexFormatVersion version;
+
+    /**
+     * The internal namespace mappings.
+     */
+    private final NamespaceMappings nsMappings;
+
+    /**
+     * The scorer of the context query
+     */
+    private Scorer subQueryScorer;
+
+    /**
+     * The scorer of the name test query
+     */
+    private Scorer nameTestScorer;
+    /**
+     * Creates a new DerefQuery based on a context
+     * query.
+     *
+     * @param context the context for this query.
+     * @param subQuery TODO
+     * @param refProperty the name of the reference property.
+     * @param nameTest a name test or null if any node is
+     *  selected.
+     * @param version the index format version.
+     * @param nsMappings the namespace mappings.
+     */
+    PredicateDerefQuery(Query subQuery, String refProperty,
+               Name nameTest, IndexFormatVersion version, NamespaceMappings nsMappings) {
+        this.subQuery = subQuery;
+        this.refProperty = refProperty;
+        this.nameTest = nameTest;
+        this.version = version;
+        this.nsMappings = nsMappings;
+    }
+
+    /**
+     * Creates a Weight instance for this query.
+     *
+     * @param searcher the Searcher instance to use.
+     * @return a DerefWeight.
+     */
+    protected Weight createWeight(Searcher searcher) {
+        return new DerefWeight(searcher);
+    }
+
+    /**
+     * Returns PredicateDerefQuery(subQuery, referenceNodeProperty, nameTest)
+     *
+     * @param field the name of a field.
+     * @return 'DerefQuery'.
+     */
+    public String toString(String field) {
+        StringBuffer sb = new StringBuffer();
+        sb.append("PredicateDerefQuery(");
+        sb.append(subQuery);
+        sb.append(", ");
+        sb.append(nameTest);
+        sb.append(", ");
+        sb.append(refProperty);
+        sb.append(")");
+        return sb.toString();
+    }
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public void extractTerms(Set terms) {
+        // no terms to extract
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Query rewrite(IndexReader reader) throws IOException {
+        Query cQuery = subQuery.rewrite(reader);
+        if (cQuery == subQuery) {
+            return this;
+        } else {
+            return new PredicateDerefQuery(subQuery, refProperty, nameTest, version, nsMappings);
+        }
+    }
+
+    //-------------------< DerefWeight >------------------------------------
+
+    /**
+     * The Weight implementation for this DerefQuery.
+     */
+    private class DerefWeight implements Weight {
+
+        /**
+         * The searcher in use
+         */
+        private final Searcher searcher;
+
+        /**
+         * Creates a new DerefWeight instance using
+         * searcher.
+         *
+         * @param searcher a Searcher instance.
+         */
+        private DerefWeight(Searcher searcher) {
+            this.searcher = searcher;
+        }
+
+        /**
+         * Returns this DerefQuery.
+         *
+         * @return this DerefQuery.
+         */
+        public Query getQuery() {
+            return PredicateDerefQuery.this;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public float getValue() {
+            return 1.0f;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public float sumOfSquaredWeights() throws IOException {
+            return 1.0f;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public void normalize(float norm) {
+        }
+
+        /**
+         * Creates a scorer for this DerefQuery.
+         *
+         * @param reader a reader for accessing the index.
+         * @return a DerefScorer.
+         * @throws IOException if an error occurs while reading from the index.
+         */
+        public Scorer scorer(IndexReader reader) throws IOException {
+            subQueryScorer = subQuery.weight(searcher).scorer(reader);
+            if (nameTest != null) {
+                nameTestScorer = new NameQuery(nameTest, version, nsMappings).weight(searcher).scorer(reader);
+            }
+            return new DerefScorer(searcher.getSimilarity(), reader);
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public Explanation explain(IndexReader reader, int doc) throws IOException {
+            return new Explanation();
+        }
+    }
+
+    //----------------------< DerefScorer >---------------------------------
+
+    /**
+     * Implements a Scorer for this DerefQuery.
+     */
+    private class DerefScorer extends Scorer {
+
+        /**
+         * An IndexReader to access the index.
+         */
+        private final IndexReader reader;
+
+        /**
+         * BitSet storing the id's of selected documents
+         */
+        private final BitSet subQueryHits;
+        
+        /**
+         * BitSet storing the id's of selected documents
+         */
+        private final BitSet hits;
+
+        /**
+         * List of UUIDs of selected nodes
+         */
+        private List uuids = null;
+
+        
+        /**
+         * The next document id to return
+         */
+        private int nextDoc = -1;
+
+        /**
+         * Creates a new DerefScorer.
+         *
+         * @param similarity the Similarity instance to use.
+         * @param reader     for index access.
+         */
+        protected DerefScorer(Similarity similarity, IndexReader reader) {
+            super(similarity);
+            this.reader = reader;
+            this.hits = new BitSet(reader.maxDoc());
+            this.subQueryHits = new BitSet(reader.maxDoc());
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public boolean next() throws IOException {
+            calculateChildren();
+            nextDoc = hits.nextSetBit(nextDoc + 1);
+            return nextDoc > -1;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public int doc() {
+            return nextDoc;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public float score() throws IOException {
+            return 1.0f;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public boolean skipTo(int target) throws IOException {
+            calculateChildren();
+            nextDoc = hits.nextSetBit(target);
+            return nextDoc > -1;
+        }
+
+        /**
+         * {@inheritDoc}
+         *
+         * @throws UnsupportedOperationException this implementation always
+         *                                       throws an UnsupportedOperationException.
+         */
+        public Explanation explain(int doc) throws IOException {
+            throw new UnsupportedOperationException();
+        }
+
+
+        /**
+         * Perform the sub query
+         * For each reference property UUID
+         *      - find document number
+         *      - if document # is in subquery bitset add to bit set
+         * Use the name test to filter the documents
+         * @throws IOException
+         */
+        private void calculateChildren() throws IOException {
+            if (uuids == null) {
+                uuids = new ArrayList();
+//                subQueryHits.clear();
+//                hits.clear();
+                subQueryScorer.score(new HitCollector() {
+                    public void collect(int doc, float score) {
+                        subQueryHits.set(doc);
+                    }
+                });
+
+                TermDocs termDocs = reader.termDocs(new Term(FieldNames.PROPERTIES_SET, refProperty));
+                String prefix = FieldNames.createNamedValue(refProperty, "");
+                while (termDocs.next()) {
+                    int doc = termDocs.doc();
+                     
+                    String[] values = reader.document(doc).getValues(FieldNames.PROPERTIES);
+                    if (values == null) {
+                        // no reference properties at all on this node
+                        continue;
+                    }
+                    for (int v = 0; v < values.length; v++) {
+                        if (values[v].startsWith(prefix)) {
+                            String uuid = values[v].substring(prefix.length());
+                            
+                            TermDocs node = reader.termDocs(new Term(FieldNames.UUID, uuid));
+                            try {
+                                while (node.next()) {
+                                    if (subQueryHits.get(node.doc())) {
+                                        hits.set(doc);
+                                    }
+                                }
+                            } finally {
+                                node.close();
+                            }
+                        }
+                    }
+                }
+                
+                // collect nameTest hits
+                final BitSet nameTestHits = new BitSet();
+                if (nameTestScorer != null) {
+                    nameTestScorer.score(new HitCollector() {
+                        public void collect(int doc, float score) {
+                            nameTestHits.set(doc);
+                        }
+                    });
+                }
+
+                // filter out the target nodes that do not match the name test
+                // if there is any name test at all.
+                if (nameTestScorer != null) {
+                    hits.and(nameTestHits);
+                }
+            }
+        }
+    }
+}

Property changes on: jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/PredicateDerefQuery.java
___________________________________________________________________
Added: svn:executable
   + *
Added: svn:keywords
   + Author Date Id Revision
Added: svn:eol-style
   + native

Index: jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/DerefQuery.java
===================================================================
--- jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/DerefQuery.java	(revision 790368)
+++ jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/DerefQuery.java	(working copy)
@@ -114,7 +114,15 @@
      * @return 'DerefQuery'.
      */
     public String toString(String field) {
-        return "DerefQuery";
+        StringBuffer sb = new StringBuffer();
+        sb.append("DerefQuery(");
+        sb.append(refProperty);
+        sb.append(", ");
+        sb.append(contextQuery);
+        sb.append(", ");
+        sb.append(nameTest);
+        sb.append(")");
+        return sb.toString();
     }
 
     /**
@@ -291,6 +299,14 @@
             throw new UnsupportedOperationException();
         }
 
+        /**
+         * 1. do context query
+         * 2. go through each document from the query
+         * 3. find reference property UUIDs
+         * 4. Use UUIDs to find document number
+         * 5. Use the name test to filter the documents
+         * @throws IOException
+         */
         private void calculateChildren() throws IOException {
             if (uuids == null) {
                 uuids = new ArrayList();
Index: jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/ParentAxisQuery.java
===================================================================
--- jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/ParentAxisQuery.java	(revision 790368)
+++ jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/ParentAxisQuery.java	(working copy)
@@ -124,7 +124,13 @@
      * @return 'ParentAxisQuery'.
      */
     public String toString(String field) {
-        return "ParentAxisQuery";
+        StringBuffer sb = new StringBuffer();
+        sb.append("ParentAxisQuery(");
+        sb.append(contextQuery);
+        sb.append(", ");
+        sb.append(nameTest);
+        sb.append(")");
+        return sb.toString();
     }
 
     //-----------------------< ParentAxisWeight >-------------------------------
Index: jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/LuceneQueryBuilder.java
===================================================================
--- jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/LuceneQueryBuilder.java	(revision 790368)
+++ jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/LuceneQueryBuilder.java	(working copy)
@@ -40,11 +40,11 @@
 import org.apache.jackrabbit.core.state.ItemStateManager;
 import org.apache.jackrabbit.spi.Name;
 import org.apache.jackrabbit.spi.Path;
-import org.apache.jackrabbit.spi.commons.conversion.MalformedPathException;
+import org.apache.jackrabbit.spi.PathFactory;
 import org.apache.jackrabbit.spi.commons.conversion.NameException;
 import org.apache.jackrabbit.spi.commons.conversion.NamePathResolver;
 import org.apache.jackrabbit.spi.commons.name.NameConstants;
-import org.apache.jackrabbit.spi.commons.name.PathBuilder;
+import org.apache.jackrabbit.spi.commons.name.PathFactoryImpl;
 import org.apache.jackrabbit.spi.commons.query.AndQueryNode;
 import org.apache.jackrabbit.spi.commons.query.DefaultQueryNodeVisitor;
 import org.apache.jackrabbit.spi.commons.query.DerefQueryNode;
@@ -67,11 +67,11 @@
 import org.apache.jackrabbit.util.XMLChar;
 import org.apache.lucene.analysis.Analyzer;
 import org.apache.lucene.index.Term;
+import org.apache.lucene.queryParser.ParseException;
+import org.apache.lucene.queryParser.QueryParser;
 import org.apache.lucene.search.BooleanQuery;
 import org.apache.lucene.search.Query;
 import org.apache.lucene.search.BooleanClause.Occur;
-import org.apache.lucene.queryParser.QueryParser;
-import org.apache.lucene.queryParser.ParseException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -88,6 +88,8 @@
      */
     private static final Logger log = LoggerFactory.getLogger(LuceneQueryBuilder.class);
 
+    private static final PathFactory PATH_FACTORY = PathFactoryImpl.getInstance();
+    
     /**
      * Root node of the abstract query tree
      */
@@ -493,12 +495,18 @@
 
         NameQuery nameTest = null;
         if (node.getNameTest() != null) {
+            if (node.getNameTest().equals(PATH_FACTORY.getParentElement())) {
+                andQuery.add(new ParentAxisQuery(context, null, indexFormatVersion, nsMappings), Occur.MUST);
+                return andQuery;
+            }
             nameTest = new NameQuery(node.getNameTest(), indexFormatVersion, nsMappings);
         }
 
         if (node.getIncludeDescendants()) {
             if (nameTest != null) {
-                andQuery.add(new DescendantSelfAxisQuery(context, nameTest, false), Occur.MUST);
+                andQuery.add(nameTest, Occur.MUST);
+                return new DescendantSelfAxisQuery(context, andQuery, false);
+//                andQuery.add(new DescendantSelfAxisQuery(context, nameTest, false), Occur.MUST);
             } else {
                 // descendant-or-self with nametest=*
                 if (predicates.length > 0) {
@@ -586,6 +594,9 @@
     }
 
     public Object visit(RelationQueryNode node, Object data) throws RepositoryException {
+        LocationStepQueryNode top = (LocationStepQueryNode) node.getTopQueryNode();
+        Name propertyName = top.getNameTest();
+        
         Query query;
         String[] stringValues = new String[1];
         switch (node.getValueType()) {
@@ -607,7 +618,6 @@
                         || node.getOperation() == QueryConstants.OPERATION_NE_GENERAL
                         || node.getOperation() == QueryConstants.OPERATION_NE_VALUE) {
                     // only use coercing on non-range operations
-                    Name propertyName = node.getRelativePath().getNameElement().getName();
                     stringValues = getStringValues(propertyName, node.getStringValue());
                 } else {
                     stringValues[0] = node.getStringValue();
@@ -641,37 +651,37 @@
             }
         }, null);
 
-        Path relPath = node.getRelativePath();
+        PathQueryNode relPath = node.getRelativePath();
         if (node.getOperation() == QueryConstants.OPERATION_SIMILAR) {
             // this is a bit ugly:
             // add the name of a dummy property because relPath actually
             // references a property. whereas the relPath of the similar
             // operation references a node
-            PathBuilder builder;
-            if (relPath == null) {
-                builder = new PathBuilder();
-            } else {
-                builder = new PathBuilder(relPath);
-            }
-            builder.addLast(NameConstants.JCR_PRIMARYTYPE);
-            try {
-                relPath = builder.getPath();
-            } catch (MalformedPathException e) {
-                // will never happen
-            }
+            propertyName = NameConstants.JCR_PRIMARYTYPE;
+//            PathBuilder builder;
+//            if (relPath == null) {
+//                builder = new PathBuilder();
+//            } else {
+//                builder = new PathBuilder(relPath);
+//            }
+//            builder.addLast(NameConstants.JCR_PRIMARYTYPE);
+//            try {
+//                relPath = builder.getPath();
+//            } catch (MalformedPathException e) {
+//                // will never happen
+//            }
         }
         String field = "";
         try {
-            field = resolver.getJCRName(relPath.getNameElement().getName());
+            field = resolver.getJCRName(propertyName);
         } catch (NamespaceException e) {
             // should never happen
             exceptions.add(e);
         }
 
         // support for fn:name()
-        Name propName = relPath.getNameElement().getName();
-        if (propName.getNamespaceURI().equals(SearchManager.NS_FN_URI)
-                && propName.getLocalName().equals("name()")) {
+        if (propertyName.getNamespaceURI().equals(SearchManager.NS_FN_URI)
+                && propertyName.getLocalName().equals("name()")) {
             if (node.getValueType() != QueryConstants.TYPE_STRING) {
                 exceptions.add(new InvalidQueryException("Name function can "
                         + "only be used in conjunction with a string literal"));
@@ -863,41 +873,119 @@
             }
         }
 
-        if (relPath.getLength() > 1) {
+        if (relPath.getPathSteps().length > 1) {
             // child axis in relation
-            Path.Element[] elements = relPath.getElements();
+            LocationStepQueryNode[] steps = relPath.getPathSteps();
             // elements.length - 1 = property name
             // elements.length - 2 = last child axis name test
-            for (int i = elements.length - 2; i >= 0; i--) {
+            boolean selectParent = true; 
+            for (int i = steps.length - 2; i >= 0; i--) {
+                LocationStepQueryNode step = steps[i];
                 Name name = null;
-                if (!elements[i].getName().equals(RelationQueryNode.STAR_NAME_TEST)) {
-                    name = elements[i].getName();
+                if (!RelationQueryNode.STAR_NAME_TEST.equals(steps[i].getNameTest())) {
+                    name = steps[i].getNameTest();
                 }
-                if (i == elements.length - 2) {
-                    // join name test with property query if there is one
-                    if (name != null) {
-                        Query nameTest = new NameQuery(name,
-                                indexFormatVersion, nsMappings);
-                        BooleanQuery and = new BooleanQuery();
-                        and.add(query, Occur.MUST);
-                        and.add(nameTest, Occur.MUST);
-                        query = and;
+                if (i == steps.length - 2) {
+                    if (step instanceof DerefQueryNode) {
+                        query = createPredicateDeref(query, (DerefQueryNode) step, data);
+                        if (steps.length == 2) {
+                            selectParent = false;
+                        }
+                    } else if (step instanceof LocationStepQueryNode) {
+                        // join name test with property query if there is one
+                        if (name != null) {
+                            if (!name.equals(PATH_FACTORY.getParentElement().getName())) {
+                                Query nameTest = new NameQuery(name,
+                                        indexFormatVersion, nsMappings);
+                                BooleanQuery and = new BooleanQuery();
+                                and.add(query, Occur.MUST);
+                                and.add(nameTest, Occur.MUST);
+    
+                                query = and;
+                            } else {
+                                // If we're searching the parent, we want to return the child axis,
+                                // not the parent because this is part of the predicate. For instance,
+                                // if the query is //child[../base], this part of the code is operating
+                                // on the "../base" portion. So we want to return all the child nodes
+                                // of "base", which will then be matched against the non predicate part.
+                                query = new ChildAxisQuery(sharedItemMgr,
+                                                           query,
+                                                           null,
+                                                           indexFormatVersion,
+                                                           nsMappings);
+                                selectParent = false;
+                            }
+                        } else {
+                            // otherwise the query can be used as is
+                        }
+                    }
+                } else if (name != null && name.equals(PATH_FACTORY.getParentElement().getName())) {
+                    // We need to select one of the properties if we haven't already.
+                    if (selectParent) { 
+                        query = new ParentAxisQuery(query, null,
+                                                    indexFormatVersion, nsMappings);
+
+                        selectParent = false;
+                    }
+
+                    // See the note above on searching parents
+                    query = new ChildAxisQuery(sharedItemMgr,
+                                               query,
+                                               null,
+                                               indexFormatVersion,
+                                               nsMappings);
+                } else {
+                    if (step instanceof LocationStepQueryNode) {
+                        query = new ParentAxisQuery(query, name, indexFormatVersion, nsMappings);
                     } else {
-                        // otherwise the query can be used as is
+                        throw new UnsupportedOperationException();
                     }
-                } else {
-                    query = new ParentAxisQuery(query, name,
-                            indexFormatVersion, nsMappings);
                 }
             }
             // finally select the parent of the selected nodes
-            query = new ParentAxisQuery(query, null,
-                    indexFormatVersion, nsMappings);
+            if (selectParent) {
+                query = new ParentAxisQuery(query, null, indexFormatVersion, nsMappings);
+            }
         }
 
         return query;
     }
+    
+    public Query createPredicateDeref(Query subQuery, DerefQueryNode node, Object data) throws RepositoryException {
+        Query context = (Query) data;
+        
+        if (context == null) {
+            exceptions.add(new IllegalArgumentException("Unsupported query"));
+        }
 
+        try {
+            String refProperty = resolver.getJCRName(node.getRefProperty());
+
+//            Query refPropQuery = Util.createMatchAllQuery(refProperty, indexFormatVersion);
+//            subQuery = new DescendantSelfAxisQuery(subQuery, refPropQuery, false);
+
+            context = new PredicateDerefQuery(subQuery, refProperty, node.getNameTest(),
+                    indexFormatVersion, nsMappings);
+
+            // attach predicates
+            Object[] predicates = node.acceptOperands(this, data);
+            if (predicates.length > 0) {
+                BooleanQuery andQuery = new BooleanQuery();
+                for (int i = 0; i < predicates.length; i++) {
+                    andQuery.add((Query) predicates[i], Occur.MUST);
+                }
+                andQuery.add(context, Occur.MUST);
+                context = andQuery;
+            }
+            
+        } catch (NamespaceException e) {
+            // should never happen
+            exceptions.add(e);
+        }
+
+        return context;
+    }
+
     public Object visit(OrderQueryNode node, Object data) {
         return data;
     }
Index: jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/LocationStepQueryNode.java
===================================================================
--- jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/LocationStepQueryNode.java	(revision 790368)
+++ jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/LocationStepQueryNode.java	(working copy)
@@ -46,7 +46,7 @@
      * TODO: The root location step should be refactored somehow
      */
     public static final Name EMPTY_NAME = NameFactoryImpl.getInstance().create("", "");
-
+    
     /** Empty QueryNode array for us as return value */
     private static final QueryNode[] EMPTY = new QueryNode[0];
 
Index: jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/xpath/XPathQueryBuilder.java
===================================================================
--- jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/xpath/XPathQueryBuilder.java	(revision 790368)
+++ jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/xpath/XPathQueryBuilder.java	(working copy)
@@ -424,7 +424,10 @@
                         if (tmpRelPath == null) {
                             tmpRelPath = new PathBuilder();
                         }
-                        tmpRelPath.addLast(tmp.getRelativePath().getNameElement());
+                        PathQueryNode relPath = tmp.getRelativePath();
+                        LocationStepQueryNode[] steps = relPath.getPathSteps();
+                        
+                        tmpRelPath.addLast(steps[steps.length-1].getNameTest());
                     }
                 }
                 break;
@@ -545,7 +548,11 @@
                 }
                 break;
             case JJTDOTDOT:
-                exceptions.add(new InvalidQueryException("Parent axis is not supported"));
+                if (queryNode instanceof LocationStepQueryNode) {
+                    ((LocationStepQueryNode) queryNode).setNameTest(PATH_FACTORY.getParentElement().getName());
+                } else {
+                    ((RelationQueryNode) queryNode).addPathElement(PATH_FACTORY.getParentElement());
+                }
                 break;
             default:
                 // per default traverse
@@ -943,47 +950,13 @@
                     }
                     if (queryNode.getType() == QueryNode.TYPE_PATH) {
                         PathQueryNode pathNode = (PathQueryNode) queryNode;
-                        DerefQueryNode derefNode = factory.createDerefQueryNode(pathNode, null, false);
-
-                        // assign property name
-                        node.jjtGetChild(1).jjtAccept(this, derefNode);
-                        // check property name
-                        if (derefNode.getRefProperty() == null) {
-                            exceptions.add(new InvalidQueryException("Wrong first argument type for jcr:deref"));
-                        }
-
-                        SimpleNode literal = (SimpleNode) node.jjtGetChild(2).jjtGetChild(0);
-                        if (literal.getId() == JJTSTRINGLITERAL) {
-                            String value = literal.getValue();
-                            // strip quotes
-                            value = value.substring(1, value.length() - 1);
-                            if (!value.equals("*")) {
-                                Name name = null;
-                                try {
-                                    name = decode(resolver.getQName(value));
-                                } catch (NameException e) {
-                                    exceptions.add(new InvalidQueryException("Illegal name: " + value));
-                                }
-                                derefNode.setNameTest(name);
-                            }
-                        } else {
-                            exceptions.add(new InvalidQueryException("Second argument for jcr:deref must be a String"));
-                        }
-
-                        // check if descendant
-                        if (!descendant) {
-                            Node p = node.jjtGetParent();
-                            for (int i = 0; i < p.jjtGetNumChildren(); i++) {
-                                SimpleNode c = (SimpleNode) p.jjtGetChild(i);
-                                if (c == node) {
-                                    break;
-                                }
-                                descendant = (c.getId() == JJTSLASHSLASH
-                                        || c.getId() == JJTROOTDESCENDANTS);
-                            }
-                        }
-                        derefNode.setIncludeDescendants(descendant);
-                        pathNode.addPathStep(derefNode);
+                        
+                        pathNode.addPathStep(createDerefQueryNode(node, descendant, pathNode));
+                    } else if (queryNode.getType() == QueryNode.TYPE_RELATION) {
+                        RelationQueryNode relNode = (RelationQueryNode) queryNode;
+                        DerefQueryNode deref = createDerefQueryNode(node, descendant, relNode.getTopQueryNode());
+                        
+                        relNode.addStep(deref);
                     } else {
                         exceptions.add(new InvalidQueryException("Unsupported location for jcr:deref()"));
                     }
@@ -1103,6 +1076,51 @@
         return queryNode;
     }
 
+    private DerefQueryNode createDerefQueryNode(SimpleNode node, boolean descendant, QueryNode pathNode)
+        throws NamespaceException {
+        DerefQueryNode derefNode = factory.createDerefQueryNode(pathNode, null, false);
+
+        // assign property name
+        node.jjtGetChild(1).jjtAccept(this, derefNode);
+        // check property name
+        if (derefNode.getRefProperty() == null) {
+            exceptions.add(new InvalidQueryException("Wrong first argument type for jcr:deref"));
+        }
+
+        SimpleNode literal = (SimpleNode) node.jjtGetChild(2).jjtGetChild(0);
+        if (literal.getId() == JJTSTRINGLITERAL) {
+            String value = literal.getValue();
+            // strip quotes
+            value = value.substring(1, value.length() - 1);
+            if (!value.equals("*")) {
+                Name name = null;
+                try {
+                    name = decode(resolver.getQName(value));
+                } catch (NameException e) {
+                    exceptions.add(new InvalidQueryException("Illegal name: " + value));
+                }
+                derefNode.setNameTest(name);
+            }
+        } else {
+            exceptions.add(new InvalidQueryException("Second argument for jcr:deref must be a String"));
+        }
+
+        // check if descendant
+        if (!descendant) {
+            Node p = node.jjtGetParent();
+            for (int i = 0; i < p.jjtGetNumChildren(); i++) {
+                SimpleNode c = (SimpleNode) p.jjtGetChild(i);
+                if (c == node) {
+                    break;
+                }
+                descendant = (c.getId() == JJTSLASHSLASH
+                        || c.getId() == JJTROOTDESCENDANTS);
+            }
+        }
+        derefNode.setIncludeDescendants(descendant);
+        return derefNode;
+    }
+
     private OrderQueryNode.OrderSpec createOrderSpec(SimpleNode node,
                                                      OrderQueryNode queryNode) {
         SimpleNode child = (SimpleNode) node.jjtGetChild(0);
Index: jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/xpath/QueryFormat.java
===================================================================
--- jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/xpath/QueryFormat.java	(revision 790368)
+++ jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/xpath/QueryFormat.java	(working copy)
@@ -345,29 +345,13 @@
 
             StringBuffer propPath = new StringBuffer();
             // only encode if not position function
-            Path relPath = node.getRelativePath();
+            PathQueryNode relPath = node.getRelativePath();
             if (relPath == null) {
                 propPath.append(".");
-            } else if (relPath.getNameElement().getName().equals(XPathQueryBuilder.FN_POSITION_FULL)) {
-                propPath.append(resolver.getJCRName(XPathQueryBuilder.FN_POSITION_FULL));
+//            } else if (relPath.getNameElement().getName().equals(XPathQueryBuilder.FN_POSITION_FULL)) {
+//                propPath.append(resolver.getJCRName(XPathQueryBuilder.FN_POSITION_FULL));
             } else {
-                Path.Element[] elements = relPath.getElements();
-                String slash = "";
-                for (int i = 0; i < elements.length; i++) {
-                    propPath.append(slash);
-                    slash = "/";
-                    if (i == elements.length - 1 && node.getOperation() != OPERATION_SIMILAR) {
-                        propPath.append("@");
-                    }
-                    if (elements[i].getName().equals(RelationQueryNode.STAR_NAME_TEST)) {
-                        propPath.append("*");
-                    } else {
-                        propPath.append(resolver.getJCRName(encode(elements[i].getName())));
-                    }
-                    if (elements[i].getIndex() != 0) {
-                        propPath.append("[").append(elements[i].getIndex()).append("]");
-                    }
-                }
+                visit(relPath, data);
             }
 
             // surround name with property function
Index: jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/DefaultQueryNodeFactory.java
===================================================================
--- jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/DefaultQueryNodeFactory.java	(revision 790368)
+++ jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/DefaultQueryNodeFactory.java	(working copy)
@@ -89,7 +89,7 @@
      */
     public RelationQueryNode createRelationQueryNode(QueryNode parent,
                                                      int operation) {
-        return new RelationQueryNode(parent, operation);
+        return new RelationQueryNode(parent, operation, this);
     }
 
     /**
Index: jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/RelationQueryNode.java
===================================================================
--- jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/RelationQueryNode.java	(revision 790368)
+++ jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/RelationQueryNode.java	(working copy)
@@ -22,10 +22,8 @@
 
 import org.apache.jackrabbit.spi.Name;
 import org.apache.jackrabbit.spi.Path;
-import org.apache.jackrabbit.spi.commons.conversion.MalformedPathException;
+import org.apache.jackrabbit.spi.Path.Element;
 import org.apache.jackrabbit.spi.commons.name.NameFactoryImpl;
-import org.apache.jackrabbit.spi.commons.name.PathBuilder;
-
 /**
  * Implements a query node that defines property value relation.
  */
@@ -41,7 +39,7 @@
     /**
      * The relative path to the property.
      */
-    private Path relPath;
+    private PathQueryNode relPath;
 
     /**
      * If true this relation query node contains a value preceded
@@ -90,16 +88,24 @@
      */
     private int type;
 
+    private final QueryNodeFactory factory;
+
+    private QueryNode top;
+
     /**
      * Creates a new RelationQueryNode without a type nor value
      * assigned.
      *
      * @param parent the parent node for this query node.
+     * @param defaultQueryNodeFactory 
      */
-    protected RelationQueryNode(QueryNode parent, int operation) {
+    protected RelationQueryNode(QueryNode parent, int operation, QueryNodeFactory factory) {
         super(parent);
         this.operation = operation;
-    }
+        this.factory = factory;
+        this.relPath = factory.createPathQueryNode(this);
+        this.top = relPath;
+    }           
 
     /**
      * {@inheritDoc}
@@ -147,29 +153,14 @@
      * @deprecated Use {@link #getRelativePath()} instead.
      */
     public Name getProperty() {
-        return relPath == null ? null : relPath.getNameElement().getName();
+        return top instanceof LocationStepQueryNode ? 
+            ((LocationStepQueryNode) top).getNameTest() : null;
     }
 
     /**
-     * Sets a new property name for this relation query node.
-     *
-     * @param name the new property name.
-     * @deprecated Use {@link #setRelativePath(Path)} instead.
-     */
-    public void setProperty(Name name) {
-        PathBuilder builder = new PathBuilder();
-        builder.addLast(name);
-        try {
-            this.relPath = builder.getPath();
-        } catch (MalformedPathException e) {
-            // path is always valid
-        }
-    }
-
-    /**
      * @return the relative path that references the property in this relation.
      */
-    public Path getRelativePath() {
+    public PathQueryNode getRelativePath() {
         return relPath;
     }
 
@@ -183,7 +174,11 @@
         if (relPath != null && relPath.isAbsolute()) {
             throw new IllegalArgumentException("relPath must be relative");
         }
-        this.relPath = relPath;
+        
+        Element[] elements = relPath.getElements();
+        for (int i = 0; i < elements.length; i++) {
+            addPathElement(elements[i]);
+        }
     }
 
     /**
@@ -193,25 +188,20 @@
      * @param element the path element to append.
      */
     public void addPathElement(Path.Element element) {
-        PathBuilder builder = new PathBuilder();
-        if (relPath != null) {
-            builder.addAll(relPath.getElements());
-        }
-        builder.addLast(element);
-        try {
-            relPath = builder.getPath();
-        }
-        catch (MalformedPathException e) {
-            // path is always valid
-        }
-        // try to normalize the path
-        try {
-          relPath = relPath.getNormalizedPath();
-        } catch (RepositoryException e) {
-            // just keep the original in that case
-        }
+        LocationStepQueryNode step = factory.createLocationStepQueryNode(top);
+        step.setNameTest(element.getName());
+        addStep(step);
     }
 
+    public void addStep(LocationStepQueryNode step) {
+        relPath.addPathStep(step);
+        top = step;
+    }
+    
+    public QueryNode getTopQueryNode() {
+        return top;
+    }
+
     /**
      * Returns the long value if this relation if of type
      * {@link #TYPE_LONG}.
@@ -336,4 +326,5 @@
         }
         return false;
     }
+
 }
Index: jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/QueryTreeDump.java
===================================================================
--- jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/QueryTreeDump.java	(revision 790368)
+++ jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/QueryTreeDump.java	(working copy)
@@ -205,14 +205,14 @@
         buffer.append(PADDING, 0, indent);
         buffer.append("+ RelationQueryNode: Op: ");
         buffer.append(QueryConstants.OPERATION_NAMES.getName(node.getOperation()));
-        buffer.append(" Prop=");
-        Path relPath = node.getRelativePath();
+        buffer.append(" Prop=[");
+        PathQueryNode relPath = node.getRelativePath();
         if (relPath == null) {
             buffer.append(relPath);
         } else {
-            appendPath(relPath, buffer);
+            visit(relPath, buffer);
         }
-        buffer.append(" Type=").append(QueryConstants.TYPE_NAMES.getName(node.getValueType()));
+        buffer.append("] Type=").append(QueryConstants.TYPE_NAMES.getName(node.getValueType()));
         if (node.getValueType() == QueryConstants.TYPE_DATE) {
             buffer.append(" Value=").append(node.getDateValue());
         } else if (node.getValueType() == QueryConstants.TYPE_DOUBLE) {
Index: jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/sql/QueryFormat.java
===================================================================
--- jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/sql/QueryFormat.java	(revision 790368)
+++ jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/query/sql/QueryFormat.java	(working copy)
@@ -406,14 +406,14 @@
         StringBuffer sb = (StringBuffer) data;
         try {
             StringBuffer propName = new StringBuffer();
-            Path relPath = node.getRelativePath();
+            PathQueryNode relPath = node.getRelativePath();
             if (relPath == null) {
                 propName.append(".");
-            } else if (relPath.getLength() > 1) {
+            } else if (relPath.getPathSteps().length > 1) {
                 exceptions.add(new InvalidQueryException("Child axis not supported in SQL"));
                 return data;
             } else {
-                appendName(relPath.getNameElement().getName(), resolver, propName);
+                visit(relPath, data);
             }
             // surround name with property function
             node.acceptOperands(this, propName);