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 2016/10/07 12:34:52 UTC

svn commit: r1763756 - in /jackrabbit/oak/trunk/oak-core/src: main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexPlan.java test/java/org/apache/jackrabbit/oak/query/index/IndexSelectionTest.java

Author: thomasm
Date: Fri Oct  7 12:34:52 2016
New Revision: 1763756

URL: http://svn.apache.org/viewvc?rev=1763756&view=rev
Log:
OAK-4904 For unique indexes avoid consulting indexes other than property index

Added:
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/index/IndexSelectionTest.java
Modified:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexPlan.java

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=1763756&r1=1763755&r2=1763756&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 Oct  7 12:34:52 2016
@@ -54,7 +54,7 @@ public class PropertyIndexPlan {
     /**
      * The cost overhead to use the index in number of read operations.
      */
-    static final double COST_OVERHEAD = 2;
+    public static final double COST_OVERHEAD = 2;
 
     /**
      * The maximum cost when the index can be used.
@@ -83,6 +83,8 @@ public class PropertyIndexPlan {
 
     private final PathFilter pathFilter;
 
+    private final boolean unique;
+
     PropertyIndexPlan(String name, NodeState root, NodeState definition,
                       Filter filter){
         this(name, root, definition, filter, Mounts.defaultMountInfoProvider());
@@ -91,6 +93,7 @@ public class PropertyIndexPlan {
     PropertyIndexPlan(String name, NodeState root, NodeState definition,
                       Filter filter, MountInfoProvider mountInfoProvider) {
         this.name = name;
+        this.unique = definition.getBoolean(IndexConstants.UNIQUE_PROPERTY_NAME);
         this.definition = definition;
         this.properties = newHashSet(definition.getNames(PROPERTY_NAMES));
         pathFilter = PathFilter.from(definition.builder());
@@ -107,7 +110,7 @@ public class PropertyIndexPlan {
         Set<String> bestValues = emptySet();
         int bestDepth = 1;
 
-        if (matchesNodeTypes && 
+        if (matchesNodeTypes &&
                 pathFilter.areAllDescendantsIncluded(filter.getPath())) {
             for (String property : properties) {
                 PropertyRestriction restriction =
@@ -145,10 +148,20 @@ public class PropertyIndexPlan {
                         cost += strategy.count(filter, root, definition,
                                 values, MAX_COST);
                     }
+                    if (unique && cost <= 1) {
+                        // for unique index, for the normal case
+                        // (that is, for a regular lookup)
+                        // no further reads are needed
+                        cost = 0;
+                    }
                     if (cost < bestCost) {
                         bestDepth = depth;
                         bestValues = values;
                         bestCost = cost;
+                        if (bestCost == 0) {
+                            // shortcut: not possible to top this
+                            break;
+                        }
                     }
                 }
             }
@@ -207,8 +220,6 @@ public class PropertyIndexPlan {
 
     Set<IndexStoreStrategy> getStrategies(NodeState definition,
             MountInfoProvider mountInfoProvider) {
-        boolean unique = definition
-                .getBoolean(IndexConstants.UNIQUE_PROPERTY_NAME);
         return Multiplexers.getStrategies(unique, mountInfoProvider,
                 definition, INDEX_CONTENT_NODE_NAME);
     }

Added: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/index/IndexSelectionTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/index/IndexSelectionTest.java?rev=1763756&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/index/IndexSelectionTest.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/index/IndexSelectionTest.java Fri Oct  7 12:34:52 2016
@@ -0,0 +1,137 @@
+/*
+ * 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.oak.query.index;
+
+import java.util.List;
+import java.util.UUID;
+
+import javax.annotation.Nonnull;
+
+import com.google.common.collect.ImmutableList;
+import org.apache.jackrabbit.JcrConstants;
+import org.apache.jackrabbit.oak.Oak;
+import org.apache.jackrabbit.oak.api.ContentRepository;
+import org.apache.jackrabbit.oak.plugins.index.property.PropertyIndexEditorProvider;
+import org.apache.jackrabbit.oak.plugins.index.property.PropertyIndexPlan;
+import org.apache.jackrabbit.oak.plugins.index.property.PropertyIndexProvider;
+import org.apache.jackrabbit.oak.plugins.nodetype.write.InitialContent;
+import org.apache.jackrabbit.oak.query.AbstractQueryTest;
+import org.apache.jackrabbit.oak.spi.query.Cursor;
+import org.apache.jackrabbit.oak.spi.query.Filter;
+import org.apache.jackrabbit.oak.spi.query.QueryIndex;
+import org.apache.jackrabbit.oak.spi.query.QueryIndexProvider;
+import org.apache.jackrabbit.oak.spi.security.OpenSecurityProvider;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+import org.apache.jackrabbit.oak.util.NodeUtil;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+public class IndexSelectionTest extends AbstractQueryTest {
+    private TestIndexProvider testIndexProvider = new TestIndexProvider();
+    @Override
+    protected ContentRepository createRepository() {
+        return new Oak()
+                .with(new OpenSecurityProvider())
+                .with(new InitialContent())
+                .with(new PropertyIndexEditorProvider())
+                .with(new PropertyIndexProvider())
+                .with(testIndexProvider)
+                .createContentRepository();
+    }
+
+    @Test
+    public void uuidIndexQuery() throws Exception{
+        NodeUtil node = new NodeUtil(root.getTree("/"));
+        String uuid = UUID.randomUUID().toString();
+        node.setString(JcrConstants.JCR_UUID, uuid);
+        root.commit();
+
+        assertQuery("SELECT * FROM [nt:base] WHERE [jcr:uuid] = '"+uuid+"' ",
+                ImmutableList.of("/"));
+        assertEquals("Test index plan should not be invoked", 
+                0, testIndexProvider.index.invocationCount);
+    }
+    
+    @Test
+    public void uuidIndexNotNullQuery() throws Exception{
+        NodeUtil node = new NodeUtil(root.getTree("/"));
+        String uuid = UUID.randomUUID().toString();
+        node.setString(JcrConstants.JCR_UUID, uuid);
+        root.commit();
+
+        assertQuery("SELECT * FROM [nt:base] WHERE [jcr:uuid] is not null",
+                ImmutableList.of("/"));
+        assertEquals("Test index plan should be invoked", 
+                1, testIndexProvider.index.invocationCount);
+    }
+
+    @Test
+    public void uuidIndexInListQuery() throws Exception{
+        NodeUtil node = new NodeUtil(root.getTree("/"));
+        String uuid = UUID.randomUUID().toString();
+        String uuid2 = UUID.randomUUID().toString();
+        node.setString(JcrConstants.JCR_UUID, uuid);
+        root.commit();
+
+        assertQuery("SELECT * FROM [nt:base] WHERE [jcr:uuid] in('" + uuid + "', '" + uuid2 + "')",
+                ImmutableList.of("/"));
+        assertEquals("Test index plan should be invoked", 
+                1, testIndexProvider.index.invocationCount);
+    }
+
+    private static class TestIndexProvider implements QueryIndexProvider {
+        TestIndex index = new TestIndex();
+        @Nonnull
+        @Override
+        public List<? extends QueryIndex> getQueryIndexes(NodeState nodeState) {
+            return ImmutableList.<QueryIndex>of(index);
+        }
+    }
+
+    private static class TestIndex implements QueryIndex {
+        int invocationCount = 0;
+        @Override
+        public double getMinimumCost() {
+            return PropertyIndexPlan.COST_OVERHEAD + 0.1;
+        }
+
+        @Override
+        public double getCost(Filter filter, NodeState rootState) {
+            invocationCount++;
+            return Double.POSITIVE_INFINITY;
+        }
+
+        @Override
+        public Cursor query(Filter filter, NodeState rootState) {
+            return null;
+        }
+
+        @Override
+        public String getPlan(Filter filter, NodeState rootState) {
+            return null;
+        }
+
+        @Override
+        public String getIndexName() {
+            return "test-index";
+        }
+    }
+}
\ No newline at end of file