Index: src/main/java/org/apache/jackrabbit/core/query/lucene/ChildAxisQuery.java =================================================================== --- src/main/java/org/apache/jackrabbit/core/query/lucene/ChildAxisQuery.java (revision 759047) +++ src/main/java/org/apache/jackrabbit/core/query/lucene/ChildAxisQuery.java (working copy) @@ -264,7 +264,7 @@ throws IOException { if (sort.getSort().length == 0 && matchesAnyChildNode()) { Query context = getContextQuery(); - return new ChildNodesQueryHits(searcher.evaluate(context, sort), session); + return new ChildNodesQueryHits(searcher.evaluate(context), session); } else { return null; } Index: src/main/java/org/apache/jackrabbit/core/query/lucene/DescendantSelfAxisQuery.java =================================================================== --- src/main/java/org/apache/jackrabbit/core/query/lucene/DescendantSelfAxisQuery.java (revision 759047) +++ src/main/java/org/apache/jackrabbit/core/query/lucene/DescendantSelfAxisQuery.java (working copy) @@ -229,7 +229,7 @@ if (sort.getSort().length == 0 && subQueryMatchesAll()) { // maps path String to NodeId Map startingPoints = new TreeMap(); - QueryHits result = searcher.evaluate(getContextQuery(), sort); + QueryHits result = searcher.evaluate(getContextQuery()); try { // minLevels 0 and 1 are handled with a series of // NodeTraversingQueryHits directly on result. For minLevels >= 2 Index: src/main/java/org/apache/jackrabbit/core/query/lucene/JackrabbitIndexSearcher.java =================================================================== --- src/main/java/org/apache/jackrabbit/core/query/lucene/JackrabbitIndexSearcher.java (revision 759047) +++ src/main/java/org/apache/jackrabbit/core/query/lucene/JackrabbitIndexSearcher.java (working copy) @@ -55,25 +55,31 @@ /** * Executes the query and returns the hits that match the query. * - * @param query the query to execute. - * @param sort the sort criteria. + * @param query the query to execute. + * @param sort the sort criteria. + * @param resultFetchHint a hint on how many results should be fetched. * @return the query hits. * @throws IOException if an error occurs while executing the query. */ - public MultiColumnQueryHits execute(Query query, Sort sort) throws IOException { - return new QueryHitsAdapter(evaluate(query, sort), + public MultiColumnQueryHits execute(Query query, + Sort sort, + long resultFetchHint) + throws IOException { + return new QueryHitsAdapter(evaluate(query, sort, resultFetchHint), QueryImpl.DEFAULT_SELECTOR_NAME); } /** * Evaluates the query and returns the hits that match the query. * - * @param query the query to execute. - * @param sort the sort criteria. + * @param query the query to execute. + * @param sort the sort criteria. + * @param resultFetchHint a hint on how many results should be fetched. * @return the query hits. * @throws IOException if an error occurs while executing the query. */ - public QueryHits evaluate(Query query, Sort sort) throws IOException { + public QueryHits evaluate(Query query, Sort sort, long resultFetchHint) + throws IOException { query = query.rewrite(reader); QueryHits hits = null; if (query instanceof JackrabbitQuery) { @@ -83,9 +89,21 @@ if (sort == null) { hits = new LuceneQueryHits(reader, this, query); } else { - hits = new SortedLuceneQueryHits(reader, this, query, sort); + hits = new SortedLuceneQueryHits( + reader, this, query, sort, resultFetchHint); } } return hits; } + + /** + * Evaluates the query and returns the hits that match the query. + * + * @param query the query to execute. + * @return the query hits. + * @throws IOException if an error occurs while executing the query. + */ + public QueryHits evaluate(Query query) throws IOException { + return evaluate(query, new Sort(), Integer.MAX_VALUE); + } } Index: src/main/java/org/apache/jackrabbit/core/query/lucene/JackrabbitQuery.java =================================================================== --- src/main/java/org/apache/jackrabbit/core/query/lucene/JackrabbitQuery.java (revision 759047) +++ src/main/java/org/apache/jackrabbit/core/query/lucene/JackrabbitQuery.java (working copy) @@ -17,6 +17,7 @@ package org.apache.jackrabbit.core.query.lucene; import org.apache.lucene.search.Sort; +import org.apache.lucene.search.Query; import org.apache.jackrabbit.core.SessionImpl; import java.io.IOException; @@ -34,7 +35,7 @@ * this query should be executed using the regular Lucene API. *
* Important note: an implementation must not call - * {@link JackrabbitIndexSearcher#execute(org.apache.lucene.search.Query, Sort)} + * {@link JackrabbitIndexSearcher#execute(Query, Sort, long)} * with this query instance as a parameter, otherwise a stack overflow will * occur. * Index: src/main/java/org/apache/jackrabbit/core/query/lucene/QueryResultImpl.java =================================================================== --- src/main/java/org/apache/jackrabbit/core/query/lucene/QueryResultImpl.java (revision 759047) +++ src/main/java/org/apache/jackrabbit/core/query/lucene/QueryResultImpl.java (working copy) @@ -237,12 +237,13 @@ * Executes the query for this result and returns hits. The caller must * close the query hits when he is done using it. * + * @param resultFetchHint a hint on how many results should be fetched. * @return hits for this query result. * @throws IOException if an error occurs while executing the query. */ - protected MultiColumnQueryHits executeQuery() throws IOException { + protected MultiColumnQueryHits executeQuery(long resultFetchHint) throws IOException { return index.executeQuery(session, queryImpl, - query, orderProps, orderSpecs); + query, orderProps, orderSpecs, resultFetchHint); } //--------------------------------< internal >------------------------------ @@ -291,7 +292,7 @@ MultiColumnQueryHits result = null; try { long time = System.currentTimeMillis(); - result = executeQuery(); + result = executeQuery(maxResultSize); log.debug("query executed in {} ms", new Long(System.currentTimeMillis() - time)); Index: src/main/java/org/apache/jackrabbit/core/query/lucene/SearchIndex.java =================================================================== --- src/main/java/org/apache/jackrabbit/core/query/lucene/SearchIndex.java (revision 759047) +++ src/main/java/org/apache/jackrabbit/core/query/lucene/SearchIndex.java (working copy) @@ -730,21 +730,25 @@ /** * Executes the query on the search index. - * @param session the session that executes the query. - * @param queryImpl the query impl. - * @param query the lucene query. - * @param orderProps name of the properties for sort order. - * @param orderSpecs the order specs for the sort order properties. - *true indicates ascending order, false indicates
- * descending.
+ *
+ * @param session the session that executes the query.
+ * @param queryImpl the query impl.
+ * @param query the lucene query.
+ * @param orderProps name of the properties for sort order.
+ * @param orderSpecs the order specs for the sort order properties.
+ * true indicates ascending order,
+ * false indicates descending.
+ * @param resultFetchHint a hint on how many results should be fetched.
* @return the query hits.
* @throws IOException if an error occurs while searching the index.
*/
public MultiColumnQueryHits executeQuery(SessionImpl session,
- AbstractQueryImpl queryImpl,
- Query query,
- Path[] orderProps,
- boolean[] orderSpecs) throws IOException {
+ AbstractQueryImpl queryImpl,
+ Query query,
+ Path[] orderProps,
+ boolean[] orderSpecs,
+ long resultFetchHint)
+ throws IOException {
checkOpen();
Sort sort = new Sort(createSortFields(orderProps, orderSpecs));
@@ -752,7 +756,8 @@
final IndexReader reader = getIndexReader(queryImpl.needsSystemTree());
JackrabbitIndexSearcher searcher = new JackrabbitIndexSearcher(session, reader);
searcher.setSimilarity(getSimilarity());
- return new FilterMultiColumnQueryHits(searcher.execute(query, sort)) {
+ return new FilterMultiColumnQueryHits(
+ searcher.execute(query, sort, resultFetchHint)) {
public void close() throws IOException {
try {
super.close();
Index: src/main/java/org/apache/jackrabbit/core/query/lucene/SortedLuceneQueryHits.java
===================================================================
--- src/main/java/org/apache/jackrabbit/core/query/lucene/SortedLuceneQueryHits.java (revision 759047)
+++ src/main/java/org/apache/jackrabbit/core/query/lucene/SortedLuceneQueryHits.java (working copy)
@@ -23,6 +23,8 @@
import org.apache.lucene.index.IndexReader;
import org.apache.jackrabbit.core.NodeId;
import org.apache.jackrabbit.uuid.UUID;
+import org.slf4j.LoggerFactory;
+import org.slf4j.Logger;
import java.io.IOException;
import java.util.List;
@@ -36,6 +38,21 @@
public final class SortedLuceneQueryHits extends AbstractQueryHits {
/**
+ * The Logger instance for this class.
+ */
+ private static final Logger log = LoggerFactory.getLogger(SortedLuceneQueryHits.class);
+
+ /**
+ * The upper limit for the initial fetch size.
+ */
+ private static final int MAX_FETCH_SIZE = 32 * 1024;
+
+ /**
+ * The lower limit for the initial fetch size.
+ */
+ private static final int MIN_FETCH_SIZE = 32;
+
+ /**
* The IndexReader in use by the lucene hits.
*/
private final IndexReader reader;
@@ -73,25 +90,30 @@
/**
* Number of hits to retrieve.
*/
- private int numHits = 50;
+ private int numHits;
/**
* Creates a new QueryHits instance wrapping hits.
*
- * @param reader the IndexReader in use.
- * @param searcher the index searcher.
- * @param query the query to execute.
- * @param sort the sort criteria.
+ * @param reader the IndexReader in use.
+ * @param searcher the index searcher.
+ * @param query the query to execute.
+ * @param sort the sort criteria.
+ * @param resultFetchHint a hint on how many results should be fetched.
* @throws IOException if an error occurs while reading from the index.
*/
public SortedLuceneQueryHits(IndexReader reader,
JackrabbitIndexSearcher searcher,
Query query,
- Sort sort) throws IOException {
+ Sort sort,
+ long resultFetchHint) throws IOException {
this.reader = reader;
this.searcher = searcher;
this.query = query;
this.sort = sort;
+ this.numHits = (int) Math.min(
+ Math.max(resultFetchHint, MIN_FETCH_SIZE),
+ MAX_FETCH_SIZE);
getHits();
}
@@ -110,7 +132,8 @@
// no more score nodes
return null;
} else if (hitIndex >= scoreNodes.size()) {
- // refill
+ // refill at least numHits or twice hitIndex
+ this.numHits = Math.max(this.numHits, hitIndex * 2);
getHits();
}
return (ScoreNode) scoreNodes.get(hitIndex);
@@ -128,20 +151,18 @@
//-------------------------------< internal >-------------------------------
- private int getHits() throws IOException {
- // double hits
- numHits *= 2;
+ private void getHits() throws IOException {
TopFieldDocCollector collector = new TopFieldDocCollector(reader, sort, numHits);
searcher.search(query, collector);
this.size = collector.getTotalHits();
ScoreDoc[] docs = collector.topDocs().scoreDocs;
- int num = 0;
for (int i = scoreNodes.size(); i < docs.length; i++) {
String uuid = reader.document(docs[i].doc).get(FieldNames.UUID);
NodeId id = new NodeId(UUID.fromString(uuid));
scoreNodes.add(new ScoreNode(id, docs[i].score));
- num++;
}
- return num;
+ log.debug("getHits() {}/{}", new Integer(scoreNodes.size()), new Integer(numHits));
+ // double hits for next round
+ numHits *= 2;
}
}
Index: src/test/java/org/apache/jackrabbit/core/integration/daily/DailyIntegrationTest.java
===================================================================
--- src/test/java/org/apache/jackrabbit/core/integration/daily/DailyIntegrationTest.java (revision 759047)
+++ src/test/java/org/apache/jackrabbit/core/integration/daily/DailyIntegrationTest.java (working copy)
@@ -29,6 +29,7 @@
import org.apache.jackrabbit.core.ConcurrentVersioningWithTransactionsTest;
import org.apache.jackrabbit.core.LockTest;
import org.apache.jackrabbit.core.ReadVersionsWhileModified;
+import org.apache.jackrabbit.core.query.LargeResultSetTest;
import org.apache.jackrabbit.core.lock.ConcurrentLockingTest;
import org.apache.jackrabbit.core.lock.ConcurrentLockingWithTransactionsTest;
@@ -56,6 +57,7 @@
suite.addTestSuite(ReadVersionsWhileModified.class);
suite.addTestSuite(ConcurrentLockingTest.class);
suite.addTestSuite(ConcurrentLockingWithTransactionsTest.class);
+ suite.addTestSuite(LargeResultSetTest.class);
return suite;
}
Index: src/test/java/org/apache/jackrabbit/core/query/AbstractIndexingTest.java
===================================================================
--- src/test/java/org/apache/jackrabbit/core/query/AbstractIndexingTest.java (revision 759047)
+++ src/test/java/org/apache/jackrabbit/core/query/AbstractIndexingTest.java (working copy)
@@ -39,15 +39,9 @@
protected void tearDown() throws Exception {
cleanUpTestRoot(session);
+ session.logout();
session = null;
testRootNode = null;
super.tearDown();
}
-
- /**
- * @return the query handler inside the {@link #qm query manager}.
- */
- protected QueryHandler getQueryHandler() {
- return ((QueryManagerImpl) qm).getQueryHandler();
- }
}
Index: src/test/java/org/apache/jackrabbit/core/query/AbstractQueryTest.java
===================================================================
--- src/test/java/org/apache/jackrabbit/core/query/AbstractQueryTest.java (revision 759047)
+++ src/test/java/org/apache/jackrabbit/core/query/AbstractQueryTest.java (working copy)
@@ -250,4 +250,11 @@
return qm.createQuery(statement, Query.XPATH).execute();
}
}
+
+ /**
+ * @return the query handler inside the {@link #qm query manager}.
+ */
+ protected QueryHandler getQueryHandler() {
+ return ((QueryManagerImpl) qm).getQueryHandler();
+ }
}
Index: src/test/java/org/apache/jackrabbit/core/query/LargeResultSetTest.java
===================================================================
--- src/test/java/org/apache/jackrabbit/core/query/LargeResultSetTest.java (revision 0)
+++ src/test/java/org/apache/jackrabbit/core/query/LargeResultSetTest.java (revision 0)
@@ -0,0 +1,84 @@
+/*
+ * 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.RepositoryException;
+import javax.jcr.Node;
+import javax.jcr.NodeIterator;
+import javax.jcr.query.Query;
+import javax.jcr.query.RowIterator;
+import javax.jcr.query.QueryResult;
+
+import org.apache.jackrabbit.core.query.lucene.SearchIndex;
+
+/**
+ * LargeResultSetTest...
+ */
+public class LargeResultSetTest extends AbstractQueryTest {
+
+ public void testResultSet() throws RepositoryException {
+ createNodes(testRootNode, 10, 5, 0);
+ superuser.save();
+
+ SearchIndex index = (SearchIndex) getQueryHandler();
+ int resultFetchSize = index.getResultFetchSize();
+ try {
+ String stmt = testPath + "//*[@" + jcrPrimaryType + "] order by @jcr:score descending";
+
+ // with result fetch size Integer.MAX_VALUE
+ readResult(executeQuery(stmt));
+
+ // with result fetch size 100
+ index.setResultFetchSize(100);
+ readResult(executeQuery(stmt));
+
+ // with 100 limit
+ QueryImpl query = (QueryImpl) qm.createQuery(stmt, Query.XPATH);
+ query.setLimit(100);
+ readResult(query.execute());
+ } finally {
+ index.setResultFetchSize(resultFetchSize);
+ }
+
+ for (NodeIterator it = testRootNode.getNodes(); it.hasNext(); ) {
+ it.nextNode().remove();
+ superuser.save();
+ }
+ }
+
+ private void readResult(QueryResult result) throws RepositoryException {
+ for (RowIterator rows = result.getRows(); rows.hasNext(); ) {
+ rows.nextRow();
+ }
+ }
+
+ private int createNodes(Node n, int nodesPerLevel, int levels, int count)
+ throws RepositoryException {
+ levels--;
+ for (int i = 0; i < nodesPerLevel; i++) {
+ Node child = n.addNode("node" + i);
+ count++;
+ if (count % 10000 == 0) {
+ superuser.save();
+ }
+ if (levels > 0) {
+ count = createNodes(child, nodesPerLevel, levels, count);
+ }
+ }
+ return count;
+ }
+}
Property changes on: src\test\java\org\apache\jackrabbit\core\query\LargeResultSetTest.java
___________________________________________________________________
Added: svn:eol-style
+ native