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);