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 da...@apache.org on 2014/08/05 12:39:47 UTC
svn commit: r1615904 - in /jackrabbit/oak/trunk/oak-core/src:
main/java/org/apache/jackrabbit/oak/plugins/index/property/
main/java/org/apache/jackrabbit/oak/spi/query/
test/java/org/apache/jackrabbit/oak/plugins/index/property/
Author: davide
Date: Tue Aug 5 10:39:47 2014
New Revision: 1615904
URL: http://svn.apache.org/r1615904
Log:
OAK-1980 - Use index on non-root node
Applying OAK-1980.patch. Thanks Marcel.
Modified:
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/spi/query/Cursors.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/query/QueryIndex.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
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=1615904&r1=1615903&r2=1615904&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 Tue Aug 5 10:39:47 2014
@@ -17,6 +17,7 @@
package org.apache.jackrabbit.oak.plugins.index.property;
+import static org.apache.jackrabbit.oak.plugins.index.property.OrderedIndex.OrderDirection;
import static org.apache.jackrabbit.oak.plugins.index.property.OrderedIndex.TYPE;
import java.util.ArrayList;
@@ -24,8 +25,8 @@ import java.util.Collection;
import java.util.List;
import org.apache.jackrabbit.oak.api.PropertyValue;
-import org.apache.jackrabbit.oak.api.Type;
import org.apache.jackrabbit.oak.commons.PathUtils;
+import org.apache.jackrabbit.oak.plugins.index.property.strategy.OrderedContentMirrorStoreStrategy;
import org.apache.jackrabbit.oak.spi.query.Cursor;
import org.apache.jackrabbit.oak.spi.query.Cursors;
import org.apache.jackrabbit.oak.spi.query.Filter;
@@ -36,8 +37,6 @@ import org.apache.jackrabbit.oak.spi.sta
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import com.google.common.collect.ImmutableList;
-
/**
* A property index that supports ordering keys.
*/
@@ -65,7 +64,7 @@ public class OrderedPropertyIndex implem
/**
* @return an builder with some initial common settings
*/
- private static IndexPlan.Builder getIndexPlanBuilder(final Filter filter) {
+ static IndexPlan.Builder getIndexPlanBuilder(final Filter filter) {
IndexPlan.Builder b = new IndexPlan.Builder();
b.setCostPerExecution(1); // we're local. Low-cost
// we're local but slightly more expensive than a standard PropertyIndex
@@ -98,70 +97,20 @@ public class OrderedPropertyIndex implem
OrderedPropertyIndexLookup lookup = getLookup(root);
Collection<PropertyRestriction> restrictions = filter.getPropertyRestrictions();
+ String filterPath = filter.getPath();
// first we process the sole orders as we could be in a situation where we don't have
// a where condition indexed but we do for order. In that case we will return always the
// whole index
if (sortOrder != null) {
for (OrderEntry oe : sortOrder) {
- String propertyName = PathUtils.getName(oe.getPropertyName());
- if (lookup.isIndexed(propertyName, "/", filter)) {
- IndexPlan.Builder b = getIndexPlanBuilder(filter);
- b.setSortOrder(ImmutableList.of(new OrderEntry(
- oe.getPropertyName(),
- Type.UNDEFINED,
- lookup.isAscending(root, propertyName, filter) ? OrderEntry.Order.ASCENDING
- : OrderEntry.Order.DESCENDING)));
- b.setEstimatedEntryCount(lookup.getEstimatedEntryCount(propertyName, null,
- filter, null));
- IndexPlan plan = b.build();
- LOG.debug("plan: {}", plan);
- plans.add(plan);
- }
+ lookup.collectPlans(filter, filterPath, oe, plans);
}
}
// then we add plans for each restriction that could apply to us
for (Filter.PropertyRestriction pr : restrictions) {
- String propertyName = PathUtils.getName(pr.propertyName);
- if (lookup.isIndexed(propertyName, "/", filter)) {
- PropertyValue value = null;
- boolean createPlan = false;
- if (pr.first == null && pr.last == null) {
- // open query: [property] is not null
- value = null;
- createPlan = true;
- } else if (pr.first != null && pr.first.equals(pr.last) && pr.firstIncluding
- && pr.lastIncluding) {
- // [property]=[value]
- value = pr.first;
- createPlan = true;
- } else if (pr.first != null && !pr.first.equals(pr.last)) {
- // '>' & '>=' use cases
- value = pr.first;
- createPlan = true;
- } else if (pr.last != null && !pr.last.equals(pr.first)) {
- // '<' & '<='
- value = pr.last;
- createPlan = true;
- }
- if (createPlan) {
- // we always return a sorted set
- IndexPlan.Builder b = getIndexPlanBuilder(filter);
- b.setSortOrder(ImmutableList.of(new OrderEntry(
- propertyName,
- Type.UNDEFINED,
- lookup.isAscending(root, propertyName, filter) ? OrderEntry.Order.ASCENDING
- : OrderEntry.Order.DESCENDING)));
- long count = lookup.getEstimatedEntryCount(propertyName, value, filter, pr);
- b.setEstimatedEntryCount(count);
- LOG.debug("estimatedCount: {}", count);
-
- IndexPlan plan = b.build();
- LOG.debug("plan: {}", plan);
- plans.add(plan);
- }
- }
+ lookup.collectPlans(filter, filterPath, pr, plans);
}
return plans;
@@ -171,15 +120,12 @@ public class OrderedPropertyIndex implem
public String getPlanDescription(IndexPlan plan, NodeState root) {
LOG.debug("getPlanDescription({}, {})", plan, root);
StringBuilder buff = new StringBuilder("ordered");
- OrderedPropertyIndexLookup lookup = getLookup(root);
- Filter filter = plan.getFilter();
+ NodeState definition = plan.getDefinition();
int depth = 1;
boolean found = false;
- for (PropertyRestriction pr : filter.getPropertyRestrictions()) {
+ if (plan.getPropertyRestriction() != null) {
+ PropertyRestriction pr = plan.getPropertyRestriction();
String propertyName = PathUtils.getName(pr.propertyName);
- if (!lookup.isIndexed(propertyName, "/", filter)) {
- continue;
- }
String operation = null;
PropertyValue value = null;
// TODO support pr.list
@@ -193,13 +139,13 @@ public class OrderedPropertyIndex implem
value = pr.first;
} else if (pr.first != null && !pr.first.equals(pr.last)) {
// '>' & '>=' use cases
- if (lookup.isAscending(root, propertyName, filter)) {
+ if (OrderDirection.isAscending(definition)) {
value = pr.first;
operation = pr.firstIncluding ? ">=" : ">";
}
} else if (pr.last != null && !pr.last.equals(pr.first)) {
// '<' & '<='
- if (!lookup.isAscending(root, propertyName, filter)) {
+ if (!OrderDirection.isAscending(definition)) {
value = pr.last;
operation = pr.lastIncluding ? "<=" : "<";
}
@@ -207,21 +153,14 @@ public class OrderedPropertyIndex implem
if (operation != null) {
buff.append(' ').append(propertyName).append(' ').
append(operation).append(' ').append(value);
- } else {
- continue;
+ found = true;
}
- // stop with the first property that is indexed
- found = true;
- break;
}
List<OrderEntry> sortOrder = plan.getSortOrder();
if (!found && sortOrder != null && !sortOrder.isEmpty()) {
// we could be here if we have a query where the ORDER BY makes us play it.
for (OrderEntry oe : sortOrder) {
String propertyName = PathUtils.getName(oe.getPropertyName());
- if (!lookup.isIndexed(propertyName, "/", null)) {
- continue;
- }
depth = PathUtils.getDepth(oe.getPropertyName());
buff.append(" order by ").append(propertyName);
// stop with the first property that is indexed
@@ -243,34 +182,34 @@ public class OrderedPropertyIndex implem
Filter filter = plan.getFilter();
List<OrderEntry> sortOrder = plan.getSortOrder();
Iterable<String> paths = null;
- Cursor cursor = null;
- OrderedPropertyIndexLookup lookup = getLookup(root);
- Collection<PropertyRestriction> prs = filter.getPropertyRestrictions();
+ OrderedContentMirrorStoreStrategy strategy
+ = OrderedPropertyIndexLookup.getStrategy(plan.getDefinition());
int depth = 1;
- for (PropertyRestriction pr : prs) {
+ PropertyRestriction pr = plan.getPropertyRestriction();
+ if (pr != null) {
String propertyName = PathUtils.getName(pr.propertyName);
- depth = PathUtils.getDepth(pr.propertyName);
- if (lookup.isIndexed(propertyName, "/", filter)) {
- paths = lookup.query(filter, propertyName, pr);
- }
+ depth = PathUtils.getDepth(propertyName);
+ paths = strategy.query(plan.getFilter(), propertyName,
+ plan.getDefinition(), pr);
}
if (paths == null && sortOrder != null && !sortOrder.isEmpty()) {
// we could be here if we have a query where the ORDER BY makes us play it.
for (OrderEntry oe : sortOrder) {
String propertyName = PathUtils.getName(oe.getPropertyName());
depth = PathUtils.getDepth(oe.getPropertyName());
- if (lookup.isIndexed(propertyName, "/", null)) {
- paths = lookup.query(filter, propertyName, new PropertyRestriction());
- }
+ paths = strategy.query(plan.getFilter(), propertyName,
+ plan.getDefinition(), new PropertyRestriction());
}
}
+
if (paths == null) {
// if still here then something went wrong.
throw new IllegalStateException(
"OrderedPropertyIndex index is used even when no index is available for filter "
+ filter);
}
- cursor = Cursors.newPathCursor(paths, filter.getQueryEngineSettings());
+ Cursor cursor = Cursors.newPathCursor(paths, filter.getQueryEngineSettings());
+ cursor = Cursors.newPrefixCursor(cursor, plan.getPathPrefix());
if (depth > 1) {
cursor = Cursors.newAncestorCursor(cursor, depth - 1, filter.getQueryEngineSettings());
}
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=1615904&r1=1615903&r2=1615904&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 Tue Aug 5 10:39:47 2014
@@ -24,7 +24,9 @@ import static org.apache.jackrabbit.oak.
import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.PROPERTY_NAMES;
import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.TYPE_PROPERTY_NAME;
import static org.apache.jackrabbit.oak.plugins.index.property.PropertyIndex.encode;
+import static org.apache.jackrabbit.oak.spi.query.QueryIndex.OrderEntry.Order;
+import java.util.List;
import java.util.Set;
import javax.annotation.Nullable;
@@ -34,27 +36,33 @@ import org.apache.jackrabbit.oak.api.Pro
import org.apache.jackrabbit.oak.api.Type;
import org.apache.jackrabbit.oak.commons.PathUtils;
import org.apache.jackrabbit.oak.plugins.index.property.OrderedIndex.OrderDirection;
-import org.apache.jackrabbit.oak.plugins.index.property.strategy.IndexStoreStrategy;
import org.apache.jackrabbit.oak.plugins.index.property.strategy.OrderedContentMirrorStoreStrategy;
import org.apache.jackrabbit.oak.spi.query.Filter;
import org.apache.jackrabbit.oak.spi.query.Filter.PropertyRestriction;
+import org.apache.jackrabbit.oak.spi.query.QueryIndex;
import org.apache.jackrabbit.oak.spi.state.ChildNodeEntry;
import org.apache.jackrabbit.oak.spi.state.NodeState;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.collect.ImmutableList;
/**
*
*/
public class OrderedPropertyIndexLookup {
+ private static final Logger LOG = LoggerFactory.getLogger(OrderedPropertyIndexLookup.class);
+
/**
* the standard Ascending ordered index
*/
- private static final IndexStoreStrategy STORE = new OrderedContentMirrorStoreStrategy();
+ private static final OrderedContentMirrorStoreStrategy STORE = new OrderedContentMirrorStoreStrategy();
/**
* the descending ordered index
*/
- private static final IndexStoreStrategy REVERSED_STORE = new OrderedContentMirrorStoreStrategy(OrderDirection.DESC);
+ private static final OrderedContentMirrorStoreStrategy REVERSED_STORE = new OrderedContentMirrorStoreStrategy(OrderDirection.DESC);
/**
* we're slightly more expensive than the standard PropertyIndex.
@@ -68,8 +76,19 @@ public class OrderedPropertyIndexLookup
private NodeState root;
+ private String name;
+
+ private OrderedPropertyIndexLookup parent;
+
public OrderedPropertyIndexLookup(NodeState root) {
+ this(root, "", null);
+ }
+
+ public OrderedPropertyIndexLookup(NodeState root, String name,
+ OrderedPropertyIndexLookup parent) {
this.root = root;
+ this.name = name;
+ this.parent = parent;
}
/**
@@ -129,7 +148,7 @@ public class OrderedPropertyIndexLookup
return null;
}
- IndexStoreStrategy getStrategy(NodeState indexMeta) {
+ static OrderedContentMirrorStoreStrategy getStrategy(NodeState indexMeta) {
if (OrderDirection.isAscending(indexMeta)) {
return STORE;
} else {
@@ -144,31 +163,6 @@ public class OrderedPropertyIndexLookup
}
/**
- * Checks whether the named property is indexed somewhere along the given
- * path. Lookup starts at the current path (at the root of this object) and
- * traverses down the path.
- *
- * @param propertyName property name
- * @param path lookup path
- * @param filter for the node type restriction (null if no node type restriction)
- * @return true if the property is indexed
- */
- public boolean isIndexed(String propertyName, String path, Filter filter) {
- if (PathUtils.denotesRoot(path)) {
- return getIndexNode(root, propertyName, filter) != null;
- }
-
- NodeState node = root;
- for (String s : PathUtils.elements(path)) {
- if (getIndexNode(node, propertyName, filter) != null) {
- return true;
- }
- node = node.getChildNode(s);
- }
- return false;
- }
-
- /**
* retrieve the type of the index
*
* @return the type
@@ -209,23 +203,131 @@ public class OrderedPropertyIndexLookup
if (indexMeta == null) {
throw new IllegalArgumentException("No index for " + propertyName);
}
- return ((OrderedContentMirrorStoreStrategy) getStrategy(indexMeta)).query(filter,
- propertyName, indexMeta, pr);
+ return getStrategy(indexMeta).query(filter, propertyName, indexMeta, pr);
}
/**
- * return an estimated count to be used in IndexPlans.
+ * Collect plans for ordered indexes along the given path and order entry.
*
- * @param propertyName
- * @param value
- * @param filter
- * @param pr
- * @return the estimated count
+ * @param filter a filter description.
+ * @param path a relative path from this lookup to the filter path.
+ * @param oe an order entry.
+ * @param plans collected plans are added to this list.
*/
- public long getEstimatedEntryCount(String propertyName, PropertyValue value, Filter filter,
- PropertyRestriction pr) {
- NodeState indexMeta = getIndexNode(root, propertyName, filter);
- OrderedContentMirrorStoreStrategy strategy = (OrderedContentMirrorStoreStrategy) getStrategy(indexMeta);
- return strategy.count(indexMeta, pr, MAX_COST);
+ void collectPlans(Filter filter,
+ String path,
+ QueryIndex.OrderEntry oe,
+ List<QueryIndex.IndexPlan> plans) {
+ String propertyName = PathUtils.getName(oe.getPropertyName());
+ NodeState definition = getIndexNode(root, propertyName, filter);
+ if (definition != null) {
+ Order order = OrderDirection.isAscending(definition)
+ ? Order.ASCENDING : Order.DESCENDING;
+ long entryCount = getStrategy(definition).count(definition, (PropertyRestriction) null, MAX_COST);
+ QueryIndex.IndexPlan.Builder b = OrderedPropertyIndex.getIndexPlanBuilder(filter);
+ b.setSortOrder(ImmutableList.of(new QueryIndex.OrderEntry(oe.getPropertyName(), Type.UNDEFINED, order)));
+ b.setEstimatedEntryCount(entryCount);
+ b.setDefinition(definition);
+ b.setPathPrefix(getPath());
+ QueryIndex.IndexPlan plan = b.build();
+ LOG.debug("plan: {}", plan);
+ plans.add(plan);
+ }
+ // walk down path
+ String remainder = "";
+ OrderedPropertyIndexLookup lookup = null;
+ for (String element : PathUtils.elements(path)) {
+ if (lookup == null) {
+ lookup = new OrderedPropertyIndexLookup(
+ root.getChildNode(element), element, this);
+ } else {
+ remainder = PathUtils.concat(remainder, element);
+ }
+ }
+ if (lookup != null) {
+ lookup.collectPlans(filter, remainder, oe, plans);
+ }
+ }
+
+ /**
+ * Collect plans for ordered indexes along the given path and property
+ * restriction.
+ *
+ * @param filter a filter description.
+ * @param path a relative path from this lookup to the filter path.
+ * @param pr a property restriction.
+ * @param plans collected plans are added to this list.
+ */
+ void collectPlans(Filter filter,
+ String path,
+ PropertyRestriction pr,
+ List<QueryIndex.IndexPlan> plans) {
+ String propertyName = PathUtils.getName(pr.propertyName);
+ NodeState definition = getIndexNode(root, propertyName, filter);
+ if (definition != null) {
+ PropertyValue value = null;
+ boolean createPlan = false;
+ if (pr.first == null && pr.last == null) {
+ // open query: [property] is not null
+ value = null;
+ createPlan = true;
+ } else if (pr.first != null && pr.first.equals(pr.last) && pr.firstIncluding
+ && pr.lastIncluding) {
+ // [property]=[value]
+ value = pr.first;
+ createPlan = true;
+ } else if (pr.first != null && !pr.first.equals(pr.last)) {
+ // '>' & '>=' use cases
+ value = pr.first;
+ createPlan = true;
+ } else if (pr.last != null && !pr.last.equals(pr.first)) {
+ // '<' & '<='
+ value = pr.last;
+ createPlan = true;
+ }
+ if (createPlan) {
+ // we always return a sorted set
+ Order order = OrderDirection.isAscending(definition)
+ ? Order.ASCENDING : Order.DESCENDING;
+ QueryIndex.IndexPlan.Builder b = OrderedPropertyIndex.getIndexPlanBuilder(filter);
+ b.setDefinition(definition);
+ b.setSortOrder(ImmutableList.of(new QueryIndex.OrderEntry(
+ propertyName, Type.UNDEFINED, order)));
+ long count = getStrategy(definition).count(definition, pr, MAX_COST);
+ b.setEstimatedEntryCount(count);
+ b.setPropertyRestriction(pr);
+ b.setPathPrefix(getPath());
+
+ QueryIndex.IndexPlan plan = b.build();
+ LOG.debug("plan: {}", plan);
+ plans.add(plan);
+ }
+ }
+ // walk down path
+ String remainder = "";
+ OrderedPropertyIndexLookup lookup = null;
+ for (String element : PathUtils.elements(path)) {
+ if (lookup == null) {
+ lookup = new OrderedPropertyIndexLookup(
+ root.getChildNode(element), element, this);
+ } else {
+ remainder = PathUtils.concat(remainder, element);
+ }
+ }
+ if (lookup != null) {
+ lookup.collectPlans(filter, remainder, pr, plans);
+ }
+ }
+
+ private String getPath() {
+ return buildPath(new StringBuilder()).toString();
+ }
+
+ private StringBuilder buildPath(StringBuilder sb) {
+ if (parent != null) {
+ parent.buildPath(sb);
+ sb.append("/").append(name);
+ }
+ return sb;
}
}
\ No newline at end of file
Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/query/Cursors.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/query/Cursors.java?rev=1615904&r1=1615903&r2=1615904&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/query/Cursors.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/query/Cursors.java Tue Aug 5 10:39:47 2014
@@ -24,6 +24,7 @@ import java.util.List;
import javax.annotation.Nullable;
+import org.apache.jackrabbit.oak.api.PropertyValue;
import org.apache.jackrabbit.oak.commons.PathUtils;
import org.apache.jackrabbit.oak.plugins.memory.MemoryChildNodeEntry;
import org.apache.jackrabbit.oak.query.FilterIterators;
@@ -71,6 +72,23 @@ public class Cursors {
public static Cursor newPathCursor(Iterable<String> paths, QueryEngineSettings settings) {
return new PathCursor(paths.iterator(), true, settings);
}
+
+ /**
+ * Creates a cursor which wraps another cursor and adds a path prefix to
+ * each of row of the wrapped cursor. This method will return the passed
+ * cursor as is if {@code path} is the empty string or the root path ("/").
+ *
+ * @param c the cursor to wrap.
+ * @param path the path prefix.
+ * @return the cursor.
+ */
+ public static Cursor newPrefixCursor(Cursor c, String path) {
+ if (path.isEmpty() || PathUtils.denotesRoot(path)) {
+ // no need to wrap
+ return c;
+ }
+ return new PrefixCursor(c, path);
+ }
/**
* Creates a {@link Cursor} over paths, and make the result distinct.
@@ -202,6 +220,48 @@ public class Cursors {
}
/**
+ * A cursor which wraps another cursor and adds a path prefix to each of
+ * row of the wrapped cursor.
+ */
+ private static final class PrefixCursor extends AbstractCursor {
+
+ private final Cursor c;
+ private final String path;
+
+ PrefixCursor(Cursor c, String prefix) {
+ this.c = c;
+ this.path = prefix;
+ }
+
+ @Override
+ public IndexRow next() {
+ final IndexRow r = c.next();
+ return new IndexRow() {
+
+ @Override
+ public String getPath() {
+ String sub = r.getPath();
+ if (PathUtils.isAbsolute(sub)) {
+ return path + sub;
+ } else {
+ return PathUtils.concat(path, r.getPath());
+ }
+ }
+
+ @Override
+ public PropertyValue getValue(String columnName) {
+ return r.getValue(columnName);
+ }
+ };
+ }
+
+ @Override
+ public boolean hasNext() {
+ return c.hasNext();
+ }
+ }
+
+ /**
* A cursor that reads all nodes in a given subtree.
*/
private static class TraversingCursor extends AbstractCursor {
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=1615904&r1=1615903&r2=1615904&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 Tue Aug 5 10:39:47 2014
@@ -27,6 +27,8 @@ import org.apache.jackrabbit.oak.api.Typ
import org.apache.jackrabbit.oak.plugins.index.aggregate.NodeAggregator;
import org.apache.jackrabbit.oak.spi.state.NodeState;
+import static org.apache.jackrabbit.oak.spi.query.Filter.PropertyRestriction;
+
/**
* Represents an index. The index should use the data in the filter if possible
* to speed up reading.
@@ -153,7 +155,7 @@ public interface QueryIndex {
* Get the query plan description (for logging purposes).
*
* @param plan the index plan
- * @param rootState root state of the current repository snapshot
+ * @param root root state of the current repository snapshot
* @return the query plan description
*/
String getPlanDescription(IndexPlan plan, NodeState root);
@@ -243,6 +245,29 @@ public interface QueryIndex {
* @return the sort order
*/
List<OrderEntry> getSortOrder();
+
+ /**
+ * The node state with the index definition.
+ *
+ * @return the node state with the index definition.
+ */
+ NodeState getDefinition();
+
+ /**
+ * The path prefix for this index plan.
+ * @return
+ */
+ String getPathPrefix();
+
+ /**
+ * 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.
+ *
+ * @return the restriction this plan is based on or <code>null</code>.
+ */
+ @CheckForNull
+ PropertyRestriction getPropertyRestriction();
/**
* A builder for index plans.
@@ -257,6 +282,9 @@ public interface QueryIndex {
protected boolean isFulltextIndex;
protected boolean includesNodeData;
protected List<OrderEntry> sortOrder;
+ protected NodeState definition;
+ protected PropertyRestriction propRestriction;
+ protected String pathPrefix = "/";
public Builder setCostPerExecution(double costPerExecution) {
this.costPerExecution = costPerExecution;
@@ -297,7 +325,22 @@ public interface QueryIndex {
this.sortOrder = sortOrder;
return this;
}
-
+
+ public Builder setDefinition(NodeState definition) {
+ this.definition = definition;
+ return this;
+ }
+
+ public Builder setPropertyRestriction(PropertyRestriction restriction) {
+ this.propRestriction = restriction;
+ return this;
+ }
+
+ public Builder setPathPrefix(String pathPrefix) {
+ this.pathPrefix = pathPrefix;
+ return this;
+ }
+
public IndexPlan build() {
return new IndexPlan() {
@@ -319,7 +362,14 @@ public interface QueryIndex {
private final List<OrderEntry> sortOrder =
Builder.this.sortOrder == null ?
null : new ArrayList<OrderEntry>(
- Builder.this.sortOrder);
+ Builder.this.sortOrder);
+ private final NodeState definition =
+ Builder.this.definition;
+ private final PropertyRestriction propRestriction =
+ Builder.this.propRestriction;
+ private final String pathPrefix =
+ Builder.this.pathPrefix;
+
@Override
public String toString() {
return String.format(
@@ -330,7 +380,10 @@ public interface QueryIndex {
+ " isDelayed : %s,"
+ " isFulltextIndex : %s,"
+ " includesNodeData : %s,"
- + " sortOrder : %s }",
+ + " sortOrder : %s,"
+ + " definition : %s,"
+ + " propertyRestriction : %s,"
+ + " pathPrefix : %s }",
costPerExecution,
costPerEntry,
estimatedEntryCount,
@@ -338,7 +391,10 @@ public interface QueryIndex {
isDelayed,
isFulltextIndex,
includesNodeData,
- sortOrder
+ sortOrder,
+ definition,
+ propRestriction,
+ pathPrefix
);
}
@@ -386,10 +442,24 @@ public interface QueryIndex {
public List<OrderEntry> getSortOrder() {
return sortOrder;
}
-
+
+ @Override
+ public NodeState getDefinition() {
+ return definition;
+ }
+
+ @Override
+ public PropertyRestriction getPropertyRestriction() {
+ return propRestriction;
+ }
+
+ @Override
+ public String getPathPrefix() {
+ return pathPrefix;
+ }
};
}
-
+
}
}
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=1615904&r1=1615903&r2=1615904&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 Tue Aug 5 10:39:47 2014
@@ -44,29 +44,6 @@ import com.google.common.collect.Immutab
* tests the Cost-related part of the provider/strategy
*/
public class OrderedIndexCostTest extends BasicOrderedPropertyIndexQueryTest {
- /**
- * convenience class that return an always indexed strategy
- */
- private static class AlwaysIndexedOrderedPropertyIndex extends OrderedPropertyIndex {
- @Override
- AlwaysIndexedLookup getLookup(NodeState root) {
- return new AlwaysIndexedLookup(root);
- }
-
- /**
- * convenience class that always return true at the isIndexed test
- */
- private static class AlwaysIndexedLookup extends OrderedPropertyIndexLookup {
- public AlwaysIndexedLookup(NodeState root) {
- super(root);
- }
-
- @Override
- public boolean isIndexed(String propertyName, String path, Filter filter) {
- return true;
- }
- }
- }
@Override
protected void createTestIndexNode() throws Exception {
@@ -147,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 AlwaysIndexedOrderedPropertyIndex();
+ OrderedPropertyIndex index = new OrderedPropertyIndex();
NodeBuilder builder = InitialContent.INITIAL_CONTENT.builder();
defineAscendingIndex(builder);
NodeState root = builder.getNodeState();
@@ -171,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 AlwaysIndexedOrderedPropertyIndex();
+ OrderedPropertyIndex index = new OrderedPropertyIndex();
NodeBuilder builder = InitialContent.INITIAL_CONTENT.builder();
defineAscendingIndex(builder);
NodeState root = builder.getNodeState();
@@ -196,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 AlwaysIndexedOrderedPropertyIndex();
+ OrderedPropertyIndex index = new OrderedPropertyIndex();
NodeBuilder builder = InitialContent.INITIAL_CONTENT.builder();
defineAscendingIndex(builder);
NodeState root = builder.getNodeState();
@@ -215,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 AlwaysIndexedOrderedPropertyIndex();
+ OrderedPropertyIndex index = new OrderedPropertyIndex();
NodeBuilder builder = InitialContent.INITIAL_CONTENT.builder();
defineAscendingIndex(builder);
NodeState root = builder.getNodeState();
@@ -235,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 AlwaysIndexedOrderedPropertyIndex();
+ OrderedPropertyIndex index = new OrderedPropertyIndex();
NodeBuilder builder = InitialContent.INITIAL_CONTENT.builder();
defineDescendingIndex(builder);
NodeState root = builder.getNodeState();
@@ -255,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 AlwaysIndexedOrderedPropertyIndex();
+ OrderedPropertyIndex index = new OrderedPropertyIndex();
NodeBuilder builder = InitialContent.INITIAL_CONTENT.builder();
defineDescendingIndex(builder);
NodeState root = builder.getNodeState();
@@ -276,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 AlwaysIndexedOrderedPropertyIndex();
+ OrderedPropertyIndex index = new OrderedPropertyIndex();
NodeBuilder builder = InitialContent.INITIAL_CONTENT.builder();
defineDescendingIndex(builder);
NodeState root = builder.getNodeState();
@@ -296,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 AlwaysIndexedOrderedPropertyIndex();
+ OrderedPropertyIndex index = new OrderedPropertyIndex();
NodeBuilder builder = InitialContent.INITIAL_CONTENT.builder();
defineDescendingIndex(builder);
NodeState root = builder.getNodeState();
@@ -317,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 AlwaysIndexedOrderedPropertyIndex();
+ OrderedPropertyIndex index = new OrderedPropertyIndex();
NodeBuilder builder = InitialContent.INITIAL_CONTENT.builder();
defineDescendingIndex(builder);
NodeState root = builder.getNodeState();
@@ -340,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 AlwaysIndexedOrderedPropertyIndex();
+ OrderedPropertyIndex index = new OrderedPropertyIndex();
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=1615904&r1=1615903&r2=1615904&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 Tue Aug 5 10:39:47 2014
@@ -22,6 +22,7 @@ import static junit.framework.Assert.ass
import static org.apache.jackrabbit.JcrConstants.JCR_PRIMARYTYPE;
import static org.apache.jackrabbit.JcrConstants.JCR_SYSTEM;
import static org.apache.jackrabbit.JcrConstants.NT_UNSTRUCTURED;
+import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.INDEX_DEFINITIONS_NAME;
import static org.apache.jackrabbit.oak.plugins.nodetype.NodeTypeConstants.JCR_NODE_TYPES;
import static org.junit.Assert.assertNotNull;
@@ -47,7 +48,6 @@ import org.apache.jackrabbit.oak.api.Res
import org.apache.jackrabbit.oak.api.Tree;
import org.apache.jackrabbit.oak.api.Type;
import org.apache.jackrabbit.oak.commons.PathUtils;
-import org.apache.jackrabbit.oak.plugins.index.IndexConstants;
import org.apache.jackrabbit.oak.plugins.index.IndexUpdateProvider;
import org.apache.jackrabbit.oak.plugins.index.IndexUtils;
import org.apache.jackrabbit.oak.plugins.index.property.OrderedIndex.OrderDirection;
@@ -78,9 +78,13 @@ public class OrderedPropertyIndexQueryTe
@Override
protected void createTestIndexNode() throws Exception {
- Tree index = root.getTree("/");
- IndexUtils.createIndexDefinition(new NodeUtil(index.getChild(IndexConstants.INDEX_DEFINITIONS_NAME)),
- TEST_INDEX_NAME, false, new String[] { ORDERED_PROPERTY }, null, OrderedIndex.TYPE);
+ createTestIndexNode("/");
+ }
+
+ protected void createTestIndexNode(String path) throws Exception {
+ Tree index = root.getTree(path);
+ IndexUtils.createIndexDefinition(new NodeUtil(index.getChild(INDEX_DEFINITIONS_NAME)),
+ TEST_INDEX_NAME, false, new String[] { ORDERED_PROPERTY }, null, OrderedIndex.TYPE);
root.commit();
}
@@ -446,7 +450,7 @@ public class OrderedPropertyIndexQueryTe
NodeBuilder root = EmptyNodeState.EMPTY_NODE.builder();
- IndexUtils.createIndexDefinition(root.child(IndexConstants.INDEX_DEFINITIONS_NAME),
+ IndexUtils.createIndexDefinition(root.child(INDEX_DEFINITIONS_NAME),
TEST_INDEX_NAME, false, ImmutableList.of(ORDERED_PROPERTY), null, OrderedIndex.TYPE,
ImmutableMap.<String, String> of());
@@ -523,7 +527,7 @@ public class OrderedPropertyIndexQueryTe
RepositoryException, CommitFailedException {
NodeBuilder root = EmptyNodeState.EMPTY_NODE.builder();
- IndexUtils.createIndexDefinition(root.child(IndexConstants.INDEX_DEFINITIONS_NAME),
+ IndexUtils.createIndexDefinition(root.child(INDEX_DEFINITIONS_NAME),
TEST_INDEX_NAME, false, ImmutableList.of(ORDERED_PROPERTY), null, OrderedIndex.TYPE,
ImmutableMap.<String, String> of());
@@ -564,7 +568,7 @@ public class OrderedPropertyIndexQueryTe
public void planOrderAndWhereMixed() throws IllegalArgumentException, RepositoryException, CommitFailedException {
NodeBuilder root = EmptyNodeState.EMPTY_NODE.builder();
- IndexUtils.createIndexDefinition(root.child(IndexConstants.INDEX_DEFINITIONS_NAME),
+ IndexUtils.createIndexDefinition(root.child(INDEX_DEFINITIONS_NAME),
TEST_INDEX_NAME, false, ImmutableList.of(ORDERED_PROPERTY), null, OrderedIndex.TYPE,
ImmutableMap.<String, String> of());
@@ -867,9 +871,57 @@ public class OrderedPropertyIndexQueryTe
.iterator();
assertRightOrder(Lists.newArrayList(filtered), results);
- assertFalse("We should have looped throuhg all the results", results.hasNext());
+ assertFalse("We should have looped through all the results", results.hasNext());
setTraversalEnabled(true);
}
+
+ @Test
+ public void indexDefinitionBelowRoot() throws Exception {
+ setTraversalEnabled(false);
+
+ // remove the default test index definition
+ root.getTree("/" + INDEX_DEFINITIONS_NAME + "/" + TEST_INDEX_NAME).remove();
+ root.getTree("/").addChild("test").addChild(INDEX_DEFINITIONS_NAME);
+ root.commit();
+ createTestIndexNode("/test");
+
+ Tree test = root.getTree("/test");
+ List<ValuePathTuple> nodes = addChildNodes(generateOrderedValues(NUMBER_OF_NODES), test,
+ OrderDirection.ASC, Type.STRING);
+ root.commit();
+
+ // querying
+ Iterator<? extends ResultRow> results;
+ results = executeQuery(String.format("SELECT * FROM [%s] as s WHERE s.foo IS NOT NULL and ISDESCENDANTNODE(s, '/test')", NT_UNSTRUCTURED), SQL2, null)
+ .getRows().iterator();
+ assertRightOrder(nodes, results);
+
+ setTraversalEnabled(true);
+ }
+
+ @Test
+ public void indexDefinitionBelowRootOrderBy() throws Exception {
+ setTraversalEnabled(false);
+
+ // remove the default test index definition
+ root.getTree("/" + INDEX_DEFINITIONS_NAME + "/" + TEST_INDEX_NAME).remove();
+ root.getTree("/").addChild("test").addChild(INDEX_DEFINITIONS_NAME);
+ root.commit();
+ createTestIndexNode("/test");
+
+ Tree test = root.getTree("/test");
+ List<ValuePathTuple> nodes = addChildNodes(generateOrderedValues(NUMBER_OF_NODES), test,
+ OrderDirection.ASC, Type.STRING);
+ root.commit();
+
+ // querying
+ Iterator<? extends ResultRow> results;
+ results = executeQuery(String.format("SELECT * FROM [%s] as s WHERE ISDESCENDANTNODE(s, '/test') ORDER BY s.foo", NT_UNSTRUCTURED), SQL2, null)
+ .getRows().iterator();
+ assertRightOrder(nodes, results);
+
+ setTraversalEnabled(true);
+ }
}
\ No newline at end of file