This is an automated email from the ASF dual-hosted git repository.

mkataria pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/jackrabbit-oak.git


The following commit(s) were added to refs/heads/trunk by this push:
     new 07809da  OAK-9457: Lucene index is always used over ES index when the 
cost is minimal (#298)
07809da is described below

commit 07809da9e4171f92189591c6bb1f1157687e30d9
Author: Mohit Kataria <mkata...@apache.org>
AuthorDate: Fri Jun 25 12:00:34 2021 +0530

    OAK-9457: Lucene index is always used over ES index when the cost is 
minimal (#298)
---
 .../org/apache/jackrabbit/oak/query/QueryImpl.java |  10 +-
 .../jackrabbit/oak/IndexCostEvaluationTest.java    | 192 +++++++++++++++++++++
 .../plugins/index/lucene/LucenePropertyIndex.java  |   6 -
 .../plugins/index/elastic/query/ElasticIndex.java  |   8 -
 .../index/search/spi/query/FulltextIndex.java      |   7 +
 5 files changed, 207 insertions(+), 16 deletions(-)

diff --git 
a/oak-core/src/main/java/org/apache/jackrabbit/oak/query/QueryImpl.java 
b/oak-core/src/main/java/org/apache/jackrabbit/oak/query/QueryImpl.java
index f91cd3b..fc21ecb 100644
--- a/oak-core/src/main/java/org/apache/jackrabbit/oak/query/QueryImpl.java
+++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/query/QueryImpl.java
@@ -1041,8 +1041,14 @@ public class QueryImpl implements Query {
             QueryIndex index = queryIndexes.get(i);
             double minCost = index.getMinimumCost();
             if (minCost > bestCost) {
-                // Stop looking if the minimum cost is higher than the current 
best cost
-                break;
+                if (Math.abs(minCost - bestIndex.getMinimumCost()) < .00001) {
+                    // Continue with cost evaluation if minimum cost of both 
indexes are same i.e both indexes are on par.
+                    LOG.debug("minCost: {} of index :{} > best Cost: {} from 
index: {}, but both indexes have same minimum cost - cost evaluation will 
continue", minCost, index.getIndexName(), bestCost, bestIndex.getIndexName());
+                } else {
+                    // Stop looking if the minimum cost is higher than the 
current best cost
+                    LOG.debug("minCost: {} of index :{} < best Cost: {} from 
index: {}. Further index evaluation will be skipped", minCost, 
index.getIndexName(), bestCost, bestIndex.getIndexName());
+                    break;
+                }
             }
 
             double cost;
diff --git 
a/oak-jcr/src/test/java/org/apache/jackrabbit/oak/IndexCostEvaluationTest.java 
b/oak-jcr/src/test/java/org/apache/jackrabbit/oak/IndexCostEvaluationTest.java
new file mode 100644
index 0000000..b5cf426
--- /dev/null
+++ 
b/oak-jcr/src/test/java/org/apache/jackrabbit/oak/IndexCostEvaluationTest.java
@@ -0,0 +1,192 @@
+/*
+ * 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;
+
+import ch.qos.logback.classic.Level;
+import com.google.common.collect.ImmutableList;
+import org.apache.jackrabbit.api.JackrabbitRepository;
+import org.apache.jackrabbit.api.JackrabbitSession;
+import org.apache.jackrabbit.oak.commons.junit.LogCustomizer;
+import org.apache.jackrabbit.oak.jcr.Jcr;
+import org.apache.jackrabbit.oak.plugins.index.property.PropertyIndexPlan;
+import org.apache.jackrabbit.oak.query.QueryEngineSettings;
+import org.apache.jackrabbit.oak.query.index.FilterImpl;
+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.state.NodeState;
+import org.jetbrains.annotations.NotNull;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+import javax.jcr.Node;
+import javax.jcr.Repository;
+import javax.jcr.SimpleCredentials;
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.junit.Assert.assertTrue;
+
+public class IndexCostEvaluationTest {
+    @Rule
+    public TemporaryFolder temporaryFolder = new TemporaryFolder(new 
File("target"));
+
+    private static final String TEST_USER_NAME = "testUserName";
+
+    private Repository repository = null;
+    private JackrabbitSession session = null;
+    private Node root = null;
+    private LogCustomizer custom;
+
+    @Before
+    public void before() throws Exception {
+        custom = LogCustomizer
+                .forLogger(
+                        "org.apache.jackrabbit.oak.query.QueryImpl")
+                .enable(Level.DEBUG).create();
+        custom.starting();
+
+
+        TestIndexProvider testProvider = new TestIndexProvider();
+        TestIndexProvider2 testProvider2 = new TestIndexProvider2();
+        TestIndexProvider3 testProvider3 = new TestIndexProvider3();
+
+        Jcr jcr = new Jcr()
+                .with((QueryIndexProvider) testProvider)
+                .with((QueryIndexProvider) testProvider2)
+                .with((QueryIndexProvider) testProvider3);
+
+        repository = jcr.createRepository();
+        session = (JackrabbitSession) repository.login(new 
SimpleCredentials("admin", "admin".toCharArray()));
+        root = session.getRootNode();
+    }
+
+
+    @After
+    public void after() {
+        custom.finished();
+        session.logout();
+        if (repository instanceof JackrabbitRepository) {
+            ((JackrabbitRepository) repository).shutdown();
+        }
+    }
+
+    // In cases where two indexes have same min cost i.e. both indexes are on 
par, we don't skip cost evaluation
+    // even of cost from previous index is less than min cost of new index.
+    @Test
+    public void costEvaluationTest() throws Exception {
+        boolean evaluationContinueLogPresent = false;
+        boolean evaluationSkipLogPresent = false;
+        for (String log : custom.getLogs()) {
+            if (log.equals("minCost: 2.1 of index :test-index2 > best Cost: 
2.0 from index: test-index, but both indexes have same minimum cost - cost 
evaluation will continue")) {
+                evaluationContinueLogPresent = true;
+            }
+            if (log.equals("minCost: 2.11 of index :test-index3 < best Cost: 
2.0 from index: test-index. Further index evaluation will be skipped")) {
+                evaluationSkipLogPresent = true;
+            }
+        }
+        assertTrue(evaluationContinueLogPresent);
+        assertTrue(evaluationSkipLogPresent);
+    }
+
+    private class TestIndexProvider implements QueryIndexProvider {
+        TestIndex index = new TestIndex();
+
+        @Override
+        public @NotNull List<? extends QueryIndex> getQueryIndexes(NodeState 
nodeState) {
+            return ImmutableList.<QueryIndex>of(index);
+        }
+    }
+
+    private class TestIndexProvider2 extends TestIndexProvider {
+        public TestIndexProvider2() {
+            this.index = new TestIndex() {
+                public String getIndexName() {
+                    return "test-index2";
+                }
+            };
+        }
+    }
+
+    private class TestIndexProvider3 extends TestIndexProvider {
+        public TestIndexProvider3() {
+            this.index = new TestIndex() {
+                public String getIndexName() {
+                    return "test-index3";
+                }
+
+                public double getMinimumCost() {
+                    return PropertyIndexPlan.COST_OVERHEAD + 0.11;
+                }
+            };
+        }
+    }
+
+    private class TestIndex implements QueryIndex, 
QueryIndex.AdvancedQueryIndex {
+
+        @Override
+        public double getMinimumCost() {
+            return PropertyIndexPlan.COST_OVERHEAD + 0.1;
+        }
+
+        @Override
+        public double getCost(Filter filter, NodeState rootState) {
+            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";
+        }
+
+        @Override
+        public List<IndexPlan> getPlans(Filter filter, List<OrderEntry> 
sortOrder, NodeState rootState) {
+            IndexPlan.Builder b = new IndexPlan.Builder();
+            Filter f = new FilterImpl(null, "SELECT * FROM [nt:file]", new 
QueryEngineSettings());
+            IndexPlan plan1 = 
b.setEstimatedEntryCount(10).setPlanName("testIndexPlan1").setFilter(f).build();
+            List<IndexPlan> indexList = new ArrayList<IndexPlan>();
+
+            indexList.add(plan1);
+            return indexList;
+        }
+
+        @Override
+        public String getPlanDescription(IndexPlan plan, NodeState root) {
+            return null;
+        }
+
+        @Override
+        public Cursor query(IndexPlan plan, NodeState rootState) {
+            return null;
+        }
+    }
+}
diff --git 
a/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LucenePropertyIndex.java
 
b/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LucenePropertyIndex.java
index 2a31b0b..811675c 100644
--- 
a/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LucenePropertyIndex.java
+++ 
b/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LucenePropertyIndex.java
@@ -201,7 +201,6 @@ public class LucenePropertyIndex extends FulltextIndex {
     private final boolean CACHE_FACET_RESULTS =
             Boolean.parseBoolean(System.getProperty(CACHE_FACET_RESULTS_NAME, 
"true"));
 
-    private static double MIN_COST = 2.1;
     private static boolean FLAG_CACHE_FACET_RESULTS_CHANGE = true;
 
     private static final Logger LOG = LoggerFactory
@@ -242,11 +241,6 @@ public class LucenePropertyIndex extends FulltextIndex {
     }
 
     @Override
-    public double getMinimumCost() {
-        return MIN_COST;
-    }
-
-    @Override
     public String getIndexName() {
         return "lucene-property";
     }
diff --git 
a/oak-search-elastic/src/main/java/org/apache/jackrabbit/oak/plugins/index/elastic/query/ElasticIndex.java
 
b/oak-search-elastic/src/main/java/org/apache/jackrabbit/oak/plugins/index/elastic/query/ElasticIndex.java
index 2779bf0..a7cac11 100644
--- 
a/oak-search-elastic/src/main/java/org/apache/jackrabbit/oak/plugins/index/elastic/query/ElasticIndex.java
+++ 
b/oak-search-elastic/src/main/java/org/apache/jackrabbit/oak/plugins/index/elastic/query/ElasticIndex.java
@@ -46,9 +46,6 @@ class ElasticIndex extends FulltextIndex {
     // no concept of rewound in ES (even if it might be doing it internally, 
we can't do much about it
     private static final IteratorRewoundStateProvider 
REWOUND_STATE_PROVIDER_NOOP = () -> 0;
 
-    // # OAK-9419 
-    private static final double MIN_COST = 2.2;
-
     private final ElasticIndexTracker elasticIndexTracker;
 
     ElasticIndex(ElasticIndexTracker elasticIndexTracker) {
@@ -76,11 +73,6 @@ class ElasticIndex extends FulltextIndex {
     }
 
     @Override
-    public double getMinimumCost() {
-        return MIN_COST;
-    }
-
-    @Override
     public String getIndexName() {
         return "elasticsearch";
     }
diff --git 
a/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/query/FulltextIndex.java
 
b/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/query/FulltextIndex.java
index ea63ff0..aebcd1b 100644
--- 
a/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/query/FulltextIndex.java
+++ 
b/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/query/FulltextIndex.java
@@ -74,6 +74,8 @@ public abstract class FulltextIndex implements 
AdvancedQueryIndex, QueryIndex, N
 
     public static final String ATTR_PLAN_RESULT = "oak.fulltext.planResult";
 
+    private static final double MIN_COST = 2.1;
+
     protected abstract IndexNode acquireIndexNode(String indexPath);
 
     protected abstract String getType();
@@ -100,6 +102,11 @@ public abstract class FulltextIndex implements 
AdvancedQueryIndex, QueryIndex, N
     }
 
     @Override
+    public double getMinimumCost() {
+        return MIN_COST;
+    }
+
+    @Override
     public List<IndexPlan> getPlans(Filter filter, List<OrderEntry> sortOrder, 
NodeState rootState) {
         Collection<String> indexPaths = new IndexLookup(rootState, 
getIndexDefinitionPredicate())
                 .collectIndexNodePaths(filter);

Reply via email to