You are viewing a plain text version of this content. The canonical link for it is here.
Posted to oak-commits@jackrabbit.apache.org by da...@apache.org on 2014/06/12 13:21:54 UTC

svn commit: r1602124 [1/3] - in /jackrabbit/oak/branches/1.0: ./ oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/ oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/ oak-core/src/test/java/org/apach...

Author: davide
Date: Thu Jun 12 11:21:53 2014
New Revision: 1602124

URL: http://svn.apache.org/r1602124
Log:
OAK-1570 - Improve perfomances of the OrderedIndex while insert

merged into 1.0 branch


Modified:
    jackrabbit/oak/branches/1.0/   (props changed)
    jackrabbit/oak/branches/1.0/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedIndex.java
    jackrabbit/oak/branches/1.0/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/ContentMirrorStoreStrategy.java
    jackrabbit/oak/branches/1.0/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/OrderedContentMirrorStoreStrategy.java
    jackrabbit/oak/branches/1.0/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/BasicOrderedPropertyIndexQueryTest.java
    jackrabbit/oak/branches/1.0/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexDescendingQueryTest.java
    jackrabbit/oak/branches/1.0/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexQueryTest.java
    jackrabbit/oak/branches/1.0/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/OrderedContentMirrorStorageStrategyTest.java
    jackrabbit/oak/branches/1.0/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/OrderedIndexConcurrentClusterIT.java

Propchange: jackrabbit/oak/branches/1.0/
------------------------------------------------------------------------------
  Merged /jackrabbit/oak/trunk:r1600088

Modified: jackrabbit/oak/branches/1.0/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedIndex.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.0/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedIndex.java?rev=1602124&r1=1602123&r2=1602124&view=diff
==============================================================================
--- jackrabbit/oak/branches/1.0/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedIndex.java (original)
+++ jackrabbit/oak/branches/1.0/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedIndex.java Thu Jun 12 11:21:53 2014
@@ -91,6 +91,14 @@ public interface OrderedIndex {
         public static boolean isDescending(NodeState indexMeta) {
             return DESC.equals(fromIndexMeta(indexMeta));
         }
+        
+        public boolean isAscending() {
+            return ASC.equals(this);
+        }
+        
+        public boolean isDescending() {
+            return DESC.equals(this);
+        }
 
         /**
          * convenience method that tells if the provided index definition is ascending
@@ -116,4 +124,34 @@ public interface OrderedIndex {
      * the default direction for sorting the index
      */
     OrderDirection DEFAULT_DIRECTION = OrderDirection.ASC;
+    
+    /**
+     * defines the default distribution of items across the skip list. It's with a factor of 10%
+     * having therefore
+     * 
+     * <dl>
+     *  <dt>lane 0:</dt> <dd>100.0% (the base linked list)</dd>
+     *  <dt>lane 1:</dt> <dd>10.0%</dd>
+     *  <dt>lane 2:</dt> <dd>1.0%</dd>
+     *  <dt>lane 3:</dt> <dd>0.1%</dd>
+     * </dl>
+     */
+    double DEFAULT_PROBABILITY = 0.1;
+    
+    /**
+     * the number of lanes used in the SkipList 
+     */
+    int LANES = 4;
+    
+    /**
+     * Convenience Predicate that will force the implementor to expose what we're searching for
+     *
+     * @param <T>
+     */
+    interface Predicate<T> extends com.google.common.base.Predicate<T> {
+        /**
+         * @return the string we're searching for during this predicate
+         */
+        String getSearchFor();
+    }
 }

Modified: jackrabbit/oak/branches/1.0/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/ContentMirrorStoreStrategy.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.0/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/ContentMirrorStoreStrategy.java?rev=1602124&r1=1602123&r2=1602124&view=diff
==============================================================================
--- jackrabbit/oak/branches/1.0/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/ContentMirrorStoreStrategy.java (original)
+++ jackrabbit/oak/branches/1.0/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/ContentMirrorStoreStrategy.java Thu Jun 12 11:21:53 2014
@@ -100,7 +100,7 @@ public class ContentMirrorStoreStrategy 
             }
 
             // Prune all index nodes that are no longer needed
-            prune(index, builders);
+            prune(index, builders, key);
         }
     }
 
@@ -431,8 +431,9 @@ public class ContentMirrorStoreStrategy 
      *            the current index
      * @param builders
      *            list of nodes to prune
+     * @param key the key of the index we're processing
      */
-    void prune(final NodeBuilder index, final Deque<NodeBuilder> builders) {
+    void prune(final NodeBuilder index, final Deque<NodeBuilder> builders, final String key) {
         for (NodeBuilder node : builders) {
             if (node.getBoolean("match") || node.getChildNodeCount(1) > 0) {
                 return;

Modified: jackrabbit/oak/branches/1.0/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/OrderedContentMirrorStoreStrategy.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.0/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/OrderedContentMirrorStoreStrategy.java?rev=1602124&r1=1602123&r2=1602124&view=diff
==============================================================================
--- jackrabbit/oak/branches/1.0/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/OrderedContentMirrorStoreStrategy.java (original)
+++ jackrabbit/oak/branches/1.0/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/OrderedContentMirrorStoreStrategy.java Thu Jun 12 11:21:53 2014
@@ -24,6 +24,7 @@ import java.util.Collections;
 import java.util.Deque;
 import java.util.Iterator;
 import java.util.NoSuchElementException;
+import java.util.Random;
 
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
@@ -32,6 +33,7 @@ import org.apache.jackrabbit.oak.api.Pro
 import org.apache.jackrabbit.oak.api.Type;
 import org.apache.jackrabbit.oak.plugins.index.property.OrderedIndex;
 import org.apache.jackrabbit.oak.plugins.index.property.OrderedIndex.OrderDirection;
+import org.apache.jackrabbit.oak.plugins.index.property.OrderedIndex.Predicate;
 import org.apache.jackrabbit.oak.plugins.memory.EmptyNodeState;
 import org.apache.jackrabbit.oak.spi.query.Filter;
 import org.apache.jackrabbit.oak.spi.query.Filter.PropertyRestriction;
@@ -42,8 +44,9 @@ import org.apache.jackrabbit.oak.spi.sta
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.google.common.base.Predicate;
 import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
 
 /**
  * Same as for {@link ContentMirrorStoreStrategy} but the order of the keys is kept by using the
@@ -60,6 +63,15 @@ import com.google.common.base.Strings;
  * </code>
  */
 public class OrderedContentMirrorStoreStrategy extends ContentMirrorStoreStrategy {
+    /**
+     * convenience property for initialising an empty multi-value :next
+     */
+    public static final Iterable<String> EMPTY_NEXT = ImmutableList.of("", "", "", "");
+    
+    /**
+     * convenience property that represent an empty :next as array
+     */
+    public static final String[] EMPTY_NEXT_ARRAY = Iterables.toArray(EMPTY_NEXT, String.class);
 
     /**
      * the property linking to the next node
@@ -75,11 +87,12 @@ public class OrderedContentMirrorStoreSt
      * a NodeState used for easy creating of an empty :start
      */
     public static final NodeState EMPTY_START_NODE = EmptyNodeState.EMPTY_NODE.builder()
-                                                                              .setProperty(NEXT, "")
-                                                                              .getNodeState();
+        .setProperty(NEXT, EMPTY_NEXT, Type.STRINGS).getNodeState();
 
     private static final Logger LOG = LoggerFactory.getLogger(OrderedContentMirrorStoreStrategy.class);
-
+    
+    private static final Random RND = new Random(System.currentTimeMillis());
+    
     /**
      * the direction of the index.
      */
@@ -96,80 +109,73 @@ public class OrderedContentMirrorStoreSt
     
     @Override
     NodeBuilder fetchKeyNode(@Nonnull NodeBuilder index, @Nonnull String key) {
-        NodeBuilder localkey = null;
+        // this is where the actual adding and maintenance of index's keys happen
+        NodeBuilder node = null;
         NodeBuilder start = index.child(START);
-
-        // identifying the right place for insert
-        String n = start.getString(NEXT);
-        if (Strings.isNullOrEmpty(n)) {
-            // new/empty index
-            localkey = index.child(key);
-            localkey.setProperty(NEXT, "");
-            start.setProperty(NEXT, key);
-        } else {
-            // specific use-case where the item has to be added as first of the list
-            String nextKey = n;
-            Iterable<? extends ChildNodeEntry> children = getChildNodeEntries(index.getNodeState(),
-                                                                              true);
-            for (ChildNodeEntry child : children) {
-                nextKey = child.getNodeState().getString(NEXT);
-                if (Strings.isNullOrEmpty(nextKey)) {
-                    // we're at the last element, therefore our 'key' has to be appended
-                    index.getChildNode(child.getName()).setProperty(NEXT, key);
-                    localkey = index.child(key);
-                    localkey.setProperty(NEXT, "");
-                } else {
-                    if (isInsertHere(key, nextKey)) {
-                        index.getChildNode(child.getName()).setProperty(NEXT, key);
-                        localkey = index.child(key);
-                        localkey.setProperty(NEXT, nextKey);
-                        break;
-                    }
-                }
-            }
+        Predicate<ChildNodeEntry> condition = direction.isAscending() 
+            ? new PredicateGreaterThan(key, true)
+            : new PredicateLessThan(key, true);
+        ChildNodeEntry[] walked = new ChildNodeEntry[OrderedIndex.LANES];
+        
+        if (Strings.isNullOrEmpty(getPropertyNext(start))) {
+            // it means we're in an empty/new index. Setting properly the :start's :next
+            setPropertyNext(start, EMPTY_NEXT_ARRAY);
         }
-
-        return localkey;
-    }
-
-    /**
-     * tells whether or not the is right to insert here a new item.
-     * 
-     * @param newItemKey the new item key to be added
-     * @param existingItemKey the 'here' of the existing index
-     * @return true for green light on insert false otherwise.
-     */
-    private boolean isInsertHere(@Nonnull String newItemKey, @Nonnull String existingItemKey) {
-        if (OrderDirection.ASC.equals(direction)) {
-            return newItemKey.compareTo(existingItemKey) < 0;
+        
+        // we use the seek for seeking the right spot. The walkedLanes will have all our
+        // predecessors
+        ChildNodeEntry entry = seek(index.getNodeState(), condition, walked);
+        if (entry != null && entry.getName().equals(key)) {
+            // it's an existing node. We should not need to update anything around pointers
+            node = index.getChildNode(key);
         } else {
-            return newItemKey.compareTo(existingItemKey) > 0;
+            // the entry does not exits yet
+            node = index.child(key);
+            // it's a brand new node. let's start setting an empty next
+            setPropertyNext(node, EMPTY_NEXT_ARRAY);
+            int lane = getLane();
+            String next;
+            NodeBuilder predecessor;
+            for (int l = lane; l >= 0; l--) {
+                // let's update the predecessors starting from the coin-flip lane
+                predecessor = index.getChildNode(walked[l].getName());
+                next = getPropertyNext(predecessor, l);
+                setPropertyNext(predecessor, key, l);
+                setPropertyNext(node, next, l);
+            }
         }
+        return node;
     }
                                         
     @Override
-    void prune(final NodeBuilder index, final Deque<NodeBuilder> builders) {
+    void prune(final NodeBuilder index, final Deque<NodeBuilder> builders, final String key) {
         for (NodeBuilder node : builders) {
             if (node.hasProperty("match") || node.getChildNodeCount(1) > 0) {
                 return;
             } else if (node.exists()) {
                 if (node.hasProperty(NEXT)) {
-                    // it's an index key and we have to relink the list
-                    // (1) find the previous element
-                    ChildNodeEntry previous = findPrevious(
-                            index.getNodeState(), node.getNodeState());
-                    if (previous == null) {
-                        LOG.debug("previous not found. Already deleted in the meanwhile. Skipping re-link");
-                    } else {
-                        LOG.debug("previous: {}", previous);
-                        // (2) find the next element
-                        String next = node.getString(NEXT);
-                        if (next == null) {
-                            next = "";
+                    ChildNodeEntry[] walkedLanes = new ChildNodeEntry[OrderedIndex.LANES];
+                    ChildNodeEntry entry;
+                    String lane0Next, prevNext, currNext;
+                    
+                    // for as long as we have the an entry and we didn't update the lane0 we have
+                    // to keep searching and update
+                    do {
+                        entry = seek(index.getNodeState(),
+                            new PredicateEquals(key),
+                            walkedLanes
+                            );
+                        lane0Next = getPropertyNext(walkedLanes[0]);
+                        for (int lane = walkedLanes.length - 1; lane >= 0; lane--) {
+                            prevNext = getPropertyNext(walkedLanes[lane], lane);
+                            if (key.equals(prevNext)) {
+                                // if it's actually pointing to us let's deal with it
+                                currNext = getPropertyNext(node, lane);
+                                setPropertyNext(index.getChildNode(walkedLanes[lane].getName()),
+                                    currNext, lane);
+                            }
                         }
-                        // (3) re-link the previous to the next
-                        index.getChildNode(previous.getName()).setProperty(NEXT, next);
-                    }
+                    } while (entry != null && !key.equals(lane0Next));
                 }
                 node.remove();
             }
@@ -177,43 +183,6 @@ public class OrderedContentMirrorStoreSt
     }
 
     /**
-     * find the previous item (ChildNodeEntry) in the index given the provided NodeState for
-     * comparison
-     * 
-     * in an index sorted in ascending manner where we have @{code [1, 2, 3, 4, 5]} if we ask for 
-     * a previous given 4 it will be 3. previous(4)=3.
-     * 
-     * in an index sorted in descending manner where we have @{code [5, 4, 3, 2, 1]} if we as for
-     * a previous given 4 it will be 5. previous(4)=5.
-     * 
-     * @param index the index we want to look into ({@code :index})
-     * @param node the node we want to compare
-     * @return the previous item or null if not found.
-     */
-    @Nullable
-    ChildNodeEntry findPrevious(@Nonnull final NodeState index, @Nonnull final NodeState node) {
-        ChildNodeEntry previous = null;
-        ChildNodeEntry current = null;
-        boolean found = false;
-        Iterator<? extends ChildNodeEntry> it = getChildNodeEntries(index, true).iterator();
-
-        while (!found && it.hasNext()) {
-            current = it.next();
-            if (previous == null) {
-                // first iteration
-                previous = current;
-            } else {
-                found = node.equals(current.getNodeState());
-                if (!found) {
-                    previous = current;
-                }
-            }
-        }
-
-        return found ? previous : null;
-    }
-
-    /**
      * retrieve an Iterable for going through the index in the right order without the :start node
      * 
      * @param index the root of the index (:index)
@@ -239,7 +208,8 @@ public class OrderedContentMirrorStoreSt
         Iterable<? extends ChildNodeEntry> cne = null;
         final NodeState start = index.getChildNode(START);
 
-        if ((!start.exists() || Strings.isNullOrEmpty(start.getString(NEXT))) && !includeStart) {
+        String startNext = getPropertyNext(start); 
+        if ((!start.exists() || Strings.isNullOrEmpty(startNext)) && !includeStart) {
             // if the property is not there or is empty it means we're empty
             cne = Collections.emptyList();
         } else {
@@ -290,8 +260,14 @@ public class OrderedContentMirrorStoreSt
                 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);
+                    if (direction.isAscending()) {
+                        childrenIterable = new SeekedIterable(index, firstValueableItem);
+                        it = new QueryResultsWrapper(filter, indexName, childrenIterable);
+                    } else {
+                        it = new QueryResultsWrapper(filter, indexName, new BetweenIterable(index,
+                            firstValueableItem, pr.first.getValue(Type.STRING), pr.firstIncluding,
+                            direction));
+                    }
                 }
             } else {
                 String first, last;
@@ -341,8 +317,13 @@ public class OrderedContentMirrorStoreSt
             
             Iterable<String> it = Collections.emptyList();
             if (firstValueableItem != null) {
-                it = new QueryResultsWrapper(filter, indexName, new SeekedIterable(index,
-                    firstValueableItem));
+                if (direction.isAscending()) {
+                    it = new QueryResultsWrapper(filter, indexName, new BetweenIterable(index,
+                        firstValueableItem, searchfor, include, direction));
+                } else {
+                    it = new QueryResultsWrapper(filter, indexName, new SeekedIterable(index,
+                        firstValueableItem));
+                }
             }
             return it;
         } else {
@@ -356,7 +337,7 @@ public class OrderedContentMirrorStoreSt
         return value.replaceAll("%3A", ":");
     }
     
-    private static final class OrderedChildNodeEntry extends AbstractChildNodeEntry {
+    static class OrderedChildNodeEntry extends AbstractChildNodeEntry {
         private final String name;
         private final NodeState state;
 
@@ -447,6 +428,11 @@ public class OrderedContentMirrorStoreSt
                         
                         return b;
                     }
+
+                    @Override
+                    public String getSearchFor() {
+                        throw new UnsupportedOperationException();
+                    }
                 };
 
                 CountingNodeVisitor v;
@@ -497,6 +483,11 @@ public class OrderedContentMirrorStoreSt
                         
                         return b;
                     }
+
+                    @Override
+                    public String getSearchFor() {
+                        throw new UnsupportedOperationException();
+                    }
                 };
                 
                 CountingNodeVisitor v;
@@ -558,9 +549,9 @@ public class OrderedContentMirrorStoreSt
     private static class FullIterator implements Iterator<ChildNodeEntry> {
         private boolean includeStart;
         private NodeState start;
-        private NodeState current;
+        NodeState current;
         private NodeState index;
-        private String currentName;
+        String currentName;
 
         public FullIterator(NodeState index, NodeState start, boolean includeStart,
                             NodeState current) {
@@ -573,7 +564,7 @@ public class OrderedContentMirrorStoreSt
         @Override
         public boolean hasNext() {
             return (includeStart && start.equals(current))
-                   || (!includeStart && !Strings.isNullOrEmpty(current.getString(NEXT)));
+                   || (!includeStart && !Strings.isNullOrEmpty(getPropertyNext(current)));
         }
 
         @Override
@@ -585,7 +576,7 @@ public class OrderedContentMirrorStoreSt
                 includeStart = false;
             } else {
                 if (hasNext()) {
-                    currentName = current.getString(NEXT);
+                    currentName = getPropertyNext(current);
                     current = index.getChildNode(currentName);
                     entry = new OrderedChildNodeEntry(currentName, current);
                 } else {
@@ -670,6 +661,8 @@ public class OrderedContentMirrorStoreSt
             if (firstReturned) {
                 return super.next();
             } else {
+                currentName = first.getName();
+                current = first.getNodeState();
                 firstReturned = true;
                 return first;
             }
@@ -692,28 +685,116 @@ public class OrderedContentMirrorStoreSt
             return new SeekedIterator(index, start, first);
         }
     }
+
+    /**
+     * see {@link #seek(NodeState, Predicate<ChildNodeEntry>, ChildNodeEntry[])} passing null as
+     * last argument
+     */
+    ChildNodeEntry seek(@Nonnull NodeState index,
+                                      @Nonnull Predicate<ChildNodeEntry> condition) {
+        return seek(index, condition, null);
+    }
     
     /**
-     * seek for an element in the index given the provided Predicate
+     * seek for an element in the index given the provided Predicate. If {@code walkedLanes} won't
+     * be null it will have on the way out the last elements of each lane walked through during the
+     * seek.
      * 
      * @param index the index content node {@code :index}
      * @param condition the predicate to evaluate
+     * @param walkedLanes if not null will contain the last element of the walked lanes with each
+     *            lane represented by the corresponding position in the array. <b>You have</b> to
+     *            pass in an array already sized as {@link OrderedIndex#LANES} or an
+     *            {@link IllegalArgumentException} will be raised
      * @return the entry or null if not found
      */
-    static ChildNodeEntry seek(@Nonnull NodeState index,
-                                      @Nonnull Predicate<ChildNodeEntry> condition) {
+    ChildNodeEntry seek(@Nonnull final NodeState index,
+                               @Nonnull final Predicate<ChildNodeEntry> condition,
+                               @Nullable final ChildNodeEntry[] walkedLanes) {
+        boolean keepWalked = false;
+        String searchfor = condition.getSearchFor();
+        LOG.debug("seek() - Searching for: {}", condition.getSearchFor());        
+        Predicate<ChildNodeEntry> walkingPredicate = direction.isAscending() 
+                                                             ? new PredicateLessThan(searchfor, true)
+                                                             : new PredicateGreaterThan(searchfor, true);
+        // we always begin with :start
+        ChildNodeEntry current = new OrderedChildNodeEntry(START, index.getChildNode(START));
+        ChildNodeEntry found = null;
         
-        // TODO the FullIterable will have to be replaced with something else once we'll have the
-        // Skip part of the list implemented.
-        Iterable<ChildNodeEntry> children = new FullIterable(index, false);
-        ChildNodeEntry entry = null;
-        for (ChildNodeEntry child : children) {
-            if (condition.apply(child)) {
-                entry = child;
-                break;
+        if (walkedLanes != null) {
+            if (walkedLanes.length != OrderedIndex.LANES) {
+                throw new IllegalArgumentException(String.format(
+                    "Wrong size for keeping track of the Walked Lanes. Expected %d but was %d",
+                    OrderedIndex.LANES, walkedLanes.length));
+            }
+            // ensuring the right data
+            for (int i = 0; i < walkedLanes.length; i++) {
+                walkedLanes[i] = current;
             }
+            keepWalked = true;
+        }
+
+        int lane;
+        boolean stillLaning;
+        String nextkey; 
+        ChildNodeEntry next;
+
+        if ((direction.isAscending() && condition instanceof PredicateLessThan)
+            || (direction.isDescending() && condition instanceof PredicateGreaterThan)) {
+            // we're asking for a <, <= query from ascending index or >, >= from descending
+            // we have to walk the lanes from bottom to up rather than up to bottom.
+            
+            lane = 0;
+            do {
+                stillLaning = lane < OrderedIndex.LANES;
+                nextkey = getPropertyNext(current, lane);
+                next = (Strings.isNullOrEmpty(nextkey)) 
+                    ? null 
+                    : new OrderedChildNodeEntry(nextkey, index.getChildNode(nextkey));
+                if ((next == null || !walkingPredicate.apply(next)) && lane < OrderedIndex.LANES) {
+                    // if we're currently pointing to NIL or the next element does not fit the search
+                    // but we still have lanes left
+                    lane++;
+                } else {
+                    if (condition.apply(next)) {
+                        found = next;
+                    } else {
+                        current = next;
+                        if (keepWalked && current != null) {
+                            walkedLanes[lane] = current;
+                        }
+                    }
+                }
+            } while (((next != null && walkingPredicate.apply(next)) || stillLaning) && (found == null));
+        } else {
+            lane = OrderedIndex.LANES - 1;
+            
+            do {
+                stillLaning = lane > 0;
+                nextkey = getPropertyNext(current, lane);
+                next = (Strings.isNullOrEmpty(nextkey)) 
+                    ? null 
+                    : new OrderedChildNodeEntry(nextkey, index.getChildNode(nextkey));
+                if ((next == null || !walkingPredicate.apply(next)) && lane > 0) {
+                    // if we're currently pointing to NIL or the next element does not fit the search
+                    // but we still have lanes left, let's lower the lane;
+                    lane--;
+                } else {
+                    if (condition.apply(next)) {
+                        found = next;
+                    } else {
+                        current = next;
+                        if (keepWalked && current != null) {
+                            for (int l = lane; l >= 0; l--) {
+                                walkedLanes[l] = current;
+                            }
+                        }
+                    }
+                }
+            } while (((next != null && walkingPredicate.apply(next)) || stillLaning) && (found == null));
         }
-        return entry;
+        
+        return found;
     }
     
     /**
@@ -730,6 +811,11 @@ public class OrderedContentMirrorStoreSt
         public boolean apply(ChildNodeEntry arg0) {
             return arg0 != null && searchfor.equals(arg0.getName());
         }
+
+        @Override
+        public String getSearchFor() {
+            return searchfor;
+        }
     }
     
     /**
@@ -760,6 +846,11 @@ public class OrderedContentMirrorStoreSt
             
             return b;
         }
+        
+        @Override
+        public String getSearchFor() {
+            return searchfor;
+        }
     }
 
     /**
@@ -788,6 +879,11 @@ public class OrderedContentMirrorStoreSt
 
             return b;
         }
+        
+        @Override
+        public String getSearchFor() {
+            return searchfor;
+        }
     }
     
     /**
@@ -838,23 +934,156 @@ public class OrderedContentMirrorStoreSt
                 
                 @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));
+                    // splitting in multiple lines for easier debugging
+                    boolean compareTo, equals, apply;
+                    if (d.equals(OrderDirection.ASC)) {
+                        compareTo = v.compareTo(input) > 0;
+                    } else {
+                        compareTo = v.compareTo(input) < 0;
+                    }
+                    
+                    equals =  v.equals(input);
+                    apply = compareTo || (i && equals);
+                    return apply;
+                }
+
+                @Override
+                public String getSearchFor() {
+                    throw new UnsupportedOperationException();
                 }
             };
         }
 
         @Override
         public boolean hasNext() {
-            boolean next = super.hasNext();
+            boolean hasNext = super.hasNext();
             String name = getCurrentName();
+            
+            if (name != null && hasNext) {
+                String next = getPropertyNext(current);
+                hasNext = hasNext && condition.apply(next);
+            }
+            return hasNext;
+        }
+    }
 
-            if (name != null && next) {
-                name = convert(name);
-                next = next && condition.apply(name);
+    /**
+     * set the value of the the :next at position 0
+     * 
+     * @param node the node to modify
+     * @param next the 'next' value
+     */
+    static void setPropertyNext(@Nonnull final NodeBuilder node, final String... next) {
+        if (node != null && next != null) {
+            String n1 = (next.length > 0) ? next[0] : "";
+            String n2 = (next.length > 1) ? next[1] : "";
+            String n3 = (next.length > 2) ? next[2] : "";
+            String n4 = (next.length > 3) ? next[3] : "";
+            
+            node.setProperty(NEXT, ImmutableList.of(n1, n2, n3, n4), Type.STRINGS);
+        }
+    }
+    
+    /**
+     * set the value of the :next at the given position. If the property :next won't be there by the
+     * time this method is invoked it won't perform any action
+     * 
+     * @param node
+     * @param value
+     * @param lane
+     */
+    static void setPropertyNext(@Nonnull final NodeBuilder node, 
+                                final String value, final int lane) {
+        if (node != null && value != null && lane >= 0 && lane < OrderedIndex.LANES) {
+            PropertyState next = node.getProperty(NEXT);
+            if (next != null) {
+                String[] values = Iterables.toArray(next.getValue(Type.STRINGS), String.class);
+                values[lane] = value;
+                setPropertyNext(node, values);
             }
-            return next;
         }
     }
-}
+
+    /**
+     * see {@link #getPropertyNext(NodeState, int)} by providing '0' as lane
+     */
+    static String getPropertyNext(@Nonnull final NodeState nodeState) {
+        return getPropertyNext(nodeState, 0);
+    }
+    
+    /**
+     * return the 'next' value at the provided position
+     * 
+     * @param nodeState the node state to inspect
+     * @return the next value
+     */
+    static String getPropertyNext(@Nonnull final NodeState state, final int lane) {
+        String next = "";
+        PropertyState ps = state.getProperty(NEXT);
+        if (ps != null) {
+            next = (lane < OrderedIndex.LANES) ? ps.getValue(Type.STRING, lane)
+                                               : "";
+        }
+        return next;
+    }
+    
+    /**
+     * short-cut for using NodeBuilder. See {@code getNext(NodeState)}
+     */
+    static String getPropertyNext(@Nonnull final NodeBuilder node) {
+        return getPropertyNext(node.getNodeState());
+    }
+
+    /**
+     * short-cut for using NodeBuilder. See {@code getNext(NodeState)}
+     */
+    static String getPropertyNext(@Nonnull final NodeBuilder node, final int lane) {
+        return getPropertyNext(node.getNodeState(), lane);
+    }
+
+    /**
+     * short-cut for using ChildNodeEntry. See {@code getNext(NodeState)}
+     */
+    static String getPropertyNext(@Nonnull final ChildNodeEntry child) {
+        return getPropertyNext(child.getNodeState());
+    }
+
+    static String getPropertyNext(@Nonnull final ChildNodeEntry child, int lane) {
+        return getPropertyNext(child.getNodeState(), lane);
+    }
+
+    /**
+     * retrieve the lane to be updated based on probabilistic approach.
+     * 
+     * Having 4 lanes if we have the 3 to be updated it means we'll have to update lanes
+     * 0,1,2 and 3. If we'll have 2 only 0,1,2 and so on.
+     * 
+     * Lane 0 will always be updated as it's the base linked list.
+     * 
+     * It uses {@code getLane(Random)} by passing a {@code new Random(System.currentTimeMillis())}
+     * 
+     * @see OrderedIndex.DEFAULT_PROBABILITY
+     * 
+     * @return the lane to start updating from.
+     */
+    public int getLane() {
+        return getLane(RND);
+    }
+    
+    /**
+     * used for mocking purposes or advanced uses. Use the {@code getLane()} where possible
+     * 
+     * @param rnd the Random generator to be used for probability
+     * @return the lane to be updated. 
+     */
+    int getLane(@Nonnull final Random rnd) {
+        final int maxLanes = OrderedIndex.LANES - 1;
+        int lane = 0;
+        
+        while (rnd.nextDouble() < OrderedIndex.DEFAULT_PROBABILITY && lane < maxLanes) {
+            lane++;
+        }
+        
+        return lane;
+    }
+}
\ No newline at end of file

Modified: jackrabbit/oak/branches/1.0/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/BasicOrderedPropertyIndexQueryTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.0/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/BasicOrderedPropertyIndexQueryTest.java?rev=1602124&r1=1602123&r2=1602124&view=diff
==============================================================================
--- jackrabbit/oak/branches/1.0/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/BasicOrderedPropertyIndexQueryTest.java (original)
+++ jackrabbit/oak/branches/1.0/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/BasicOrderedPropertyIndexQueryTest.java Thu Jun 12 11:21:53 2014
@@ -63,8 +63,7 @@ public abstract class BasicOrderedProper
     /**
      * formatter for date conversions
      */
-    protected static final SimpleDateFormat ISO_8601_2000 = new SimpleDateFormat(
-        "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
+    protected static final String ISO_8601_2000 = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"; 
 
     /**
      * generate a list of values to be used as ordered set. Will return something like
@@ -198,8 +197,9 @@ public abstract class BasicOrderedProper
         List<String> values = new ArrayList<String>(amount);
         Calendar lstart = (Calendar) start.clone();
         int hours = (OrderDirection.DESC.equals(direction)) ? -12 : 12;
+        SimpleDateFormat sdf = new SimpleDateFormat(ISO_8601_2000);
         for (int i = 0; i < amount; i++) {
-            values.add(ISO_8601_2000.format(lstart.getTime()));
+            values.add(sdf.format(lstart.getTime()));
             lstart.add(Calendar.HOUR_OF_DAY, hours);
         }
 

Modified: jackrabbit/oak/branches/1.0/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexDescendingQueryTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.0/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexDescendingQueryTest.java?rev=1602124&r1=1602123&r2=1602124&view=diff
==============================================================================
--- jackrabbit/oak/branches/1.0/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexDescendingQueryTest.java (original)
+++ jackrabbit/oak/branches/1.0/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexDescendingQueryTest.java Thu Jun 12 11:21:53 2014
@@ -22,6 +22,7 @@ import static junit.framework.Assert.ass
 import static org.apache.jackrabbit.JcrConstants.NT_UNSTRUCTURED;
 
 import java.text.ParseException;
+import java.text.SimpleDateFormat;
 import java.util.Calendar;
 import java.util.Collections;
 import java.util.Iterator;
@@ -162,7 +163,7 @@ public class OrderedPropertyIndexDescend
 
         Calendar searchForCalendar = (Calendar) start.clone();
         searchForCalendar.add(Calendar.HOUR_OF_DAY, 36);
-        String searchFor = ISO_8601_2000.format(searchForCalendar.getTime());
+        String searchFor = new SimpleDateFormat(ISO_8601_2000).format(searchForCalendar.getTime());
         
         // re-sorting descending for matching the actual index direction
         Collections.sort(nodes, Collections.reverseOrder());
@@ -206,7 +207,7 @@ public class OrderedPropertyIndexDescend
 
         Calendar searchForCalendar = (Calendar) start.clone();
         searchForCalendar.add(Calendar.HOUR_OF_DAY, 36);
-        String searchFor = ISO_8601_2000.format(searchForCalendar.getTime());
+        String searchFor = new SimpleDateFormat(ISO_8601_2000).format(searchForCalendar.getTime());
         
         // re-sorting descending for matching the actual index direction
         Collections.sort(nodes, Collections.reverseOrder());
@@ -251,7 +252,7 @@ public class OrderedPropertyIndexDescend
 
         Calendar searchForCalendar = (Calendar) start.clone();
         searchForCalendar.add(Calendar.HOUR_OF_DAY, -36);
-        String searchFor = ISO_8601_2000.format(searchForCalendar.getTime());
+        String searchFor = new SimpleDateFormat(ISO_8601_2000).format(searchForCalendar.getTime());
         Map<String, PropertyValue> filter = ImmutableMap.of(ORDERED_PROPERTY,
             PropertyValues.newDate(searchFor));
         Iterator<? extends ResultRow> results = executeQuery(
@@ -291,7 +292,7 @@ public class OrderedPropertyIndexDescend
 
         Calendar searchForCalendar = (Calendar) start.clone();
         searchForCalendar.add(Calendar.HOUR_OF_DAY, -36);
-        String searchFor = ISO_8601_2000.format(searchForCalendar.getTime());
+        String searchFor = new SimpleDateFormat(ISO_8601_2000).format(searchForCalendar.getTime());
         Map<String, PropertyValue> filter = ImmutableMap.of(ORDERED_PROPERTY,
             PropertyValues.newDate(searchFor));
         Iterator<? extends ResultRow> results = executeQuery(
@@ -334,7 +335,7 @@ public class OrderedPropertyIndexDescend
 
         Calendar searchForCalendar = (Calendar) start.clone();
         searchForCalendar.add(Calendar.HOUR_OF_DAY, 36);
-        String searchFor = ISO_8601_2000.format(searchForCalendar.getTime());
+        String searchFor = new SimpleDateFormat(ISO_8601_2000).format(searchForCalendar.getTime());
         
         // re-sorting descending for matching the actual index direction
         Collections.sort(nodes, Collections.reverseOrder());
@@ -372,12 +373,13 @@ public class OrderedPropertyIndexDescend
 
         Calendar searchForCalendarStart = (Calendar) start.clone();
         searchForCalendarStart.add(Calendar.HOUR_OF_DAY, 36);
-        String searchForStart = ISO_8601_2000.format(searchForCalendarStart.getTime());
+        SimpleDateFormat sdf = new SimpleDateFormat(ISO_8601_2000);
+        String searchForStart = sdf.format(searchForCalendarStart.getTime());
 
         Calendar endCalendar = Calendar.getInstance();
-        endCalendar.setTime(ISO_8601_2000.parse(nodes.get(nodes.size() - 1).getValue()));
+        endCalendar.setTime(sdf.parse(nodes.get(nodes.size() - 1).getValue()));
         endCalendar.add(Calendar.HOUR_OF_DAY, -36);
-        String searchForEnd = ISO_8601_2000.format(endCalendar.getTime());
+        String searchForEnd = sdf.format(endCalendar.getTime());
 
         Map<String, PropertyValue> filter = ImmutableMap.of("start",
             PropertyValues.newDate(searchForStart), "end", PropertyValues.newDate(searchForEnd));
@@ -432,12 +434,13 @@ public class OrderedPropertyIndexDescend
         
         Calendar searchForCalendarStart = (Calendar) start.clone();
         searchForCalendarStart.add(Calendar.HOUR_OF_DAY, 36);
-        String searchForStart = ISO_8601_2000.format(searchForCalendarStart.getTime());
+        SimpleDateFormat sdf = new SimpleDateFormat(ISO_8601_2000);
+        String searchForStart = sdf.format(searchForCalendarStart.getTime());
 
         Calendar endCalendar = Calendar.getInstance();
-        endCalendar.setTime(ISO_8601_2000.parse(nodes.get(nodes.size() - 1).getValue()));
+        endCalendar.setTime(sdf.parse(nodes.get(nodes.size() - 1).getValue()));
         endCalendar.add(Calendar.HOUR_OF_DAY, -36);
-        String searchForEnd = ISO_8601_2000.format(endCalendar.getTime());
+        String searchForEnd = sdf.format(endCalendar.getTime());
 
         Map<String, PropertyValue> filter = ImmutableMap.of("start",
             PropertyValues.newDate(searchForStart), "end", PropertyValues.newDate(searchForEnd));
@@ -479,12 +482,13 @@ public class OrderedPropertyIndexDescend
         
         Calendar searchForCalendarStart = (Calendar) start.clone();
         searchForCalendarStart.add(Calendar.HOUR_OF_DAY, 36);
-        String searchForStart = ISO_8601_2000.format(searchForCalendarStart.getTime());
+        SimpleDateFormat sdf = new SimpleDateFormat(ISO_8601_2000);
+        String searchForStart = sdf.format(searchForCalendarStart.getTime());
 
         Calendar endCalendar = Calendar.getInstance();
-        endCalendar.setTime(ISO_8601_2000.parse(nodes.get(nodes.size() - 1).getValue()));
+        endCalendar.setTime(sdf.parse(nodes.get(nodes.size() - 1).getValue()));
         endCalendar.add(Calendar.HOUR_OF_DAY, -36);
-        String searchForEnd = ISO_8601_2000.format(endCalendar.getTime());
+        String searchForEnd = sdf.format(endCalendar.getTime());
 
         Map<String, PropertyValue> filter = ImmutableMap.of("start",
             PropertyValues.newDate(searchForStart), "end", PropertyValues.newDate(searchForEnd));
@@ -526,12 +530,13 @@ public class OrderedPropertyIndexDescend
 
         Calendar searchForCalendarStart = (Calendar) start.clone();
         searchForCalendarStart.add(Calendar.HOUR_OF_DAY, 36);
-        String searchForStart = ISO_8601_2000.format(searchForCalendarStart.getTime());
+        SimpleDateFormat sdf = new SimpleDateFormat(ISO_8601_2000);
+        String searchForStart = sdf.format(searchForCalendarStart.getTime());
 
         Calendar endCalendar = Calendar.getInstance();
-        endCalendar.setTime(ISO_8601_2000.parse(nodes.get(nodes.size() - 1).getValue()));
+        endCalendar.setTime(sdf.parse(nodes.get(nodes.size() - 1).getValue()));
         endCalendar.add(Calendar.HOUR_OF_DAY, -36);
-        String searchForEnd = ISO_8601_2000.format(endCalendar.getTime());
+        String searchForEnd = sdf.format(endCalendar.getTime());
 
         Map<String, PropertyValue> filter = ImmutableMap.of("start",
             PropertyValues.newDate(searchForStart), "end", PropertyValues.newDate(searchForEnd));

Modified: jackrabbit/oak/branches/1.0/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexQueryTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.0/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexQueryTest.java?rev=1602124&r1=1602123&r2=1602124&view=diff
==============================================================================
--- jackrabbit/oak/branches/1.0/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexQueryTest.java (original)
+++ jackrabbit/oak/branches/1.0/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexQueryTest.java Thu Jun 12 11:21:53 2014
@@ -25,6 +25,7 @@ import static org.apache.jackrabbit.oak.
 import static org.junit.Assert.assertNotNull;
 
 import java.text.ParseException;
+import java.text.SimpleDateFormat;
 import java.util.Calendar;
 import java.util.Collections;
 import java.util.Iterator;
@@ -33,6 +34,7 @@ import java.util.Map;
 
 import javax.jcr.RepositoryException;
 
+
 import org.apache.jackrabbit.JcrConstants;
 import org.apache.jackrabbit.oak.api.CommitFailedException;
 import org.apache.jackrabbit.oak.api.PropertyValue;
@@ -170,7 +172,7 @@ public class OrderedPropertyIndexQueryTe
 
         Calendar searchForCalendar = (Calendar) start.clone();
         searchForCalendar.add(Calendar.HOUR_OF_DAY, 36);
-        String searchFor = ISO_8601_2000.format(searchForCalendar.getTime());
+        String searchFor = new SimpleDateFormat(ISO_8601_2000).format(searchForCalendar.getTime());
         Map<String, PropertyValue> filter = ImmutableMap.of(ORDERED_PROPERTY,
             PropertyValues.newDate(searchFor));
         Iterator<? extends ResultRow> results = executeQuery(
@@ -209,7 +211,7 @@ public class OrderedPropertyIndexQueryTe
 
         Calendar searchForCalendar = (Calendar) start.clone();
         searchForCalendar.add(Calendar.HOUR_OF_DAY, 36);
-        String searchFor = ISO_8601_2000.format(searchForCalendar.getTime());
+        String searchFor = new SimpleDateFormat(ISO_8601_2000).format(searchForCalendar.getTime());
         Map<String, PropertyValue> filter = ImmutableMap.of(ORDERED_PROPERTY,
             PropertyValues.newDate(searchFor));
         Iterator<? extends ResultRow> results = executeQuery(
@@ -227,8 +229,6 @@ public class OrderedPropertyIndexQueryTe
     /**
      * test the range query in case of '<' condition
      *
-     * in this case as we're ascending we're expecting an empty resultset with the proper
-     * provider. not the lowcost one.
      * @throws Exception
      */
     @Test
@@ -251,7 +251,7 @@ public class OrderedPropertyIndexQueryTe
 
         Calendar searchForCalendar = (Calendar) start.clone();
         searchForCalendar.add(Calendar.HOUR_OF_DAY, -36);
-        String searchFor = ISO_8601_2000.format(searchForCalendar.getTime());
+        String searchFor = new SimpleDateFormat(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);
@@ -307,7 +307,7 @@ public class OrderedPropertyIndexQueryTe
 
         Calendar searchForCalendar = (Calendar) start.clone();
         searchForCalendar.add(Calendar.HOUR_OF_DAY, -36);
-        String searchFor = ISO_8601_2000.format(searchForCalendar.getTime());
+        String searchFor = new SimpleDateFormat(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);
@@ -614,7 +614,7 @@ public class OrderedPropertyIndexQueryTe
 
         Calendar searchForCalendar = (Calendar) start.clone();
         searchForCalendar.add(Calendar.HOUR_OF_DAY, 36);
-        String searchFor = ISO_8601_2000.format(searchForCalendar.getTime());
+        String searchFor = new SimpleDateFormat(ISO_8601_2000).format(searchForCalendar.getTime());
         Iterator<? extends ResultRow> results = executeQuery(String.format(query, searchFor), SQL2,
             null).getRows().iterator();
         Iterator<ValuePathTuple> filtered = Iterables.filter(nodes,
@@ -648,12 +648,13 @@ public class OrderedPropertyIndexQueryTe
 
         Calendar searchForCalendarStart = (Calendar) start.clone();
         searchForCalendarStart.add(Calendar.HOUR_OF_DAY, 36);
-        String searchForStart = ISO_8601_2000.format(searchForCalendarStart.getTime());
+        SimpleDateFormat sdf  = new SimpleDateFormat(ISO_8601_2000);
+        String searchForStart = sdf.format(searchForCalendarStart.getTime());
 
         Calendar endCalendar = Calendar.getInstance();
-        endCalendar.setTime(ISO_8601_2000.parse(nodes.get(nodes.size() - 1).getValue()));
+        endCalendar.setTime(sdf.parse(nodes.get(nodes.size() - 1).getValue()));
         endCalendar.add(Calendar.HOUR_OF_DAY, -36);
-        String searchForEnd = ISO_8601_2000.format(endCalendar.getTime());
+        String searchForEnd = sdf.format(endCalendar.getTime());
 
         Map<String, PropertyValue> filter = ImmutableMap.of("start",
             PropertyValues.newDate(searchForStart), "end", PropertyValues.newDate(searchForEnd));
@@ -693,12 +694,13 @@ public class OrderedPropertyIndexQueryTe
 
         Calendar searchForCalendarStart = (Calendar) start.clone();
         searchForCalendarStart.add(Calendar.HOUR_OF_DAY, 36);
-        String searchForStart = ISO_8601_2000.format(searchForCalendarStart.getTime());
+        SimpleDateFormat sdf = new SimpleDateFormat(ISO_8601_2000);
+        String searchForStart = sdf.format(searchForCalendarStart.getTime());
 
         Calendar endCalendar = Calendar.getInstance();
-        endCalendar.setTime(ISO_8601_2000.parse(nodes.get(nodes.size() - 1).getValue()));
+        endCalendar.setTime(sdf.parse(nodes.get(nodes.size() - 1).getValue()));
         endCalendar.add(Calendar.HOUR_OF_DAY, -36);
-        String searchForEnd = ISO_8601_2000.format(endCalendar.getTime());
+        String searchForEnd = sdf.format(endCalendar.getTime());
 
         Map<String, PropertyValue> filter = ImmutableMap.of("start",
             PropertyValues.newDate(searchForStart), "end", PropertyValues.newDate(searchForEnd));
@@ -738,12 +740,13 @@ public class OrderedPropertyIndexQueryTe
         
         Calendar searchForCalendarStart = (Calendar) start.clone();
         searchForCalendarStart.add(Calendar.HOUR_OF_DAY, 36);
-        String searchForStart = ISO_8601_2000.format(searchForCalendarStart.getTime());
+        SimpleDateFormat sdf = new SimpleDateFormat(ISO_8601_2000);
+        String searchForStart = sdf.format(searchForCalendarStart.getTime());
 
         Calendar endCalendar = Calendar.getInstance();
-        endCalendar.setTime(ISO_8601_2000.parse(nodes.get(nodes.size() - 1).getValue()));
+        endCalendar.setTime(sdf.parse(nodes.get(nodes.size() - 1).getValue()));
         endCalendar.add(Calendar.HOUR_OF_DAY, -36);
-        String searchForEnd = ISO_8601_2000.format(endCalendar.getTime());
+        String searchForEnd = sdf.format(endCalendar.getTime());
 
         Map<String, PropertyValue> filter = ImmutableMap.of("start",
             PropertyValues.newDate(searchForStart), "end", PropertyValues.newDate(searchForEnd));
@@ -753,10 +756,6 @@ public class OrderedPropertyIndexQueryTe
         Iterator<ValuePathTuple> filtered = Iterables.filter(nodes,
             new ValuePathTuple.BetweenPredicate(searchForStart, searchForEnd, false, true))
             .iterator();
-
-        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());
@@ -784,15 +783,16 @@ public class OrderedPropertyIndexQueryTe
         List<ValuePathTuple> nodes = addChildNodes(
             generateOrderedDates(10, 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());
+        SimpleDateFormat sdf = new SimpleDateFormat(ISO_8601_2000);
+        String searchForStart = sdf.format(searchForCalendarStart.getTime());
 
         Calendar endCalendar = Calendar.getInstance();
-        endCalendar.setTime(ISO_8601_2000.parse(nodes.get(nodes.size() - 1).getValue()));
+        endCalendar.setTime(sdf.parse(nodes.get(nodes.size() - 1).getValue()));
         endCalendar.add(Calendar.HOUR_OF_DAY, -36);
-        String searchForEnd = ISO_8601_2000.format(endCalendar.getTime());
+        String searchForEnd = sdf.format(endCalendar.getTime());
 
         Map<String, PropertyValue> filter = ImmutableMap.of("start",
             PropertyValues.newDate(searchForStart), "end", PropertyValues.newDate(searchForEnd));
@@ -800,10 +800,6 @@ public class OrderedPropertyIndexQueryTe
             .iterator();
 
         Iterator<ValuePathTuple> filtered = Iterables.filter(nodes,
-            new ValuePathTuple.BetweenPredicate(searchForStart, searchForEnd, false, true))
-            .iterator();
-
-        filtered = Iterables.filter(nodes,
             new ValuePathTuple.BetweenPredicate(searchForStart, searchForEnd, true, true))
             .iterator();
         
@@ -813,4 +809,4 @@ public class OrderedPropertyIndexQueryTe
         setTravesalEnabled(true);
 
     }
-}
+}
\ No newline at end of file