diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndex.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndex.java index 91557f2..bfee995 100644 --- a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndex.java +++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndex.java @@ -150,6 +150,7 @@ public class OrderedPropertyIndex extends PropertyIndex implements AdvancedQuery : OrderEntry.Order.DESCENDING))); long count = lookup.getEstimatedEntryCount(propertyName, value, filter, pr); b.setEstimatedEntryCount(count); + b.setPropertyRestriction(pr); LOG.debug("estimatedCount: {}", count); IndexPlan plan = b.build(); @@ -276,7 +277,7 @@ public class OrderedPropertyIndex extends PropertyIndex implements AdvancedQuery } } else { // if for some reasons it's not an Ordered Lookup we delegate up the chain - cursor = super.query(filter, root); + cursor = super.query(plan, root); } return cursor; } 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..4a1c7f8 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 @@ -18,7 +18,9 @@ package org.apache.jackrabbit.oak.plugins.index.property; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; +import java.util.ArrayList; import java.util.HashSet; +import java.util.List; import java.util.Set; import org.apache.jackrabbit.oak.api.PropertyValue; @@ -28,8 +30,11 @@ import org.apache.jackrabbit.oak.spi.query.Cursor; 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.AdvancedQueryIndex; 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; @@ -80,7 +85,7 @@ import com.google.common.collect.Iterables; * @see QueryIndex * @see PropertyIndexLookup */ -class PropertyIndex implements QueryIndex { +class PropertyIndex implements QueryIndex, AdvancedQueryIndex { // TODO the max string length should be removed, or made configurable private static final int MAX_STRING_LENGTH = 100; @@ -90,6 +95,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; @@ -130,50 +137,151 @@ class PropertyIndex implements QueryIndex { return new PropertyIndexLookup(root); } + /** + * retrieve the cost for the query. + */ @Override public double getCost(Filter filter, NodeState root) { - if (filter.getFullTextConstraint() != null) { - // not an appropriate index for full-text search - return Double.POSITIVE_INFINITY; - } - if (filter.containsNativeConstraint()) { - // not an appropriate index for native search - return Double.POSITIVE_INFINITY; + throw new UnsupportedOperationException("Not supported as implementing AdvancedQueryIndex"); + } + + @Override + public Cursor query(Filter filter, NodeState root) { + throw new UnsupportedOperationException("Not supported as implementing AdvancedQueryIndex"); + } + + @Override + public String getPlan(Filter filter, NodeState root) { + throw new UnsupportedOperationException("Not supported as implementing AdvancedQueryIndex"); + } + + /** + * @return an builder with some initial common settings + */ + private static IndexPlan.Builder getIndexPlanBuilder(final Filter filter) { + IndexPlan.Builder b = new IndexPlan.Builder(); + b.setCostPerExecution(1); + b.setCostPerEntry(1); + b.setFulltextIndex(false); // we're never full-text + b.setIncludesNodeData(false); // we should not include node data + b.setFilter(filter); + // TODO it's synch for now but we should investigate the indexMeta + b.setDelayed(false); + return b; + } + + @Override + public List getPlans(Filter filter, List sortOrder, NodeState root) { + if (LOG.isDebugEnabled()) { + LOG.debug("getPlans(Filter, List, NodeState)"); + LOG.debug("getPlans() - filter: {} - ", filter); + LOG.debug("getPlans() - rootState: {} - ", root); } + List plans = new ArrayList(); PropertyIndexLookup lookup = getLookup(root); - for (PropertyRestriction pr : filter.getPropertyRestrictions()) { + + // then we add plans for each restriction that could apply to us + for (Filter.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); + 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); + long count = lookup.getEstimatedEntryCount(propertyName, value, filter, pr); + b.setEstimatedEntryCount(count); + b.setPropertyRestriction(pr); + LOG.debug("estimatedCount: {}", count); + + IndexPlan plan = b.build(); + LOG.debug("plan: {}", plan); + plans.add(plan); + } + } + } + + return plans; + } + + @Override + public String getPlanDescription(IndexPlan plan, NodeState root) { + StringBuilder buff = new StringBuilder("property"); + StringBuilder notIndexed = new StringBuilder(); + PropertyIndexLookup lookup = getLookup(root); + Filter filter = plan.getFilter(); + PropertyRestriction pr = plan.getPropertyRestriction(); + + for (PropertyRestriction filterRes : filter.getPropertyRestrictions()) { + String propertyName = PathUtils.getName(filterRes.propertyName); + if (lookup.isIndexed(propertyName, "/", filter)) { + if (filterRes == pr) { + buff.append(" usedIndex["); + } else { + buff.append(" unusedIndex["); + } + if (filterRes.firstIncluding && filterRes.lastIncluding + && filterRes.first != null && filterRes.first.equals(filterRes.last)) { + buff.append(propertyName).append('=').append(filterRes.first); + } else { + buff.append(propertyName); + } + buff.append(']'); + } else if (filterRes.list != null) { + buff.append(" list[").append(propertyName).append(" IN("); + int i = 0; + for (PropertyValue pv : filterRes.list) { + if (i++ > 0) { + buff.append(", "); } - return cost; + buff.append(pv); + } + buff.append(')').append(']'); + } else { + if (filterRes.firstIncluding && filterRes.lastIncluding + && filterRes.first != null && filterRes.first.equals(filterRes.last)) { + notIndexed.append(propertyName).append('=').append(filterRes.first); } else { - // processed as "[property] is not null" - return lookup.getCost(filter, propertyName, null); + notIndexed.append(propertyName); } } } - // not an appropriate index - return Double.POSITIVE_INFINITY; + if (notIndexed.length() > 0) { + buff.append(" notIndexed[").append(notIndexed.toString().trim()).append(']'); + } + return buff.toString(); } @Override - public Cursor query(Filter filter, NodeState root) { + public Cursor query(IndexPlan plan, NodeState root) { Iterable paths = null; PropertyIndexLookup lookup = getLookup(root); int depth = 1; - for (PropertyRestriction pr : filter.getPropertyRestrictions()) { + + PropertyRestriction pr = plan.getPropertyRestriction(); + Filter filter = plan.getFilter(); + + if (pr != null) { String propertyName = PathUtils.getName(pr.propertyName); depth = PathUtils.getDepth(pr.propertyName); // TODO support indexes on a path @@ -184,7 +292,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 +301,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; } } } @@ -211,44 +316,5 @@ class PropertyIndex implements QueryIndex { } return c; } - - @Override - public String getPlan(Filter filter, NodeState root) { - StringBuilder buff = new StringBuilder("property"); - StringBuilder notIndexed = new StringBuilder(); - 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)) { - buff.append(' ').append(propertyName).append('=').append(pr.first); - } else { - buff.append(' ').append(propertyName); - } - } else if (pr.list != null) { - buff.append(' ').append(propertyName).append(" IN("); - int i = 0; - for (PropertyValue pv : pr.list) { - if (i++ > 0) { - buff.append(", "); - } - buff.append(pv); - } - buff.append(')'); - } else { - notIndexed.append(' ').append(propertyName); - if (!pr.toString().isEmpty()) { - notIndexed.append(':').append(pr); - } - } - } - if (notIndexed.length() > 0) { - buff.append(" (").append(notIndexed.toString().trim()).append(")"); - } - return buff.toString(); - } } \ No newline at end of file diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexLookup.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexLookup.java index ae18d9b..c0635e9 100644 --- a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexLookup.java +++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexLookup.java @@ -38,6 +38,7 @@ import org.apache.jackrabbit.oak.plugins.index.property.strategy.ContentMirrorSt import org.apache.jackrabbit.oak.plugins.index.property.strategy.IndexStoreStrategy; import org.apache.jackrabbit.oak.plugins.index.property.strategy.UniqueEntryStoreStrategy; 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; @@ -183,6 +184,21 @@ public class PropertyIndexLookup { } /** + * return an estimated count to be used in IndexPlans. + * + * @param propertyName + * @param value + * @param filter + * @param pr + * @return the estimated count + */ + public long getEstimatedEntryCount(String propertyName, PropertyValue value, Filter filter, + PropertyRestriction pr) { + NodeState indexMeta = getIndexNode(root, propertyName, filter); + return getStrategy(indexMeta).count(indexMeta, encode(value), MAX_COST); + } + + /** * retrieve the type of the index * * @return the type diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/query/QueryIndex.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/query/QueryIndex.java index a5cfe10..e7db79e 100644 --- a/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/query/QueryIndex.java +++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/query/QueryIndex.java @@ -25,6 +25,7 @@ import javax.annotation.CheckForNull; import org.apache.jackrabbit.oak.api.Type; import org.apache.jackrabbit.oak.plugins.index.aggregate.NodeAggregator; +import org.apache.jackrabbit.oak.spi.query.Filter.PropertyRestriction; import org.apache.jackrabbit.oak.spi.state.NodeState; /** @@ -243,6 +244,13 @@ public interface QueryIndex { * @return the sort order */ List getSortOrder(); + + /** + * The property restriction identified with this plan. + * + * @return the property restriction + */ + PropertyRestriction getPropertyRestriction(); /** * A builder for index plans. @@ -257,6 +265,7 @@ public interface QueryIndex { protected boolean isFulltextIndex; protected boolean includesNodeData; protected List sortOrder; + protected PropertyRestriction propertyRestriction; public Builder setCostPerExecution(double costPerExecution) { this.costPerExecution = costPerExecution; @@ -297,6 +306,11 @@ public interface QueryIndex { this.sortOrder = sortOrder; return this; } + + public Builder setPropertyRestriction(PropertyRestriction pr) { + this.propertyRestriction = pr; + return this; + } public IndexPlan build() { @@ -327,6 +341,7 @@ public interface QueryIndex { + " costPerEntry : %s," + " estimatedEntryCount : %s," + " filter : %s," + + " propertyRestriction : %s," + " isDelayed : %s," + " isFulltextIndex : %s," + " includesNodeData : %s," @@ -335,6 +350,7 @@ public interface QueryIndex { costPerEntry, estimatedEntryCount, filter, + propertyRestriction, isDelayed, isFulltextIndex, includesNodeData, @@ -386,7 +402,12 @@ public interface QueryIndex { public List getSortOrder() { return sortOrder; } - + + @Override + public PropertyRestriction getPropertyRestriction() { + return propertyRestriction; + } + }; } diff --git a/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexQueryTest.java b/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexQueryTest.java index bf59c6f..01d4105 100644 --- a/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexQueryTest.java +++ b/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexQueryTest.java @@ -33,7 +33,6 @@ import org.apache.jackrabbit.oak.api.ContentRepository; import org.apache.jackrabbit.oak.api.PropertyValue; import org.apache.jackrabbit.oak.api.ResultRow; import org.apache.jackrabbit.oak.api.Tree; -import org.apache.jackrabbit.oak.plugins.index.nodetype.NodeTypeIndexProvider; import org.apache.jackrabbit.oak.plugins.nodetype.write.InitialContent; import org.apache.jackrabbit.oak.query.AbstractQueryTest; import org.apache.jackrabbit.oak.spi.lifecycle.RepositoryInitializer; @@ -57,6 +56,18 @@ public class PropertyIndexQueryTest extends AbstractQueryTest { .with(new OpenSecurityProvider()) .with(new PropertyIndexProvider()) .with(new PropertyIndexEditorProvider()) + .with(new RepositoryInitializer() { + + @Override + public void initialize(NodeBuilder builder) { + createIndexDefinition( + builder.child(INDEX_DEFINITIONS_NAME), + "indexedProperty1", + true, + false, + ImmutableSet.of("indexedProperty1"), null); + } + }) .createContentRepository(); } diff --git a/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/RelativePathTest.java b/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/RelativePathTest.java index 9807390..f83a887 100644 --- a/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/RelativePathTest.java +++ b/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/RelativePathTest.java @@ -74,7 +74,7 @@ public class RelativePathTest extends AbstractQueryTest { Query.JCR_SQL2); assertEquals(1, lines.size()); // make sure it used the property index - assertTrue(lines.get(0).contains("property myProp")); + assertTrue(lines.get(0).contains("property usedIndex[myProp]")); assertQuery( "select [jcr:path] from [nt:base] where [n/myProp] = 'foo'", 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..27e3ec1 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 @@ -27,7 +27,7 @@ explain select * from [nt:base] where [jcr:uuid] like '%' -[nt:base] as [nt:base] /* property jcr:uuid +[nt:base] as [nt:base] /* property usedIndex[jcr:uuid] where [nt:base].[jcr:uuid] like cast('%' as string) */ explain select e.[jcr:path] @@ -42,7 +42,7 @@ explain select e.[jcr:path] and name(c) = 'd' and name(d) = 'e' and (e.[jcr:uuid] = '1' or e.[jcr:uuid] = '2' or e.[jcr:uuid] = '3' or e.[jcr:uuid] = '4') -[nt:base] as [e] /* property jcr:uuid +[nt:base] as [e] /* property usedIndex[jcr:uuid] where ([e].[jcr:uuid] is not null) and ([e].[jcr:uuid] in(cast('1' as string), cast('2' as string), cast('3' as string), cast('4' as string))) */ inner join [nt:base] as [d] /* traverse "* && //parent/of/join" @@ -71,7 +71,7 @@ explain select e.[jcr:path] and name(c) = 'd' and name(d) = 'e' and (e.[jcr:uuid] = '1' or e.[jcr:uuid] = '2' or e.[jcr:uuid] = '3' or e.[jcr:uuid] = '4') -[nt:base] as [e] /* property jcr:uuid +[nt:base] as [e] /* property usedIndex[jcr:uuid] where ([e].[jcr:uuid] is not null) and ([e].[jcr:uuid] in(cast('1' as string), cast('2' as string), cast('3' as string), cast('4' as string))) */ inner join [nt:base] as [d] /* traverse "* && //parent/of/join" @@ -117,7 +117,7 @@ explain select b.[jcr:uuid] from [nt:base] as a inner join [nt:base] as b on isdescendantnode(b, a) where (a.[jcr:uuid] = '1' or a.[jcr:uuid] = '2') -[nt:base] as [a] /* property jcr:uuid +[nt:base] as [a] /* property usedIndex[jcr:uuid] where ([a].[jcr:uuid] is not null) and ([a].[jcr:uuid] in(cast('1' as string), cast('2' as string))) */ inner join [nt:base] as [b] /* traverse "* && //path/from/join//*" */ @@ -128,50 +128,50 @@ explain select b.[jcr:uuid] inner join [nt:base] as b on isdescendantnode(b, a) where (a.[jcr:uuid] = '1' or a.[jcr:uuid] = '2') and b.[jcr:uuid] is not null -[nt:base] as [a] /* property jcr:uuid +[nt:base] as [a] /* property usedIndex[jcr:uuid] where ([a].[jcr:uuid] is not null) and ([a].[jcr:uuid] in(cast('1' as string), cast('2' as string))) */ - inner join [nt:base] as [b] /* property jcr:uuid + inner join [nt:base] as [b] /* property usedIndex[jcr:uuid] where [b].[jcr:uuid] is not null */ on isdescendantnode([b], [a]) explain select * from [nt:base] where length([jcr:uuid])=1 or upper([jcr:uuid])='1' or lower([jcr:uuid])='3' -[nt:base] as [nt:base] /* property jcr:uuid +[nt:base] as [nt:base] /* property usedIndex[jcr:uuid] where [nt:base].[jcr:uuid] is not null */ explain select * from [nt:base] where [jcr:uuid] = '1' or ([jcr:uuid] = '2' and [b] = '3') -[nt:base] as [nt:base] /* property jcr:uuid +[nt:base] as [nt:base] /* property usedIndex[jcr:uuid] where ([nt:base].[jcr:uuid] is not null) and ([nt:base].[jcr:uuid] in(cast('1' as string), cast('2' as string))) */ explain select * from [nt:base] where [jcr:uuid] in('1', '2') -[nt:base] as [nt:base] /* property jcr:uuid +[nt:base] as [nt:base] /* property usedIndex[jcr:uuid] where [nt:base].[jcr:uuid] in(cast('1' as string), cast('2' as string)) */ explain select * from [nt:base] where [jcr:uuid] = '1' or [jcr:uuid] = '2' -[nt:base] as [nt:base] /* property jcr:uuid +[nt:base] as [nt:base] /* property usedIndex[jcr:uuid] where ([nt:base].[jcr:uuid] is not null) and ([nt:base].[jcr:uuid] in(cast('1' as string), cast('2' as string))) */ explain select * from [nt:base] where [jcr:uuid] = '123' -[nt:base] as [nt:base] /* property jcr:uuid=123 +[nt:base] as [nt:base] /* property usedIndex[jcr:uuid=123] where [nt:base].[jcr:uuid] = cast('123' as string) */ explain select * from [nt:base] where [jcr:uuid] is not null -[nt:base] as [nt:base] /* property jcr:uuid +[nt:base] as [nt:base] /* property usedIndex[jcr:uuid] where [nt:base].[jcr:uuid] is not null */ explain select * @@ -179,9 +179,9 @@ explain select * inner join [nt:base] as b on isdescendantnode(b, a) where a.[jcr:uuid] is not null and b.[jcr:uuid] is not null -[nt:base] as [a] /* property jcr:uuid +[nt:base] as [a] /* property usedIndex[jcr:uuid] where [a].[jcr:uuid] is not null */ - inner join [nt:base] as [b] /* property jcr:uuid + inner join [nt:base] as [b] /* property usedIndex[jcr:uuid] where [b].[jcr:uuid] is not null */ on isdescendantnode([b], [a]) @@ -190,10 +190,10 @@ explain select * inner join [nt:base] as b on isdescendantnode(b, a) where (a.[jcr:uuid]=1 or a.[jcr:uuid]=2) and (b.[jcr:uuid]=3 or b.[jcr:uuid]=4) -[nt:base] as [a] /* property jcr:uuid +[nt:base] as [a] /* property usedIndex[jcr:uuid] where ([a].[jcr:uuid] is not null) and ([a].[jcr:uuid] in(cast('1' as long), cast('2' as long))) */ - inner join [nt:base] as [b] /* property jcr:uuid + inner join [nt:base] as [b] /* property usedIndex[jcr:uuid] where ([b].[jcr:uuid] is not null) and ([b].[jcr:uuid] in(cast('3' as long), cast('4' as long))) */ on isdescendantnode([b], [a]) @@ -203,7 +203,7 @@ explain select * inner join [nt:base] as b on isdescendantnode(b, a) where a.[jcr:uuid] is not null and b.[x] is not null -[nt:base] as [a] /* property jcr:uuid +[nt:base] as [a] /* property usedIndex[jcr:uuid] where [a].[jcr:uuid] is not null */ inner join [nt:base] as [b] /* traverse "* && //path/from/join//*" where [b].[x] is not null */ @@ -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 usedIndex[jcr:uuid] notIndexed[rep:excerpt] where [nt:base].[jcr:uuid] is not null */ commit / + "test": { "jcr:uuid": "xyz", "a": { "jcr:uuid": "123" } } @@ -248,3 +248,30 @@ select [jcr:path] /test commit / - "test" + +commit / + "test": { "jcr:uuid": "123", "indexedProperty1": "bar" } +commit / + "test2": { "jcr:uuid": "456", "indexedProperty1": "bar" } +commit / + "test3": { "jcr:uuid": "789", "indexedProperty1": "bar" } +commit / + "test4": { "jcr:uuid": "012", "indexedProperty1": "baz" } +commit / + "test5": { "jcr:uuid": "345", "indexedProperty1": "baz", "nonIndexProperty": "foo" } + +explain select * + from [nt:base] + where [jcr:uuid] = "123" and [indexedProperty1] = "bar" +[nt:base] as [nt:base] /* property unusedIndex[indexedProperty1=bar] usedIndex[jcr:uuid=123] + where ([nt:base].[jcr:uuid] = cast('123' as string)) + and ([nt:base].[indexedProperty1] = cast('bar' as string)) */ + +explain select * + from [nt:base] + where [jcr:uuid] is not null and [indexedProperty1] = "baz" and nonIndexedProperty = "foo" +[nt:base] as [nt:base] /* property usedIndex[indexedProperty1=baz] unusedIndex[jcr:uuid] notIndexed[nonIndexedProperty=foo] + where (([nt:base].[jcr:uuid] is not null) + and ([nt:base].[indexedProperty1] = cast('baz' as string))) + and ([nt:base].[nonIndexedProperty] = cast('foo' as string)) */ + +commit / - "test" +commit / - "test2" +commit / - "test3" +commit / - "test4" +commit / - "test5"