### Eclipse Workspace Patch 1.0 #P oak-core Index: src/test/java/org/apache/jackrabbit/oak/plugins/index/nodetype/NodeTypeIndexTest.java =================================================================== --- src/test/java/org/apache/jackrabbit/oak/plugins/index/nodetype/NodeTypeIndexTest.java (revision 1604905) +++ src/test/java/org/apache/jackrabbit/oak/plugins/index/nodetype/NodeTypeIndexTest.java (working copy) @@ -72,6 +72,9 @@ // remove "rep:security" as it interferes with tests root.getChildNode("rep:security").remove(); + + // remove "entryCount", so the node type index cost is not fixed + root.getChildNode("oak:index").getChildNode("nodetype").removeProperty("entryCount"); addFolder(root, "folder-1"); addFolder(root, "folder-2"); Index: src/main/java/org/apache/jackrabbit/oak/plugins/index/IndexConstants.java =================================================================== --- src/main/java/org/apache/jackrabbit/oak/plugins/index/IndexConstants.java (revision 1604905) +++ src/main/java/org/apache/jackrabbit/oak/plugins/index/IndexConstants.java (working copy) @@ -39,6 +39,8 @@ String ENTRY_COUNT_PROPERTY_NAME = "entryCount"; + String KEY_COUNT_PROPERTY_NAME = "keyCount"; + /** * Marks a unique property index. */ Index: src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/ContentMirrorStoreStrategy.java =================================================================== --- src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/ContentMirrorStoreStrategy.java (revision 1604905) +++ src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/ContentMirrorStoreStrategy.java (working copy) @@ -19,6 +19,7 @@ import static com.google.common.collect.Queues.newArrayDeque; import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.INDEX_CONTENT_NODE_NAME; import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.ENTRY_COUNT_PROPERTY_NAME; +import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.KEY_COUNT_PROPERTY_NAME; import java.util.Deque; import java.util.Iterator; @@ -177,6 +178,20 @@ if (size == 0) { return 0; } + PropertyState ec = indexMeta.getProperty(ENTRY_COUNT_PROPERTY_NAME); + if (ec != null) { + long entryCount = ec.getValue(Type.LONG); + long keyCount = entryCount / 10000; + ec = indexMeta.getProperty(KEY_COUNT_PROPERTY_NAME); + if (ec != null) { + keyCount = ec.getValue(Type.LONG); + } + // cast to double to avoid overflow + // (entryCount could be Long.MAX_VALUE) + // the cost is not multiplied by the size, + // otherwise the traversing index might be used + return (long) ((double) entryCount / keyCount) + size; + } max = Math.max(10, max / size); int i = 0; for (String p : values) { #P oak-jcr Index: src/test/java/org/apache/jackrabbit/oak/jcr/query/QueryPlanTest.java =================================================================== --- src/test/java/org/apache/jackrabbit/oak/jcr/query/QueryPlanTest.java (revision 1604905) +++ src/test/java/org/apache/jackrabbit/oak/jcr/query/QueryPlanTest.java (working copy) @@ -46,6 +46,55 @@ } @Test + // OAK-1902 + public void propertyIndexVersusNodeTypeIndex() throws Exception { + Session session = getAdminSession(); + Node nt = session.getRootNode().getNode("oak:index").getNode("nodetype"); + nt.setProperty("entryCount", Long.MAX_VALUE); + QueryManager qm = session.getWorkspace().getQueryManager(); + Node testRootNode = session.getRootNode().addNode("testroot"); + for (int i = 0; i < 100; i++) { + Node n = testRootNode.addNode("n" + i, "oak:Unstructured"); + n.addMixin("mix:referenceable"); + } + session.save(); + + Query q; + QueryResult result; + RowIterator it; + + String xpath = "/jcr:root/a/b/c/d/e/f/g/h/i/j/k/element(*, oak:Unstructured)"; + q = qm.createQuery("explain " + xpath, "xpath"); + result = q.execute(); + it = result.getRows(); + assertTrue(it.hasNext()); + String plan = it.nextRow().getValue("plan").getString(); + // System.out.println("plan: " + plan); + // should use the node type index + assertEquals("[oak:Unstructured] as [a] " + + "/* Filter(query=explain select [jcr:path], [jcr:score], * " + + "from [oak:Unstructured] as a " + + "where ischildnode(a, '/a/b/c/d/e/f/g/h/i/j/k') " + + "/* xpath: /jcr:root/a/b/c/d/e/f/g/h/i/j/k/element(*, oak:Unstructured) */" + + ", path=/a/b/c/d/e/f/g/h/i/j/k/*) where " + + "ischildnode([a], [/a/b/c/d/e/f/g/h/i/j/k]) */", + plan); + + String xpath2 = "/jcr:root/a/b/c/d/e/f/g/h/i/j/k/element(*, oak:Unstructured)[@jcr:uuid]"; + q = qm.createQuery("explain " + xpath2 + "", "xpath"); + result = q.execute(); + it = result.getRows(); + assertTrue(it.hasNext()); + plan = it.nextRow().getValue("plan").getString(); + // System.out.println("plan: " + plan); + // should use the index on "jcr:uuid" + assertEquals("[oak:Unstructured] as [a] " + + "/* property jcr:uuid where ([a].[jcr:uuid] is not null) " + + "and (ischildnode([a], [/a/b/c/d/e/f/g/h/i/j/k])) */", + plan); + } + + @Test // OAK-1903 public void propertyEqualsVersusPropertyNotNull() throws Exception { Session session = getAdminSession();