Author: catholicon
Date: Mon Oct 2 01:19:56 2017
New Revision: 1810289
URL: http://svn.apache.org/viewvc?rev=1810289&view=rev
Log:
OAK-6734: IndexPlan: add supportsPathRestrictions
Implement changes for oak-core (QueryImpl mainly) and tests
Added:
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/TraversalAvoidanceTest.java
(with props)
Modified:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/aggregate/AggregateIndexPlan.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/QueryImpl.java
jackrabbit/oak/trunk/oak-query-spi/src/main/java/org/apache/jackrabbit/oak/spi/query/QueryIndex.java
Modified:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/aggregate/AggregateIndexPlan.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/aggregate/AggregateIndexPlan.java?rev=1810289&r1=1810288&r2=1810289&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/aggregate/AggregateIndexPlan.java
(original)
+++
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/aggregate/AggregateIndexPlan.java
Mon Oct 2 01:19:56 2017
@@ -197,6 +197,11 @@ public class AggregateIndexPlan implemen
}
@Override
+ public boolean getSupportsPathRestriction() {
+ return false;
+ }
+
+ @Override
@CheckForNull
public Object getAttribute(String name) {
return null;
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=1810289&r1=1810288&r2=1810289&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
Mon Oct 2 01:19:56 2017
@@ -1032,18 +1032,28 @@ public class QueryImpl implements Query
}
}
potentiallySlowTraversalQuery = bestIndex == null;
- if (traversalEnabled) {
- TraversingIndex traversal = new TraversingIndex();
- double cost = traversal.getCost(filter, rootState);
+ if (bestPlan != null &&
+ (filter.getPathRestriction() ==
Filter.PathRestriction.NO_RESTRICTION ||
+ bestPlan.getSupportsPathRestriction())) {
if (LOG.isDebugEnabled()) {
- logDebug("cost for " + traversal.getIndexName() + " is " +
cost);
+ logDebug("Ignoring traversal. Params:: best index:" +
bestIndex + ";" +
+ " property restriction: " +
filter.getPathRestriction() + ";" +
+ " best plans supports path restriction: " +
bestPlan.getSupportsPathRestriction());
}
- if (cost < bestCost || bestCost == Double.POSITIVE_INFINITY) {
- bestCost = cost;
- bestPlan = null;
- bestIndex = traversal;
- if (potentiallySlowTraversalQuery) {
- potentiallySlowTraversalQuery =
traversal.isPotentiallySlow(filter, rootState);
+ } else {
+ if (traversalEnabled) {
+ TraversingIndex traversal = new TraversingIndex();
+ double cost = traversal.getCost(filter, rootState);
+ if (LOG.isDebugEnabled()) {
+ logDebug("cost for " + traversal.getIndexName() + " is " +
cost);
+ }
+ if (cost < bestCost || bestCost == Double.POSITIVE_INFINITY) {
+ bestCost = cost;
+ bestPlan = null;
+ bestIndex = traversal;
+ if (potentiallySlowTraversalQuery) {
+ potentiallySlowTraversalQuery =
traversal.isPotentiallySlow(filter, rootState);
+ }
}
}
}
Added:
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/TraversalAvoidanceTest.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/TraversalAvoidanceTest.java?rev=1810289&view=auto
==============================================================================
---
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/TraversalAvoidanceTest.java
(added)
+++
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/TraversalAvoidanceTest.java
Mon Oct 2 01:19:56 2017
@@ -0,0 +1,238 @@
+/*
+ * 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;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+import org.apache.jackrabbit.oak.InitialContent;
+import org.apache.jackrabbit.oak.Oak;
+import org.apache.jackrabbit.oak.api.ContentRepository;
+import org.apache.jackrabbit.oak.api.Result;
+import org.apache.jackrabbit.oak.spi.query.Cursor;
+import org.apache.jackrabbit.oak.spi.query.Filter;
+import org.apache.jackrabbit.oak.spi.query.IndexRow;
+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.junit.Assert;
+import org.junit.Test;
+
+import javax.annotation.Nonnull;
+import java.util.List;
+
+public class TraversalAvoidanceTest extends AbstractQueryTest {
+ private static final String QUERY = "SELECT * FROM [nt:base]";
+ private static final String PATH_RESTRICTED_QUERY = "SELECT * FROM
[nt:base] WHERE ISDESCENDANTNODE('/content')";
+ private static final String PATH_RESTRICTED_SLOW_TRAVERSAL_QUERY = "SELECT
* FROM [nt:base] WHERE ISDESCENDANTNODE('/jcr:system')";
+
+ private TestQueryIndexProvider testIndexProvider = new
TestQueryIndexProvider();
+ @Override
+ protected ContentRepository createRepository() {
+ return new Oak()
+ .with(new OpenSecurityProvider())
+ .with(new InitialContent())
+ .with(testIndexProvider)
+ .createContentRepository();
+ }
+
+ @Test
+ public void noPlansLetTraversalWin() {
+ assertPlanSelection(QUERY, "traverse", "Traversal must be used if
nothing else participates");
+ assertPlanSelection(PATH_RESTRICTED_QUERY, "traverse", "Traversal must
be used if nothing" +
+ " else participates");
+ assertPlanSelection(PATH_RESTRICTED_SLOW_TRAVERSAL_QUERY, "traverse",
"Traversal must be" +
+ " used if nothing else participates");
+ }
+
+ @Test
+ public void singlePlanWithoutPathRestrictionWins() {
+ testIndexProvider.addPlan("plan1", 1000000, false);
+
+ assertPlanSelection(QUERY, "plan1", "Valid plan without path
restriction must win");
+ }
+
+ @Test
+ public void singlePlanWithPathRestriction() {
+ testIndexProvider.addPlan("plan1", 1000000, true);
+
+ assertPlanSelection(PATH_RESTRICTED_QUERY, "plan1", "Valid plan which
evaluate path" +
+ " restrictions wins with query having path restriction");
+
+ testIndexProvider.restPlans();
+ testIndexProvider.addPlan("plan1", 1000000, false);
+
+ assertPlanSelection(PATH_RESTRICTED_QUERY, "traverse", "Valid plan
which evaluate path" +
+ " restrictions wins with query having path restriction");
+
+ testIndexProvider.restPlans();
+ testIndexProvider.addPlan("plan1", 10, false);
+ assertPlanSelection(PATH_RESTRICTED_SLOW_TRAVERSAL_QUERY, "plan1",
"cost wars still prevail");
+ }
+
+ @Test
+ public void competingPlans() {
+ testIndexProvider.addPlan("plan1", 1000000, true);
+ testIndexProvider.addPlan("plan2", 100, true);
+
+ assertPlanSelection(QUERY, "plan2", "Low cost must win");
+ assertPlanSelection(PATH_RESTRICTED_QUERY, "plan2", "Low cost must
win");
+ assertPlanSelection(PATH_RESTRICTED_SLOW_TRAVERSAL_QUERY, "plan2",
"Low cost must win");
+
+ testIndexProvider.restPlans();
+ testIndexProvider.addPlan("plan1", 1000000, false);
+ testIndexProvider.addPlan("plan2", 100, true);
+
+ assertPlanSelection(QUERY, "plan2", "Low cost must win");
+ assertPlanSelection(PATH_RESTRICTED_QUERY, "plan2", "Low cost must
win");
+ assertPlanSelection(PATH_RESTRICTED_SLOW_TRAVERSAL_QUERY, "plan2",
"Low cost must win");
+
+ testIndexProvider.restPlans();
+ testIndexProvider.addPlan("plan1", 1000000, true);
+ testIndexProvider.addPlan("plan2", 100, false);
+
+ assertPlanSelection(QUERY, "plan2", "Low cost must win");
+ assertPlanSelection(PATH_RESTRICTED_QUERY, "traverse", "Low cost must
win");
+ assertPlanSelection(PATH_RESTRICTED_SLOW_TRAVERSAL_QUERY, "plan2",
"Low cost must win");
+
+ testIndexProvider.restPlans();
+ testIndexProvider.addPlan("plan1", 1000000, false);
+ testIndexProvider.addPlan("plan2", 100, false);
+
+ assertPlanSelection(QUERY, "plan2", "Low cost must win");
+ assertPlanSelection(PATH_RESTRICTED_QUERY, "traverse", "Low cost must
win");
+ assertPlanSelection(PATH_RESTRICTED_SLOW_TRAVERSAL_QUERY, "plan2",
"Low cost must win");
+ }
+
+ class TestQueryIndexProvider implements QueryIndexProvider {
+ private final TestQueryIndex queryIndex = new TestQueryIndex();
+
+ void addPlan(String name, long cost, boolean supportsPathRestriction) {
+ queryIndex.addPlan(name, cost, supportsPathRestriction);
+ }
+
+ void restPlans() {
+ queryIndex.resetPlans();
+ }
+
+ @Nonnull
+ @Override
+ public List<? extends QueryIndex> getQueryIndexes(NodeState nodeState)
{
+ return ImmutableList.of(queryIndex);
+ }
+ }
+
+ class TestQueryIndex implements QueryIndex, QueryIndex.AdvancedQueryIndex {
+ final String name;
+
+ final List<IndexPlan> plans;
+
+ TestQueryIndex() {
+ this("EmptyName");
+ }
+
+ TestQueryIndex(String name) {
+ this.name = name;
+ plans = Lists.newArrayListWithCapacity(5);
+ }
+
+ @Override
+ public double getMinimumCost() {
+ return 1000;//some high number
+ }
+
+ @Override
+ public double getCost(Filter filter, NodeState rootState) {
+ return getCost();
+ }
+ private double getCost() {
+ return 500;//arbitrary cost - useless as we are AdvanceQueryIndex
+ }
+
+ @Override
+ public Cursor query(Filter filter, NodeState rootState) {
+ return query();
+ }
+
+ @Override
+ public Cursor query(IndexPlan plan, NodeState rootState) {
+ return query();
+ }
+ private Cursor query() {
+ return new TestEmptyCursor();
+ }
+
+ @Override
+ public String getPlan(Filter filter, NodeState rootState) {
+ return "Unimportant plan";
+ }
+
+ @Override
+ public String getIndexName() {
+ return "test index";
+ }
+
+ @Override
+ public List<IndexPlan> getPlans(Filter filter, List<OrderEntry>
sortOrder, NodeState rootState) {
+ return ImmutableList.copyOf(plans);
+ }
+
+ @Override
+ public String getPlanDescription(IndexPlan plan, NodeState root) {
+ return "unimportant plan description (" + plan.getPlanName() + ")";
+ }
+
+ void addPlan(String name, long cost, boolean supportsPathRestriction) {
+ plans.add(new IndexPlan.Builder()
+ .setCostPerEntry(1)
+ .setCostPerExecution(1)
+ .setEstimatedEntryCount(cost)
+ .setSupportsPathRestriction(supportsPathRestriction)
+ .setPlanName(name)
+ .build());
+ }
+
+ void resetPlans() {
+ plans.clear();
+ }
+ }
+
+ class TestEmptyCursor implements Cursor {
+
+ @Override
+ public boolean hasNext() {
+ return false;
+ }
+
+ @Override
+ public IndexRow next() {
+ return null;
+ }
+
+ @Override
+ public long getSize(Result.SizePrecision precision, long max) {
+ return 0;
+ }
+ }
+
+ private String explain(String query){
+ String explain = "explain " + query;
+ return executeQuery(explain, SQL2).get(0);
+ }
+
+ private void assertPlanSelection(String query, String expectedPlan, String
message) {
+ String explain = explain(query);
+ Assert.assertTrue(message, explain.contains(expectedPlan));
+ }
+}
Propchange:
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/TraversalAvoidanceTest.java
------------------------------------------------------------------------------
svn:eol-style = native
Modified:
jackrabbit/oak/trunk/oak-query-spi/src/main/java/org/apache/jackrabbit/oak/spi/query/QueryIndex.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-query-spi/src/main/java/org/apache/jackrabbit/oak/spi/query/QueryIndex.java?rev=1810289&r1=1810288&r2=1810289&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-query-spi/src/main/java/org/apache/jackrabbit/oak/spi/query/QueryIndex.java
(original)
+++
jackrabbit/oak/trunk/oak-query-spi/src/main/java/org/apache/jackrabbit/oak/spi/query/QueryIndex.java
Mon Oct 2 01:19:56 2017
@@ -290,6 +290,11 @@ public interface QueryIndex {
String getPathPrefix();
/**
+ * Whether index for this plan supports handling path restrictions
natively.
+ */
+ boolean getSupportsPathRestriction();
+
+ /**
* The property restriction for this index plan or <code>null</code> if
* this index plan isn't base on a property restriction. E.g. a plan
* based on an order by clause in the query.
@@ -344,6 +349,7 @@ public interface QueryIndex {
protected NodeState definition;
protected PropertyRestriction propRestriction;
protected String pathPrefix = "/";
+ protected boolean supportsPathRestriction = false;
protected Map<String, Object> attributes = Maps.newHashMap();
protected String planName;
@@ -402,6 +408,11 @@ public interface QueryIndex {
return this;
}
+ public Builder setSupportsPathRestriction(boolean
supportsPathRestriction) {
+ this.supportsPathRestriction = supportsPathRestriction;
+ return this;
+ }
+
public Builder setAttribute(String key, Object value) {
this.attributes.put(key, value);
return this;
@@ -440,6 +451,8 @@ public interface QueryIndex {
Builder.this.propRestriction;
private final String pathPrefix =
Builder.this.pathPrefix;
+ private final boolean supportsPathRestriction =
+ Builder.this.supportsPathRestriction;
private final Map<String, Object> attributes =
Builder.this.attributes;
private final String planName = Builder.this.planName;
@@ -457,7 +470,8 @@ public interface QueryIndex {
+ " sortOrder : %s,"
+ " definition : %s,"
+ " propertyRestriction : %s,"
- + " pathPrefix : %s }",
+ + " pathPrefix : %s,"
+ + " supportsPathRestriction : %s }",
costPerExecution,
costPerEntry,
estimatedEntryCount,
@@ -468,7 +482,8 @@ public interface QueryIndex {
sortOrder,
definition,
propRestriction,
- pathPrefix
+ pathPrefix,
+ supportsPathRestriction
);
}
@@ -533,6 +548,11 @@ public interface QueryIndex {
}
@Override
+ public boolean getSupportsPathRestriction() {
+ return supportsPathRestriction;
+ }
+
+ @Override
protected Object clone() throws CloneNotSupportedException
{
return super.clone();
}