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 2008/11/21 15:45:20 UTC

svn commit: r719592 - in /jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene: ChildAxisQuery.java DescendantSelfAxisQuery.java

Author: mreutegg
Date: Fri Nov 21 06:45:20 2008
New Revision: 719592

URL: http://svn.apache.org/viewvc?rev=719592&view=rev
Log:
JCR-1872: Improve performance of simple path queries

Modified:
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/ChildAxisQuery.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/DescendantSelfAxisQuery.java

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/ChildAxisQuery.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/ChildAxisQuery.java?rev=719592&r1=719591&r2=719592&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/ChildAxisQuery.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/ChildAxisQuery.java Fri Nov 21 06:45:20 2008
@@ -40,6 +40,8 @@
 import org.apache.lucene.search.Similarity;
 import org.apache.lucene.search.Weight;
 import org.apache.lucene.search.Sort;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import java.io.IOException;
 import java.util.Iterator;
@@ -47,6 +49,8 @@
 import java.util.Set;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.HashSet;
+import java.util.ArrayList;
 
 /**
  * Implements a lucene <code>Query</code> which returns the child nodes of the
@@ -55,6 +59,17 @@
 class ChildAxisQuery extends Query implements JackrabbitQuery {
 
     /**
+     * The logger instance for this class.
+     */
+    private static final Logger log = LoggerFactory.getLogger(ChildAxisQuery.class);
+
+    /**
+     * Threshold when children calculation is switched to
+     * {@link HierarchyResolvingChildrenCalculator}.
+     */
+    private static int CONTEXT_SIZE_THRESHOLD = 10;
+
+    /**
      * The item state manager containing persistent item states.
      */
     private final ItemStateManager itemMgr;
@@ -222,13 +237,20 @@
     }
 
     /**
-     * Always returns 'ChildAxisQuery'.
-     *
-     * @param field the name of a field.
-     * @return 'ChildAxisQuery'.
+     * {@inheritDoc}
      */
     public String toString(String field) {
-        return "ChildAxisQuery";
+        StringBuffer sb = new StringBuffer();
+        sb.append("ChildAxisQuery(");
+        sb.append(contextQuery);
+        sb.append(", ");
+        sb.append(nameTest);
+        if (position != LocationStepQueryNode.NONE) {
+            sb.append(", ");
+            sb.append(position);
+        }
+        sb.append(")");
+        return sb.toString();
     }
 
     //-------------------< JackrabbitQuery >------------------------------------
@@ -416,57 +438,42 @@
         private void calculateChildren() throws IOException {
             if (hits == null) {
 
-                // collect all context nodes
-                Map uuids = new HashMap();
-                final Hits contextHits = new AdaptingHits();
-                contextScorer.score(new HitCollector() {
-                    public void collect(int doc, float score) {
-                        contextHits.set(doc);
-                    }
-                });
-
-                // read the uuids of the context nodes
-                for (int i = contextHits.next(); i > -1; i = contextHits.next()) {
-                    String uuid = reader.document(i, FieldSelectors.UUID).get(FieldNames.UUID);
-                    uuids.put(new Integer(i), uuid);
-                }
-
-                // collect all children of the context nodes
-                Hits childrenHits = new AdaptingHits();
-                if (nameTestScorer != null) {
-                    Hits nameHits = new ScorerHits(nameTestScorer);
-                    for (int h = nameHits.next(); h > -1; h = nameHits.next()) {
-                        if (uuids.containsKey(new Integer(hResolver.getParent(h)))) {
-                            childrenHits.set(h);
+                final ChildrenCalculator[] calc = new ChildrenCalculator[1];
+                if (nameTestScorer == null) {
+                    // always use simple in that case
+                    calc[0] = new SimpleChildrenCalculator(reader, hResolver);
+                    contextScorer.score(new HitCollector() {
+                        public void collect(int doc, float score) {
+                            calc[0].collectContextHit(doc);
                         }
-                    }
+                    });
                 } else {
-                    // get child node entries for each hit
-                    for (Iterator it = uuids.values().iterator(); it.hasNext(); ) {
-                        String uuid = (String) it.next();
-                        NodeId id = new NodeId(UUID.fromString(uuid));
-                        try {
-                            NodeState state = (NodeState) itemMgr.getItemState(id);
-                            Iterator entries = state.getChildNodeEntries().iterator();
-                            while (entries.hasNext()) {
-                                NodeId childId = ((ChildNodeEntry) entries.next()).getId();
-                                Term uuidTerm = new Term(FieldNames.UUID, childId.getUUID().toString());
-                                TermDocs docs = reader.termDocs(uuidTerm);
-                                try {
-                                    if (docs.next()) {
-                                        childrenHits.set(docs.doc());
+                    // start simple but switch once threshold is reached
+                    calc[0] = new SimpleChildrenCalculator(reader, hResolver);
+                    contextScorer.score(new HitCollector() {
+
+                        private List docIds = new ArrayList();
+
+                        public void collect(int doc, float score) {
+                            calc[0].collectContextHit(doc);
+                            if (docIds != null) {
+                                docIds.add(new Integer(doc));
+                                if (docIds.size() > CONTEXT_SIZE_THRESHOLD) {
+                                    // switch
+                                    calc[0] = new HierarchyResolvingChildrenCalculator(
+                                            reader, hResolver);
+                                    for (Iterator it = docIds.iterator(); it.hasNext(); ) {
+                                        calc[0].collectContextHit(((Integer) it.next()).intValue());
                                     }
-                                } finally {
-                                    docs.close();
+                                    // indicate that we switched
+                                    docIds = null;
                                 }
                             }
-                        } catch (ItemStateException e) {
-                            // does not exist anymore -> ignore
                         }
-                    }
+                    });
                 }
 
-                hits = childrenHits;
+                hits = calc[0].getHits();
             }
         }
 
@@ -537,4 +544,172 @@
             return true;
         }
     }
+
+    /**
+     * Base class to calculate the children for a context query.
+     */
+    private abstract class ChildrenCalculator {
+
+        /**
+         * The current index reader.
+         */
+        protected final IndexReader reader;
+
+        /**
+         * The current hierarchy resolver.
+         */
+        protected final HierarchyResolver hResolver;
+
+        /**
+         * Creates a new children calculator with the given index reader and
+         * hierarchy resolver.
+         *
+         * @param reader the current index reader.
+         * @param hResolver the current hierarchy resolver.
+         */
+        public ChildrenCalculator(IndexReader reader,
+                                  HierarchyResolver hResolver) {
+            this.reader = reader;
+            this.hResolver = hResolver;
+        }
+
+        /**
+         * Collects a context hit.
+         *
+         * @param doc the lucene document number of the context hit.
+         */
+        protected abstract void collectContextHit(int doc);
+
+        /**
+         * @return the hits that contains the children.
+         * @throws IOException if an error occurs while reading from the index.
+         */
+        public abstract Hits getHits() throws IOException;
+    }
+
+    /**
+     * An implementation of a children calculator using the item state manager.
+     */
+    private final class SimpleChildrenCalculator extends ChildrenCalculator {
+
+        /**
+         * The context hits.
+         */
+        private final Hits contextHits = new AdaptingHits();
+
+        /**
+         * Creates a new simple children calculator.
+         *
+         * @param reader the current index reader.
+         * @param hResolver the current hierarchy resolver.
+         */
+        public SimpleChildrenCalculator(IndexReader reader,
+                                        HierarchyResolver hResolver) {
+            super(reader, hResolver);
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        protected void collectContextHit(int doc) {
+            contextHits.set(doc);
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public Hits getHits() throws IOException {
+            // read the uuids of the context nodes
+            Map uuids = new HashMap();
+            for (int i = contextHits.next(); i > -1; i = contextHits.next()) {
+                String uuid = reader.document(i, FieldSelectors.UUID).get(FieldNames.UUID);
+                uuids.put(new Integer(i), uuid);
+            }
+
+            // get child node entries for each hit
+            Hits childrenHits = new AdaptingHits();
+            for (Iterator it = uuids.values().iterator(); it.hasNext(); ) {
+                String uuid = (String) it.next();
+                NodeId id = new NodeId(UUID.fromString(uuid));
+                try {
+                    long time = System.currentTimeMillis();
+                    NodeState state = (NodeState) itemMgr.getItemState(id);
+                    time = System.currentTimeMillis() - time;
+                    log.debug("got NodeState with id {} in {} ms.", id, new Long(time));
+                    Iterator entries;
+                    if (nameTest != null) {
+                        entries = state.getChildNodeEntries(nameTest).iterator();
+                    } else {
+                        // get all children
+                        entries = state.getChildNodeEntries().iterator();
+                    }
+                    while (entries.hasNext()) {
+                        NodeId childId = ((ChildNodeEntry) entries.next()).getId();
+                        Term uuidTerm = new Term(FieldNames.UUID, childId.getUUID().toString());
+                        TermDocs docs = reader.termDocs(uuidTerm);
+                        try {
+                            if (docs.next()) {
+                                childrenHits.set(docs.doc());
+                            }
+                        } finally {
+                            docs.close();
+                        }
+                    }
+                } catch (ItemStateException e) {
+                    // does not exist anymore -> ignore
+                }
+            }
+            return childrenHits;
+        }
+    }
+
+    /**
+     * An implementation of a children calculator that uses the hierarchy
+     * resolver. This implementation requires that
+     * {@link ChildAxisQuery#nameTestScorer} is non null.
+     */
+    private final class HierarchyResolvingChildrenCalculator
+            extends ChildrenCalculator {
+
+        /**
+         * The document numbers of the context hits.
+         */
+        private final Set docIds = new HashSet();
+
+        /**
+         * Creates a new hierarchy resolving children calculator.
+         *
+         * @param reader the current index reader.
+         * @param hResolver the current hierarchy resolver.
+         */
+        public HierarchyResolvingChildrenCalculator(IndexReader reader,
+                                                    HierarchyResolver hResolver) {
+            super(reader, hResolver);
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        protected void collectContextHit(int doc) {
+            docIds.add(new Integer(doc));
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public Hits getHits() throws IOException {
+            long time = System.currentTimeMillis();
+            Hits childrenHits = new AdaptingHits();
+            Hits nameHits = new ScorerHits(nameTestScorer);
+            for (int h = nameHits.next(); h > -1; h = nameHits.next()) {
+                if (docIds.contains(new Integer(hResolver.getParent(h)))) {
+                    childrenHits.set(h);
+                }
+            }
+            time = System.currentTimeMillis() - time;
+
+            log.debug("Filtered hits in {} ms.", new Long(time));
+            return childrenHits;
+        }
+    }
 }

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/DescendantSelfAxisQuery.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/DescendantSelfAxisQuery.java?rev=719592&r1=719591&r2=719592&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/DescendantSelfAxisQuery.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/DescendantSelfAxisQuery.java Fri Nov 21 06:45:20 2008
@@ -26,6 +26,8 @@
 import org.apache.lucene.search.Weight;
 import org.apache.lucene.search.Sort;
 import org.apache.jackrabbit.core.SessionImpl;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import javax.jcr.Node;
 import javax.jcr.RepositoryException;
@@ -44,6 +46,11 @@
 class DescendantSelfAxisQuery extends Query implements JackrabbitQuery {
 
     /**
+     * The logger instance for this class.
+     */
+    private static final Logger log = LoggerFactory.getLogger(DescendantSelfAxisQuery.class);
+
+    /**
      * The context query
      */
     private final Query contextQuery;
@@ -169,13 +176,18 @@
     }
 
     /**
-     * Always returns 'DescendantSelfAxisQuery'.
-     *
-     * @param field the name of a field.
-     * @return 'DescendantSelfAxisQuery'.
+     * {@inheritDoc}
      */
     public String toString(String field) {
-        return "DescendantSelfAxisQuery";
+        StringBuffer sb = new StringBuffer();
+        sb.append("DescendantSelfAxisQuery(");
+        sb.append(contextQuery);
+        sb.append(", ");
+        sb.append(subQuery);
+        sb.append(", ");
+        sb.append(minLevels);
+        sb.append(")");
+        return sb.toString();
     }
 
     /**
@@ -477,12 +489,22 @@
 
         private void collectContextHits() throws IOException {
             if (!contextHitsCalculated) {
+                long time = System.currentTimeMillis();
                 contextScorer.score(new HitCollector() {
                     public void collect(int doc, float score) {
                         contextHits.set(doc);
                     }
                 }); // find all
                 contextHitsCalculated = true;
+                time = System.currentTimeMillis() - time;
+                if (log.isDebugEnabled()) {
+                    log.debug("Collected {} context hits in {} ms for {}",
+                            new Object[]{
+                                    new Integer(contextHits.cardinality()),
+                                    new Long(time),
+                                    DescendantSelfAxisQuery.this
+                            });
+                }
             }
         }