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