You are viewing a plain text version of this content. The canonical link for it is here.
Posted to oak-commits@jackrabbit.apache.org by th...@apache.org on 2015/09/25 08:17:36 UTC

svn commit: r1705216 - in /jackrabbit/oak/trunk: oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/aggregate/ oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/diffindex/ oak-core/src/main/java/org/apache/jackrabbit/oak/plugin...

Author: thomasm
Date: Fri Sep 25 06:17:35 2015
New Revision: 1705216

URL: http://svn.apache.org/viewvc?rev=1705216&view=rev
Log:
OAK-2679 Query engine: cache execution plans

Modified:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/aggregate/AggregateIndex.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/aggregate/package-info.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/diffindex/UUIDDiffIndex.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/nodetype/NodeTypeIndex.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/nodetype/NodeTypeIndexLookup.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndex.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexLookup.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexProvider.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndex.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexLookup.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexPlan.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/package-info.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/reference/ReferenceIndex.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/QueryImpl.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/index/TraversingIndex.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/query/QueryIndex.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/query/package-info.java
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/LowCostOrderedPropertyIndexProvider.java
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedIndexCostTest.java
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexQueryTest.java
    jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/query/QueryTest.java
    jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndex.java
    jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LucenePropertyIndex.java
    jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LowCostLuceneIndexProvider.java
    jackrabbit/oak/trunk/oak-solr-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/solr/query/SolrQueryIndex.java
    jackrabbit/oak/trunk/oak-solr-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/solr/query/SolrQueryIndexProvider.java

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/aggregate/AggregateIndex.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/aggregate/AggregateIndex.java?rev=1705216&r1=1705215&r2=1705216&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/aggregate/AggregateIndex.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/aggregate/AggregateIndex.java Fri Sep 25 06:17:35 2015
@@ -55,6 +55,11 @@ public class AggregateIndex implements A
     }
 
     @Override
+    public double getMinimumCost() {
+        return baseIndex.getMinimumCost();
+    }
+
+    @Override
     public double getCost(Filter filter, NodeState rootState) {
         throw new UnsupportedOperationException("Not supported as implementing AdvancedQueryIndex");
     }

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/aggregate/package-info.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/aggregate/package-info.java?rev=1705216&r1=1705215&r2=1705216&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/aggregate/package-info.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/aggregate/package-info.java Fri Sep 25 06:17:35 2015
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-@Version("1.1.0")
+@Version("1.2.0")
 @Export(optional = "provide:=true")
 package org.apache.jackrabbit.oak.plugins.index.aggregate;
 

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/diffindex/UUIDDiffIndex.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/diffindex/UUIDDiffIndex.java?rev=1705216&r1=1705215&r2=1705216&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/diffindex/UUIDDiffIndex.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/diffindex/UUIDDiffIndex.java Fri Sep 25 06:17:35 2015
@@ -25,6 +25,11 @@ public class UUIDDiffIndex extends DiffI
     }
 
     @Override
+    public double getMinimumCost() {
+        return 0;
+    }
+
+    @Override
     public String getIndexName() {
         return "UUIDDiffIndex";
     }

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/nodetype/NodeTypeIndex.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/nodetype/NodeTypeIndex.java?rev=1705216&r1=1705215&r2=1705216&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/nodetype/NodeTypeIndex.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/nodetype/NodeTypeIndex.java Fri Sep 25 06:17:35 2015
@@ -36,6 +36,11 @@ import org.apache.jackrabbit.oak.spi.sta
 class NodeTypeIndex implements QueryIndex, JcrConstants {
 
     @Override
+    public double getMinimumCost() {
+        return NodeTypeIndexLookup.MINIMUM_COST;
+    }
+
+    @Override
     public double getCost(Filter filter, NodeState root) {
         // TODO don't call getCost for such queries
         if (filter.getFullTextConstraint() != null) {

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/nodetype/NodeTypeIndexLookup.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/nodetype/NodeTypeIndexLookup.java?rev=1705216&r1=1705215&r2=1705216&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/nodetype/NodeTypeIndexLookup.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/nodetype/NodeTypeIndexLookup.java Fri Sep 25 06:17:35 2015
@@ -31,6 +31,11 @@ import com.google.common.collect.Iterabl
  */
 class NodeTypeIndexLookup implements JcrConstants {
 
+    /**
+     * Derived from {@link #getCost(Filter)}
+     */
+    static final double MINIMUM_COST = PropertyIndexLookup.COST_OVERHEAD;
+
     private final NodeState root;
 
     public NodeTypeIndexLookup(NodeState root) {

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndex.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndex.java?rev=1705216&r1=1705215&r2=1705216&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndex.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndex.java Fri Sep 25 06:17:35 2015
@@ -44,13 +44,29 @@ public class OrderedPropertyIndex implem
 
     private static final Logger LOG = LoggerFactory.getLogger(OrderedPropertyIndex.class);
 
+    /**
+     * We're local. Low-cost
+     */
+    private static final double COST_PER_EXECUTION = 3;
+
+    private final OrderedPropertyIndexProvider indexProvider;
+
+    public OrderedPropertyIndex(OrderedPropertyIndexProvider indexProvider) {
+        this.indexProvider = indexProvider;
+    }
+
+    @Override
+    public double getMinimumCost() {
+        return COST_PER_EXECUTION;
+    }
+
     @Override
     public String getIndexName() {
         return TYPE;
     }
 
     OrderedPropertyIndexLookup getLookup(NodeState root) {
-        return new OrderedPropertyIndexLookup(root);
+        return new OrderedPropertyIndexLookup(indexProvider, root);
     }
 
     /**
@@ -68,7 +84,7 @@ public class OrderedPropertyIndex implem
      */
     static IndexPlan.Builder getIndexPlanBuilder(final Filter filter) {
         IndexPlan.Builder b = new IndexPlan.Builder();
-        b.setCostPerExecution(1); // we're local. Low-cost
+        b.setCostPerExecution(COST_PER_EXECUTION);
         // we're local but slightly more expensive than a standard PropertyIndex
         b.setCostPerEntry(1.3);
         b.setFulltextIndex(false); // we're never full-text

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexLookup.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexLookup.java?rev=1705216&r1=1705215&r2=1705216&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexLookup.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexLookup.java Fri Sep 25 06:17:35 2015
@@ -76,18 +76,21 @@ public class OrderedPropertyIndexLookup
      */
     private static final int MAX_COST = 100;
 
+    private final OrderedPropertyIndexProvider indexProvider;
+
     private NodeState root;
 
     private String name;
 
     private OrderedPropertyIndexLookup parent;
 
-    public OrderedPropertyIndexLookup(NodeState root) {
-        this(root, "", null);
+    public OrderedPropertyIndexLookup(OrderedPropertyIndexProvider indexProvider, NodeState root) {
+        this(indexProvider, root, "", null);
     }
 
-    public OrderedPropertyIndexLookup(NodeState root, String name,
+    public OrderedPropertyIndexLookup(OrderedPropertyIndexProvider indexProvider, NodeState root, String name,
                                       OrderedPropertyIndexLookup parent) {
+        this.indexProvider = indexProvider;
         this.root = root;
         this.name = name;
         this.parent = parent;
@@ -106,9 +109,14 @@ public class OrderedPropertyIndexLookup
      */
     @Nullable
     NodeState getIndexNode(NodeState node, String propertyName, Filter filter) {
+        if (parent == null && !indexProvider.mayHaveRootIndexes()) {
+            return null;
+        }
+
         // keep a fallback to a matching index def that has *no* node type constraints
         // (initially, there is no fallback)
         NodeState fallback = null;
+        boolean hasRootIndexes = false;
 
         NodeState state = node.getChildNode(INDEX_DEFINITIONS_NAME);
         for (ChildNodeEntry entry : state.getChildNodeEntries()) {
@@ -117,6 +125,8 @@ public class OrderedPropertyIndexLookup
             if (type == null || type.isArray() || !getType().equals(type.getValue(Type.STRING))) {
                 continue;
             }
+            hasRootIndexes = true;
+
             if (contains(index.getNames(PROPERTY_NAMES), propertyName)) {
                 NodeState indexContent = index.getChildNode(INDEX_CONTENT_NODE_NAME);
                 if (!indexContent.exists()) {
@@ -140,6 +150,10 @@ public class OrderedPropertyIndexLookup
                 }
             }
         }
+
+        if (parent == null) {
+            indexProvider.indicateRootIndexes(hasRootIndexes);
+        }
         return fallback;
     }
 
@@ -243,7 +257,7 @@ public class OrderedPropertyIndexLookup
         for (String element : PathUtils.elements(path)) {
             if (lookup == null) {
                 lookup = new OrderedPropertyIndexLookup(
-                        root.getChildNode(element), element, this);
+                        indexProvider, root.getChildNode(element), element, this);
             } else {
                 remainder = PathUtils.concat(remainder, element);
             }
@@ -308,7 +322,7 @@ public class OrderedPropertyIndexLookup
         for (String element : PathUtils.elements(path)) {
             if (lookup == null) {
                 lookup = new OrderedPropertyIndexLookup(
-                        root.getChildNode(element), element, this);
+                        indexProvider, root.getChildNode(element), element, this);
             } else {
                 remainder = PathUtils.concat(remainder, element);
             }

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexProvider.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexProvider.java?rev=1705216&r1=1705215&r2=1705216&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexProvider.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexProvider.java Fri Sep 25 06:17:35 2015
@@ -18,6 +18,7 @@
 package org.apache.jackrabbit.oak.plugins.index.property;
 
 import java.util.List;
+import java.util.concurrent.TimeUnit;
 
 import javax.annotation.Nonnull;
 
@@ -33,9 +34,46 @@ import com.google.common.collect.Immutab
 @Service(QueryIndexProvider.class)
 public class OrderedPropertyIndexProvider implements QueryIndexProvider {
 
+    private static final long DEFAULT_NO_INDEX_CACHE_TIMEOUT = TimeUnit.SECONDS.toMillis(30);
+
+    /**
+     * How often it should check for a new ordered property index.
+     */
+    private static long noIndexCacheTimeout = DEFAULT_NO_INDEX_CACHE_TIMEOUT;
+
+    /**
+     * The last time when it checked for the existence of an ordered property index AND could not find any.
+     */
+    private volatile long lastNegativeIndexCheck;
+
     @Override
     @Nonnull
     public List<? extends QueryIndex> getQueryIndexes(NodeState nodeState) {
-        return ImmutableList.<QueryIndex> of(new OrderedPropertyIndex());
+        return ImmutableList.<QueryIndex> of(new OrderedPropertyIndex(this));
+    }
+
+    /**
+     * @return <code>true</code> if there may be any ordered indexes below the root path
+     */
+    boolean mayHaveRootIndexes() {
+        return System.currentTimeMillis() - lastNegativeIndexCheck > noIndexCacheTimeout;
+    }
+
+    /**
+     * Indicates whether or not there are ordered indexes below the root path
+     *
+     * @param hasRootIndexes
+     */
+    void indicateRootIndexes(boolean hasRootIndexes) {
+        lastNegativeIndexCheck = hasRootIndexes ? 0 : System.currentTimeMillis();
     }
+
+    public static void setCacheTimeoutForTesting(long timeout) {
+        noIndexCacheTimeout = timeout;
+    }
+
+    public static void resetCacheTimeoutForTesting() {
+        noIndexCacheTimeout = DEFAULT_NO_INDEX_CACHE_TIMEOUT;
+    }
+
 }

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndex.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndex.java?rev=1705216&r1=1705215&r2=1705216&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndex.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndex.java Fri Sep 25 06:17:35 2015
@@ -98,6 +98,11 @@ class PropertyIndex implements QueryInde
 
     private static final Logger LOG = LoggerFactory.getLogger(PropertyIndex.class);
 
+    /**
+     * Cached property index plan
+     */
+    private PropertyIndexPlan plan;
+
     static Set<String> encode(PropertyValue value) {
         if (value == null) {
             return null;
@@ -121,7 +126,22 @@ class PropertyIndex implements QueryInde
         return values;
     }
 
-    private static PropertyIndexPlan plan(NodeState root, Filter filter) {
+    private PropertyIndexPlan getPlan(NodeState root, Filter filter) {
+        // Reuse cached plan if the filter is the same (which should always be the case). The filter is compared as a
+        // string because it would not be possible to use its equals method since the preparing flag would be different
+        // and creating a separate isSimilar method is not worth the effort since it would not be used anymore once the
+        // PropertyIndex has been refactored to an AdvancedQueryIndex (which will make the plan cache obsolete).
+        PropertyIndexPlan plan = this.plan;
+        if (plan != null && plan.getFilter().toString().equals(filter.toString())) {
+            return plan;
+        } else {
+            plan = createPlan(root, filter);
+            this.plan = plan;
+            return plan;
+        }
+    }
+
+    private static PropertyIndexPlan createPlan(NodeState root, Filter filter) {
         PropertyIndexPlan bestPlan = null;
 
         // TODO support indexes on a path
@@ -138,6 +158,10 @@ class PropertyIndex implements QueryInde
                             plan.getName(), plan.getCost());
                     if (bestPlan == null || plan.getCost() < bestPlan.getCost()) {
                         bestPlan = plan;
+                        // Stop comparing if the costs are the minimum
+                        if (plan.getCost() == PropertyIndexPlan.COST_OVERHEAD) {
+                            break;
+                        }
                     }
                 }
             }
@@ -149,6 +173,11 @@ class PropertyIndex implements QueryInde
     //--------------------------------------------------------< QueryIndex >--
 
     @Override
+    public double getMinimumCost() {
+        return PropertyIndexPlan.COST_OVERHEAD;
+    }
+
+    @Override
     public String getIndexName() {
         return PROPERTY;
     }
@@ -163,8 +192,12 @@ class PropertyIndex implements QueryInde
             // not an appropriate index for native search
             return Double.POSITIVE_INFINITY;
         }
+        if (filter.getPropertyRestrictions().isEmpty() && filter.getSelector().getSelectorConstraints().isEmpty()) {
+            // not an appropriate index for no property restrictions & selector constraints
+            return Double.POSITIVE_INFINITY;
+        }
 
-        PropertyIndexPlan plan = plan(root, filter);
+        PropertyIndexPlan plan = getPlan(root, filter);
         if (plan != null) {
             return plan.getCost();
         } else {
@@ -174,7 +207,7 @@ class PropertyIndex implements QueryInde
 
     @Override
     public Cursor query(Filter filter, NodeState root) {
-        PropertyIndexPlan plan = plan(root, filter);
+        PropertyIndexPlan plan = getPlan(root, filter);
         checkState(plan != null,
                 "Property index is used even when no index"
                 + " is available for filter " + filter);
@@ -183,7 +216,7 @@ class PropertyIndex implements QueryInde
 
     @Override
     public String getPlan(Filter filter, NodeState root) {
-        PropertyIndexPlan plan = plan(root, filter);
+        PropertyIndexPlan plan = getPlan(root, filter);
         if (plan != null) {
             return plan.toString();
         } else {

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexLookup.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexLookup.java?rev=1705216&r1=1705215&r2=1705216&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexLookup.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexLookup.java Fri Sep 25 06:17:35 2015
@@ -63,7 +63,7 @@ public class PropertyIndexLookup {
     /**
      * The cost overhead to use the index in number of read operations.
      */
-    private static final double COST_OVERHEAD = 2;
+    public static final double COST_OVERHEAD = 2;
     
     /**
      * The maximum cost when the index can be used.

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexPlan.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexPlan.java?rev=1705216&r1=1705215&r2=1705216&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexPlan.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexPlan.java Fri Sep 25 06:17:35 2015
@@ -31,7 +31,6 @@ import java.util.Set;
 import org.apache.jackrabbit.oak.api.PropertyValue;
 import org.apache.jackrabbit.oak.commons.PathUtils;
 import org.apache.jackrabbit.oak.plugins.index.PathFilter;
-import org.apache.jackrabbit.oak.plugins.index.PathFilter.Result;
 import org.apache.jackrabbit.oak.plugins.index.property.strategy.ContentMirrorStoreStrategy;
 import org.apache.jackrabbit.oak.plugins.index.property.strategy.IndexStoreStrategy;
 import org.apache.jackrabbit.oak.plugins.index.property.strategy.UniqueEntryStoreStrategy;
@@ -58,7 +57,7 @@ public class PropertyIndexPlan {
     /**
      * The cost overhead to use the index in number of read operations.
      */
-    private static final double COST_OVERHEAD = 2;
+    static final double COST_OVERHEAD = 2;
 
     /**
      * The maximum cost when the index can be used.
@@ -260,6 +259,10 @@ public class PropertyIndexPlan {
         return cursor;
     }
 
+    Filter getFilter() {
+        return filter;
+    }
+
     //------------------------------------------------------------< Object >--
 
     @Override

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/package-info.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/package-info.java?rev=1705216&r1=1705215&r2=1705216&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/package-info.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/package-info.java Fri Sep 25 06:17:35 2015
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-@Version("1.0.1")
+@Version("2.0.0")
 @Export(optional = "provide:=true")
 package org.apache.jackrabbit.oak.plugins.index.property;
 

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/reference/ReferenceIndex.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/reference/ReferenceIndex.java?rev=1705216&r1=1705215&r2=1705216&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/reference/ReferenceIndex.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/reference/ReferenceIndex.java Fri Sep 25 06:17:35 2015
@@ -53,6 +53,13 @@ class ReferenceIndex implements QueryInd
 
     private static final ContentMirrorStoreStrategy STORE = new ContentMirrorStoreStrategy();
 
+    private static final double COST = 1;
+
+    @Override
+    public double getMinimumCost() {
+        return COST;
+    }
+
     @Override
     public String getIndexName() {
         return NAME;
@@ -72,7 +79,7 @@ class ReferenceIndex implements QueryInd
         for (PropertyRestriction pr : filter.getPropertyRestrictions()) {
             if (isEqualityRestrictionOnType(pr, REFERENCE) ||
                     isEqualityRestrictionOnType(pr, WEAKREFERENCE)) {
-                return 1;
+                return COST;
             }
         }
         // not an appropriate index

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/QueryImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/QueryImpl.java?rev=1705216&r1=1705215&r2=1705216&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/QueryImpl.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/QueryImpl.java Fri Sep 25 06:17:35 2015
@@ -28,6 +28,7 @@ import com.google.common.collect.Abstrac
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 
+import com.google.common.collect.Ordering;
 import org.apache.jackrabbit.oak.api.PropertyValue;
 import org.apache.jackrabbit.oak.api.Tree;
 import org.apache.jackrabbit.oak.api.Type;
@@ -122,6 +123,13 @@ public class QueryImpl implements Query
 
     private static final Logger LOG = LoggerFactory.getLogger(QueryImpl.class);
 
+    private static final Ordering<QueryIndex> MINIMAL_COST_ORDERING = new Ordering<QueryIndex>() {
+        @Override
+        public int compare(QueryIndex left, QueryIndex right) {
+            return Double.compare(left.getMinimumCost(), right.getMinimumCost());
+        }
+    };
+
     SourceImpl source;
     final String statement;
     final HashMap<String, PropertyValue> bindVariableMap = new HashMap<String, PropertyValue>();
@@ -928,7 +936,16 @@ public class QueryImpl implements Query
 
         double bestCost = Double.POSITIVE_INFINITY;
         IndexPlan bestPlan = null;
-        for (QueryIndex index : indexProvider.getQueryIndexes(rootState)) {
+
+        // Sort the indexes according to their minimum cost to be able to skip the remaining indexes if the cost of the
+        // current index is below the minimum cost of the next index.
+        final List<? extends QueryIndex> queryIndexes = MINIMAL_COST_ORDERING
+                .sortedCopy(indexProvider.getQueryIndexes(rootState));
+
+        for (int i = 0; i < queryIndexes.size(); i++) {
+            final QueryIndex index = queryIndexes.get(i);
+            final QueryIndex nextIndex = (i < queryIndexes.size()) ? queryIndexes.get(i) : null;
+
             double cost;
             String indexName = index.getIndexName();
             IndexPlan indexPlan = null;
@@ -998,6 +1015,10 @@ public class QueryImpl implements Query
                 bestIndex = index;
                 bestPlan = indexPlan;
             }
+            // Stop looking for a better index if the current best cost is lower than the next minimum cost
+            if (nextIndex != null && bestCost <= nextIndex.getMinimumCost()) {
+                break;
+            }
         }
 
         if (traversalEnabled) {

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/index/TraversingIndex.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/index/TraversingIndex.java?rev=1705216&r1=1705215&r2=1705216&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/index/TraversingIndex.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/index/TraversingIndex.java Fri Sep 25 06:17:35 2015
@@ -34,6 +34,11 @@ import org.apache.jackrabbit.oak.spi.sta
 public class TraversingIndex implements QueryIndex {
 
     @Override
+    public double getMinimumCost() {
+        return 0;
+    }
+
+    @Override
     public Cursor query(Filter filter, NodeState rootState) {
         return Cursors.newTraversingCursor(filter, rootState);
     }

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/query/QueryIndex.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/query/QueryIndex.java?rev=1705216&r1=1705215&r2=1705216&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/query/QueryIndex.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/query/QueryIndex.java Fri Sep 25 06:17:35 2015
@@ -53,7 +53,16 @@ import static org.apache.jackrabbit.oak.
  * even thought it will be set in the filter.
  */
 public interface QueryIndex {
-    
+
+    /**
+     * Returns the minimum cost which {@link #getCost(Filter, NodeState)} would return in the best possible case.
+     * <p>
+     * The implementation should return a static/cached value because it is called very often.
+     *
+     * @return the minimum cost for the index
+     */
+    double getMinimumCost();
+
     /**
      * Estimate the worst-case cost to query with the given filter. The returned
      * cost is a value between 1 (very fast; lookup of a unique node) and the
@@ -336,7 +345,7 @@ public interface QueryIndex {
             protected PropertyRestriction propRestriction;
             protected String pathPrefix = "/";
             protected Map<String, Object> attributes = Maps.newHashMap();
-            protected String planName = null;
+            protected String planName;
 
             public Builder setCostPerExecution(double costPerExecution) {
                 this.costPerExecution = costPerExecution;
@@ -543,7 +552,7 @@ public interface QueryIndex {
                     }
 
                     @Override
-                    public String getPlanName(){
+                    public String getPlanName() {
                         return planName;
                     }
                 };

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/query/package-info.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/query/package-info.java?rev=1705216&r1=1705215&r2=1705216&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/query/package-info.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/query/package-info.java Fri Sep 25 06:17:35 2015
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-@Version("3.0.0")
+@Version("4.0.0")
 @Export(optional = "provide:=true")
 package org.apache.jackrabbit.oak.spi.query;
 

Modified: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/LowCostOrderedPropertyIndexProvider.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/LowCostOrderedPropertyIndexProvider.java?rev=1705216&r1=1705215&r2=1705216&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/LowCostOrderedPropertyIndexProvider.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/LowCostOrderedPropertyIndexProvider.java Fri Sep 25 06:17:35 2015
@@ -30,13 +30,22 @@ import com.google.common.collect.Immutab
 public class LowCostOrderedPropertyIndexProvider extends OrderedPropertyIndexProvider {
     @Override
     public List<? extends QueryIndex> getQueryIndexes(NodeState nodeState) {
-        return ImmutableList.<QueryIndex> of(new LowCostOrderedPropertyIndex());
+        return ImmutableList.<QueryIndex> of(new LowCostOrderedPropertyIndex(this));
     }
 
     private static class LowCostOrderedPropertyIndex extends OrderedPropertyIndex {
+        public LowCostOrderedPropertyIndex(LowCostOrderedPropertyIndexProvider indexProvider) {
+            super(indexProvider);
+        }
+
         @Override
-        public double getCost(Filter filter, NodeState root) {
+        public double getMinimumCost() {
             return 1e-3;
         }
+
+        @Override
+        public double getCost(Filter filter, NodeState root) {
+            return getMinimumCost();
+        }
     }
 }

Modified: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedIndexCostTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedIndexCostTest.java?rev=1705216&r1=1705215&r2=1705216&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedIndexCostTest.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedIndexCostTest.java Fri Sep 25 06:17:35 2015
@@ -92,7 +92,7 @@ public class OrderedIndexCostTest extend
 
     @Test @Ignore("As of OAK-622 this should no longer be used. Removing later.")
     public void costFullTextConstraint() {
-        OrderedPropertyIndex index = new OrderedPropertyIndex();
+        OrderedPropertyIndex index = new OrderedPropertyIndex(new OrderedPropertyIndexProvider());
         NodeState root = InitialContent.INITIAL_CONTENT;
         Filter filter = EasyMock.createNiceMock(Filter.class);
         FullTextExpression fte = EasyMock.createNiceMock(FullTextExpression.class);
@@ -106,7 +106,7 @@ public class OrderedIndexCostTest extend
 
     @Test @Ignore("As of OAK-622 this should no longer be used. Removing later.")
     public void costContainsNativeConstraints(){
-        OrderedPropertyIndex index = new OrderedPropertyIndex();
+        OrderedPropertyIndex index = new OrderedPropertyIndex(new OrderedPropertyIndexProvider());
         NodeState root = InitialContent.INITIAL_CONTENT;
         Filter filter = EasyMock.createNiceMock(Filter.class);
         EasyMock.expect(filter.containsNativeConstraint()).andReturn(true).anyTimes();
@@ -124,7 +124,7 @@ public class OrderedIndexCostTest extend
      */
     @Test @Ignore("As of OAK-622 this should no longer be used. Removing later.")
     public void costGreaterThanAscendingDirection() throws Exception {
-        OrderedPropertyIndex index = new OrderedPropertyIndex();
+        OrderedPropertyIndex index = new OrderedPropertyIndex(new OrderedPropertyIndexProvider());
         NodeBuilder builder = InitialContent.INITIAL_CONTENT.builder();
         defineAscendingIndex(builder);
         NodeState root = builder.getNodeState();
@@ -148,7 +148,7 @@ public class OrderedIndexCostTest extend
      */
     @Test @Ignore("As of OAK-622 this should no longer be used. Removing later.")
     public void costGreaterThanEqualAscendingDirection() throws IllegalArgumentException, RepositoryException {
-        OrderedPropertyIndex index = new OrderedPropertyIndex();
+        OrderedPropertyIndex index = new OrderedPropertyIndex(new OrderedPropertyIndexProvider());
         NodeBuilder builder = InitialContent.INITIAL_CONTENT.builder();
         defineAscendingIndex(builder);
         NodeState root = builder.getNodeState();
@@ -173,7 +173,7 @@ public class OrderedIndexCostTest extend
      */
     @Test @Ignore("As of OAK-622 this should no longer be used. Removing later.")
     public void costLessThanAscendingDirection() throws IllegalArgumentException, RepositoryException {
-        OrderedPropertyIndex index = new OrderedPropertyIndex();
+        OrderedPropertyIndex index = new OrderedPropertyIndex(new OrderedPropertyIndexProvider());
         NodeBuilder builder = InitialContent.INITIAL_CONTENT.builder();
         defineAscendingIndex(builder);
         NodeState root = builder.getNodeState();
@@ -192,7 +192,7 @@ public class OrderedIndexCostTest extend
 
     @Test @Ignore("As of OAK-622 this should no longer be used. Removing later.")
     public void costLessThanEqualsAscendingDirection() throws IllegalArgumentException, RepositoryException {
-        OrderedPropertyIndex index = new OrderedPropertyIndex();
+        OrderedPropertyIndex index = new OrderedPropertyIndex(new OrderedPropertyIndexProvider());
         NodeBuilder builder = InitialContent.INITIAL_CONTENT.builder();
         defineAscendingIndex(builder);
         NodeState root = builder.getNodeState();
@@ -212,7 +212,7 @@ public class OrderedIndexCostTest extend
 
     @Test @Ignore("As of OAK-622 this should no longer be used. Removing later.")
     public void costGreaterThanDescendingDirection() throws IllegalArgumentException, RepositoryException {
-        OrderedPropertyIndex index = new OrderedPropertyIndex();
+        OrderedPropertyIndex index = new OrderedPropertyIndex(new OrderedPropertyIndexProvider());
         NodeBuilder builder = InitialContent.INITIAL_CONTENT.builder();
         defineDescendingIndex(builder);
         NodeState root = builder.getNodeState();
@@ -232,7 +232,7 @@ public class OrderedIndexCostTest extend
 
     @Test @Ignore("As of OAK-622 this should no longer be used. Removing later.")
     public void costGreaterEqualThanDescendingDirection() throws IllegalArgumentException, RepositoryException {
-        OrderedPropertyIndex index = new OrderedPropertyIndex();
+        OrderedPropertyIndex index = new OrderedPropertyIndex(new OrderedPropertyIndexProvider());
         NodeBuilder builder = InitialContent.INITIAL_CONTENT.builder();
         defineDescendingIndex(builder);
         NodeState root = builder.getNodeState();
@@ -253,7 +253,7 @@ public class OrderedIndexCostTest extend
 
     @Test @Ignore("As of OAK-622 this should no longer be used. Removing later.")
     public void costLessThanDescendingDirection() throws IllegalArgumentException, RepositoryException {
-        OrderedPropertyIndex index = new OrderedPropertyIndex();
+        OrderedPropertyIndex index = new OrderedPropertyIndex(new OrderedPropertyIndexProvider());
         NodeBuilder builder = InitialContent.INITIAL_CONTENT.builder();
         defineDescendingIndex(builder);
         NodeState root = builder.getNodeState();
@@ -273,7 +273,7 @@ public class OrderedIndexCostTest extend
 
     @Test @Ignore("As of OAK-622 this should no longer be used. Removing later.")
     public void costLessThanEqualDescendingDirection() throws IllegalArgumentException, RepositoryException {
-        OrderedPropertyIndex index = new OrderedPropertyIndex();
+        OrderedPropertyIndex index = new OrderedPropertyIndex(new OrderedPropertyIndexProvider());
         NodeBuilder builder = InitialContent.INITIAL_CONTENT.builder();
         defineDescendingIndex(builder);
         NodeState root = builder.getNodeState();
@@ -294,7 +294,7 @@ public class OrderedIndexCostTest extend
 
     @Test @Ignore("As of OAK-622 this should no longer be used. Removing later.")
     public void costBetweenDescendingDirection() throws IllegalArgumentException, RepositoryException {
-        OrderedPropertyIndex index = new OrderedPropertyIndex();
+        OrderedPropertyIndex index = new OrderedPropertyIndex(new OrderedPropertyIndexProvider());
         NodeBuilder builder = InitialContent.INITIAL_CONTENT.builder();
         defineDescendingIndex(builder);
         NodeState root = builder.getNodeState();
@@ -317,7 +317,7 @@ public class OrderedIndexCostTest extend
 
     @Test @Ignore("As of OAK-622 this should no longer be used. Removing later.")
     public void costBetweenAscendingDirection() throws IllegalArgumentException, RepositoryException {
-        OrderedPropertyIndex index = new OrderedPropertyIndex();
+        OrderedPropertyIndex index = new OrderedPropertyIndex(new OrderedPropertyIndexProvider());
         NodeBuilder builder = InitialContent.INITIAL_CONTENT.builder();
         defineAscendingIndex(builder);
         NodeState root = builder.getNodeState();

Modified: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexQueryTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexQueryTest.java?rev=1705216&r1=1705215&r2=1705216&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexQueryTest.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexQueryTest.java Fri Sep 25 06:17:35 2015
@@ -507,7 +507,7 @@ public class OrderedPropertyIndexQueryTe
 
         NodeState indexed = HOOK.processCommit(before, after, CommitInfo.EMPTY);
 
-        final OrderedPropertyIndex index = new OrderedPropertyIndex();
+        final OrderedPropertyIndex index = new OrderedPropertyIndex(new OrderedPropertyIndexProvider());
         final String nodeTypeName = JcrConstants.NT_BASE;
 
         Filter filter = createFilter(indexed, nodeTypeName);
@@ -583,7 +583,7 @@ public class OrderedPropertyIndexQueryTe
 
         NodeState indexed = HOOK.processCommit(before, after, CommitInfo.EMPTY);
 
-        final OrderedPropertyIndex index = new OrderedPropertyIndex();
+        final OrderedPropertyIndex index = new OrderedPropertyIndex(new OrderedPropertyIndexProvider());
         final String nodeTypeName = JcrConstants.NT_BASE;
         Filter filter = createFilter(indexed, nodeTypeName);
 
@@ -624,7 +624,7 @@ public class OrderedPropertyIndexQueryTe
 
         NodeState indexed = HOOK.processCommit(before, after, CommitInfo.EMPTY);
 
-        final OrderedPropertyIndex index = new OrderedPropertyIndex();
+        final OrderedPropertyIndex index = new OrderedPropertyIndex(new OrderedPropertyIndexProvider());
         final String nodeTypeName = JcrConstants.NT_BASE;
         FilterImpl filter = createFilter(indexed, nodeTypeName);
         filter.restrictProperty("somethingNotIndexed", Operator.EQUAL, PropertyValues.newLong(1L));

Modified: jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/query/QueryTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/query/QueryTest.java?rev=1705216&r1=1705215&r2=1705216&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/query/QueryTest.java (original)
+++ jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/query/QueryTest.java Fri Sep 25 06:17:35 2015
@@ -58,6 +58,9 @@ import org.apache.jackrabbit.oak.commons
 import org.apache.jackrabbit.oak.commons.json.JsopTokenizer;
 import org.apache.jackrabbit.oak.jcr.AbstractRepositoryTest;
 import org.apache.jackrabbit.oak.jcr.NodeStoreFixture;
+import org.apache.jackrabbit.oak.plugins.index.property.OrderedPropertyIndexProvider;
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Ignore;
 import org.junit.Test;
 
@@ -69,7 +72,17 @@ public class QueryTest extends AbstractR
     public QueryTest(NodeStoreFixture fixture) {
         super(fixture);
     }
-    
+
+    @Before
+    public void disableCaching() {
+        OrderedPropertyIndexProvider.setCacheTimeoutForTesting(0);
+    }
+
+    @After
+    public void enableCaching() {
+        OrderedPropertyIndexProvider.resetCacheTimeoutForTesting();
+    }
+
     @Test
     public void join() throws Exception {
         Session session = getAdminSession();

Modified: jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndex.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndex.java?rev=1705216&r1=1705215&r2=1705216&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndex.java (original)
+++ jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndex.java Fri Sep 25 06:17:35 2015
@@ -154,6 +154,7 @@ public class LuceneIndex implements Adva
     private static final Logger LOG = LoggerFactory
             .getLogger(LuceneIndex.class);
     public static final String NATIVE_QUERY_FUNCTION = "native*lucene";
+    private static double MIN_COST = 2.2;
 
     /**
      * IndexPaln Attribute name which refers to the path of Lucene index to be used
@@ -178,6 +179,11 @@ public class LuceneIndex implements Adva
     }
 
     @Override
+    public double getMinimumCost() {
+        return MIN_COST;
+    }
+
+    @Override
     public String getIndexName() {
         return "lucene";
     }

Modified: jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LucenePropertyIndex.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LucenePropertyIndex.java?rev=1705216&r1=1705215&r2=1705216&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LucenePropertyIndex.java (original)
+++ jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LucenePropertyIndex.java Fri Sep 25 06:17:35 2015
@@ -163,6 +163,8 @@ import static org.apache.lucene.search.B
  */
 public class LucenePropertyIndex implements AdvancedQueryIndex, QueryIndex, NativeQueryIndex,
         AdvanceFulltextQueryIndex {
+    
+    private static double MIN_COST = 2.1;
 
     private static final Logger LOG = LoggerFactory
             .getLogger(LucenePropertyIndex.class);
@@ -191,6 +193,11 @@ public class LucenePropertyIndex impleme
     }
 
     @Override
+    public double getMinimumCost() {
+        return MIN_COST;
+    }
+
+    @Override
     public String getIndexName() {
         return "lucene-property";
     }

Modified: jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LowCostLuceneIndexProvider.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LowCostLuceneIndexProvider.java?rev=1705216&r1=1705215&r2=1705216&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LowCostLuceneIndexProvider.java (original)
+++ jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LowCostLuceneIndexProvider.java Fri Sep 25 06:17:35 2015
@@ -36,19 +36,23 @@ public class LowCostLuceneIndexProvider
     }
 
     private static class LowCostLuceneIndex extends LuceneIndex {
-
         public LowCostLuceneIndex(IndexTracker tracker, NodeAggregator aggregator) {
             super(tracker, aggregator);
         }
 
         @Override
+        public double getMinimumCost() {
+            return 1e-3;
+        }
+
+        @Override
         public List<IndexPlan> getPlans(Filter filter, List<OrderEntry> sortOrder, NodeState rootState) {
             String indexPath = new LuceneIndexLookup(rootState).getOldFullTextIndexPath(filter, tracker);
             if (indexPath == null){
                 return Collections.emptyList();
             }
             return Collections.singletonList(planBuilder(filter)
-                    .setCostPerExecution(1e-3)
+                    .setCostPerExecution(getMinimumCost())
                     .setAttribute(ATTR_INDEX_PATH, indexPath)
                     .build());
         }

Modified: jackrabbit/oak/trunk/oak-solr-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/solr/query/SolrQueryIndex.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-solr-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/solr/query/SolrQueryIndex.java?rev=1705216&r1=1705215&r2=1705216&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-solr-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/solr/query/SolrQueryIndex.java (original)
+++ jackrabbit/oak/trunk/oak-solr-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/solr/query/SolrQueryIndex.java Fri Sep 25 06:17:35 2015
@@ -77,6 +77,10 @@ public class SolrQueryIndex implements F
 
     static final String NATIVE_LUCENE_QUERY = "native*lucene";
 
+    private static double MIN_COST = 2.3;
+
+    private static double COST_FOR_SINGLE_RESTRICTION = 10;
+
     private final Logger log = LoggerFactory.getLogger(SolrQueryIndex.class);
 
     private final String name;
@@ -104,6 +108,11 @@ public class SolrQueryIndex implements F
     }
 
     @Override
+    public double getMinimumCost() {
+        return MIN_COST;
+    }
+
+    @Override
     public String getIndexName() {
         return name;
     }
@@ -111,7 +120,7 @@ public class SolrQueryIndex implements F
     @Override
     public double getCost(Filter filter, NodeState root) {
         // cost is inverse proportional to the number of matching restrictions, infinite if no restriction matches
-        double cost = 10d / getMatchingFilterRestrictions(filter);
+        double cost = COST_FOR_SINGLE_RESTRICTION / getMatchingFilterRestrictions(filter);
         if (log.isDebugEnabled()) {
             log.debug("Solr: cost for {} is {}", name, cost);
         }

Modified: jackrabbit/oak/trunk/oak-solr-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/solr/query/SolrQueryIndexProvider.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-solr-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/solr/query/SolrQueryIndexProvider.java?rev=1705216&r1=1705215&r2=1705216&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-solr-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/solr/query/SolrQueryIndexProvider.java (original)
+++ jackrabbit/oak/trunk/oak-solr-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/solr/query/SolrQueryIndexProvider.java Fri Sep 25 06:17:35 2015
@@ -17,9 +17,11 @@
 package org.apache.jackrabbit.oak.plugins.index.solr.query;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 import java.util.WeakHashMap;
+import java.util.concurrent.TimeUnit;
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
 
@@ -49,6 +51,11 @@ import static org.apache.jackrabbit.oak.
  */
 public class SolrQueryIndexProvider implements QueryIndexProvider {
 
+    /**
+     * How often it should check for a new Solr index.
+     */
+    private static final long NO_INDEX_CACHE_TIMEOUT = TimeUnit.SECONDS.toMillis(30);
+
     private final Logger log = LoggerFactory.getLogger(getClass());
 
     private final SolrServerProvider solrServerProvider;
@@ -59,6 +66,11 @@ public class SolrQueryIndexProvider impl
 
     private final Map<NodeState, LMSEstimator> estimators = new WeakHashMap<NodeState, LMSEstimator>();
 
+    /**
+     * The last time when it checked for the existence of a Solr index AND could not find any.
+     */
+    private volatile long lastNegativeIndexCheck = 0;
+
     public SolrQueryIndexProvider(@Nonnull SolrServerProvider solrServerProvider, @Nonnull OakSolrConfigurationProvider oakSolrConfigurationProvider,
                                   @Nullable NodeAggregator nodeAggregator) {
         this.oakSolrConfigurationProvider = oakSolrConfigurationProvider;
@@ -73,7 +85,19 @@ public class SolrQueryIndexProvider impl
     @Nonnull
     @Override
     public List<? extends QueryIndex> getQueryIndexes(NodeState nodeState) {
+        List<? extends QueryIndex> queryIndexes;
+        // Return immediately if there are not any Solr indexes and the cache timeout has not been exceeded
+        long currentTime = System.currentTimeMillis();
+        if (currentTime - lastNegativeIndexCheck > NO_INDEX_CACHE_TIMEOUT) {
+            queryIndexes = internalGetQueryIndexes(nodeState);
+            lastNegativeIndexCheck = queryIndexes.isEmpty() ? currentTime : 0;
+        } else {
+            queryIndexes = Collections.emptyList();
+        }
+        return queryIndexes;
+    }
 
+    private List<? extends QueryIndex> internalGetQueryIndexes(NodeState nodeState) {
         List<QueryIndex> tempIndexes = new ArrayList<QueryIndex>();
         NodeState definitions = nodeState.getChildNode(INDEX_DEFINITIONS_NAME);
         for (ChildNodeEntry entry : definitions.getChildNodeEntries()) {