PropertyIndex implements QueryIndex { @Override public double getCost(Filter filter, NodeState root) { if (filter.getFullTextConstraint() != null) { return Double.POSITIVE_INFINITY; } if (filter.containsNativeConstraint()) { return Double.POSITIVE_INFINITY; } if (filter.getPropertyRestrictions().isEmpty()) { return Double.POSITIVE_INFINITY; } PropertyIndexPlan plan = getPlan(root, filter); if (plan != null) { return plan.getCost(); } else { return Double.POSITIVE_INFINITY; } } PropertyIndex(MountInfoProvider mountInfoProvider); @Override double getMinimumCost(); @Override String getIndexName(); @Override String getIndexName(Filter filter, NodeState root); @Override double getCost(Filter filter, NodeState root); @Override Cursor query(Filter filter, NodeState root); @Override String getPlan(Filter filter, NodeState root);  }
@Test public void costEstimation() throws Exception { NodeState root = INITIAL_CONTENT; NodeBuilder builder = root.builder(); NodeBuilder index = createIndexDefinition(builder.child(INDEX_DEFINITIONS_NAME), "foo", true, false, ImmutableSet.of("foo"), null); index.setProperty("entryCount", -1); NodeState before = builder.getNodeState(); for (int i = 0; i < MANY; i++) { builder.child("n" + i).setProperty("foo", "x" + i % 20); } NodeState after = builder.getNodeState(); NodeState indexed = HOOK.processCommit(before, after, CommitInfo.EMPTY); FilterImpl f = createFilter(indexed, NT_BASE); PropertyIndexLookup lookup = new PropertyIndexLookup(indexed); double cost; cost = lookup.getCost(f, "foo", PropertyValues.newString("x1")); assertTrue("cost: " + cost, cost >= 6.5 && cost <= 7.5); cost = lookup.getCost(f, "foo", PropertyValues.newString( Arrays.asList("x1", "x2"))); assertTrue("cost: " + cost, cost >= 11.5 && cost <= 12.5); cost = lookup.getCost(f, "foo", PropertyValues.newString( Arrays.asList("x1", "x2", "x3", "x4", "x5"))); assertTrue("cost: " + cost, cost >= 26.5 && cost <= 27.5); cost = lookup.getCost(f, "foo", PropertyValues.newString( Arrays.asList("x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x0"))); assertTrue("cost: " + cost, cost >= 51.5 && cost <= 52.5); cost = lookup.getCost(f, "foo", null); assertTrue("cost: " + cost, cost >= MANY); }
@Test public void pathBasedCostEstimation() throws Exception { NodeState root = INITIAL_CONTENT; NodeBuilder builder = root.builder(); NodeBuilder index = createIndexDefinition(builder.child(INDEX_DEFINITIONS_NAME), "foo", true, false, ImmutableSet.of("foo"), null); index.setProperty("entryCount", -1); builder.setProperty(COUNT_PROPERTY_NAME, (long) MANY * 2, Type.LONG); NodeState before = builder.getNodeState(); NodeBuilder path1 = builder.child("path1"); NodeBuilder path2 = builder.child("path2"); for (int i = 0; i < MANY; i++) { path1.child("n" + i).setProperty("foo", "x" + i % 20); path2.child("n" + i).setProperty("foo", "x" + i % 20); } path1.setProperty(COUNT_PROPERTY_NAME, (long) MANY, Type.LONG); NodeState after = builder.getNodeState(); NodeState indexed = HOOK.processCommit(before, after, CommitInfo.EMPTY); FilterImpl f = createFilter(indexed, NT_BASE); f.restrictPath("/path1", Filter.PathRestriction.ALL_CHILDREN); PropertyIndexLookup lookup = new PropertyIndexLookup(indexed); double cost; cost = lookup.getCost(f, "foo", PropertyValues.newString("x1")); assertTrue("cost: " + cost, cost >= 10 && cost <= 14); cost = lookup.getCost(f, "foo", PropertyValues.newString( Arrays.asList("x1", "x2"))); assertTrue("cost: " + cost, cost >= 20 && cost <= 24); cost = lookup.getCost(f, "foo", PropertyValues.newString( Arrays.asList("x1", "x2", "x3", "x4", "x5"))); assertTrue("cost: " + cost, cost >= 50 && cost <= 54); cost = lookup.getCost(f, "foo", PropertyValues.newString( Arrays.asList("x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x0"))); assertTrue("cost: " + cost, cost >= 120 && cost <= 124); cost = lookup.getCost(f, "foo", null); assertTrue("cost: " + cost, cost >= MANY); }
@Test public void costMaxEstimation() throws Exception { NodeState root = EmptyNodeState.EMPTY_NODE; NodeBuilder builder = root.builder(); createIndexDefinition(builder.child(INDEX_DEFINITIONS_NAME), "foo", true, false, ImmutableSet.of("foo"), null); NodeState before = builder.getNodeState(); int nodes = 101; int levels = 12; NodeBuilder data = builder; for (int i = 0; i < levels; i++) { data = data.child("l" + i); } for (int i = 0; i < nodes; i++) { NodeBuilder c = data.child("c_" + i); c.setProperty("foo", "azerty"); } for (int i = 0; i < 10000; i++) { data.child("cx_" + i); } NodeState after = builder.getNodeState(); NodeState indexed = HOOK.processCommit(before, after, CommitInfo.EMPTY); FilterImpl f = createFilter(indexed, NT_BASE); PropertyIndexLookup lookup = new PropertyIndexLookup(indexed); double cost = lookup.getCost(f, "foo", PropertyValues.newString("azerty")); double traversal = new TraversingIndex().getCost(f, indexed); assertTrue("Estimated cost for " + nodes + " nodes should not be higher than traversal (" + cost + " < " + traversal + ")", cost < traversal); }
@Test public void testPropertyLookup() throws Exception { NodeState root = INITIAL_CONTENT; NodeBuilder builder = root.builder(); NodeBuilder index = createIndexDefinition(builder.child(INDEX_DEFINITIONS_NAME), "foo", true, false, ImmutableSet.of("foo"), null); index.setProperty("entryCount", -1); NodeState before = builder.getNodeState(); builder.child("a").setProperty("foo", "abc"); builder.child("b").setProperty("foo", Arrays.asList("abc", "def"), Type.STRINGS); for (int i = 0; i < MANY; i++) { builder.child("n" + i).setProperty("foo", "xyz"); } NodeState after = builder.getNodeState(); NodeState indexed = HOOK.processCommit(before, after, CommitInfo.EMPTY); FilterImpl f = createFilter(indexed, NT_BASE); PropertyIndexLookup lookup = new PropertyIndexLookup(indexed); assertEquals(ImmutableSet.of("a", "b"), find(lookup, "foo", "abc", f)); assertEquals(ImmutableSet.of("b"), find(lookup, "foo", "def", f)); assertEquals(ImmutableSet.of(), find(lookup, "foo", "ghi", f)); assertEquals(MANY, find(lookup, "foo", "xyz", f).size()); assertEquals(MANY + 2, find(lookup, "foo", null, f).size()); double cost; cost = lookup.getCost(f, "foo", PropertyValues.newString("xyz")); assertTrue("cost: " + cost, cost >= MANY); cost = lookup.getCost(f, "foo", null); assertTrue("cost: " + cost, cost >= MANY); }
@Test public void valuePattern() throws Exception { NodeState root = EMPTY_NODE; NodeBuilder builder = root.builder(); NodeBuilder index = builder.child(INDEX_DEFINITIONS_NAME); NodeBuilder indexDef = createIndexDefinition( index, "fooIndex", true, false, ImmutableSet.of("foo"), null); indexDef.setProperty(IndexConstants.VALUE_PATTERN, "(a.*|b)"); NodeState before = builder.getNodeState(); builder.child("a") .setProperty(JCR_PRIMARYTYPE, NT_UNSTRUCTURED, Type.NAME) .setProperty("foo", "a"); builder.child("a1") .setProperty(JCR_PRIMARYTYPE, NT_UNSTRUCTURED, Type.NAME) .setProperty("foo", "a1"); builder.child("b") .setProperty(JCR_PRIMARYTYPE, NT_UNSTRUCTURED, Type.NAME) .setProperty("foo", "b"); builder.child("c") .setProperty(JCR_PRIMARYTYPE, NT_UNSTRUCTURED, Type.NAME) .setProperty("foo", "c"); NodeState after = builder.getNodeState(); NodeState indexed = HOOK.processCommit(before, after, CommitInfo.EMPTY); FilterImpl f = createFilter(after, NT_UNSTRUCTURED); PropertyIndexLookup lookup = new PropertyIndexLookup(indexed); PropertyIndex pIndex = new PropertyIndex(Mounts.defaultMountInfoProvider()); assertEquals(ImmutableSet.of("a"), find(lookup, "foo", "a", f)); assertEquals(ImmutableSet.of("a1"), find(lookup, "foo", "a1", f)); assertEquals(ImmutableSet.of("b"), find(lookup, "foo", "b", f)); assertTrue(pIndex.getCost(f, indexed) == Double.POSITIVE_INFINITY); ArrayList<PropertyValue> list = new ArrayList<PropertyValue>(); list.add(PropertyValues.newString("c")); f.restrictPropertyAsList("foo", list); assertTrue(pIndex.getCost(f, indexed) == Double.POSITIVE_INFINITY); f = createFilter(after, NT_UNSTRUCTURED); list = new ArrayList<PropertyValue>(); list.add(PropertyValues.newString("a")); f.restrictPropertyAsList("foo", list); assertTrue(pIndex.getCost(f, indexed) < Double.POSITIVE_INFINITY); }
@Test public void testPathExclude() throws Exception { NodeState root = INITIAL_CONTENT; NodeBuilder builder = root.builder(); NodeBuilder index = createIndexDefinition(builder.child(INDEX_DEFINITIONS_NAME), "foo", true, false, ImmutableSet.of("foo"), null); index.setProperty(createProperty(PROP_EXCLUDED_PATHS, of("/test/a"), Type.STRINGS)); NodeState before = builder.getNodeState(); builder.child("test").child("a").setProperty("foo", "abc"); builder.child("test").child("b").setProperty("foo", "abc"); NodeState after = builder.getNodeState(); NodeState indexed = HOOK.processCommit(before, after, CommitInfo.EMPTY); FilterImpl f = createFilter(indexed, NT_BASE); f.restrictProperty("foo", Operator.EQUAL, PropertyValues.newString("abc")); PropertyIndexLookup lookup = new PropertyIndexLookup(indexed); assertEquals(ImmutableSet.of("test/b"), find(lookup, "foo", "abc", f)); PropertyIndexPlan plan = new PropertyIndexPlan("plan", root, index.getNodeState(), f); assertTrue(Double.POSITIVE_INFINITY == plan.getCost()); f.setPath("/test2"); plan = new PropertyIndexPlan("plan", root, index.getNodeState(), f); assertTrue(Double.POSITIVE_INFINITY != plan.getCost()); f.setPath("/test"); plan = new PropertyIndexPlan("plan", root, index.getNodeState(), f); assertTrue(Double.POSITIVE_INFINITY == plan.getCost()); }
@Test public void testPathIncludeExclude() throws Exception { NodeState root = INITIAL_CONTENT; NodeBuilder builder = root.builder(); NodeBuilder index = createIndexDefinition(builder.child(INDEX_DEFINITIONS_NAME), "foo", true, false, ImmutableSet.of("foo"), null); index.setProperty(createProperty(PROP_INCLUDED_PATHS, of("/test/a"), Type.STRINGS)); index.setProperty(createProperty(PROP_EXCLUDED_PATHS, of("/test/a/b"), Type.STRINGS)); NodeState before = builder.getNodeState(); builder.child("test").child("a").setProperty("foo", "abc"); builder.child("test").child("a").child("b").setProperty("foo", "abc"); NodeState after = builder.getNodeState(); NodeState indexed = HOOK.processCommit(before, after, CommitInfo.EMPTY); FilterImpl f = createFilter(indexed, NT_BASE); f.restrictProperty("foo", Operator.EQUAL, PropertyValues.newString("abc")); PropertyIndexLookup lookup = new PropertyIndexLookup(indexed); assertEquals(ImmutableSet.of("test/a"), find(lookup, "foo", "abc", f)); PropertyIndexPlan plan = new PropertyIndexPlan("plan", root, index.getNodeState(), f); assertTrue(Double.POSITIVE_INFINITY == plan.getCost()); f.setPath("/test/a/x"); plan = new PropertyIndexPlan("plan", root, index.getNodeState(), f); assertTrue(Double.POSITIVE_INFINITY != plan.getCost()); f.setPath("/test/a/b"); plan = new PropertyIndexPlan("plan", root, index.getNodeState(), f); assertTrue(Double.POSITIVE_INFINITY == plan.getCost()); f.setPath("/test/a"); plan = new PropertyIndexPlan("plan", root, index.getNodeState(), f); assertTrue(Double.POSITIVE_INFINITY == plan.getCost()); }
@Test public void testPathMismatch() throws Exception { NodeState root = INITIAL_CONTENT; NodeBuilder builder = root.builder(); NodeBuilder index = createIndexDefinition(builder.child(INDEX_DEFINITIONS_NAME), "foo", true, false, ImmutableSet.of("foo"), null); index.setProperty(createProperty(PROP_INCLUDED_PATHS, of("/test/a"), Type.STRINGS)); index.setProperty(createProperty(PROP_EXCLUDED_PATHS, of("/test/a/b"), Type.STRINGS)); NodeState before = builder.getNodeState(); builder.child("test").child("a").setProperty("foo", "abc"); builder.child("test").child("a").child("b").setProperty("foo", "abc"); NodeState after = builder.getNodeState(); NodeState indexed = HOOK.processCommit(before, after, CommitInfo.EMPTY); FilterImpl f = createFilter(indexed, NT_BASE); f.restrictPath("/test2", Filter.PathRestriction.ALL_CHILDREN); PropertyIndexPlan plan = new PropertyIndexPlan("plan", root, index.getNodeState(), f); assertTrue(Double.POSITIVE_INFINITY == plan.getCost()); }