diff --git oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndex.java oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndex.java index 885220a..dae6583 100644 --- oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndex.java +++ oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndex.java @@ -119,30 +119,32 @@ public class OrderedPropertyIndex extends PropertyIndex implements AdvancedQuery String propertyName = PathUtils.getName(pr.propertyName); if (lookup.isIndexed(propertyName, "/", filter)) { PropertyValue value = null; - boolean createPlan = true; + 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; -// ----------- DISABLING RANGE QUERIES FOR NOW. EASYING THE INTEGRATION WITH OAK-622 [BEGIN] -// } else if (pr.first != null && !pr.first.equals(pr.last)) { -// // '>' & '>=' use cases -// if (lookup.isAscending(root, propertyName, filter)) { -// value = pr.first; -// } else { -// createPlan = false; -// } -// } else if (pr.last != null && !pr.last.equals(pr.first)) { -// // '<' & '<=' -// if (!lookup.isAscending(root, propertyName, filter)) { -// value = pr.last; -// } else { -// createPlan = false; -// } -// ----------- DISABLING RANGE QUERIES FOR NOW. EASYING THE INTEGRATION WITH OAK-622 [ END ] + createPlan = true; + } else if (pr.first != null && !pr.first.equals(pr.last)) { + // '>' & '>=' use cases + if (lookup.isAscending(root, propertyName, filter)) { + value = pr.first; + createPlan = true; + } else { + createPlan = false; + } + } else if (pr.last != null && !pr.last.equals(pr.first)) { + // '<' & '<=' + if (!lookup.isAscending(root, propertyName, filter)) { + value = pr.last; + createPlan = true; + } else { + createPlan = false; + } } if (createPlan) { // we always return a sorted set @@ -174,7 +176,6 @@ public class OrderedPropertyIndex extends PropertyIndex implements AdvancedQuery LOG.debug("getPlanDescription() - plan: {}", plan); LOG.error("Not implemented yet"); throw new UnsupportedOperationException("Not implemented yet."); -// return null; } @Override diff --git oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/OrderedContentMirrorStoreStrategy.java oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/OrderedContentMirrorStoreStrategy.java index d731dbb..95c4206 100644 --- oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/OrderedContentMirrorStoreStrategy.java +++ oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/OrderedContentMirrorStoreStrategy.java @@ -328,15 +328,26 @@ public class OrderedContentMirrorStoreStrategy extends ContentMirrorStoreStrateg Iterator children = getChildNodeEntries( index).iterator(); pi.setPathContainsValue(true); - pi.enqueue(Iterators.filter(children, - new Predicate() { + pi.enqueue(Iterators.filter(children, new Predicate() { @Override public boolean apply(ChildNodeEntry entry) { - String value = lpr.first - .getValue(Type.STRING); + boolean b = false; + String start = lpr.first.getValue(Type.STRING); + String last = (lpr.last != null) ? lpr.last.getValue(Type.STRING) + : null; String name = convert(entry.getName()); - return value.compareTo(name) < 0 || (lpr.firstIncluding && value - .equals(name)); + if (last == null) { + // normal > and >= case + b = (start.compareTo(name) < 0 || (lpr.firstIncluding && start + .equals(name))); + } else { + // we are in the "between" case. + b = (start.compareTo(name) < 0 || (lpr.firstIncluding && start + .equals(name))) + && (last.compareTo(name) > 0 || lpr.lastIncluding + && last.equals(name)); + } + return b; } })); return pi; diff --git oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexDescendingQueryTest.java oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexDescendingQueryTest.java index 6c69e7b..47c690b 100644 --- oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexDescendingQueryTest.java +++ oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexDescendingQueryTest.java @@ -39,7 +39,6 @@ import org.apache.jackrabbit.oak.plugins.index.IndexUtils; import org.apache.jackrabbit.oak.plugins.index.property.OrderedIndex.OrderDirection; import org.apache.jackrabbit.oak.spi.query.PropertyValues; import org.apache.jackrabbit.oak.util.NodeUtil; -import org.junit.Ignore; import org.junit.Test; import com.google.common.collect.ImmutableMap; @@ -136,7 +135,7 @@ public class OrderedPropertyIndexDescendingQueryTest extends BasicOrderedPropert * test the range query in case of '>' condition * @throws Exception */ - @Test @Ignore("Disabling for now. Integration with OAK-622 and prioritising.") + @Test public void queryGreaterThan() throws Exception { initWithProperProvider(); setTravesalEnabled(false); @@ -172,7 +171,7 @@ public class OrderedPropertyIndexDescendingQueryTest extends BasicOrderedPropert * test the range query in case of '>=' condition * @throws Exception */ - @Test @Ignore("Disabling for now. Integration with OAK-622 and prioritising.") + @Test public void queryGreaterEqualThan() throws Exception { initWithProperProvider(); setTravesalEnabled(false); @@ -285,4 +284,40 @@ public class OrderedPropertyIndexDescendingQueryTest extends BasicOrderedPropert setTravesalEnabled(true); } + /** + * testing explicitly OAK-1561 use-case + * + * @throws CommitFailedException + * @throws ParseException + */ + @Test + public void queryGreaterThenWithCast() throws CommitFailedException, ParseException { + + setTravesalEnabled(false); + + final OrderDirection direction = OrderDirection.ASC; + final String query = "SELECT * FROM [nt:base] WHERE " + ORDERED_PROPERTY + + "> cast('%s' as date)"; + + // index automatically created by the framework: + // {@code createTestIndexNode()} + + // initialising the data + Tree rTree = root.getTree("/"); + Tree test = rTree.addChild("test"); + Calendar start = midnightFirstJan2013(); + addChildNodes(generateOrderedDates(NUMBER_OF_NODES, direction, start), test, direction, + Type.DATE); + root.commit(); + + Calendar searchForCalendar = (Calendar) start.clone(); + searchForCalendar.add(Calendar.HOUR_OF_DAY, 36); + String searchFor = ISO_8601_2000.format(searchForCalendar.getTime()); + Iterator results = executeQuery(String.format(query, searchFor), SQL2, + null).getRows().iterator(); + assertFalse("the index should not be used in this case", results.hasNext()); + + setTravesalEnabled(true); + + } } diff --git oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexQueryTest.java oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexQueryTest.java index 75c27a5..bc95961 100644 --- oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexQueryTest.java +++ oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexQueryTest.java @@ -55,7 +55,6 @@ import org.apache.jackrabbit.oak.spi.query.QueryIndex.IndexPlan; import org.apache.jackrabbit.oak.spi.state.NodeBuilder; import org.apache.jackrabbit.oak.spi.state.NodeState; import org.apache.jackrabbit.oak.util.NodeUtil; -import org.junit.Ignore; import org.junit.Test; import com.google.common.collect.ImmutableList; @@ -145,7 +144,7 @@ public class OrderedPropertyIndexQueryTest extends BasicOrderedPropertyIndexQuer * @throws CommitFailedException * @throws ParseException */ - @Test @Ignore("Disabling for now. Integration with OAK-622 and prioritising.") + @Test public void queryGreaterThan() throws CommitFailedException, ParseException { setTravesalEnabled(false); @@ -184,7 +183,7 @@ public class OrderedPropertyIndexQueryTest extends BasicOrderedPropertyIndexQuer * @throws CommitFailedException * @throws ParseException */ - @Test @Ignore("Disabling for now. Integration with OAK-622 and prioritising.") + @Test public void queryGreaterEqualThan() throws CommitFailedException, ParseException { setTravesalEnabled(false); @@ -226,7 +225,7 @@ public class OrderedPropertyIndexQueryTest extends BasicOrderedPropertyIndexQuer * provider. not the lowcost one. * @throws Exception */ - @Test @Ignore("Disabling for now. Integration with OAK-622 and prioritising.") + @Test public void queryLessThan() throws Exception { initWithProperProvider(); setTravesalEnabled(false); @@ -264,7 +263,7 @@ public class OrderedPropertyIndexQueryTest extends BasicOrderedPropertyIndexQuer * provider. not the lowcost one. * @throws Exception */ - @Test @Ignore("Disabling for now. Integration with OAK-622 and prioritising.") + @Test public void queryLessEqualThan() throws Exception { initWithProperProvider(); initWithProperProvider(); @@ -556,4 +555,245 @@ public class OrderedPropertyIndexQueryTest extends BasicOrderedPropertyIndexQuer setTravesalEnabled(true); } + /** + * testing explicitly OAK-1561 use-case + * + * @throws CommitFailedException + * @throws ParseException + */ + @Test + public void queryGreaterThenWithCast() throws CommitFailedException, ParseException { + + setTravesalEnabled(false); + + final OrderDirection direction = OrderDirection.ASC; + final String query = "SELECT * FROM [nt:base] WHERE " + ORDERED_PROPERTY + + "> cast('%s' as date)"; + + // index automatically created by the framework: + // {@code createTestIndexNode()} + + // initialising the data + Tree rTree = root.getTree("/"); + Tree test = rTree.addChild("test"); + Calendar start = midnightFirstJan2013(); + List nodes = addChildNodes( + generateOrderedDates(NUMBER_OF_NODES, direction, start), test, direction, Type.DATE); + root.commit(); + + Calendar searchForCalendar = (Calendar) start.clone(); + searchForCalendar.add(Calendar.HOUR_OF_DAY, 36); + String searchFor = ISO_8601_2000.format(searchForCalendar.getTime()); + Iterator results = executeQuery(String.format(query, searchFor), SQL2, + null).getRows().iterator(); + Iterator filtered = Iterables.filter(nodes, + new ValuePathTuple.GreaterThanPredicate(searchFor)).iterator(); + assertRightOrder(Lists.newArrayList(filtered), results); + assertFalse("We should have looped throuhg all the results", results.hasNext()); + + setTravesalEnabled(true); + + } + + @Test + public void queryBetweenNoIncludes() throws Exception { + setTravesalEnabled(false); + + final OrderDirection direction = OrderDirection.ASC; + final String query = "SELECT * FROM [nt:base] WHERE " + ORDERED_PROPERTY + "> $start AND " + + ORDERED_PROPERTY + " < $end"; + + // index automatically created by the framework: + // {@code createTestIndexNode()} + + // initialising the data + Tree rTree = root.getTree("/"); + Tree test = rTree.addChild("test"); + Calendar start = midnightFirstJan2013(); + + List nodes = addChildNodes( + generateOrderedDates(NUMBER_OF_NODES, direction, start), test, direction, Type.DATE); + root.commit(); + + Calendar searchForCalendarStart = (Calendar) start.clone(); + searchForCalendarStart.add(Calendar.HOUR_OF_DAY, 36); + String searchForStart = ISO_8601_2000.format(searchForCalendarStart.getTime()); + + Calendar endCalendar = Calendar.getInstance(); + endCalendar.setTime(ISO_8601_2000.parse(nodes.get(nodes.size() - 1).getValue())); + endCalendar.add(Calendar.HOUR_OF_DAY, -36); + String searchForEnd = ISO_8601_2000.format(endCalendar.getTime()); + + Map filter = ImmutableMap.of("start", + PropertyValues.newDate(searchForStart), "end", PropertyValues.newDate(searchForEnd)); + Iterator results = executeQuery(query, SQL2, filter).getRows() + .iterator(); + + Iterator filtered = Iterables.filter(nodes, + new ValuePathTuple.BetweenPredicate(searchForStart, searchForEnd, false, false)) + .iterator(); + + assertRightOrder(Lists.newArrayList(filtered), results); + assertFalse("We should have looped throuhg all the results", results.hasNext()); + + setTravesalEnabled(true); + + } + + @Test + public void queryBetweenIncludeLower() throws Exception { + setTravesalEnabled(false); + + final OrderDirection direction = OrderDirection.ASC; + final String query = "SELECT * FROM [nt:base] WHERE " + ORDERED_PROPERTY + ">= $start AND " + + ORDERED_PROPERTY + " < $end"; + + // index automatically created by the framework: + // {@code createTestIndexNode()} + + // initialising the data + Tree rTree = root.getTree("/"); + Tree test = rTree.addChild("test"); + Calendar start = midnightFirstJan2013(); + + List nodes = addChildNodes( + generateOrderedDates(NUMBER_OF_NODES, direction, start), test, direction, Type.DATE); + root.commit(); + + Calendar searchForCalendarStart = (Calendar) start.clone(); + searchForCalendarStart.add(Calendar.HOUR_OF_DAY, 36); + String searchForStart = ISO_8601_2000.format(searchForCalendarStart.getTime()); + + Calendar endCalendar = Calendar.getInstance(); + endCalendar.setTime(ISO_8601_2000.parse(nodes.get(nodes.size() - 1).getValue())); + endCalendar.add(Calendar.HOUR_OF_DAY, -36); + String searchForEnd = ISO_8601_2000.format(endCalendar.getTime()); + + Map filter = ImmutableMap.of("start", + PropertyValues.newDate(searchForStart), "end", PropertyValues.newDate(searchForEnd)); + Iterator results = executeQuery(query, SQL2, filter).getRows() + .iterator(); + + Iterator filtered = Iterables.filter(nodes, + new ValuePathTuple.BetweenPredicate(searchForStart, searchForEnd, true, false)) + .iterator(); + + assertRightOrder(Lists.newArrayList(filtered), results); + assertFalse("We should have looped throuhg all the results", results.hasNext()); + + setTravesalEnabled(true); + + } + + @Test + public void queryBetweenIncludeHigher() throws Exception { + setTravesalEnabled(false); + + final OrderDirection direction = OrderDirection.ASC; + final String query = "SELECT * FROM [nt:base] WHERE " + ORDERED_PROPERTY + "> $start AND " + + ORDERED_PROPERTY + " <= $end"; + + // index automatically created by the framework: + // {@code createTestIndexNode()} + + // initialising the data + Tree rTree = root.getTree("/"); + Tree test = rTree.addChild("test"); + Calendar start = midnightFirstJan2013(); + + List nodes = addChildNodes( + generateOrderedDates(10, direction, start), test, direction, Type.DATE); + root.commit(); + + for(ValuePathTuple n : nodes){ + System.out.println("+++" + n); + } + + Calendar searchForCalendarStart = (Calendar) start.clone(); + searchForCalendarStart.add(Calendar.HOUR_OF_DAY, 36); + String searchForStart = ISO_8601_2000.format(searchForCalendarStart.getTime()); + + Calendar endCalendar = Calendar.getInstance(); + endCalendar.setTime(ISO_8601_2000.parse(nodes.get(nodes.size() - 1).getValue())); + endCalendar.add(Calendar.HOUR_OF_DAY, -36); + String searchForEnd = ISO_8601_2000.format(endCalendar.getTime()); + + Map filter = ImmutableMap.of("start", + PropertyValues.newDate(searchForStart), "end", PropertyValues.newDate(searchForEnd)); + Iterator results = executeQuery(query, SQL2, filter).getRows() + .iterator(); + + Iterator filtered = Iterables.filter(nodes, + new ValuePathTuple.BetweenPredicate(searchForStart, searchForEnd, false, true)) + .iterator(); + + while (filtered.hasNext()) { + System.out.println("---" + filtered.next()); + } + filtered = Iterables.filter(nodes, + new ValuePathTuple.BetweenPredicate(searchForStart, searchForEnd, false, true)) + .iterator(); + + assertRightOrder(Lists.newArrayList(filtered), results); + assertFalse("We should have looped throuhg all the results", results.hasNext()); + + setTravesalEnabled(true); + + } + + @Test + public void queryBetweenIncludeBoth() throws Exception { + setTravesalEnabled(false); + + final OrderDirection direction = OrderDirection.ASC; + final String query = "SELECT * FROM [nt:base] WHERE " + ORDERED_PROPERTY + ">= $start AND " + + ORDERED_PROPERTY + " <= $end"; + + // index automatically created by the framework: + // {@code createTestIndexNode()} + + // initialising the data + Tree rTree = root.getTree("/"); + Tree test = rTree.addChild("test"); + Calendar start = midnightFirstJan2013(); + + List nodes = addChildNodes( + generateOrderedDates(10, direction, start), test, direction, Type.DATE); + root.commit(); + + for(ValuePathTuple n : nodes){ + System.out.println("+++" + n); + } + + Calendar searchForCalendarStart = (Calendar) start.clone(); + searchForCalendarStart.add(Calendar.HOUR_OF_DAY, 36); + String searchForStart = ISO_8601_2000.format(searchForCalendarStart.getTime()); + + Calendar endCalendar = Calendar.getInstance(); + endCalendar.setTime(ISO_8601_2000.parse(nodes.get(nodes.size() - 1).getValue())); + endCalendar.add(Calendar.HOUR_OF_DAY, -36); + String searchForEnd = ISO_8601_2000.format(endCalendar.getTime()); + + Map filter = ImmutableMap.of("start", + PropertyValues.newDate(searchForStart), "end", PropertyValues.newDate(searchForEnd)); + Iterator results = executeQuery(query, SQL2, filter).getRows() + .iterator(); + + Iterator filtered = Iterables.filter(nodes, + new ValuePathTuple.BetweenPredicate(searchForStart, searchForEnd, false, true)) + .iterator(); + + while (filtered.hasNext()) { + System.out.println("---" + filtered.next()); + } + filtered = Iterables.filter(nodes, + new ValuePathTuple.BetweenPredicate(searchForStart, searchForEnd, true, true)) + .iterator(); + + assertRightOrder(Lists.newArrayList(filtered), results); + assertFalse("We should have looped throuhg all the results", results.hasNext()); + + setTravesalEnabled(true); + + } } diff --git oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/ValuePathTuple.java oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/ValuePathTuple.java index d2df183..b78e611 100644 --- oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/ValuePathTuple.java +++ oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/ValuePathTuple.java @@ -87,6 +87,28 @@ public class ValuePathTuple implements Comparable { } + public static class BetweenPredicate implements Predicate { + private String start; + private String end; + private boolean includeStart; + private boolean includeEnd; + + public BetweenPredicate(String start, String end, boolean includeStart, boolean includeEnd) { + this.start = start; + this.end = end; + this.includeStart = includeStart; + this.includeEnd = includeEnd; + } + + @Override + public boolean apply(ValuePathTuple arg0) { + String other = arg0.getValue(); + return + (start.compareTo(other) < 0 || (includeStart && start.equals(other))) + && (end.compareTo(other) > 0 || (includeEnd && end.equals(other))); + } + } + ValuePathTuple(String value, String path) { this.value = value; this.path = path; diff --git oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/ValuePathTupleTest.java oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/ValuePathTupleTest.java index e2e6049..f72de42 100644 --- oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/ValuePathTupleTest.java +++ oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/ValuePathTupleTest.java @@ -149,4 +149,72 @@ public class ValuePathTupleTest { assertEquals("e", filtered.next().getValue()); assertFalse(filtered.hasNext()); } + + @Test + public void betweenNoIncludes() { + List data = ImmutableList.of( + new ValuePathTuple("a", "foobar"), + new ValuePathTuple("b", "foobar"), + new ValuePathTuple("c", "foobar"), + new ValuePathTuple("d", "foobar"), + new ValuePathTuple("e", "foobar"), + new ValuePathTuple("f", "foobar")); + Iterator filtered = Iterables.filter(data, + new ValuePathTuple.BetweenPredicate("b", "d", false, false)).iterator(); + assertTrue(filtered.hasNext()); + assertEquals("c", filtered.next().getValue()); + assertFalse(filtered.hasNext()); + } + + @Test + public void betweenIncludeStart() { + List data = ImmutableList.of( + new ValuePathTuple("a", "foobar"), + new ValuePathTuple("b", "foobar"), + new ValuePathTuple("c", "foobar"), + new ValuePathTuple("d", "foobar"), + new ValuePathTuple("e", "foobar"), + new ValuePathTuple("f", "foobar")); + Iterator filtered = Iterables.filter(data, + new ValuePathTuple.BetweenPredicate("b", "d", true, false)).iterator(); + assertTrue(filtered.hasNext()); + assertEquals("b", filtered.next().getValue()); + assertEquals("c", filtered.next().getValue()); + assertFalse(filtered.hasNext()); + } + + @Test + public void betweenIncludeEnd() { + List data = ImmutableList.of( + new ValuePathTuple("a", "foobar"), + new ValuePathTuple("b", "foobar"), + new ValuePathTuple("c", "foobar"), + new ValuePathTuple("d", "foobar"), + new ValuePathTuple("e", "foobar"), + new ValuePathTuple("f", "foobar")); + Iterator filtered = Iterables.filter(data, + new ValuePathTuple.BetweenPredicate("b", "d", false, true)).iterator(); + assertTrue(filtered.hasNext()); + assertEquals("c", filtered.next().getValue()); + assertEquals("d", filtered.next().getValue()); + assertFalse(filtered.hasNext()); + } + + @Test + public void betweenIncludeBoth() { + List data = ImmutableList.of( + new ValuePathTuple("a", "foobar"), + new ValuePathTuple("b", "foobar"), + new ValuePathTuple("c", "foobar"), + new ValuePathTuple("d", "foobar"), + new ValuePathTuple("e", "foobar"), + new ValuePathTuple("f", "foobar")); + Iterator filtered = Iterables.filter(data, + new ValuePathTuple.BetweenPredicate("b", "d", true, true)).iterator(); + assertTrue(filtered.hasNext()); + assertEquals("b", filtered.next().getValue()); + assertEquals("c", filtered.next().getValue()); + assertEquals("d", filtered.next().getValue()); + assertFalse(filtered.hasNext()); + } }