You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by mr...@apache.org on 2005/07/08 16:44:42 UTC

svn commit: r209797 - in /incubator/jackrabbit/trunk/core/src: java/org/apache/jackrabbit/core/query/lucene/ test/org/apache/jackrabbit/core/query/

Author: mreutegg
Date: Fri Jul  8 07:44:41 2005
New Revision: 209797

URL: http://svn.apache.org/viewcvs?rev=209797&view=rev
Log:
JCR-111: org.apache.lucene.search.BooleanQuery$TooManyClauses when using '>' operator

Added:
    incubator/jackrabbit/trunk/core/src/java/org/apache/jackrabbit/core/query/lucene/RangeQuery.java   (with props)
    incubator/jackrabbit/trunk/core/src/test/org/apache/jackrabbit/core/query/MassiveRangeTest.java   (with props)
Modified:
    incubator/jackrabbit/trunk/core/src/java/org/apache/jackrabbit/core/query/lucene/LuceneQueryBuilder.java
    incubator/jackrabbit/trunk/core/src/test/org/apache/jackrabbit/core/query/TestAll.java

Modified: incubator/jackrabbit/trunk/core/src/java/org/apache/jackrabbit/core/query/lucene/LuceneQueryBuilder.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/core/src/java/org/apache/jackrabbit/core/query/lucene/LuceneQueryBuilder.java?rev=209797&r1=209796&r2=209797&view=diff
==============================================================================
--- incubator/jackrabbit/trunk/core/src/java/org/apache/jackrabbit/core/query/lucene/LuceneQueryBuilder.java (original)
+++ incubator/jackrabbit/trunk/core/src/java/org/apache/jackrabbit/core/query/lucene/LuceneQueryBuilder.java Fri Jul  8 07:44:41 2005
@@ -47,7 +47,6 @@
 import org.apache.lucene.queryParser.ParseException;
 import org.apache.lucene.search.BooleanQuery;
 import org.apache.lucene.search.Query;
-import org.apache.lucene.search.RangeQuery;
 import org.apache.lucene.search.TermQuery;
 import org.apache.xerces.util.XMLChar;
 

Added: incubator/jackrabbit/trunk/core/src/java/org/apache/jackrabbit/core/query/lucene/RangeQuery.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/core/src/java/org/apache/jackrabbit/core/query/lucene/RangeQuery.java?rev=209797&view=auto
==============================================================================
--- incubator/jackrabbit/trunk/core/src/java/org/apache/jackrabbit/core/query/lucene/RangeQuery.java (added)
+++ incubator/jackrabbit/trunk/core/src/java/org/apache/jackrabbit/core/query/lucene/RangeQuery.java Fri Jul  8 07:44:41 2005
@@ -0,0 +1,325 @@
+/*
+ * Copyright 2004-2005 The Apache Software Foundation or its licensors,
+ *                     as applicable.
+ *
+ * Licensed 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 org.apache.lucene.search.Query;
+import org.apache.lucene.search.Weight;
+import org.apache.lucene.search.Searcher;
+import org.apache.lucene.search.Scorer;
+import org.apache.lucene.search.Explanation;
+import org.apache.lucene.search.Similarity;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.TermEnum;
+import org.apache.lucene.index.TermDocs;
+
+import java.io.IOException;
+import java.util.BitSet;
+
+/**
+ * Implements a variant of the lucene class {@link org.apache.lucene.search.RangeQuery}.
+ * This class does not rewrite to basic {@link org.apache.lucene.search.TermQuery}
+ * but will calculate the matching documents itself. That way a
+ * <code>TooManyClauses</code> can be avoided.
+ */
+public class RangeQuery extends Query {
+
+    /**
+     * The lower term. May be <code>null</code> if <code>upperTerm</code> is not
+     * <code>null</code>.
+     */
+    private Term lowerTerm;
+
+    /**
+     * The upper term. May be <code>null</code> if <code>lowerTerm</code> is not
+     * <code>null</code>.
+     */
+    private Term upperTerm;
+
+    /**
+     * If <code>true</code> the range interval is inclusive.
+     */
+    private boolean inclusive;
+
+    /**
+     * Creates a new RangeQuery. The lower or the upper term may be
+     * <code>null</code>, but not both!
+     *
+     * @param lowerTerm the lower term of the interval, or <code>null</code>
+     * @param upperTerm the upper term of the interval, or <code>null</code>.
+     * @param inclusive if <code>true</code> the interval is inclusive.
+     */
+    public RangeQuery(Term lowerTerm, Term upperTerm, boolean inclusive) {
+        if (lowerTerm == null && upperTerm == null) {
+            throw new IllegalArgumentException("At least one term must be non-null");
+        }
+        if (lowerTerm != null && upperTerm != null && lowerTerm.field() != upperTerm.field()) {
+            throw new IllegalArgumentException("Both terms must be for the same field");
+        }
+
+        // if we have a lowerTerm, start there. otherwise, start at beginning
+        if (lowerTerm != null) {
+            this.lowerTerm = lowerTerm;
+        } else {
+            this.lowerTerm = new Term(upperTerm.field(), "");
+        }
+
+        this.upperTerm = upperTerm;
+        this.inclusive = inclusive;
+    }
+
+    /**
+     * Creates the <code>Weight</code> for this query.
+     *
+     * @param searcher the searcher to use for the <code>Weight</code>.
+     * @return the <code>Weigth</code> for this query.
+     */
+    protected Weight createWeight(Searcher searcher) {
+        return new RangeQueryWeight(searcher);
+    }
+
+    /**
+     * Returns a string representation of this query.
+     * @param field the field name for which to create a string representation.
+     * @return a string representation of this query.
+     */
+    public String toString(String field) {
+        StringBuffer buffer = new StringBuffer();
+        if (!getField().equals(field)) {
+            buffer.append(getField());
+            buffer.append(":");
+        }
+        buffer.append(inclusive ? "[" : "{");
+        buffer.append(lowerTerm != null ? lowerTerm.text() : "null");
+        buffer.append(" TO ");
+        buffer.append(upperTerm != null ? upperTerm.text() : "null");
+        buffer.append(inclusive ? "]" : "}");
+        if (getBoost() != 1.0f) {
+            buffer.append("^");
+            buffer.append(Float.toString(getBoost()));
+        }
+        return buffer.toString();
+    }
+
+    /**
+     * Returns the field name for this query.
+     */
+    private String getField() {
+        return (lowerTerm != null ? lowerTerm.field() : upperTerm.field());
+    }
+
+    //--------------------------< RangeQueryWeight >----------------------------
+
+    /**
+     * The <code>Weight</code> implementation for this <code>RangeQuery</code>.
+     */
+    private class RangeQueryWeight implements Weight {
+
+        /**
+         * The searcher in use
+         */
+        private final Searcher searcher;
+
+        /**
+         * Creates a new <code>RangeQueryWeight</code> instance using
+         * <code>searcher</code>.
+         *
+         * @param searcher a <code>Searcher</code> instance.
+         */
+        RangeQueryWeight(Searcher searcher) {
+            this.searcher = searcher;
+        }
+
+        /**
+         * Returns this <code>RangeQuery</code>.
+         *
+         * @return this <code>RangeQuery</code>.
+         */
+        public Query getQuery() {
+            return RangeQuery.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 <code>Rangequery</code>.
+         *
+         * @param reader a reader for accessing the index.
+         * @return a <code>RangeQueryScorer</code>.
+         * @throws IOException if an error occurs while reading from the index.
+         */
+        public Scorer scorer(IndexReader reader) throws IOException {
+            return new RangeQueryScorer(searcher.getSimilarity(), reader);
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public Explanation explain(IndexReader reader, int doc) throws IOException {
+            return new Explanation();
+        }
+    }
+
+    //------------------------< RangeQueryScorer >------------------------------
+
+    /**
+     * Implements a <code>Scorer</code> for this <code>RangeQuery</code>.
+     */
+    private final class RangeQueryScorer extends Scorer {
+
+        /**
+         * The index reader to use for calculating the matching documents.
+         */
+        private final IndexReader reader;
+
+        /**
+         * The documents ids that match this range query.
+         */
+        private final BitSet hits;
+
+        /**
+         * Set to <code>true</code> when the hits have been calculated.
+         */
+        private boolean hitsCalculated = false;
+
+        /**
+         * The next document id to return
+         */
+        private int nextDoc = -1;
+
+        /**
+         * Creates a new RangeQueryScorer.
+         * @param similarity the similarity implementation.
+         * @param reader the index reader to use.
+         */
+        RangeQueryScorer(Similarity similarity, IndexReader reader) {
+            super(similarity);
+            this.reader = reader;
+            hits = new BitSet(reader.maxDoc());
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public boolean next() throws IOException {
+            calculateHits();
+            nextDoc = hits.nextSetBit(nextDoc + 1);
+            return nextDoc > -1;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public int doc() {
+            return nextDoc;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public float score() {
+            return 1.0f;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public boolean skipTo(int target) {
+            nextDoc = hits.nextSetBit(target);
+            return nextDoc > -1;
+        }
+
+        /**
+         * Returns an empty Explanation object.
+         * @return an empty Explanation object.
+         */
+        public Explanation explain(int doc) {
+            return new Explanation();
+        }
+
+        /**
+         * Calculates the ids of the documents matching this range query.
+         * @throws IOException if an error occurs while reading from the index.
+         */
+        private void calculateHits() throws IOException {
+            if (hitsCalculated) {
+                return;
+            }
+
+            TermEnum enumerator = reader.terms(lowerTerm);
+
+            try {
+                boolean checkLower = false;
+                if (!inclusive) {
+                    // make adjustments to set to exclusive
+                    checkLower = true;
+                }
+
+                String testField = getField();
+
+                do {
+                    Term term = enumerator.term();
+                    if (term != null && term.field() == testField) {
+                        if (!checkLower || term.text().compareTo(lowerTerm.text()) > 0) {
+                            checkLower = false;
+                            if (upperTerm != null) {
+                                int compare = upperTerm.text().compareTo(term.text());
+                                // if beyond the upper term, or is exclusive and
+                                // this is equal to the upper term, break out
+                                if ((compare < 0) || (!inclusive && compare == 0)) {
+                                    break;
+                                }
+                            }
+
+                            TermDocs td = reader.termDocs(term);
+                            try {
+                                while (td.next()) {
+                                    hits.set(td.doc());
+                                }
+                            } finally {
+                                td.close();
+                            }
+                        }
+                    } else {
+                        break;
+                    }
+                } while (enumerator.next());
+            } finally {
+                enumerator.close();
+            }
+            hitsCalculated = true;
+        }
+    }
+}

Propchange: incubator/jackrabbit/trunk/core/src/java/org/apache/jackrabbit/core/query/lucene/RangeQuery.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/jackrabbit/trunk/core/src/test/org/apache/jackrabbit/core/query/MassiveRangeTest.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/core/src/test/org/apache/jackrabbit/core/query/MassiveRangeTest.java?rev=209797&view=auto
==============================================================================
--- incubator/jackrabbit/trunk/core/src/test/org/apache/jackrabbit/core/query/MassiveRangeTest.java (added)
+++ incubator/jackrabbit/trunk/core/src/test/org/apache/jackrabbit/core/query/MassiveRangeTest.java Fri Jul  8 07:44:41 2005
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2004-2005 The Apache Software Foundation or its licensors,
+ *                     as applicable.
+ *
+ * Licensed 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.RepositoryException;
+import javax.jcr.Node;
+import javax.jcr.query.QueryManager;
+import javax.jcr.query.Query;
+import javax.jcr.query.QueryResult;
+
+/**
+ * Tests if a range query with a lot of results does not throw an error.
+ */
+public class MassiveRangeTest extends AbstractQueryTest {
+
+    /**
+     * Executes a range query covering 2'000 different property values.
+     */
+    public void testRangeQuery() throws RepositoryException {
+        int count = 0;
+        for (int i = 0; i < 20; i++) {
+            Node child = testRootNode.addNode("node" + i);
+            for (int j = 0; j < 100; j++) {
+                Node n = child.addNode("node" + j);
+                n.setProperty("foo", count++);
+            }
+            // save every 100 nodes
+            testRootNode.save();
+        }
+
+        QueryManager qm = superuser.getWorkspace().getQueryManager();
+        String stmt = testPath + "//*[@foo >= 0]";
+        QueryResult res = qm.createQuery(stmt, Query.XPATH).execute();
+        checkResult(res, 2000);
+    }
+}

Propchange: incubator/jackrabbit/trunk/core/src/test/org/apache/jackrabbit/core/query/MassiveRangeTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: incubator/jackrabbit/trunk/core/src/test/org/apache/jackrabbit/core/query/TestAll.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/core/src/test/org/apache/jackrabbit/core/query/TestAll.java?rev=209797&r1=209796&r2=209797&view=diff
==============================================================================
--- incubator/jackrabbit/trunk/core/src/test/org/apache/jackrabbit/core/query/TestAll.java (original)
+++ incubator/jackrabbit/trunk/core/src/test/org/apache/jackrabbit/core/query/TestAll.java Fri Jul  8 07:44:41 2005
@@ -43,6 +43,7 @@
         suite.addTestSuite(OrderByTest.class);
         suite.addTestSuite(XPathAxisTest.class);
         suite.addTestSuite(SkipDeletedNodesTest.class);
+        suite.addTestSuite(MassiveRangeTest.class);
 
         return suite;
     }