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 ju...@apache.org on 2014/07/17 15:51:14 UTC
svn commit: r1611351 - in
/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property:
OrderedPropertyIndex.java OrderedPropertyIndexLookup.java
Author: jukka
Date: Thu Jul 17 13:51:14 2014
New Revision: 1611351
URL: http://svn.apache.org/r1611351
Log:
OAK-1965: Support for constraints like: foo = 'X' OR bar = 'Y'
Decouple OrderedPropertyIndex from PropertyIndex to allow independent evolution
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
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=1611351&r1=1611350&r2=1611351&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 Thu Jul 17 13:51:14 2014
@@ -30,6 +30,7 @@ import org.apache.jackrabbit.oak.spi.que
import org.apache.jackrabbit.oak.spi.query.Cursors;
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.query.QueryIndex.AdvancedQueryIndex;
import org.apache.jackrabbit.oak.spi.state.NodeState;
import org.slf4j.Logger;
@@ -40,15 +41,14 @@ import com.google.common.collect.Immutab
/**
* A property index that supports ordering keys.
*/
-public class OrderedPropertyIndex extends PropertyIndex implements AdvancedQueryIndex {
+public class OrderedPropertyIndex implements QueryIndex, AdvancedQueryIndex {
+
private static final Logger LOG = LoggerFactory.getLogger(OrderedPropertyIndex.class);
-
- @Override
+
public String getIndexName() {
return TYPE;
}
- @Override
OrderedPropertyIndexLookup getLookup(NodeState root) {
return new OrderedPropertyIndexLookup(root);
}
@@ -58,7 +58,6 @@ public class OrderedPropertyIndex extend
*
* !!! for now we want to skip the use-case of NON range-queries !!!
*/
- @Override
public double getCost(Filter filter, NodeState root) {
throw new UnsupportedOperationException("Not supported as implementing AdvancedQueryIndex");
}
@@ -89,77 +88,72 @@ public class OrderedPropertyIndex extend
}
List<IndexPlan> plans = new ArrayList<IndexPlan>();
- PropertyIndexLookup pil = getLookup(root);
- if (pil instanceof OrderedPropertyIndexLookup) {
- OrderedPropertyIndexLookup lookup = (OrderedPropertyIndexLookup) pil;
- Collection<PropertyRestriction> restrictions = filter.getPropertyRestrictions();
-
- // 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,
+ OrderedPropertyIndexLookup lookup = getLookup(root);
+ Collection<PropertyRestriction> restrictions = filter.getPropertyRestrictions();
+
+ // 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);
- }
+ IndexPlan plan = b.build();
+ LOG.debug("plan: {}", plan);
+ plans.add(plan);
}
}
+ }
- // 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(
+ // 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);
- }
+ : 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);
}
}
- } else {
- LOG.error("Without an OrderedPropertyIndexLookup you should not end here");
}
return plans;
@@ -242,42 +236,49 @@ public class OrderedPropertyIndex extend
List<OrderEntry> sortOrder = plan.getSortOrder();
Iterable<String> paths = null;
Cursor cursor = null;
- PropertyIndexLookup pil = getLookup(root);
- if (pil instanceof OrderedPropertyIndexLookup) {
- OrderedPropertyIndexLookup lookup = (OrderedPropertyIndexLookup) pil;
- Collection<PropertyRestriction> prs = filter.getPropertyRestrictions();
- int depth = 1;
- for (PropertyRestriction pr : prs) {
- String propertyName = PathUtils.getName(pr.propertyName);
- depth = PathUtils.getDepth(pr.propertyName);
- if (lookup.isIndexed(propertyName, "/", filter)) {
- paths = lookup.query(filter, propertyName, pr);
- }
+ OrderedPropertyIndexLookup lookup = getLookup(root);
+ Collection<PropertyRestriction> prs = filter.getPropertyRestrictions();
+ int depth = 1;
+ for (PropertyRestriction pr : prs) {
+ String propertyName = PathUtils.getName(pr.propertyName);
+ depth = PathUtils.getDepth(pr.propertyName);
+ if (lookup.isIndexed(propertyName, "/", filter)) {
+ paths = lookup.query(filter, propertyName, 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());
- }
- }
- }
- 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);
+ }
+ 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());
+ }
}
- cursor = Cursors.newPathCursor(paths, filter.getQueryEngineSettings());
- if (depth > 1) {
- cursor = Cursors.newAncestorCursor(cursor, depth - 1, filter.getQueryEngineSettings());
- }
- } else {
- // if for some reasons it's not an Ordered Lookup we delegate up the chain
- cursor = super.query(filter, root);
+ }
+ 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());
+ if (depth > 1) {
+ cursor = Cursors.newAncestorCursor(cursor, depth - 1, filter.getQueryEngineSettings());
}
return cursor;
}
+
+ //--------------------------------------------------------< QueryIndex >--
+
+ @Override
+ public String getPlan(Filter filter, NodeState root) {
+ return getPlanDescription(getIndexPlanBuilder(filter).build(), root);
+ }
+
+ @Override
+ public Cursor query(Filter filter, NodeState root) {
+ return query(getIndexPlanBuilder(filter).build(), root);
+ }
+
}
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=1611351&r1=1611350&r2=1611351&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 Thu Jul 17 13:51:14 2014
@@ -17,20 +17,35 @@
package org.apache.jackrabbit.oak.plugins.index.property;
+import static com.google.common.collect.Iterables.contains;
+import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.DECLARING_NODE_TYPES;
+import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.INDEX_CONTENT_NODE_NAME;
+import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.INDEX_DEFINITIONS_NAME;
+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 java.util.Set;
+
+import javax.annotation.Nullable;
+
+import org.apache.jackrabbit.oak.api.PropertyState;
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.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.state.ChildNodeEntry;
import org.apache.jackrabbit.oak.spi.state.NodeState;
/**
*
*/
-public class OrderedPropertyIndexLookup extends PropertyIndexLookup {
- private NodeState root;
-
+public class OrderedPropertyIndexLookup {
+
/**
* the standard Ascending ordered index
*/
@@ -45,13 +60,75 @@ public class OrderedPropertyIndexLookup
* we're slightly more expensive than the standard PropertyIndex.
*/
private static final double COST_OVERHEAD = 3;
-
+
+ /**
+ * The maximum cost when the index can be used.
+ */
+ private static final int MAX_COST = 100;
+
+ private NodeState root;
+
public OrderedPropertyIndexLookup(NodeState root) {
- super(root);
this.root = root;
}
- @Override
+ /**
+ * Get the node with the index definition for the given property, if there
+ * is an applicable index with data.
+ *
+ * @param propertyName the property name
+ * @param filter the filter (which contains information of all supertypes,
+ * unless the filter matches all types)
+ * @return the node where the index definition (metadata) is stored (the
+ * parent of ":index"), or null if no index definition or index data
+ * node was found
+ */
+ @Nullable
+ NodeState getIndexNode(NodeState node, String propertyName, Filter filter) {
+ // keep a fallback to a matching index def that has *no* node type constraints
+ // (initially, there is no fallback)
+ NodeState fallback = null;
+
+ NodeState state = node.getChildNode(INDEX_DEFINITIONS_NAME);
+ for (ChildNodeEntry entry : state.getChildNodeEntries()) {
+ NodeState index = entry.getNodeState();
+ PropertyState type = index.getProperty(TYPE_PROPERTY_NAME);
+ if (type == null || type.isArray() || !getType().equals(type.getValue(Type.STRING))) {
+ continue;
+ }
+ if (contains(index.getNames(PROPERTY_NAMES), propertyName)) {
+ NodeState indexContent = index.getChildNode(INDEX_CONTENT_NODE_NAME);
+ if (!indexContent.exists()) {
+ continue;
+ }
+ Set<String> supertypes = getSuperTypes(filter);
+ if (index.hasProperty(DECLARING_NODE_TYPES)) {
+ if (supertypes != null) {
+ for (String typeName : index.getNames(DECLARING_NODE_TYPES)) {
+ if (supertypes.contains(typeName)) {
+ // TODO: prefer the most specific type restriction
+ return index;
+ }
+ }
+ }
+ } else if (supertypes == null) {
+ return index;
+ } else if (fallback == null) {
+ // update the fallback
+ fallback = index;
+ }
+ }
+ }
+ return fallback;
+ }
+
+ private static Set<String> getSuperTypes(Filter filter) {
+ if (filter != null && !filter.matchesAllTypes()) {
+ return filter.getSupertypes();
+ }
+ return null;
+ }
+
IndexStoreStrategy getStrategy(NodeState indexMeta) {
if (OrderDirection.isAscending(indexMeta)) {
return STORE;
@@ -66,12 +143,40 @@ public class OrderedPropertyIndexLookup
return OrderDirection.isAscending(indexMeta);
}
- @Override
+ /**
+ * 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
+ */
String getType() {
return OrderedIndex.TYPE;
}
-
- @Override
+
public double getCost(Filter filter, String propertyName, PropertyValue value) {
double cost = Double.POSITIVE_INFINITY;
NodeState indexMeta = getIndexNode(root, propertyName, filter);
@@ -83,6 +188,14 @@ public class OrderedPropertyIndexLookup
return cost;
}
+ public Iterable<String> query(Filter filter, String propertyName, PropertyValue value) {
+ NodeState indexMeta = getIndexNode(root, propertyName, filter);
+ if (indexMeta == null) {
+ throw new IllegalArgumentException("No index for " + propertyName);
+ }
+ return getStrategy(indexMeta).query(filter, propertyName, indexMeta, encode(value));
+ }
+
/**
* query the strategy for the provided constrains
*
@@ -102,7 +215,7 @@ public class OrderedPropertyIndexLookup
/**
* return an estimated count to be used in IndexPlans.
- *
+ *
* @param propertyName
* @param value
* @param filter