From d3690a3cc9c08c1d506e47f6f07ba7440382b434 Mon Sep 17 00:00:00 2001 From: Justin Edelson Date: Mon, 16 Jun 2014 10:54:08 -0400 Subject: [PATCH] OAK-1894 - finding cheapest property --- .../oak/plugins/index/property/PropertyIndex.java | 80 ++++++++++++++-------- .../org/apache/jackrabbit/oak/query/sql2_index.txt | 2 +- 2 files changed, 53 insertions(+), 29 deletions(-) diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndex.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndex.java index 914881c..443f0cb 100644 --- a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndex.java +++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndex.java @@ -30,6 +30,8 @@ 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.NodeState; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import com.google.common.base.Charsets; import com.google.common.collect.Iterables; @@ -90,6 +92,8 @@ class PropertyIndex implements QueryIndex { */ private static final String EMPTY_TOKEN = ":"; + private static final Logger LOG = LoggerFactory.getLogger(PropertyIndex.class); + static Set encode(PropertyValue value) { if (value == null) { return null; @@ -113,6 +117,37 @@ class PropertyIndex implements QueryIndex { return values; } + private Cheapest findCheapestProperty(Filter filter, PropertyIndexLookup lookup) { + Cheapest cost = new Cheapest(); + for (PropertyRestriction pr : filter.getPropertyRestrictions()) { + String propertyName = PathUtils.getName(pr.propertyName); + double propertyCost = Double.POSITIVE_INFINITY; + // TODO support indexes on a path + // currently, only indexes on the root node are supported + if (lookup.isIndexed(propertyName, "/", filter)) { + if (pr.firstIncluding && pr.lastIncluding + && pr.first != null && pr.first.equals(pr.last)) { + // "[property] = $value" + propertyCost = lookup.getCost(filter, propertyName, pr.first); + } else if (pr.list != null) { + propertyCost = 0; + for (PropertyValue p : pr.list) { + propertyCost += lookup.getCost(filter, propertyName, p); + } + } else { + // processed as "[property] is not null" + propertyCost = lookup.getCost(filter, propertyName, null); + } + } + LOG.debug("property cost for {} is {}", propertyName, propertyCost); + if (propertyCost < cost.cost) { + cost.cost = propertyCost; + cost.propertyRestriction = pr; + } + } + return cost; + } + //--------------------------------------------------------< QueryIndex >-- @Override @@ -142,29 +177,9 @@ class PropertyIndex implements QueryIndex { } PropertyIndexLookup lookup = getLookup(root); - for (PropertyRestriction pr : filter.getPropertyRestrictions()) { - String propertyName = PathUtils.getName(pr.propertyName); - // TODO support indexes on a path - // currently, only indexes on the root node are supported - if (lookup.isIndexed(propertyName, "/", filter)) { - if (pr.firstIncluding && pr.lastIncluding - && pr.first != null && pr.first.equals(pr.last)) { - // "[property] = $value" - return lookup.getCost(filter, propertyName, pr.first); - } else if (pr.list != null) { - double cost = 0; - for (PropertyValue p : pr.list) { - cost += lookup.getCost(filter, propertyName, p); - } - return cost; - } else { - // processed as "[property] is not null" - return lookup.getCost(filter, propertyName, null); - } - } - } - // not an appropriate index - return Double.POSITIVE_INFINITY; + Cheapest cheapest = findCheapestProperty(filter, lookup); + LOG.debug("Cheapest property cost is {} for property {}", cheapest.cost, cheapest.propertyRestriction != null ? cheapest.propertyRestriction.propertyName : null); + return cheapest.cost; } @Override @@ -173,7 +188,11 @@ class PropertyIndex implements QueryIndex { PropertyIndexLookup lookup = getLookup(root); int depth = 1; - for (PropertyRestriction pr : filter.getPropertyRestrictions()) { + + Cheapest cheapest = findCheapestProperty(filter, lookup); + PropertyRestriction pr = cheapest.propertyRestriction; + + if (pr != null) { String propertyName = PathUtils.getName(pr.propertyName); depth = PathUtils.getDepth(pr.propertyName); // TODO support indexes on a path @@ -184,7 +203,6 @@ class PropertyIndex implements QueryIndex { && pr.first != null && pr.first.equals(pr.last)) { // "[property] = $value" paths = lookup.query(filter, propertyName, pr.first); - break; } else if (pr.list != null) { for (PropertyValue pv : pr.list) { Iterable p = lookup.query(filter, propertyName, pv); @@ -194,11 +212,9 @@ class PropertyIndex implements QueryIndex { paths = Iterables.concat(paths, p); } } - break; } else { // processed as "[property] is not null" paths = lookup.query(filter, propertyName, null); - break; } } } @@ -217,7 +233,10 @@ class PropertyIndex implements QueryIndex { StringBuilder buff = new StringBuilder("property"); StringBuilder notIndexed = new StringBuilder(); PropertyIndexLookup lookup = getLookup(root); - for (PropertyRestriction pr : filter.getPropertyRestrictions()) { + Cheapest cheapest = findCheapestProperty(filter, lookup); + PropertyRestriction pr = cheapest.propertyRestriction; + + if (pr != null) { String propertyName = PathUtils.getName(pr.propertyName); // TODO support indexes on a path // currently, only indexes on the root node are supported @@ -251,4 +270,9 @@ class PropertyIndex implements QueryIndex { return buff.toString(); } + private static class Cheapest { + private double cost = Double.POSITIVE_INFINITY; + private PropertyRestriction propertyRestriction; + } + } \ No newline at end of file diff --git a/oak-core/src/test/resources/org/apache/jackrabbit/oak/query/sql2_index.txt b/oak-core/src/test/resources/org/apache/jackrabbit/oak/query/sql2_index.txt index 92d8faf..bb25041 100644 --- a/oak-core/src/test/resources/org/apache/jackrabbit/oak/query/sql2_index.txt +++ b/oak-core/src/test/resources/org/apache/jackrabbit/oak/query/sql2_index.txt @@ -212,7 +212,7 @@ explain select * explain select [rep:excerpt] from [nt:base] where [jcr:uuid] is not null -[nt:base] as [nt:base] /* property jcr:uuid (rep:excerpt) +[nt:base] as [nt:base] /* property jcr:uuid where [nt:base].[jcr:uuid] is not null */ commit / + "test": { "jcr:uuid": "xyz", "a": { "jcr:uuid": "123" } } -- 2.0.0