Author: mduerig Date: Mon May 19 12:03:29 2014 New Revision: 1595856 URL: http://svn.apache.org/r1595856 Log: OAK-1745: OrderedIndex should serve range queries regardless of direction Patch from Davide
Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndex.java jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/OrderedContentMirrorStoreStrategy.java jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexDescendingQueryTest.java jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexQueryTest.java jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/OrderedContentMirrorStorageStrategyTest.java Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndex.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndex.java?rev=1595856&r1=1595855&r2=1595856&view=diff ============================================================================== --- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndex.java (original) +++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndex.java Mon May 19 12:03:29 2014 @@ -81,10 +81,12 @@ public class OrderedPropertyIndex extend @Override public List<IndexPlan> getPlans(Filter filter, List<OrderEntry> sortOrder, NodeState root) { - LOG.debug("getPlans(Filter, List<OrderEntry>, NodeState)"); - LOG.debug("getPlans() - filter: {} - ", filter); - LOG.debug("getPlans() - sortOrder: {} - ", sortOrder); - LOG.debug("getPlans() - rootState: {} - ", root); + if (LOG.isDebugEnabled()) { + LOG.debug("getPlans(Filter, List<OrderEntry>, NodeState)"); + LOG.debug("getPlans() - filter: {} - ", filter); + LOG.debug("getPlans() - sortOrder: {} - ", sortOrder); + LOG.debug("getPlans() - rootState: {} - ", root); + } List<IndexPlan> plans = new ArrayList<IndexPlan>(); PropertyIndexLookup pil = getLookup(root); @@ -131,20 +133,12 @@ public class OrderedPropertyIndex extend 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; - } + value = pr.first; + createPlan = true; } else if (pr.last != null && !pr.last.equals(pr.first)) { // '<' & '<=' - if (!lookup.isAscending(root, propertyName, filter)) { - value = pr.last; - createPlan = true; - } else { - createPlan = false; - } + value = pr.last; + createPlan = true; } if (createPlan) { // we always return a sorted set Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/OrderedContentMirrorStoreStrategy.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/OrderedContentMirrorStoreStrategy.java?rev=1595856&r1=1595855&r2=1595856&view=diff ============================================================================== --- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/OrderedContentMirrorStoreStrategy.java (original) +++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/OrderedContentMirrorStoreStrategy.java Mon May 19 12:03:29 2014 @@ -276,25 +276,69 @@ public class OrderedContentMirrorStoreSt public Iterable<String> query(final Filter filter, final String indexName, final NodeState indexMeta, final String indexStorageNodeName, final PropertyRestriction pr) { - + final NodeState index = indexMeta.getChildNode(indexStorageNodeName); if (pr.first != null && !pr.first.equals(pr.last)) { - // '>' & '>=' use case - ChildNodeEntry firstValueableItem = seek(index, - new PredicateGreaterThan(pr.first.getValue(Type.STRING), pr.firstIncluding)); + // '>' & '>=' and between use case + ChildNodeEntry firstValueableItem; Iterable<String> it = Collections.emptyList(); - if (firstValueableItem != null) { - Iterable<ChildNodeEntry> childrenIterable = (pr.last == null) ? new SeekedIterable( - index, firstValueableItem) : new BetweenIterable(index, firstValueableItem, - pr.last.getValue(Type.STRING), pr.lastIncluding); - it = new QueryResultsWrapper(filter, indexName, childrenIterable); + Iterable<ChildNodeEntry> childrenIterable; + + if (pr.last == null) { + LOG.debug("> & >= case."); + firstValueableItem = seek(index, + new PredicateGreaterThan(pr.first.getValue(Type.STRING), pr.firstIncluding)); + if (firstValueableItem != null) { + childrenIterable = new SeekedIterable(index, firstValueableItem); + it = new QueryResultsWrapper(filter, indexName, childrenIterable); + } + } else { + String first, last; + boolean includeFirst, includeLast; + first = pr.first.getValue(Type.STRING); + last = pr.last.getValue(Type.STRING); + includeFirst = pr.firstIncluding; + includeLast = pr.lastIncluding; + + if (LOG.isDebugEnabled()) { + final String op1 = includeFirst ? ">=" : ">"; + final String op2 = includeLast ? "<=" : "<"; + LOG.debug("in between case. direction: {} - Condition: (x {} {} AND x {} {})", + new Object[] { direction, op1, first, op2, last }); + } + + if (direction.equals(OrderDirection.ASC)) { + firstValueableItem = seek(index, + new PredicateGreaterThan(first, includeFirst)); + } else { + firstValueableItem = seek(index, + new PredicateLessThan(last, includeLast)); + } + + LOG.debug("firstValueableItem: {}", firstValueableItem); + + if (firstValueableItem != null) { + childrenIterable = new BetweenIterable(index, firstValueableItem, last, + includeLast, direction); + it = new QueryResultsWrapper(filter, indexName, childrenIterable); + } } + return it; } else if (pr.last != null && !pr.last.equals(pr.first)) { // '<' & '<=' use case - ChildNodeEntry firstValueableItem = seek(index, - new PredicateLessThan(pr.last.getValue(Type.STRING), pr.lastIncluding)); + final String searchfor = pr.last.getValue(Type.STRING); + final boolean include = pr.lastIncluding; + Predicate<ChildNodeEntry> predicate = new PredicateLessThan(searchfor, include); + + LOG.debug("< & <= case. - searchfor: {} - include: {} - predicate: {}", + new Object[] { searchfor, include, predicate }); + + ChildNodeEntry firstValueableItem = seek(index, predicate); + + LOG.debug("firstValuableItem: {}", firstValueableItem); + Iterable<String> it = Collections.emptyList(); if (firstValueableItem != null) { it = new QueryResultsWrapper(filter, indexName, new SeekedIterable(index, @@ -376,18 +420,41 @@ public class OrderedContentMirrorStoreSt v.visit(content); count = v.getEstimatedCount(); } - } else if (lpr.first != null && !lpr.first.equals(lpr.last) - && OrderDirection.ASC.equals(direction)) { + } else if (lpr.first != null && !lpr.first.equals(lpr.last)) { // > & >= in ascending index + value = lpr.first.getValue(Type.STRING); + final String vv = value; + final boolean include = lpr.firstIncluding; + final OrderDirection dd = direction; + Iterable<? extends ChildNodeEntry> children = getChildNodeEntries(content); + Predicate<String> predicate = new Predicate<String>() { + private String v = vv; + private boolean i = include; + private OrderDirection d = dd; + + @Override + public boolean apply(String input) { + boolean b; + + if (d.equals(OrderDirection.ASC)) { + b = v.compareTo(input) > 0; + } else { + b = v.compareTo(input) < 0; + } + + b = b || (i && v.equals(input)); + + return b; + } + }; + CountingNodeVisitor v; - value = lpr.first.getValue(Type.STRING); int depthTotal = 0; // seeking the right starting point for (ChildNodeEntry child : children) { String converted = convert(child.getName()); - if (value.compareTo(converted) < 0 - || (lpr.firstIncluding && value.equals(converted))) { + if (predicate.apply(converted)) { // here we are let's start counting v = new CountingNodeVisitor(max); v.visit(content.getChildNode(child.getName())); @@ -403,18 +470,41 @@ public class OrderedContentMirrorStoreSt v.depthTotal = depthTotal; v.count = (int) Math.min(count, Integer.MAX_VALUE); count = v.getEstimatedCount(); - } else if (lpr.last != null && !lpr.last.equals(lpr.first) - && OrderDirection.DESC.equals(direction)) { - // > & >= in ascending index + } else if (lpr.last != null && !lpr.last.equals(lpr.first)) { + // < & <= + value = lpr.last.getValue(Type.STRING); + final String vv = value; + final boolean include = lpr.lastIncluding; + final OrderDirection dd = direction; + Iterable<? extends ChildNodeEntry> children = getChildNodeEntries(content); + Predicate<String> predicate = new Predicate<String>() { + private String v = vv; + private boolean i = include; + private OrderDirection d = dd; + + @Override + public boolean apply(String input) { + boolean b; + + if (d.equals(OrderDirection.ASC)) { + b = v.compareTo(input) < 0; + } else { + b = v.compareTo(input) > 0; + } + + b = b || (i && v.equals(input)); + + return b; + } + }; + CountingNodeVisitor v; - value = lpr.last.getValue(Type.STRING); int depthTotal = 0; // seeking the right starting point for (ChildNodeEntry child : children) { String converted = convert(child.getName()); - if (value.compareTo(converted) > 0 - || (lpr.lastIncluding && value.equals(converted))) { + if (predicate.apply(converted)) { // here we are let's start counting v = new CountingNodeVisitor(max); v.visit(content.getChildNode(child.getName())); @@ -435,7 +525,7 @@ public class OrderedContentMirrorStoreSt } return count; } - + /** * wrap an {@code Iterable<ChildNodeEntry>} in something that can be understood by the Query * Engine @@ -707,17 +797,20 @@ public class OrderedContentMirrorStoreSt private static class BetweenIterable extends SeekedIterable { private String lastKey; private boolean lastInclude; + private OrderDirection direction; - public BetweenIterable(NodeState index, ChildNodeEntry first, String lastKey, - boolean lastInclude) { + public BetweenIterable(final NodeState index, final ChildNodeEntry first, + final String lastKey, final boolean lastInclude, + final OrderDirection direction) { super(index, first); this.lastKey = lastKey; this.lastInclude = lastInclude; + this.direction = direction; } @Override public Iterator<ChildNodeEntry> iterator() { - return new BetweenIterator(index, start, first, lastKey, lastInclude); + return new BetweenIterator(index, start, first, lastKey, lastInclude, direction); } } @@ -725,9 +818,8 @@ public class OrderedContentMirrorStoreSt * iterator for iterating in the cases of BETWEEN queries. */ private static class BetweenIterator extends SeekedIterator { - private String lastKey; - private boolean lastInclude; - + private Predicate<String> condition; + /** * @param index the current index content {@code :index} * @param start the {@code :start} node @@ -735,11 +827,22 @@ public class OrderedContentMirrorStoreSt * @param lastKey the last key to be returned * @param lastInclude whether including the last key or not. */ - public BetweenIterator(NodeState index, NodeState start, ChildNodeEntry first, - String lastKey, boolean lastInclude) { + public BetweenIterator(final NodeState index, final NodeState start, + final ChildNodeEntry first, final String lastKey, + final boolean lastInclude, final OrderDirection direction) { super(index, start, first); - this.lastInclude = lastInclude; - this.lastKey = lastKey; + this.condition = new Predicate<String>() { + private String v = lastKey; + private boolean i = lastInclude; + private OrderDirection d = direction; + + @Override + public boolean apply(String input) { + return d.equals(OrderDirection.ASC) + ? v.compareTo(input) > 0 || (i && v.equals(input)) + : v.compareTo(input) < 0 || (i && v.equals(input)); + } + }; } @Override @@ -749,10 +852,9 @@ public class OrderedContentMirrorStoreSt if (name != null && next) { name = convert(name); - next = next && (lastKey.compareTo(name) > 0 || (lastInclude && lastKey - .equals(name))); + next = next && condition.apply(name); } return next; } } -} \ No newline at end of file +} Modified: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexDescendingQueryTest.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexDescendingQueryTest.java?rev=1595856&r1=1595855&r2=1595856&view=diff ============================================================================== --- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexDescendingQueryTest.java (original) +++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexDescendingQueryTest.java Mon May 19 12:03:29 2014 @@ -23,6 +23,7 @@ import static org.apache.jackrabbit.JcrC import java.text.ParseException; import java.util.Calendar; +import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Map; @@ -40,6 +41,8 @@ import org.apache.jackrabbit.oak.plugins import org.apache.jackrabbit.oak.spi.query.PropertyValues; import org.apache.jackrabbit.oak.util.NodeUtil; import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Iterables; @@ -47,6 +50,9 @@ import com.google.common.collect.Lists; public class OrderedPropertyIndexDescendingQueryTest extends BasicOrderedPropertyIndexQueryTest { + private static final Logger LOG = LoggerFactory + .getLogger(OrderedPropertyIndexDescendingQueryTest.class); + @Override protected void createTestIndexNode() throws Exception { Tree index = root.getTree("/"); @@ -150,19 +156,27 @@ public class OrderedPropertyIndexDescend Tree rTree = root.getTree("/"); Tree test = rTree.addChild("test"); Calendar start = midnightFirstJan2013(); - addChildNodes( + List<ValuePathTuple> 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()); + + // re-sorting descending for matching the actual index direction + Collections.sort(nodes, Collections.reverseOrder()); + Iterator<ValuePathTuple> filtered = Iterables.filter(nodes, + new ValuePathTuple.GreaterThanPredicate(searchFor)).iterator(); + Map<String, PropertyValue> filter = ImmutableMap.of(ORDERED_PROPERTY, PropertyValues.newDate(searchFor)); Iterator<? extends ResultRow> results = executeQuery( String.format(query, ORDERED_PROPERTY, ORDERED_PROPERTY), SQL2, filter).getRows() .iterator(); - assertFalse("We should not return any results as of the cost", results.hasNext()); + + assertRightOrder(Lists.newArrayList(filtered), results); + assertFalse("no more results expected", results.hasNext()); setTravesalEnabled(true); } @@ -186,19 +200,27 @@ public class OrderedPropertyIndexDescend Tree rTree = root.getTree("/"); Tree test = rTree.addChild("test"); Calendar start = midnightFirstJan2013(); - addChildNodes( + List<ValuePathTuple> 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()); + + // re-sorting descending for matching the actual index direction + Collections.sort(nodes, Collections.reverseOrder()); + Iterator<ValuePathTuple> filtered = Iterables.filter(nodes, + new ValuePathTuple.GreaterThanPredicate(searchFor, true)).iterator(); + Map<String, PropertyValue> filter = ImmutableMap.of(ORDERED_PROPERTY, PropertyValues.newDate(searchFor)); Iterator<? extends ResultRow> results = executeQuery( String.format(query, ORDERED_PROPERTY, ORDERED_PROPERTY), SQL2, filter).getRows() .iterator(); - assertFalse("We should not return any results as of the cost", results.hasNext()); + + assertRightOrder(Lists.newArrayList(filtered), results); + assertFalse("no more results expected", results.hasNext()); setTravesalEnabled(true); } @@ -306,18 +328,226 @@ public class OrderedPropertyIndexDescend Tree rTree = root.getTree("/"); Tree test = rTree.addChild("test"); Calendar start = midnightFirstJan2013(); - addChildNodes(generateOrderedDates(NUMBER_OF_NODES, direction, start), test, direction, - Type.DATE); + List<ValuePathTuple> 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()); + + // re-sorting descending for matching the actual index direction + Collections.sort(nodes, Collections.reverseOrder()); + Iterator<ValuePathTuple> filtered = Iterables.filter(nodes, + new ValuePathTuple.GreaterThanPredicate(searchFor, true)).iterator(); + Iterator<? extends ResultRow> results = executeQuery(String.format(query, searchFor), SQL2, null).getRows().iterator(); - assertFalse("the index should not be used in this case", results.hasNext()); + + assertRightOrder(Lists.newArrayList(filtered), results); + assertFalse("no more results expecrted", 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<ValuePathTuple> 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<String, PropertyValue> filter = ImmutableMap.of("start", + PropertyValues.newDate(searchForStart), "end", PropertyValues.newDate(searchForEnd)); + Iterator<? extends ResultRow> results = executeQuery(query, SQL2, filter).getRows() + .iterator(); + + // re-sorting according to the actual index + Collections.sort(nodes, Collections.reverseOrder()); + Iterator<ValuePathTuple> filtered = Iterables.filter(nodes, + new ValuePathTuple.BetweenPredicate(searchForStart, searchForEnd, false, false)) + .iterator(); + + if (LOG.isDebugEnabled()) { + LOG.debug("--- added nodes"); + for (ValuePathTuple n : nodes) { + LOG.debug("{}", n); + } + LOG.debug("--- expected list"); + while (filtered.hasNext()) { + LOG.debug("{}", filtered.next()); + } + 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 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<ValuePathTuple> 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<String, PropertyValue> filter = ImmutableMap.of("start", + PropertyValues.newDate(searchForStart), "end", PropertyValues.newDate(searchForEnd)); + Iterator<? extends ResultRow> results = executeQuery(query, SQL2, filter).getRows() + .iterator(); + + // re-sorting according to the actual index + Collections.sort(nodes, Collections.reverseOrder()); + Iterator<ValuePathTuple> 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); } + + @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<ValuePathTuple> 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<String, PropertyValue> filter = ImmutableMap.of("start", + PropertyValues.newDate(searchForStart), "end", PropertyValues.newDate(searchForEnd)); + Iterator<? extends ResultRow> results = executeQuery(query, SQL2, filter).getRows() + .iterator(); + + // re-sorting according to the actual index + Collections.sort(nodes, Collections.reverseOrder()); + Iterator<ValuePathTuple> 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 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<ValuePathTuple> 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<String, PropertyValue> filter = ImmutableMap.of("start", + PropertyValues.newDate(searchForStart), "end", PropertyValues.newDate(searchForEnd)); + Iterator<? extends ResultRow> results = executeQuery(query, SQL2, filter).getRows() + .iterator(); + + // re-sorting according to the actual index + Collections.sort(nodes, Collections.reverseOrder()); + Iterator<ValuePathTuple> 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); + } + } Modified: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexQueryTest.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexQueryTest.java?rev=1595856&r1=1595855&r2=1595856&view=diff ============================================================================== --- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexQueryTest.java (original) +++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexQueryTest.java Mon May 19 12:03:29 2014 @@ -27,6 +27,7 @@ import static org.junit.Assert.assertNot import java.text.ParseException; import java.util.Calendar; +import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Map; @@ -64,8 +65,12 @@ import org.apache.jackrabbit.oak.spi.sta import org.apache.jackrabbit.oak.util.NodeUtil; import org.junit.Ignore; import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class OrderedPropertyIndexQueryTest extends BasicOrderedPropertyIndexQueryTest { + private static final Logger LOG = LoggerFactory.getLogger(OrderedPropertyIndexQueryTest.class); + private static final EditorHook HOOK = new EditorHook(new IndexUpdateProvider( new OrderedPropertyIndexEditorProvider())); @@ -242,19 +247,36 @@ public class OrderedPropertyIndexQueryTe Tree rTree = root.getTree("/"); Tree test = rTree.addChild("test"); Calendar start = midnightFirstJan2013(); - addChildNodes( + List<ValuePathTuple> 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()); + + // re-sorting ascending as the index is ASC and the results will be returned as ASC + Collections.sort(nodes); + Iterator<ValuePathTuple> filtered = Iterables.filter(nodes, + new ValuePathTuple.LessThanPredicate(searchFor, false)).iterator(); + + if (LOG.isDebugEnabled()) { + LOG.debug("-- Expected results:"); + while (filtered.hasNext()) { + LOG.debug("{}", filtered.next()); + } + // re-initialising the filtered for the test + filtered = Iterables.filter(nodes, + new ValuePathTuple.LessThanPredicate(searchFor, false)).iterator(); + } + Map<String, PropertyValue> filter = ImmutableMap.of(ORDERED_PROPERTY, PropertyValues.newDate(searchFor)); Iterator<? extends ResultRow> results = executeQuery( String.format(query, ORDERED_PROPERTY, ORDERED_PROPERTY), SQL2, filter).getRows() .iterator(); - assertFalse("We should have no results as of the cost and index direction", results.hasNext()); + assertRightOrder(Lists.newArrayList(filtered), results); + assertFalse("We should have looped throught all the results", results.hasNext()); setTravesalEnabled(true); } @@ -281,19 +303,27 @@ public class OrderedPropertyIndexQueryTe Tree rTree = root.getTree("/"); Tree test = rTree.addChild("test"); Calendar start = midnightFirstJan2013(); - addChildNodes( + List<ValuePathTuple> 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()); + + // re-sorting ascending as the index is ASC and the results will be returned as ASC + Collections.sort(nodes); + Iterator<ValuePathTuple> filtered = Iterables.filter(nodes, + new ValuePathTuple.LessThanPredicate(searchFor, true)).iterator(); + Map<String, PropertyValue> filter = ImmutableMap.of(ORDERED_PROPERTY, PropertyValues.newDate(searchFor)); Iterator<? extends ResultRow> results = executeQuery( String.format(query, ORDERED_PROPERTY, ORDERED_PROPERTY), SQL2, filter).getRows() .iterator(); - assertFalse("We should have no results as of the cost and index direction", results.hasNext()); + + assertRightOrder(Lists.newArrayList(filtered), results); + assertFalse("We should have looped throught all the results", results.hasNext()); setTravesalEnabled(true); } @@ -768,11 +798,6 @@ public class OrderedPropertyIndexQueryTe List<ValuePathTuple> nodes = addChildNodes( generateOrderedDates(10, direction, start), test, direction, Type.DATE); root.commit(); - - for (ValuePathTuple n : nodes) { - n.toString(); - // System.out.println("+++" + n); - } Calendar searchForCalendarStart = (Calendar) start.clone(); searchForCalendarStart.add(Calendar.HOUR_OF_DAY, 36); @@ -792,9 +817,6 @@ public class OrderedPropertyIndexQueryTe 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(); @@ -825,11 +847,6 @@ public class OrderedPropertyIndexQueryTe List<ValuePathTuple> nodes = addChildNodes( generateOrderedDates(10, direction, start), test, direction, Type.DATE); root.commit(); - - for (ValuePathTuple n : nodes) { - n.toString(); - // System.out.println("+++" + n); - } Calendar searchForCalendarStart = (Calendar) start.clone(); searchForCalendarStart.add(Calendar.HOUR_OF_DAY, 36); @@ -849,9 +866,6 @@ public class OrderedPropertyIndexQueryTe 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(); @@ -862,4 +876,4 @@ public class OrderedPropertyIndexQueryTe setTravesalEnabled(true); } -} \ No newline at end of file +} Modified: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/OrderedContentMirrorStorageStrategyTest.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/OrderedContentMirrorStorageStrategyTest.java?rev=1595856&r1=1595855&r2=1595856&view=diff ============================================================================== --- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/OrderedContentMirrorStorageStrategyTest.java (original) +++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/OrderedContentMirrorStorageStrategyTest.java Mon May 19 12:03:29 2014 @@ -1331,7 +1331,7 @@ public class OrderedContentMirrorStorage // don't care about the actual results as long as we have something. We're reusing existing // code assertTrue(store.count(ascendingMeta, pr, maxNodeCount) > 0); - assertEquals(0, descendingStore.count(descendingMeta, pr, maxNodeCount)); + assertTrue(descendingStore.count(ascendingMeta, pr, maxNodeCount) > 0); // '>=' pr = new Filter.PropertyRestriction(); @@ -1342,7 +1342,7 @@ public class OrderedContentMirrorStorage // don't care about the actual results as long as we have something. We're reusing existing // code assertTrue(store.count(ascendingMeta, pr, maxNodeCount) > 0); - assertEquals(0, descendingStore.count(descendingMeta, pr, maxNodeCount)); + assertTrue(descendingStore.count(ascendingMeta, pr, maxNodeCount) > 0); // '<' pr = new Filter.PropertyRestriction(); @@ -1353,7 +1353,7 @@ public class OrderedContentMirrorStorage // don't care about the actual results as long as we have something. We're reusing existing // code assertTrue(descendingStore.count(descendingMeta, pr, maxNodeCount) > 0); - assertEquals(0, store.count(ascendingMeta, pr, maxNodeCount)); + assertTrue(store.count(ascendingMeta, pr, maxNodeCount) > 0); // when no conditions has been asked but just an ORDER BY pr = null;