Index: oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexEditorProvider.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexEditorProvider.java (date 1414466625000) +++ oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexEditorProvider.java (revision ) @@ -51,7 +51,7 @@ String type, NodeBuilder definition, NodeState root, IndexUpdateCallback callback) throws CommitFailedException { if (TYPE_LUCENE.equals(type)) { - return new LuceneIndexEditor(definition, analyzer, callback); + return new LuceneIndexEditor(root, definition, analyzer, callback); } return null; } Index: oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexDefinition.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexDefinition.java (date 1414466625000) +++ oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexDefinition.java (revision ) @@ -40,6 +40,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.DECLARING_NODE_TYPES; import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.ENTRY_COUNT_PROPERTY_NAME; import static org.apache.jackrabbit.oak.plugins.index.lucene.LuceneIndexConstants.BLOB_SIZE; import static org.apache.jackrabbit.oak.plugins.index.lucene.LuceneIndexConstants.EXCLUDE_PROPERTY_NAMES; @@ -71,6 +72,8 @@ private final Set orderedProps; + private final Set declaringNodeTypes; + private final boolean fullTextEnabled; private final boolean storageEnabled; @@ -111,6 +114,8 @@ this.excludes = toLowerCase(getMultiProperty(defn, EXCLUDE_PROPERTY_NAMES)); this.includes = getMultiProperty(defn, INCLUDE_PROPERTY_NAMES); this.orderedProps = getMultiProperty(defn, ORDERED_PROP_NAMES); + this.declaringNodeTypes = getMultiProperty(defn, DECLARING_NODE_TYPES); + this.blobSize = getOptionalValue(defn, BLOB_SIZE, DEFAULT_BLOB_SIZE); this.fullTextEnabled = getOptionalValue(defn, FULL_TEXT_ENABLED, true); @@ -168,6 +173,9 @@ return propertyTypes; } + public Set getDeclaringNodeTypes() { + return declaringNodeTypes; + } /** * Checks if a given property should be stored in the lucene index or not */ Index: oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexPlanner.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexPlanner.java (date 1414466625000) +++ oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexPlanner.java (revision ) @@ -21,9 +21,11 @@ import java.util.Collections; import java.util.List; +import java.util.Set; import javax.annotation.CheckForNull; +import com.google.common.collect.Sets; import org.apache.jackrabbit.oak.commons.PathUtils; import org.apache.jackrabbit.oak.query.fulltext.FullTextExpression; import org.apache.jackrabbit.oak.spi.query.Filter; @@ -88,16 +90,37 @@ //TODO Need a way to have better cost estimate to indicate that //this index can evaluate more propertyRestrictions natively (if more props are indexed) //For now we reduce cost per entry + int costPerEntryFactor = indexedProps.size(); + + // Restrict matching index when declaringNodeTypes declared + Set supertypes = getSuperTypes(filter); + if (!defn.getDeclaringNodeTypes().isEmpty()) { + if ((supertypes != null) && !Sets + .intersection(defn.getDeclaringNodeTypes(), supertypes).isEmpty()) { + // Reduce cost per entry by a small factor because number of nodes would be less + costPerEntryFactor += 1; + } else { + return null; + } + } + //this index can evaluate more propertyRestrictions natively (if more props are indexed) + //For now we reduce cost per entry IndexPlan.Builder plan = defaultPlan(); if (plan != null) { - return plan.setCostPerEntry(1.0 / indexedProps.size()); + return plan.setCostPerEntry(1.0 / costPerEntryFactor); } } //TODO Support for path restrictions - //TODO support for NodeTypes //TODO Support for property existence queries //TODO support for nodeName queries + return null; + } + + private static Set getSuperTypes(Filter filter) { + if (filter != null && !filter.matchesAllTypes()) { + return filter.getSupertypes(); + } return null; } Index: oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LucenePropertyIndexTest.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LucenePropertyIndexTest.java (date 1414466625000) +++ oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LucenePropertyIndexTest.java (revision ) @@ -29,6 +29,7 @@ import javax.jcr.PropertyType; import com.google.common.collect.ComparisonChain; +import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import org.apache.jackrabbit.JcrConstants; import org.apache.jackrabbit.oak.Oak; @@ -118,6 +119,7 @@ @Test public void indexSelectionVsNodeType() throws Exception { Tree luceneIndex = createIndex("test1", of("propa")); + // decrease cost of lucene property index luceneIndex.setProperty(IndexConstants.ENTRY_COUNT_PROPERTY_NAME, 5L, Type.LONG); // Decrease cost of node type index @@ -138,10 +140,76 @@ root.commit(); String propaQuery = "select [jcr:path] from [nt:unstructured] where [propa] = 'foo'"; - String explain = explain(propaQuery); assertThat(explain(propaQuery), containsString("lucene:test1")); assertQuery(propaQuery, paths); + } + + @Test + public void declaringNodeTypeSameProp() throws Exception { + createIndex("test1", of("propa")); + + Tree indexWithType = createIndex("test2", of("propa")); + indexWithType.setProperty(PropertyStates + .createProperty(IndexConstants.DECLARING_NODE_TYPES, of("nt:unstructured"), + Type.STRINGS)); + + Tree test = root.getTree("/").addChild("test"); + test.setProperty("jcr:primaryType", "nt:unstructured", Type.NAME); + root.commit(); + + Tree a = test.addChild("a"); + a.setProperty("jcr:primaryType", "nt:unstructured", Type.NAME); + a.setProperty("propa", "foo"); + Tree b = test.addChild("b"); + b.setProperty("jcr:primaryType", "nt:unstructured", Type.NAME); + b.setProperty("propa", "foo"); + + test.addChild("c").setProperty("propa", "foo"); + test.addChild("d").setProperty("propa", "foo"); + + root.commit(); + + String propabQuery = "select [jcr:path] from [nt:unstructured] where [propa] = 'foo'"; + assertThat(explain(propabQuery), containsString("lucene:test2")); + assertQuery(propabQuery, asList("/test/a", "/test/b")); + + String propcdQuery = "select [jcr:path] from [nt:base] where [propa] = 'foo'"; + assertThat(explain(propcdQuery), containsString("lucene:test1")); + assertQuery(propcdQuery, asList("/test/a", "/test/b", "/test/c", "/test/d")); + } + + @Test + public void declaringNodeTypeSingleIndex() throws Exception { + Tree indexWithType = createIndex("test2", of("propa", "propb")); + indexWithType.setProperty(PropertyStates + .createProperty(IndexConstants.DECLARING_NODE_TYPES, of("nt:unstructured"), + Type.STRINGS)); + + Tree test = root.getTree("/").addChild("test"); + test.setProperty("jcr:primaryType", "nt:unstructured", Type.NAME); + root.commit(); + + Tree a = test.addChild("a"); + a.setProperty("jcr:primaryType", "nt:unstructured", Type.NAME); + a.setProperty("propa", "foo"); + a.setProperty("propb", "baz"); + + Tree b = test.addChild("b"); + b.setProperty("jcr:primaryType", "nt:unstructured", Type.NAME); + b.setProperty("propa", "foo"); + b.setProperty("propb", "baz"); + + root.commit(); + + String propabQuery = "select [jcr:path] from [nt:unstructured] where [propb] = 'baz' and " + + "[propa] = 'foo'"; + assertThat(explain(propabQuery), containsString("lucene:test2")); + assertQuery(propabQuery, asList("/test/a", "/test/b")); + + String propNoIdxQuery = "select [jcr:path] from [nt:base] where [propb] = 'baz'"; + assertThat(explain(propNoIdxQuery), containsString("no-index")); + assertQuery(propNoIdxQuery, ImmutableList.of()); } @Test Index: oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexEditor.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexEditor.java (date 1414466625000) +++ oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexEditor.java (revision ) @@ -36,6 +36,7 @@ import org.apache.jackrabbit.oak.api.Type; import org.apache.jackrabbit.oak.plugins.index.IndexEditor; import org.apache.jackrabbit.oak.plugins.index.IndexUpdateCallback; +import org.apache.jackrabbit.oak.plugins.nodetype.TypePredicate; import org.apache.jackrabbit.oak.spi.commit.Editor; import org.apache.jackrabbit.oak.spi.state.NodeBuilder; import org.apache.jackrabbit.oak.spi.state.NodeState; @@ -81,20 +82,30 @@ private boolean propertiesChanged = false; - LuceneIndexEditor(NodeBuilder definition, Analyzer analyzer, + private NodeState root; + + private TypePredicate typePredicate; + + LuceneIndexEditor(NodeState root, NodeBuilder definition, Analyzer analyzer, - IndexUpdateCallback updateCallback) throws CommitFailedException { + IndexUpdateCallback updateCallback) throws CommitFailedException { this.parent = null; this.name = null; this.path = "/"; this.context = new LuceneIndexEditorContext(definition, analyzer, updateCallback); + this.root = root; + if (root != null && !context.getDefinition().getDeclaringNodeTypes().isEmpty()) { + typePredicate = new TypePredicate(root, context.getDefinition().getDeclaringNodeTypes()); - } + } + } private LuceneIndexEditor(LuceneIndexEditor parent, String name) { this.parent = parent; this.name = name; this.path = null; this.context = parent.context; + this.root = parent.root; + this.typePredicate = parent.typePredicate; } public String getPath() { @@ -199,6 +210,11 @@ private Document makeDocument(String path, NodeState state, boolean isUpdate) throws CommitFailedException { //TODO Possibly we can add support for compound properties like foo/bar //i.e. support for relative path restrictions + + // Check for declaringNodeType validity + if (typePredicate != null && !typePredicate.apply(state)) { + return null; + } List fields = new ArrayList(); boolean dirty = false;