Index: ../../../git/apache/jackrabbit-oak/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndex.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- ../../../git/apache/jackrabbit-oak/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndex.java	(revision ae7cb0810d192de626c5964346041b7a0ae7a146)
+++ ../../../git/apache/jackrabbit-oak/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndex.java	(revision )
@@ -52,14 +52,19 @@
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.Deque;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Queue;
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicReference;
 
 import javax.jcr.PropertyType;
 
+import com.google.common.collect.AbstractIterator;
+import com.google.common.collect.Queues;
+import com.google.common.collect.Sets;
 import org.apache.jackrabbit.oak.api.PropertyValue;
 import org.apache.jackrabbit.oak.plugins.index.aggregate.NodeAggregator;
 import org.apache.jackrabbit.oak.plugins.index.lucene.util.MoreLikeThisHelper;
@@ -163,6 +168,8 @@
 
     private final NodeAggregator aggregator;
 
+    public static final int LUCENE_QUERY_BATCH_SIZE = 10;
+
     public LuceneIndex(Analyzer analyzer, NodeAggregator aggregator) {
         this.analyzer = analyzer;
         this.aggregator = aggregator;
@@ -341,7 +348,7 @@
     }
 
     @Override
-    public Cursor query(Filter filter, NodeState root) {
+    public Cursor query(final Filter filter, final NodeState root) {
         if (!isLive(root)) {
             throw new IllegalStateException("Lucene index is not live");
         }
@@ -350,36 +357,39 @@
         if (relPaths.size() > 1) {
             return new MultiLuceneIndex(filter, root, relPaths).query();
         }
-        String parent = relPaths.size() == 0 ? "" : relPaths.iterator().next();
+        final String parent = relPaths.size() == 0 ? "" : relPaths.iterator().next();
+        final int parentDepth = getDepth(parent);
         // we only restrict non-full-text conditions if there is
         // no relative property in the full-text constraint
-        boolean nonFullTextConstraints = parent.isEmpty();
-        Directory directory = newDirectory(root);
+        final boolean nonFullTextConstraints = parent.isEmpty();
+        final Directory directory = newDirectory(root);
         QueryEngineSettings settings = filter.getQueryEngineSettings();
         if (directory == null) {
             return newPathCursor(Collections.<String> emptySet(), settings);
         }
-        long s = System.currentTimeMillis();
         try {
             try {
-                IndexReader reader = DirectoryReader.open(directory);
-                try {
-                    IndexSearcher searcher = new IndexSearcher(reader);
-                    List<LuceneResultRow> rows = new ArrayList<LuceneResultRow>();
-                    Query query = getQuery(filter, reader,
-                            nonFullTextConstraints, analyzer, getIndexDef(root));
+                Iterator<LuceneResultRow> itr = new AbstractIterator<LuceneResultRow>() {
+                    private final Deque<ScoreDoc> queue = Queues.newArrayDeque();
+                    private final Set<String> seenPaths = Sets.newHashSet();
+                    private ScoreDoc lastDoc;
 
-                    // TODO OAK-828
-                    HashSet<String> seenPaths = new HashSet<String>();
-                    int parentDepth = getDepth(parent);
-                    if (query != null) {
-                        // OAK-925
-                        // TODO how to best avoid loading all entries in memory?
-                        // (memory problem and performance problem)
-                        TopDocs docs = searcher
-                                .search(query, Integer.MAX_VALUE);
-                        for (ScoreDoc doc : docs.scoreDocs) {
-                            String path = reader.document(doc.doc,
+                    @Override
+                    protected LuceneResultRow computeNext() {
+                        while (!queue.isEmpty() || loadDocs()) {
+                            LuceneResultRow row = convertToRow(queue.remove());
+                            if (row != null) {
+                                return row;
+                            }
+                        }
+                        return endOfData();
+                    }
+
+                    private LuceneResultRow convertToRow(ScoreDoc doc) {
+                        IndexSearcher searcher = null;
+                        try {
+                            searcher = acquire();
+                            String path = searcher.getIndexReader().document(doc.doc,
                                     PATH_SELECTOR).get(PATH);
                             if (path != null) {
                                 if ("".equals(path)) {
@@ -396,7 +406,7 @@
                                     path = getAncestorPath(path, parentDepth);
                                     // avoid duplicate entries
                                     if (seenPaths.contains(path)) {
-                                        continue;
+                                        return null;
                                     }
                                     seenPaths.add(path);
                                 }
@@ -404,22 +414,69 @@
                                 LuceneResultRow r = new LuceneResultRow();
                                 r.path = path;
                                 r.score = doc.score;
-                                rows.add(r);
+                                return r;
                             }
+                        } catch (IOException e) {
+                            LOG.warn("query via {} failed.", LuceneIndex.this, e);
+                        } finally {
+                            release(searcher);
                         }
+                        return null;
                     }
-                    LOG.debug("query via {} took {} ms.", this,
-                            System.currentTimeMillis() - s);
-                    return new LucenePathCursor(rows, settings);
+
+                    private void release(IndexSearcher searcher) {
+                        try {
+                            if (searcher != null) {
+                                searcher.getIndexReader().close();
+                            }
+                        } catch (IOException e) {
+                            LOG.warn("query via {} failed.", LuceneIndex.this, e);
+                        }
+                    }
+
+                    /**
+                     * Loads the lucene documents in batches
+                     * @return true if any document is loaded
+                     */
+                    private boolean loadDocs() {
+                        IndexSearcher searcher = null;
+                        try {
+                            searcher = acquire();
+                            Query query = getQuery(filter, searcher.getIndexReader(),
+                                    nonFullTextConstraints, analyzer, getIndexDef(root));
+                            TopDocs docs = null;
+                            if (lastDoc != null) {
+                                docs = searcher.searchAfter(lastDoc, query, LUCENE_QUERY_BATCH_SIZE);
+                            } else {
+                                docs = searcher.search(query, LUCENE_QUERY_BATCH_SIZE);
+                            }
+
+                            for (ScoreDoc doc : docs.scoreDocs) {
+                                queue.add(doc);
+                            }
+                        } catch (IOException e) {
+                            LOG.warn("query via {} failed.", LuceneIndex.this, e);
-                } finally {
+                        } finally {
-                    reader.close();
+                            release(searcher);
-                }
+                        }
+                        if (!queue.isEmpty()) {
+                            lastDoc = queue.getLast();
+                        }
+                        return !queue.isEmpty();
+                    }
+
+                    private IndexSearcher acquire() throws IOException {
+                        IndexReader reader = DirectoryReader.open(directory);
+                        return new IndexSearcher(reader);
+                    }
+                };
+                return new LucenePathCursor(itr, settings);
             } finally {
                 directory.close();
             }
         } catch (IOException e) {
             LOG.warn("query via {} failed.", this, e);
-            return newPathCursor(Collections.<String> emptySet(), settings);
+            return newPathCursor(Collections.<String>emptySet(), settings);
         }
     }
 
@@ -937,10 +994,7 @@
         private final Cursor pathCursor;
         LuceneResultRow currentRow;
         
-        LucenePathCursor(List<LuceneResultRow> list, QueryEngineSettings settings) {
-            
-            final Iterator<LuceneResultRow> it = list.iterator();
-            
+        LucenePathCursor(final Iterator<LuceneResultRow> it, QueryEngineSettings settings) {
             Iterator<String> pathIterator = new Iterator<String>() {
 
                 @Override
