Index: src/main/java/org/apache/jackrabbit/core/query/lucene/JackrabbitIndexSearcher.java
===================================================================
--- src/main/java/org/apache/jackrabbit/core/query/lucene/JackrabbitIndexSearcher.java	(revision 1154949)
+++ src/main/java/org/apache/jackrabbit/core/query/lucene/JackrabbitIndexSearcher.java	(working copy)
@@ -106,8 +106,8 @@
             if (sort.getSort().length == 0) {
                 hits = new LuceneQueryHits(reader, this, query);
             } else {
-                hits = new SortedLuceneQueryHits(
-                        reader, this, query, sort, resultFetchHint);
+                hits = new SortedLuceneQueryHits(this, query, sort,
+                        resultFetchHint);
             }
         }
         return hits;
Index: src/main/java/org/apache/jackrabbit/core/query/lucene/SharedFieldComparatorSource.java
===================================================================
--- src/main/java/org/apache/jackrabbit/core/query/lucene/SharedFieldComparatorSource.java	(revision 1154949)
+++ src/main/java/org/apache/jackrabbit/core/query/lucene/SharedFieldComparatorSource.java	(working copy)
@@ -34,8 +34,6 @@
 import org.apache.lucene.search.FieldComparatorSource;
 
 import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
 
 /**
  * Implements a <code>FieldComparatorSource</code> for <code>FieldComparator</code>s which
@@ -110,122 +108,6 @@
     }
 
     /**
-     * Abstract base class for <code>FieldComparator</code>s which keep their values
-     * (<code>Comparable</code>s) in an array.
-     */
-    private abstract static class AbstractFieldComparator extends FieldComparatorBase {
-
-        /**
-         * The values for comparing.
-         */
-        private final Comparable[] values;
-
-        /**
-         * The index readers.
-         */
-
-        protected final List<IndexReader> readers = new ArrayList<IndexReader>();
-        /**
-         * The document number starts for the {@link #readers}.
-         */
-        protected int[] starts;
-
-        /**
-         * Create a new instance with the given number of values.
-         *
-         * @param numHits  the number of values
-         */
-        protected AbstractFieldComparator(int numHits) {
-            values = new Comparable[numHits];
-        }
-
-        /**
-         * Returns the reader index for document <code>n</code>.
-         *
-         * @param n document number.
-         * @return the reader index.
-         */
-        protected final int readerIndex(int n) {
-            int lo = 0;
-            int hi = readers.size() - 1;
-
-            while (hi >= lo) {
-                int mid = (lo + hi) >> 1;
-                int midValue = starts[mid];
-                if (n < midValue) {
-                    hi = mid - 1;
-                }
-                else if (n > midValue) {
-                    lo = mid + 1;
-                }
-                else {
-                    while (mid + 1 < readers.size() && starts[mid + 1] == midValue) {
-                        mid++;
-                    }
-                    return mid;
-                }
-            }
-            return hi;
-        }
-
-        /**
-         * Add the given value to the values array
-         *
-         * @param slot   index into values
-         * @param value  value for adding
-         */
-        @Override
-        public void setValue(int slot, Comparable value) {
-            values[slot] = value;
-        }
-
-        /**
-         * Return a value from the values array
-         *
-         * @param slot  index to retrieve
-         * @return  the retrieved value
-         */
-        @Override
-        public Comparable getValue(int slot) {
-            return values[slot];
-        }
-
-        @Override
-        public void setNextReader(IndexReader reader, int docBase) throws IOException {
-            getIndexReaders(readers, reader);
-
-            int maxDoc = 0;
-            starts = new int[readers.size() + 1];
-
-            for (int i = 0; i < readers.size(); i++) {
-                IndexReader r = readers.get(i);
-                starts[i] = maxDoc;
-                maxDoc += r.maxDoc();
-            }
-            starts[readers.size()] = maxDoc;
-        }
-
-        /**
-         * Checks if <code>reader</code> is of type {@link MultiIndexReader} and if
-         * so calls itself recursively for each reader within the
-         * <code>MultiIndexReader</code> or otherwise adds the reader to the list.
-         *
-         * @param readers  list of index readers.
-         * @param reader   reader to decompose
-         */
-        private static void getIndexReaders(List<IndexReader> readers, IndexReader reader) {
-            if (reader instanceof MultiIndexReader) {
-                for (IndexReader r : ((MultiIndexReader) reader).getIndexReaders()) {
-                    getIndexReaders(readers, r);
-                }
-            }
-            else {
-                readers.add(reader);
-            }
-        }
-    }
-
-    /**
      * A <code>FieldComparator</code> which works for order by clauses with properties
      * directly on the result nodes.
      */
Index: src/test/java/org/apache/jackrabbit/core/query/SQL2OrderByTest.java
===================================================================
--- src/test/java/org/apache/jackrabbit/core/query/SQL2OrderByTest.java	(revision 0)
+++ src/test/java/org/apache/jackrabbit/core/query/SQL2OrderByTest.java	(revision 0)
@@ -0,0 +1,156 @@
+/*
+ * 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 static javax.jcr.query.Query.JCR_SQL2;
+
+import javax.jcr.Node;
+import javax.jcr.NodeIterator;
+import javax.jcr.RepositoryException;
+import javax.jcr.query.Query;
+import javax.jcr.query.QueryResult;
+
+import org.apache.jackrabbit.commons.JcrUtils;
+import org.apache.jackrabbit.core.query.lucene.join.QueryEngine;
+
+/**
+ * Tests queries with order by.
+ */
+public class SQL2OrderByTest extends AbstractQueryTest {
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        for (Node c : JcrUtils.getChildNodes(testRootNode)) {
+            testRootNode.getSession().removeItem(c.getPath());
+        }
+        testRootNode.getSession().save();
+        super.tearDown();
+    }
+
+    public void testOrderByScore() throws RepositoryException {
+        Node n1 = testRootNode.addNode("node1");
+        Node n2 = testRootNode.addNode("node2");
+        Node n3 = testRootNode.addNode("node3");
+
+        n1.setProperty("text", "aaa");
+        n1.setProperty("value", 3);
+        superuser.save();
+
+        n2.setProperty("text", "bbb");
+        n2.setProperty("value", 2);
+        superuser.save();
+
+        n3.setProperty("text", "ccc");
+        n3.setProperty("value", 2);
+        superuser.save();
+
+        QueryResult qr = executeSQL2Query("SELECT * FROM [nt:base] WHERE ISCHILDNODE(["
+                + testRoot + "]) ORDER BY [jcr:score]");
+        checkSeq(qr, new Node[] { n1, n2, n3 });
+
+    }
+
+    public void testOrderByVal() throws RepositoryException {
+        Node n1 = testRootNode.addNode("node1");
+        Node n2 = testRootNode.addNode("node2");
+        Node n3 = testRootNode.addNode("node3");
+
+        n1.setProperty("text", "aaa");
+        n1.setProperty("value", 3);
+        n2.setProperty("text", "bbb");
+        n2.setProperty("value", 2);
+        n3.setProperty("text", "ccc");
+        n3.setProperty("value", 1);
+
+        superuser.save();
+
+        QueryResult qr = executeSQL2Query("SELECT * FROM [nt:base] WHERE ISCHILDNODE(["
+                + testRoot + "]) ORDER BY [value]");
+        checkSeq(qr, new Node[] { n3, n2, n1 });
+
+    }
+
+    private void checkSeq(QueryResult qr, Node[] nodes)
+            throws RepositoryException {
+        NodeIterator ni = qr.getNodes();
+        for (Node n : nodes) {
+            assertTrue(ni.hasNext());
+            assertEquals(n.getPath(), ni.nextNode().getPath());
+        }
+    }
+
+    // TODO order by aggregate?
+
+    public void testBig() throws RepositoryException {
+        
+        QueryEngine.NATIVE_SORT = true;
+
+        // fill workspace
+        Node root = testRootNode.addNode("node_test_big", "nt:unstructured");
+        int num = createFiles(root, 10, 3, 0);
+        superuser.save();
+//        System.out.println("created " + num);
+
+        int takes = 5;
+
+        for (int i = 0; i < takes; i++) {
+            long t = System.currentTimeMillis();
+
+            Query q = qm.createQuery(
+                    "SELECT * FROM [nt:unstructured] WHERE ISDESCENDANTNODE(["
+                            + root.getPath() + "]) ORDER BY [value]", JCR_SQL2);
+//            q.setLimit(10);
+            QueryResult qr = q.execute();
+            t = System.currentTimeMillis() - t;
+            System.out.println("select took " + t + " ms");
+
+            t = System.currentTimeMillis();
+            int c = 0;
+            NodeIterator ni = qr.getNodes();
+            while (ni.hasNext()) {
+                Node n = ni.nextNode();
+                c++;
+            }
+        }
+    }
+
+    private int createFiles(Node folder, int filesPerLevel, int levels,
+            int count) throws RepositoryException {
+        levels--;
+        for (int i = 0; i < filesPerLevel; i++) {
+            // create files
+            Node n = folder.addNode("file" + i, "nt:unstructured");
+            n.setProperty("value", count);
+            n.setProperty("leaf", "1");
+            count++;
+        }
+        if (levels > 0) {
+            for (int i = 0; i < filesPerLevel; i++) {
+                // create files
+                Node subFolder = folder
+                        .addNode("folder" + i, "nt:unstructured");
+                count = createFiles(subFolder, filesPerLevel, levels, count);
+            }
+        }
+        return count;
+    }
+}
Index: src/main/java/org/apache/jackrabbit/core/query/lucene/LuceneQueryFactory.java
===================================================================
--- src/main/java/org/apache/jackrabbit/core/query/lucene/LuceneQueryFactory.java	(revision 1154949)
+++ src/main/java/org/apache/jackrabbit/core/query/lucene/LuceneQueryFactory.java	(working copy)
@@ -107,6 +107,7 @@
 import org.apache.lucene.search.BooleanClause.Occur;
 import org.apache.lucene.search.BooleanQuery;
 import org.apache.lucene.search.Query;
+import org.apache.lucene.search.Sort;
 
 /**
  * Factory that creates Lucene queries from QOM elements.
@@ -166,10 +167,30 @@
         this.primaryTypeField = nsMappings.translateName(JCR_PRIMARYTYPE);
     }
 
-    public List<Row> execute(
-            Map<String, PropertyValue> columns, Selector selector,
-            Constraint constraint) throws RepositoryException, IOException {
+    /**
+     * @param columns
+     * @param selector
+     * @param constraint
+     * @param externalSort
+     *            if <code>true</code> it means that the lqf should just let the
+     *            QueryEngine take care of sorting and applying applying offset
+     *            and limit constraints
+     * @param offsetIn
+     *            used in pagination
+     * @param limitIn
+     *            used in pagination
+     * @return a list of rows
+     * @throws RepositoryException
+     * @throws IOException
+     */
+    public List<Row> execute(Map<String, PropertyValue> columns,
+            Selector selector, Constraint constraint, Sort sort, boolean externalSort,
+            long offsetIn, long limitIn) throws RepositoryException,
+            IOException {
         final IndexReader reader = index.getIndexReader(true);
+        final int offset = offsetIn < 0 ? 0 : (int) offsetIn;
+        final int limit = limitIn < 0 ? Integer.MAX_VALUE : (int) limitIn;
+        
         QueryHits hits = null;
         try {
             JackrabbitIndexSearcher searcher = new JackrabbitIndexSearcher(
@@ -191,26 +212,48 @@
                         searcher, reader);
             }
 
+            // TODO depending on the filters, we could push the offset info
+            // into the searcher
+            hits = searcher.evaluate(qp.mainQuery, sort, offset + limit);
+
             List<Row> rows = new ArrayList<Row>();
-            hits = searcher.evaluate(qp.mainQuery);
+            int currentNode = 0;
+            int addedNodes = 0;
+
             ScoreNode node = hits.nextScoreNode();
             while (node != null) {
+                Row row = null;
                 try {
-                    Row row = new SelectorRow(
-                            columns, evaluator, selector.getSelectorName(),
+                    row = new SelectorRow(columns, evaluator,
+                            selector.getSelectorName(),
                             session.getNodeById(node.getNodeId()),
                             node.getScore());
-                    if (filter.evaluate(row)) {
-                        rows.add(row);
-                    }
                 } catch (ItemNotFoundException e) {
                     // skip the node
                 }
+                if (row != null && filter.evaluate(row)) {
+                    // apply limit and offset rules locally if there are no
+                    // order by clauses
+                    if (externalSort) {
+                        rows.add(row);
+                    } else {
+                        if (currentNode >= offset
+                                && currentNode - offset < limit) {
+                            rows.add(row);
+                            addedNodes++;
+                        }
+                        currentNode++;
+                        // end the loop when going over the limit
+                        if (addedNodes == limit) {
+                            break;
+                        }
+                    }
+                }
                 node = hits.nextScoreNode();
             }
             return rows;
         } finally {
-            if(hits != null){
+            if (hits != null) {
                 hits.close();
             }
             Util.closeOrRelease(reader);
Index: src/main/java/org/apache/jackrabbit/core/query/lucene/AbstractFieldComparator.java
===================================================================
--- src/main/java/org/apache/jackrabbit/core/query/lucene/AbstractFieldComparator.java	(revision 0)
+++ src/main/java/org/apache/jackrabbit/core/query/lucene/AbstractFieldComparator.java	(revision 0)
@@ -0,0 +1,139 @@
+/*
+ * 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.List;
+
+import org.apache.lucene.index.IndexReader;
+
+/**
+ * Abstract base class for <code>FieldComparator</code>s which keep their values
+ * (<code>Comparable</code>s) in an array.
+ */
+public abstract class AbstractFieldComparator extends FieldComparatorBase {
+
+    /**
+     * The values for comparing.
+     */
+    private final Comparable[] values;
+
+    /**
+     * The index readers.
+     */
+
+    protected final List<IndexReader> readers = new ArrayList<IndexReader>();
+    /**
+     * The document number starts for the {@link #readers}.
+     */
+    protected int[] starts;
+
+    /**
+     * Create a new instance with the given number of values.
+     *
+     * @param numHits  the number of values
+     */
+    protected AbstractFieldComparator(int numHits) {
+        values = new Comparable[numHits];
+    }
+
+    /**
+     * Returns the reader index for document <code>n</code>.
+     *
+     * @param n document number.
+     * @return the reader index.
+     */
+    protected final int readerIndex(int n) {
+        int lo = 0;
+        int hi = readers.size() - 1;
+
+        while (hi >= lo) {
+            int mid = (lo + hi) >> 1;
+            int midValue = starts[mid];
+            if (n < midValue) {
+                hi = mid - 1;
+            }
+            else if (n > midValue) {
+                lo = mid + 1;
+            }
+            else {
+                while (mid + 1 < readers.size() && starts[mid + 1] == midValue) {
+                    mid++;
+                }
+                return mid;
+            }
+        }
+        return hi;
+    }
+
+    /**
+     * Add the given value to the values array
+     *
+     * @param slot   index into values
+     * @param value  value for adding
+     */
+    @Override
+    public void setValue(int slot, Comparable value) {
+        values[slot] = value;
+    }
+
+    /**
+     * Return a value from the values array
+     *
+     * @param slot  index to retrieve
+     * @return  the retrieved value
+     */
+    @Override
+    public Comparable getValue(int slot) {
+        return values[slot];
+    }
+
+    @Override
+    public void setNextReader(IndexReader reader, int docBase) throws IOException {
+        getIndexReaders(readers, reader);
+
+        int maxDoc = 0;
+        starts = new int[readers.size() + 1];
+
+        for (int i = 0; i < readers.size(); i++) {
+            IndexReader r = readers.get(i);
+            starts[i] = maxDoc;
+            maxDoc += r.maxDoc();
+        }
+        starts[readers.size()] = maxDoc;
+    }
+
+    /**
+     * Checks if <code>reader</code> is of type {@link MultiIndexReader} and if
+     * so calls itself recursively for each reader within the
+     * <code>MultiIndexReader</code> or otherwise adds the reader to the list.
+     *
+     * @param readers  list of index readers.
+     * @param reader   reader to decompose
+     */
+    private static void getIndexReaders(List<IndexReader> readers, IndexReader reader) {
+        if (reader instanceof MultiIndexReader) {
+            for (IndexReader r : ((MultiIndexReader) reader).getIndexReaders()) {
+                getIndexReaders(readers, r);
+            }
+        }
+        else {
+            readers.add(reader);
+        }
+    }
+}
\ No newline at end of file
Index: src/main/java/org/apache/jackrabbit/core/query/lucene/join/QueryEngine.java
===================================================================
--- src/main/java/org/apache/jackrabbit/core/query/lucene/join/QueryEngine.java	(revision 1154949)
+++ src/main/java/org/apache/jackrabbit/core/query/lucene/join/QueryEngine.java	(working copy)
@@ -25,12 +25,15 @@
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Iterator;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.TreeSet;
 
+import javax.jcr.Node;
 import javax.jcr.RepositoryException;
 import javax.jcr.Session;
 import javax.jcr.UnsupportedRepositoryOperationException;
@@ -51,15 +54,27 @@
 import javax.jcr.query.qom.Operand;
 import javax.jcr.query.qom.Ordering;
 import javax.jcr.query.qom.PropertyValue;
+import javax.jcr.query.qom.QueryObjectModelConstants;
 import javax.jcr.query.qom.QueryObjectModelFactory;
 import javax.jcr.query.qom.Selector;
 import javax.jcr.query.qom.Source;
 
 import org.apache.commons.io.IOUtils;
+import org.apache.jackrabbit.JcrConstants;
 import org.apache.jackrabbit.commons.JcrUtils;
 import org.apache.jackrabbit.commons.iterator.RowIteratorAdapter;
 import org.apache.jackrabbit.commons.query.qom.OperandEvaluator;
+import org.apache.jackrabbit.core.query.lucene.AbstractFieldComparator;
+import org.apache.jackrabbit.core.query.lucene.FieldNames;
+import org.apache.jackrabbit.core.query.lucene.FieldSelectors;
 import org.apache.jackrabbit.core.query.lucene.LuceneQueryFactory;
+import org.apache.jackrabbit.spi.commons.query.qom.DynamicOperandImpl;
+import org.apache.lucene.document.Document;
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.search.FieldComparator;
+import org.apache.lucene.search.FieldComparatorSource;
+import org.apache.lucene.search.Sort;
+import org.apache.lucene.search.SortField;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -133,10 +148,12 @@
 
     private final OperandEvaluator evaluator;
 
+    private final Session session;
+
     public QueryEngine(Session session, LuceneQueryFactory lqf,
             Map<String, Value> variables) throws RepositoryException {
         this.lqf = lqf;
-
+        this.session = session;
         Workspace workspace = session.getWorkspace();
         this.ntManager = workspace.getNodeTypeManager();
         this.qomFactory = workspace.getQueryManager().getQOMFactory();
@@ -251,7 +268,7 @@
             return new SimpleQueryResult(merger.getColumnNames(),
                     merger.getSelectorNames(), new RowIteratorAdapter(allRows));
         }
-        
+
         Set<Row> leftRows = buildLeftRowsJoin(csInfo, leftCo, printIndentation
                 + printIndentStep);
         if (log.isDebugEnabled()) {
@@ -469,9 +486,119 @@
         return sb.toString();
     }
 
+    private static final class DynamicOperandFieldComparatorSource extends
+            FieldComparatorSource {
+
+        private static final long serialVersionUID = 1L;
+
+        private final Session session;
+        private final OperandEvaluator evaluator;
+        private Map<String, Ordering> orderByProperties;
+
+        public DynamicOperandFieldComparatorSource(final Session session,
+                final OperandEvaluator evaluator,
+                final Map<String, Ordering> orderByProperties) {
+            this.session = session;
+            this.evaluator = evaluator;
+            this.orderByProperties = orderByProperties;
+        }
+
+        @Override
+        public FieldComparator newComparator(String fieldname, int numHits,
+                int sortPos, boolean reversed) throws IOException {
+            return new DynamicOperandFieldComparator(fieldname, session,
+                    evaluator, orderByProperties.get(fieldname), numHits,
+                    reversed);
+        }
+    }
+
+    private static final class DynamicOperandFieldComparator extends
+            AbstractFieldComparator {
+
+        private final String fieldname;
+        private final Session session;
+        private final OperandEvaluator evaluator;
+        private final Ordering ordering;
+        private final boolean reversed;
+
+        public DynamicOperandFieldComparator(final String fieldname,
+                final Session session, final OperandEvaluator evaluator,
+                final Ordering ordering, int numHits, final boolean reversed) {
+            super(numHits);
+            this.fieldname = fieldname;
+            this.session = session;
+            this.evaluator = evaluator;
+            this.ordering = ordering;
+            this.reversed = reversed;
+        }
+
+        @Override
+        protected Comparable sortValue(int doc) {
+            try {
+                int idx = readerIndex(doc);
+                IndexReader reader = readers.get(idx);
+                Document document = reader.document(doc - starts[idx],
+                        FieldSelectors.UUID);
+
+                // TODO if the property is present at lucene level, we could
+                // just fetch that one instead
+                final String uuid = document.get(FieldNames.UUID);
+                // TODO check uuid integrity
+                // final NodeId id = new NodeId(uuid);
+                final Node n = session.getNodeByIdentifier(uuid);
+                Value[] v = evaluator.getValues(ordering.getOperand(), n);
+                return new ValueComparableWrapper(v, reversed);
+            } catch (Exception e) {
+
+            }
+            return null;
+        }
+    }
+
+    private static class ValueComparableWrapper implements
+            Comparable<ValueComparableWrapper> {
+        private final ValueComparator comparator = new ValueComparator();
+
+        private final Value[] v;
+        private final boolean reversed;
+
+        public ValueComparableWrapper(final Value[] v, final boolean reversed) {
+            this.v = v;
+            this.reversed = reversed;
+        }
+
+        public Value[] getValue() {
+            return v;
+        }
+
+        public int compareTo(ValueComparableWrapper o) {
+            final int d = compare(v, o.getValue());
+            if (d != 0) {
+                if (reversed) {
+                    return -d;
+                }
+                return -d;
+            }
+            return 0;
+        }
+
+        private int compare(Value[] a, Value[] b) {
+            for (int i = 0; i < a.length && i < b.length; i++) {
+                int d = comparator.compare(a[i], b[i]);
+                if (d != 0) {
+                    return d;
+                }
+            }
+            return a.length - b.length;
+        }
+    }
+    
+    public static boolean NATIVE_SORT = true;
+
     protected QueryResult execute(Column[] columns, Selector selector,
             Constraint constraint, Ordering[] orderings, long offset,
             long limit, int printIndentation) throws RepositoryException {
+
         long time = System.currentTimeMillis();
 
         Map<String, NodeType> selectorMap = getSelectorNames(selector);
@@ -483,23 +610,86 @@
         String[] columnNames = columnMap.keySet().toArray(
                 new String[columnMap.size()]);
 
+        Sort sort = new Sort();
+        if (NATIVE_SORT) {
+            sort = new Sort(createSortFields(orderings));
+        }
+
+        // if true it means that the LuceneQueryFactory should just let the
+        // QueryEngine take care of sorting and applying offset and limit
+        // constraints
+        boolean externalSort = !NATIVE_SORT;
+        RowIterator rows = null;
         try {
-            RowIterator rows = new RowIteratorAdapter(lqf.execute(columnMap,
-                    selector, constraint));
-            QueryResult result = new SimpleQueryResult(columnNames,
-                    selectorNames, rows);
-            return sort(result, orderings, evaluator, offset, limit);
+            rows = new RowIteratorAdapter(lqf.execute(columnMap, selector,
+                    constraint, sort, externalSort, offset, limit));
         } catch (IOException e) {
             throw new RepositoryException("Failed to access the query index", e);
         } finally {
-            if (log.isDebugEnabled()) {
-                time = System.currentTimeMillis() - time;
-                log.debug(genString(printIndentation) + "SQL2 SELECT took "
-                        + time + " ms. selector: " + selector + ", columns: "
-                        + Arrays.toString(columnNames) + ", constraint: "
-                        + constraint);
+            log.debug(
+                    "{}SQL2 SELECT took {} ms. selector: {}, columns: {}, constraint: {}, offset {}, limit {}",
+                    new Object[] { genString(printIndentation),
+                            System.currentTimeMillis() - time, selector,
+                            Arrays.toString(columnNames), constraint, offset,
+                            limit });
+        }
+        QueryResult result = new SimpleQueryResult(columnNames, selectorNames,
+                rows);
+        if (!externalSort) {
+            return result;
+        }
+
+        long timeSort = System.currentTimeMillis();
+        QueryResult sorted = sort(result, orderings, evaluator, offset, limit);
+        log.debug("{}SQL2 SORT took {} ms.", genString(printIndentation),
+                System.currentTimeMillis() - timeSort);
+        return sorted;
+    }
+
+    protected SortField[] createSortFields(Ordering[] orderings) {
+
+        if (orderings == null || orderings.length == 0) {
+            return new SortField[] { SortField.FIELD_SCORE };
+        }
+
+        // orderings[] -> (property, ordering)
+        Map<String, Ordering> orderByProperties = new HashMap<String, Ordering>();
+        for (Ordering o : orderings) {
+            if (o.getOperand() instanceof DynamicOperandImpl) {
+                final String p = evaluator.getAffectedPropertyName(o
+                        .getOperand());
+                if (p == null) {
+                    continue;
+                }
+                if (!orderByProperties.containsKey(p)) {
+                    orderByProperties.put(p, o);
+                }
+            }
+        }
+        if (orderByProperties.isEmpty()) {
+            return new SortField[] { SortField.FIELD_SCORE };
+        }
+        final DynamicOperandFieldComparatorSource dofcs = new DynamicOperandFieldComparatorSource(
+                session, evaluator, orderByProperties);
+
+        List<SortField> sortFields = new ArrayList<SortField>();
+        Iterator<String> iterator = orderByProperties.keySet().iterator();
+        while (iterator.hasNext()) {
+            String p = iterator.next();
+            Ordering o = orderByProperties.get(p);
+            // order on jcr:score does not use the natural order as
+            // implemented in lucene. score ascending in lucene means that
+            // higher scores are first. JCR specs that lower score values
+            // are first.
+            boolean isAsc = QueryObjectModelConstants.JCR_ORDER_ASCENDING
+                    .equals(o.getOrder());
+            if (JcrConstants.JCR_SCORE.equals(p)) {
+                sortFields.add(new SortField(null, SortField.SCORE, !isAsc));
+            } else {
+                sortFields.add(new SortField(p, dofcs, isAsc));
             }
         }
+        return sortFields.toArray(new SortField[sortFields.size()]);
     }
 
     private Map<String, PropertyValue> getColumnMap(Column[] columns,
Index: src/main/java/org/apache/jackrabbit/core/query/lucene/SortedLuceneQueryHits.java
===================================================================
--- src/main/java/org/apache/jackrabbit/core/query/lucene/SortedLuceneQueryHits.java	(revision 1154949)
+++ src/main/java/org/apache/jackrabbit/core/query/lucene/SortedLuceneQueryHits.java	(working copy)
@@ -16,8 +16,10 @@
  */
 package org.apache.jackrabbit.core.query.lucene;
 
+import java.io.IOException;
+
 import org.apache.jackrabbit.core.id.NodeId;
-import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.search.IndexSearcher;
 import org.apache.lucene.search.Query;
 import org.apache.lucene.search.ScoreDoc;
 import org.apache.lucene.search.Sort;
@@ -25,11 +27,6 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
 /**
  * Wraps a lucene query result and adds a close method that allows to release
  * resources after a query has been executed and the results have been read
@@ -53,14 +50,9 @@
     private static final int MIN_FETCH_SIZE = 32;
 
     /**
-     * The IndexReader in use by the lucene hits.
-     */
-    private final IndexReader reader;
-
-    /**
      * The index searcher.
      */
-    private final JackrabbitIndexSearcher searcher;
+    private final IndexSearcher searcher;
 
     /**
      * The query to execute.
@@ -80,7 +72,7 @@
     /**
      * The score docs.
      */
-    private final List<ScoreDoc> scoreDocs = new ArrayList<ScoreDoc>();
+    private ScoreDoc[] scoreDocs = new ScoreDoc[0];
 
     /**
      * The total number of hits.
@@ -88,26 +80,30 @@
     private int size;
 
     /**
-     * Number of hits to retrieve.
+     * Number of hits to be pre-fetched from the lucene index (will be around 2x
+     * resultFetchHint).
      */
     private int numHits;
 
+    private int offset = 0;
+
     /**
      * Creates a new <code>QueryHits</code> instance wrapping <code>hits</code>.
-     *
-     * @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.
+     * 
+     * @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 pre-fetched from the
+     *            lucene index.
+     * @throws IOException
+     *             if an error occurs while reading from the index.
      */
-    public SortedLuceneQueryHits(IndexReader reader,
-                                 JackrabbitIndexSearcher searcher,
-                                 Query query,
-                                 Sort sort,
-                                 long resultFetchHint) throws IOException {
-        this.reader = reader;
+    public SortedLuceneQueryHits(IndexSearcher searcher, Query query,
+            Sort sort, long resultFetchHint) throws IOException {
         this.searcher = searcher;
         this.query = query;
         this.sort = sort;
@@ -131,13 +127,13 @@
         if (++hitIndex >= size) {
             // no more score nodes
             return null;
-        } else if (hitIndex >= scoreDocs.size()) {
+        } else if (hitIndex - offset >= scoreDocs.length) {
             // refill at least numHits or twice hitIndex
             this.numHits = Math.max(this.numHits, hitIndex * 2);
             getHits();
         }
-        ScoreDoc doc = scoreDocs.get(hitIndex);
-        String uuid = reader.document(doc.doc,
+        ScoreDoc doc = scoreDocs[hitIndex - offset];
+        String uuid = searcher.doc(doc.doc,
                 FieldSelectors.UUID).get(FieldNames.UUID);
         NodeId id = new NodeId(uuid);
         return new ScoreNode(id, doc.score, doc.doc);
@@ -159,11 +155,10 @@
         TopFieldCollector collector = TopFieldCollector.create(sort, numHits, false, true, false, false);
         searcher.search(query, collector);
         size = collector.getTotalHits();
-        ScoreDoc[] docs = collector.topDocs().scoreDocs;
-        scoreDocs.addAll(Arrays.asList(docs).subList(scoreDocs.size(), docs.length));
-        log.debug("getHits() {}/{}", scoreDocs.size(), numHits);
+        offset += scoreDocs.length;
+        scoreDocs = collector.topDocs(offset, numHits).scoreDocs;
+        log.debug("getHits() {}/{}", scoreDocs.length, numHits);
         // double hits for next round
         numHits *= 2;
     }
-
 }
