jackrabbit-oak-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From thom...@apache.org
Subject svn commit: r1575892 [1/2] - in /jackrabbit/oak/trunk/oak-core/src: main/java/org/apache/jackrabbit/oak/plugins/index/ main/java/org/apache/jackrabbit/oak/plugins/index/property/ main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/ test...
Date Mon, 10 Mar 2014 10:36:01 GMT
Author: thomasm
Date: Mon Mar 10 10:36:00 2014
New Revision: 1575892

URL: http://svn.apache.org/r1575892
Log:
OAK-1263 optimize oak index to support 'fast ORDER BY' queries to support sorting & pagination for large set of children

Added:
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/BasicOrderedPropertyIndexQueryTest.java
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/LowCostOrderedPropertyIndexProvider.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/ValuePathTuple.java
Modified:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/IndexUtils.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedIndex.java
    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/OrderedPropertyIndexEditor.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/OrderedPropertyIndexEditorTest.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/IndexUtils.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/IndexUtils.java?rev=1575892&r1=1575891&r2=1575892&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/IndexUtils.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/IndexUtils.java Mon Mar 10 10:36:00 2014
@@ -30,6 +30,7 @@ import static org.apache.jackrabbit.oak.
 import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.UNIQUE_PROPERTY_NAME;
 
 import java.util.Collection;
+import java.util.Map;
 
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
@@ -128,16 +129,8 @@ public class IndexUtils {
                                              @Nonnull String[] propertyNames, 
                                              @Nullable String[] declaringNodeTypeNames, 
                                              @Nonnull String propertyIndexType) throws RepositoryException {
-        NodeUtil entry = indexNode.getOrAddChild(indexDefName, INDEX_DEFINITIONS_NODE_TYPE);
-        entry.setString(TYPE_PROPERTY_NAME, propertyIndexType);
-        entry.setBoolean(REINDEX_PROPERTY_NAME, true);
-        if (unique) {
-            entry.setBoolean(UNIQUE_PROPERTY_NAME, true);
-        }
-        if (declaringNodeTypeNames != null && declaringNodeTypeNames.length > 0) {
-            entry.setNames(DECLARING_NODE_TYPES, declaringNodeTypeNames);
-        }
-        entry.setNames(PROPERTY_NAMES, propertyNames);
+        createIndexDefinition(indexNode, indexDefName, unique, propertyNames,
+            declaringNodeTypeNames, propertyIndexType, null);
     }
 
     public static void createReferenceIndex(@Nonnull NodeBuilder index) {
@@ -161,4 +154,41 @@ public class IndexUtils {
                 && type.getValue(Type.STRING).equals(typeIn);
     }
 
+    /**
+     * Create a new property index definition below the given {@code indexNode} of the provided
+     * {@code propertyIndexType}.
+     * 
+     * @param indexNode
+     * @param indexDefName
+     * @param unique
+     * @param propertyNames
+     * @param declaringNodeTypeNames
+     * @param propertyIndexType
+     * @param properties any additional property to be added to the index definition.
+     * @throws RepositoryException
+     */
+    public static void createIndexDefinition(@Nonnull NodeUtil indexNode, 
+                                             @Nonnull String indexDefName, 
+                                             boolean unique, 
+                                             @Nonnull String[] propertyNames, 
+                                             @Nullable String[] declaringNodeTypeNames, 
+                                             @Nonnull String propertyIndexType,
+                                             Map<String, String> properties) throws RepositoryException {
+        NodeUtil entry = indexNode.getOrAddChild(indexDefName, INDEX_DEFINITIONS_NODE_TYPE);
+        entry.setString(TYPE_PROPERTY_NAME, propertyIndexType);
+        entry.setBoolean(REINDEX_PROPERTY_NAME, true);
+        if (unique) {
+            entry.setBoolean(UNIQUE_PROPERTY_NAME, true);
+        }
+        if (declaringNodeTypeNames != null && declaringNodeTypeNames.length > 0) {
+            entry.setNames(DECLARING_NODE_TYPES, declaringNodeTypeNames);
+        }
+        entry.setNames(PROPERTY_NAMES, propertyNames);
+
+        if (properties != null) {
+            for (String k : properties.keySet()) {
+                entry.setString(k, properties.get(k));
+            }
+        }
+    }
 }

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedIndex.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedIndex.java?rev=1575892&r1=1575891&r2=1575892&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedIndex.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedIndex.java Mon Mar 10 10:36:00 2014
@@ -17,10 +17,65 @@
 
 package org.apache.jackrabbit.oak.plugins.index.property;
 
+import javax.annotation.CheckForNull;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
 /**
  * interface for shared constants around different actors: QueryIndex, IndexEditors,
  * IndexEditorProviders, ...
  */
 public interface OrderedIndex {
+    /**
+     * enum for easing the order direction of the index
+     */
+    enum OrderDirection {
+        /**
+         * ascending order configuration (default)
+         */
+        ASC("ascending"), 
+        
+        /**
+         * descending order configuration
+         */
+        DESC("descending");
+        
+        private final String direction;
+        private OrderDirection(String direction) {
+            this.direction = direction;
+        }
+        public String getDirection() {
+            return direction;
+        }
+        
+        /**
+         * retrieve an {@code OrderDirection} from a provided String. Will return null in case of no-match
+         * 
+         * @param direction the direction of the sorting: ascending or descending
+         * @return the order
+         */
+        @Nullable @CheckForNull 
+        public static OrderDirection fromString(@Nonnull final String direction) {
+            for (OrderDirection d : OrderDirection.values()) {
+                if (d.getDirection().equalsIgnoreCase(direction)) {
+                    return d;
+                }
+            }
+            return null;
+        }
+    };
+    
     String TYPE = "ordered";
+    
+    /**
+     * the 'key' used for specifying the direction of the index when providing the configuration
+     * 
+     * {@code "propertyNames"="foobar", "direction"="ascending" }
+     */
+    String DIRECTION = "direction";
+    
+    /**
+     * the default direction for sorting the index
+     */
+    OrderDirection DEFAULT_DIRECTION = OrderDirection.ASC;
 }

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=1575892&r1=1575891&r2=1575892&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 Mar 10 10:36:00 2014
@@ -19,10 +19,16 @@ package org.apache.jackrabbit.oak.plugin
 
 import static org.apache.jackrabbit.oak.plugins.index.property.OrderedIndex.TYPE;
 
+import org.apache.jackrabbit.oak.spi.query.Filter;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
+/**
+ * A property index that supports ordering keys.
+ */
 public class OrderedPropertyIndex extends PropertyIndex {
-
+    private static final Logger LOG = LoggerFactory.getLogger(OrderedPropertyIndex.class);
     @Override
     public String getIndexName() {
         return TYPE;
@@ -32,4 +38,11 @@ public class OrderedPropertyIndex extend
     PropertyIndexLookup getLookup(NodeState root) {
         return new OrderedPropertyIndexLookup(root);
     }
+
+    @Override
+    public double getCost(Filter filter, NodeState root) {
+        //we don't want the index to be used yet
+        LOG.warn("this index will always return Double.POSITIVE_INFINITY and therefore never work");
+        return Double.POSITIVE_INFINITY;
+    }
 }

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexEditor.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexEditor.java?rev=1575892&r1=1575891&r2=1575892&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexEditor.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexEditor.java Mon Mar 10 10:36:00 2014
@@ -22,14 +22,13 @@ import java.util.Set;
 
 import javax.annotation.Nonnull;
 
-import org.apache.jackrabbit.oak.api.CommitFailedException;
 import org.apache.jackrabbit.oak.api.PropertyState;
 import org.apache.jackrabbit.oak.api.Type;
 import org.apache.jackrabbit.oak.plugins.index.IndexConstants;
 import org.apache.jackrabbit.oak.plugins.index.IndexUpdateCallback;
+import org.apache.jackrabbit.oak.plugins.index.property.OrderedIndex.OrderDirection;
 import org.apache.jackrabbit.oak.plugins.index.property.strategy.IndexStoreStrategy;
 import org.apache.jackrabbit.oak.plugins.index.property.strategy.OrderedContentMirrorStoreStrategy;
-import org.apache.jackrabbit.oak.spi.commit.Editor;
 import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
 import org.slf4j.Logger;
@@ -37,40 +36,68 @@ import org.slf4j.LoggerFactory;
 
 import com.google.common.base.Strings;
 
+/**
+ * Index editor for keeping an ordered property index up to date.
+ */
 public class OrderedPropertyIndexEditor extends PropertyIndexEditor {
-    private static final Logger log = LoggerFactory.getLogger(OrderedPropertyIndexEditor.class);
-    private static final IndexStoreStrategy ORDERED_MIRROR = new OrderedContentMirrorStoreStrategy();
+    /**
+     * the default Ascending ordered StoreStrategy
+     */
+    static final IndexStoreStrategy ORDERED_MIRROR = new OrderedContentMirrorStoreStrategy();
+    
+    /**
+     * the Descending ordered StoreStrategy
+     */
+    static final IndexStoreStrategy ORDERED_MIRROR_DESCENDING = new OrderedContentMirrorStoreStrategy(OrderDirection.DESC);
 
+    private static final Logger LOG = LoggerFactory.getLogger(OrderedPropertyIndexEditor.class);
+    
     private final Set<String> propertyNames;
 
     private boolean properlyConfigured;
 
+    private OrderDirection direction = OrderedIndex.DEFAULT_DIRECTION;
+
     public OrderedPropertyIndexEditor(NodeBuilder definition, NodeState root,
                                       IndexUpdateCallback callback) {
         super(definition, root, callback);
 
+        // configuring propertyNames
         Set<String> pns = null;
-
         PropertyState names = definition.getProperty(IndexConstants.PROPERTY_NAMES);
         if (names != null) {
             String value = names.getValue(Type.NAME, 0);
             if (Strings.isNullOrEmpty(value)) {
-                log.warn("Empty value passed as propertyNames. Index not properly configured. Ignoring.");
+                LOG.warn("Empty value passed as propertyNames. Index not properly configured. Ignoring.");
             } else {
                 if (names.isArray()) {
-                    log.warn("Only single value supported. '{}' only will be used.", value);
+                    LOG.warn("Only single value supported. '{}' only will be used.", value);
                 }
                 pns = Collections.singleton(value);
                 this.properlyConfigured = true;
             }
         }
-
         this.propertyNames = pns;
+
+        // configuring direction
+        String propertyDirection = definition.getString(OrderedIndex.DIRECTION);
+        if (propertyDirection == null) {
+            LOG.info("Using default direction for sorting: {}", this.direction);
+        } else {
+            OrderDirection dir = OrderDirection.fromString(propertyDirection);
+            if (dir == null) {
+                LOG.warn("An unknown direction has been specified for sorting: '{}'. Using default one. {}",
+                         propertyDirection, this.direction);
+            } else {
+                this.direction = dir;
+            }
+        }
     }
 
     OrderedPropertyIndexEditor(OrderedPropertyIndexEditor parent, String name) {
         super(parent, name);
         this.propertyNames = parent.getPropertyNames();
+        this.direction = parent.getDirection();
     }
 
     /**
@@ -80,7 +107,11 @@ public class OrderedPropertyIndexEditor 
      */
     @Override
     IndexStoreStrategy getStrategy(boolean unique) {
-        return ORDERED_MIRROR;
+        IndexStoreStrategy store = ORDERED_MIRROR;
+        if (!OrderedIndex.DEFAULT_DIRECTION.equals(getDirection())) {
+            store = ORDERED_MIRROR_DESCENDING;
+        }
+        return store;
     }
 
     public boolean isProperlyConfigured() {
@@ -98,53 +129,11 @@ public class OrderedPropertyIndexEditor 
         return new OrderedPropertyIndexEditor(this, name);
     }
 
-    @Override
-    public void enter(NodeState before, NodeState after) {
-        log.debug("enter() - before: {} - after: {}", before, after);
-        super.enter(before, after);
-    }
-
-    @Override
-    public void leave(NodeState before, NodeState after) throws CommitFailedException {
-        log.debug("leave() - before: {} - after: {}", before, after);
-        super.leave(before, after);
-    }
-
-    @Override
-    public void propertyAdded(PropertyState after) {
-        log.debug("propertyAdded() - after: {}", after);
-        super.propertyAdded(after);
-    }
-
-    @Override
-    public void propertyChanged(PropertyState before, PropertyState after) {
-        log.debug("propertyChanged() - before: {} - after: {}", before, after);
-        super.propertyChanged(before, after);
-    }
-
-    @Override
-    public void propertyDeleted(PropertyState before) {
-        log.debug("propertyDeleted() -  before: {}", before);
-        super.propertyDeleted(before);
-    }
-
-    @Override
-    public Editor childNodeAdded(String name, NodeState after) {
-        log.debug("childNodeAdded() - name: {} - after: {}", name, after);
-        return super.childNodeAdded(name, after);
-    }
-
-    @Override
-    public Editor childNodeChanged(String name, NodeState before, NodeState after) {
-        log.debug("childNodeChanged() - name: {} - before: {} - after: {}", new Object[] { name,
-                                                                                          before,
-                                                                                          after });
-        return super.childNodeChanged(name, before, after);
-    }
-
-    @Override
-    public Editor childNodeDeleted(String name, NodeState before) {
-        log.debug("childNodeDeleted() - name: {} - before: {}", name, before);
-        return super.childNodeDeleted(name, before);
+    /**
+     * 
+     * @return the direction of the index configuration
+     */
+    public OrderDirection getDirection() {
+        return direction;
     }
 }

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=1575892&r1=1575891&r2=1575892&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 Mar 10 10:36:00 2014
@@ -26,6 +26,8 @@ import java.util.Set;
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
 
+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.memory.EmptyNodeState;
 import org.apache.jackrabbit.oak.spi.state.AbstractChildNodeEntry;
 import org.apache.jackrabbit.oak.spi.state.ChildNodeEntry;
@@ -51,7 +53,6 @@ import com.google.common.base.Strings;
  * </code>
  */
 public class OrderedContentMirrorStoreStrategy extends ContentMirrorStoreStrategy {
-    private static final Logger log = LoggerFactory.getLogger(OrderedContentMirrorStoreStrategy.class);
 
     /**
      * the property linking to the next node
@@ -70,9 +71,25 @@ public class OrderedContentMirrorStoreSt
                                                                               .setProperty(NEXT, "")
                                                                               .getNodeState();
 
+    private static final Logger LOG = LoggerFactory.getLogger(OrderedContentMirrorStoreStrategy.class);
+
+    /**
+     * the direction of the index.
+     */
+    private OrderDirection direction = OrderedIndex.DEFAULT_DIRECTION;
+
+    public OrderedContentMirrorStoreStrategy() {
+        super();
+    }
+    
+    public OrderedContentMirrorStoreStrategy(OrderDirection direction) {
+        this();
+        this.direction = direction;
+    }
+    
     @Override
     NodeBuilder fetchKeyNode(@Nonnull NodeBuilder index, @Nonnull String key) {
-        log.debug("fetchKeyNode() - index: {} - key: {}", index, key);
+        LOG.debug("fetchKeyNode() - index: {} - key: {}", index, key);
         NodeBuilder localkey = null;
         NodeBuilder start = index.child(START);
 
@@ -86,26 +103,21 @@ public class OrderedContentMirrorStoreSt
         } else {
             // specific use-case where the item has to be added as first of the list
             String nextKey = n;
-            if (key.compareTo(nextKey) < 0) {
-                localkey = index.child(key);
-                localkey.setProperty(NEXT, nextKey);
-                start.setProperty(NEXT, key);
-            } else {
-                Iterable<? extends ChildNodeEntry> children = getChildNodeEntries(index.getNodeState());
-                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
+            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, "");
-                    } else {
-                        if (key.compareTo(nextKey) < 0) {
-                            index.getChildNode(child.getName()).setProperty(NEXT, key);
-                            localkey = index.child(key);
-                            localkey.setProperty(NEXT, nextKey);
-                            break;
-                        }
+                        localkey.setProperty(NEXT, nextKey);
+                        break;
                     }
                 }
             }
@@ -114,6 +126,21 @@ public class OrderedContentMirrorStoreSt
         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;
+        } else {
+            return newItemKey.compareTo(existingItemKey) > 0;
+        }
+    }
+                                        
     @Override
     void prune(final NodeBuilder index, final Deque<NodeBuilder> builders) {
         for (NodeBuilder node : builders) {
@@ -122,11 +149,12 @@ public class OrderedContentMirrorStoreSt
             } else if (node.exists()) {
                 if (node.hasProperty(NEXT)) {
                     // it's an index key and we have to relink the list
-                    ChildNodeEntry previous = findPrevious(index.getNodeState(),
-                                                           node.getNodeState()); // (1) find the
-                                                                                 // previous element
-                    log.debug("previous: {}", previous);
-                    String next = node.getString(NEXT); // (2) find the next element
+                    // (1) find the previous element
+                    ChildNodeEntry previous = findPrevious(
+                            index.getNodeState(), node.getNodeState());
+                    LOG.debug("previous: {}", previous);
+                    // (2) find the next element
+                    String next = node.getString(NEXT); 
                     if (next == null) {
                         next = "";
                     }
@@ -138,6 +166,20 @@ 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;
@@ -158,16 +200,16 @@ public class OrderedContentMirrorStoreSt
             }
         }
 
-        return (found) ? previous : null;
+        return found ? previous : null;
     }
 
     @Override
     public void update(NodeBuilder index, String path, Set<String> beforeKeys,
                        Set<String> afterKeys) {
-        log.debug("update() - index     : {}", index);
-        log.debug("update() - path      : {}", path);
-        log.debug("update() - beforeKeys: {}", beforeKeys);
-        log.debug("update() - afterKeys : {}", afterKeys);
+        LOG.debug("update() - index     : {}", index);
+        LOG.debug("update() - path      : {}", path);
+        LOG.debug("update() - beforeKeys: {}", beforeKeys);
+        LOG.debug("update() - afterKeys : {}", afterKeys);
         super.update(index, path, beforeKeys, afterKeys);
     }
 
@@ -203,8 +245,8 @@ public class OrderedContentMirrorStoreSt
         } else {
             cne = new Iterable<ChildNodeEntry>() {
                 private NodeState localIndex = index;
-                private NodeState localStart = ((includeStart && !start.exists()) ? EMPTY_START_NODE
-                                                                             : start);
+                private NodeState localStart = includeStart && !start.exists() ? EMPTY_START_NODE
+                                                                             : start;
                 private NodeState current = localStart;
                 private boolean localIncludeStart = includeStart;
 
@@ -214,7 +256,7 @@ public class OrderedContentMirrorStoreSt
 
                         @Override
                         public boolean hasNext() {
-                            return ((localIncludeStart && localStart.equals(current)) || (!localIncludeStart && !Strings.isNullOrEmpty(current.getString(NEXT))));
+                            return (localIncludeStart && localStart.equals(current)) || (!localIncludeStart && !Strings.isNullOrEmpty(current.getString(NEXT)));
                         }
 
                         @Override
@@ -270,4 +312,4 @@ public class OrderedContentMirrorStoreSt
             return state;
         }
     }
-}
+}
\ No newline at end of file

Added: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/BasicOrderedPropertyIndexQueryTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/BasicOrderedPropertyIndexQueryTest.java?rev=1575892&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/BasicOrderedPropertyIndexQueryTest.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/BasicOrderedPropertyIndexQueryTest.java Mon Mar 10 10:36:00 2014
@@ -0,0 +1,174 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.oak.plugins.index.property;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertTrue;
+import static org.apache.jackrabbit.JcrConstants.JCR_PRIMARYTYPE;
+import static org.apache.jackrabbit.JcrConstants.NT_UNSTRUCTURED;
+
+import java.text.DecimalFormat;
+import java.text.NumberFormat;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Random;
+
+import javax.annotation.Nonnull;
+
+import org.apache.jackrabbit.oak.Oak;
+import org.apache.jackrabbit.oak.api.ContentRepository;
+import org.apache.jackrabbit.oak.api.ResultRow;
+import org.apache.jackrabbit.oak.api.Tree;
+import org.apache.jackrabbit.oak.api.Type;
+import org.apache.jackrabbit.oak.plugins.index.property.OrderedIndex.OrderDirection;
+import org.apache.jackrabbit.oak.plugins.nodetype.write.InitialContent;
+import org.apache.jackrabbit.oak.query.AbstractQueryTest;
+import org.apache.jackrabbit.oak.spi.security.OpenSecurityProvider;
+
+public abstract class BasicOrderedPropertyIndexQueryTest extends AbstractQueryTest {
+    /**
+     * the property used by the index
+     */
+    public static final String ORDERED_PROPERTY = "foo";
+    
+    /**
+     * number of nodes to create for testing.
+     * 
+     * It has been found during development that in some cases the order of the nodes creation
+     * within the persistence where the actual expected order.
+     * 
+     * The higher the value the lower the chance for this to happen.
+     */
+    protected static final int NUMBER_OF_NODES = 50;
+
+    /**
+     * generate a list of values to be used as ordered set. Will return something like
+     * {@code value000, value001, value002, ...}
+     * 
+     * 
+     * @param amount
+     * @param direction the direction of the sorting
+     * @return a list of {@code amount} values ordered as specified by {@code direction}
+     */
+    protected static List<String> generateOrderedValues(int amount, OrderDirection direction) {
+        if (amount > 1000) {
+            throw new RuntimeException("amount cannot be greater than 1000");
+        }
+        List<String> values = new ArrayList<String>(amount);
+        NumberFormat nf = new DecimalFormat("000");
+
+        if (OrderDirection.DESC.equals(direction)) {
+            for (int i = amount; i > 0; i--) {
+                values.add(String.format("value%s", String.valueOf(nf.format(i))));
+            }
+        } else {
+            for (int i = 0; i < amount; i++) {
+                values.add(String.format("value%s", String.valueOf(nf.format(i))));
+            }
+        }
+        return values;
+    }
+    
+    /**
+     * as {@code generateOrderedValues(int, OrderDirection)} by forcing OrderDirection.ASC
+     * 
+     * @param amount
+     * @return
+     */
+    protected static List<String> generateOrderedValues(int amount) {
+        return generateOrderedValues(amount, OrderDirection.ASC);
+    }
+    
+    /**
+     * create a child node for the provided father
+     * 
+     * @param father
+     * @param name
+     *            the name of the node to create
+     * @param propName
+     *            the name of the property to assign
+     * @param propValue
+     *            the value of the property to assign
+     * @return
+     */
+    static Tree child(Tree father, String name, String propName, String propValue) {
+        Tree child = father.addChild(name);
+        child.setProperty(JCR_PRIMARYTYPE, NT_UNSTRUCTURED, Type.NAME);
+        child.setProperty(propName, propValue, Type.STRING);
+        return child;
+    }
+
+    @Override
+    protected ContentRepository createRepository() {
+        return new Oak().with(new InitialContent())
+            .with(new OpenSecurityProvider())
+            .with(new LowCostOrderedPropertyIndexProvider())
+            .with(new OrderedPropertyIndexEditorProvider())
+            .createContentRepository();
+    }
+
+    /**
+     * convenience method that adds a bunch of nodes in random order and return the order in which
+     * they should be presented by the OrderedIndex
+     * 
+     * @param values the values of the property that will be indexed
+     * @param father the father under which add the nodes
+     * @param direction the direction of the items to be added.
+     * @return
+     */
+    protected List<ValuePathTuple> addChildNodes(final List<String> values, final Tree father,
+                                                 OrderDirection direction) {
+        List<ValuePathTuple> nodes = new ArrayList<ValuePathTuple>();
+        Random rnd = new Random();
+        int counter = 0;
+        while (!values.isEmpty()) {
+            String v = values.remove(rnd.nextInt(values.size()));
+            Tree t = child(father, String.format("n%s", counter++), ORDERED_PROPERTY, v);
+            nodes.add(new ValuePathTuple(v, t.getPath()));
+        }
+
+        if (OrderDirection.DESC.equals(direction)) {
+            Collections.sort(nodes, Collections.reverseOrder());
+        } else {
+            Collections.sort(nodes);
+        }
+        return nodes;
+    }
+
+    /**
+     * assert the right order of the returned resultset
+     * 
+     * @param orderedSequence the right order in which the resultset should be returned
+     * @param resultset the resultset
+     */
+    protected void assertRightOrder(@Nonnull final List<ValuePathTuple> orderedSequence,
+                                    @Nonnull final Iterator<? extends ResultRow> resultset) {
+        assertTrue("No results returned", resultset.hasNext());
+        int counter = 0;
+        while (resultset.hasNext() && counter < orderedSequence.size()) {
+            ResultRow row = resultset.next();
+            assertEquals(
+                String.format("Wrong path at the element '%d'", counter), 
+                orderedSequence.get(counter).getPath(),
+                row.getPath()
+            );
+            counter++;
+        }
+    }
+}

Added: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/LowCostOrderedPropertyIndexProvider.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/LowCostOrderedPropertyIndexProvider.java?rev=1575892&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/LowCostOrderedPropertyIndexProvider.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/LowCostOrderedPropertyIndexProvider.java Mon Mar 10 10:36:00 2014
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.oak.plugins.index.property;
+
+import java.util.List;
+
+import org.apache.jackrabbit.oak.spi.query.Filter;
+import org.apache.jackrabbit.oak.spi.query.QueryIndex;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+
+import com.google.common.collect.ImmutableList;
+
+/**
+ * convenience provider for going around the INFINITE cost of the actual index implementation
+ */
+public class LowCostOrderedPropertyIndexProvider extends OrderedPropertyIndexProvider {
+    @Override
+    public List<? extends QueryIndex> getQueryIndexes(NodeState nodeState) {
+        return ImmutableList.<QueryIndex> of(new LowCostOrderedPropertyIndex());
+    }
+
+    private static class LowCostOrderedPropertyIndex extends OrderedPropertyIndex {
+        @Override
+        public double getCost(Filter filter, NodeState root) {
+            return 1e-3;
+        }
+    }
+}

Added: 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=1575892&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexDescendingQueryTest.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexDescendingQueryTest.java Mon Mar 10 10:36:00 2014
@@ -0,0 +1,128 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.oak.plugins.index.property;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+import static org.apache.jackrabbit.JcrConstants.NT_UNSTRUCTURED;
+
+import java.text.ParseException;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import javax.jcr.RepositoryException;
+
+import org.apache.jackrabbit.oak.api.CommitFailedException;
+import org.apache.jackrabbit.oak.api.PropertyValue;
+import org.apache.jackrabbit.oak.api.ResultRow;
+import org.apache.jackrabbit.oak.api.Tree;
+import org.apache.jackrabbit.oak.plugins.index.IndexConstants;
+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.Test;
+
+import com.google.common.collect.ImmutableMap;
+
+
+public class OrderedPropertyIndexDescendingQueryTest extends BasicOrderedPropertyIndexQueryTest {
+    @Override
+    protected void createTestIndexNode() throws Exception {
+        Tree index = root.getTree("/");
+        IndexUtils.createIndexDefinition(
+            new NodeUtil(index.getChild(IndexConstants.INDEX_DEFINITIONS_NAME)),
+            TEST_INDEX_NAME, 
+            false, 
+            new String[] { ORDERED_PROPERTY }, 
+            null, 
+            OrderedIndex.TYPE,
+            ImmutableMap.of(
+                OrderedIndex.DIRECTION, OrderedIndex.OrderDirection.DESC.getDirection()
+            )
+        );
+        root.commit();
+    }
+    
+    /**
+     * Query the index for retrieving all the entries
+     * 
+     * @throws CommitFailedException
+     * @throws ParseException
+     * @throws RepositoryException
+     */
+    @Test
+    public void queryAllEntries() throws CommitFailedException, ParseException, RepositoryException {
+        setTravesalEnabled(false);
+
+        // index automatically created by the framework:
+        // {@code createTestIndexNode()}
+
+        Tree rTree = root.getTree("/");
+        Tree test = rTree.addChild("test");
+        List<ValuePathTuple> nodes = addChildNodes(
+            generateOrderedValues(NUMBER_OF_NODES, OrderDirection.DESC), test, OrderDirection.DESC);
+        root.commit();
+        
+        // querying
+        Iterator<? extends ResultRow> results;
+        results = executeQuery(
+            String.format("SELECT * from [%s] WHERE foo IS NOT NULL", NT_UNSTRUCTURED), SQL2, null)
+            .getRows().iterator();
+        assertRightOrder(nodes, results);
+
+        setTravesalEnabled(true);
+    }
+    
+    /**
+     * test the index for returning the items related to a single key
+     * 
+     * @throws CommitFailedException
+     * @throws ParseException
+     */
+    @Test
+    public void queryOneKey() throws CommitFailedException, ParseException {
+        setTravesalEnabled(false);
+
+        // index automatically created by the framework:
+        // {@code createTestIndexNode()}
+
+        Tree rTree = root.getTree("/");
+        Tree test = rTree.addChild("test");
+        List<ValuePathTuple> nodes = addChildNodes(generateOrderedValues(NUMBER_OF_NODES), test,
+            OrderDirection.DESC);
+        root.commit();
+
+        // getting the middle of the random list of nodes
+        ValuePathTuple searchfor = nodes.get(NUMBER_OF_NODES / 2);
+
+        Map<String, PropertyValue> filter = ImmutableMap.of(ORDERED_PROPERTY,
+            PropertyValues.newString(searchfor.getValue()));
+        String query = "SELECT * FROM [%s] WHERE %s=$%s";
+        Iterator<? extends ResultRow> results = executeQuery(
+            String.format(query, NT_UNSTRUCTURED, ORDERED_PROPERTY, ORDERED_PROPERTY), 
+            SQL2, 
+            filter).getRows().iterator();
+        assertTrue("one element is expected", results.hasNext());
+        assertEquals("wrong path returned", searchfor.getPath(), results.next().getPath());
+        assertFalse("there should be not any more items", results.hasNext());
+
+        setTravesalEnabled(true);
+    }
+}

Modified: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexEditorTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexEditorTest.java?rev=1575892&r1=1575891&r2=1575892&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexEditorTest.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexEditorTest.java Mon Mar 10 10:36:00 2014
@@ -14,7 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 package org.apache.jackrabbit.oak.plugins.index.property;
 
 import static org.easymock.EasyMock.createNiceMock;
@@ -23,6 +22,7 @@ import static org.easymock.EasyMock.repl
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
 import java.util.Arrays;
@@ -30,12 +30,17 @@ import java.util.Arrays;
 import org.apache.jackrabbit.oak.api.PropertyState;
 import org.apache.jackrabbit.oak.api.Type;
 import org.apache.jackrabbit.oak.plugins.index.IndexConstants;
+import org.apache.jackrabbit.oak.plugins.index.property.OrderedIndex.OrderDirection;
+import org.apache.jackrabbit.oak.plugins.memory.EmptyNodeState;
 import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
 import org.junit.Test;
 
+/**
+ * Tests the ordered index.
+ */
 public class OrderedPropertyIndexEditorTest {
    
-   @Test public void isProperlyConfiguredWithPropertyNames(){
+   @Test public void isProperlyConfiguredWithPropertyNames() {
       NodeBuilder definition = createNiceMock(NodeBuilder.class);
       PropertyState names = createNiceMock(PropertyState.class);
       expect(names.count()).andReturn(1);
@@ -44,41 +49,101 @@ public class OrderedPropertyIndexEditorT
       replay(definition);
       
       OrderedPropertyIndexEditor ie = new OrderedPropertyIndexEditor(definition, null, null);
-      assertFalse("With empty or missing property the index should not work.",ie.isProperlyConfigured());
+      assertFalse("With empty or missing property the index should not work.", ie.isProperlyConfigured());
    }
    
-   @Test public void isProperlyConfiguredSingleValuePropertyNames(){
+   @Test public void isProperlyConfiguredSingleValuePropertyNames() {
       NodeBuilder definition = createNiceMock(NodeBuilder.class);
       PropertyState names = createNiceMock(PropertyState.class);
       expect(names.count()).andReturn(1);
-      expect(names.getValue(Type.NAME,0)).andReturn("jcr:lastModified").anyTimes();
+      expect(names.getValue(Type.NAME, 0)).andReturn("jcr:lastModified").anyTimes();
       expect(definition.getProperty(IndexConstants.PROPERTY_NAMES)).andReturn(names).anyTimes();
       replay(names);
       replay(definition);
       
       OrderedPropertyIndexEditor ie = new OrderedPropertyIndexEditor(definition, null, null);
-      assertNotNull("With a correct property set 'propertyNames' can't be null",ie.getPropertyNames());
-      assertEquals(1,ie.getPropertyNames().size());
-      assertEquals("jcr:lastModified",ie.getPropertyNames().iterator().next());
-      assertTrue("Expecting a properly configured index",ie.isProperlyConfigured());
+      assertNotNull("With a correct property set 'propertyNames' can't be null", ie.getPropertyNames());
+      assertEquals(1, ie.getPropertyNames().size());
+      assertEquals("jcr:lastModified", ie.getPropertyNames().iterator().next());
+      assertTrue("Expecting a properly configured index", ie.isProperlyConfigured());
    }
    
-   @Test public void multiValueProperty(){
+   @Test public void multiValueProperty() {
       NodeBuilder definition = createNiceMock(NodeBuilder.class);
       PropertyState names = createNiceMock(PropertyState.class);
       expect(names.isArray()).andReturn(true).anyTimes();
       expect(names.count()).andReturn(2).anyTimes();
-      expect(names.getValue(Type.NAME,0)).andReturn("jcr:lastModified").anyTimes();
-      expect(names.getValue(Type.NAME,1)).andReturn("foo:bar").anyTimes();
-      expect(names.getValue(Type.NAMES)).andReturn(Arrays.asList("jcr:lastModified","foo:bar")).anyTimes();
+      expect(names.getValue(Type.NAME, 0)).andReturn("jcr:lastModified").anyTimes();
+      expect(names.getValue(Type.NAME, 1)).andReturn("foo:bar").anyTimes();
+      expect(names.getValue(Type.NAMES)).andReturn(Arrays.asList("jcr:lastModified", "foo:bar")).anyTimes();
       expect(definition.getProperty(IndexConstants.PROPERTY_NAMES)).andReturn(names).anyTimes();
       replay(names);
       replay(definition);
 
       OrderedPropertyIndexEditor ie = new OrderedPropertyIndexEditor(definition, null, null);
-      assertNotNull("With a correct property set 'propertyNames' can't be null",ie.getPropertyNames());
-      assertEquals("When multiple properties are a passed only the first one is taken", 1,ie.getPropertyNames().size());
-      assertEquals("jcr:lastModified",ie.getPropertyNames().iterator().next());
-      assertTrue("Expecting a properly configured index",ie.isProperlyConfigured());
-   }   
-}
+      assertNotNull("With a correct property set 'propertyNames' can't be null", ie.getPropertyNames());
+      assertEquals("When multiple properties are a passed only the first one is taken", 1, ie.getPropertyNames().size());
+      assertEquals("jcr:lastModified", ie.getPropertyNames().iterator().next());
+      assertTrue("Expecting a properly configured index", ie.isProperlyConfigured());
+   }
+   
+   @Test
+   public void orderedDirectionFromString() {
+       assertNull("A non-existing order direction should result in null", OrderDirection.fromString("foobar"));
+       assertEquals(OrderDirection.ASC, OrderDirection.fromString("ascending"));
+       assertFalse(OrderDirection.ASC.equals(OrderDirection.fromString("descending")));
+       assertEquals(OrderDirection.DESC, OrderDirection.fromString("descending"));
+       assertFalse(OrderDirection.DESC.equals(OrderDirection.fromString("ascending")));
+   }
+   
+   @Test
+   public void orderDirectionDefinitionNotSpecified() {
+       final String property = "foobar";
+       NodeBuilder definition = EmptyNodeState.EMPTY_NODE.builder();
+       definition.setProperty(IndexConstants.PROPERTY_NAMES, property);
+       OrderedPropertyIndexEditor editor = new OrderedPropertyIndexEditor(definition, null, null);
+       assertNotNull(editor.getPropertyNames());
+       assertEquals(property, editor.getPropertyNames().iterator().next());
+       assertEquals(OrderedIndex.OrderDirection.ASC, editor.getDirection());
+   }
+
+   @Test 
+   public void orderDirectionDefinitionDescending() {
+       final String property = "foobar";
+       NodeBuilder definition = EmptyNodeState.EMPTY_NODE.builder();
+       definition.setProperty(IndexConstants.PROPERTY_NAMES, property);
+       definition.setProperty(OrderedIndex.DIRECTION, "descending");
+       OrderedPropertyIndexEditor editor = new OrderedPropertyIndexEditor(definition, null, null);
+       assertNotNull(editor.getPropertyNames());
+       assertEquals(property, editor.getPropertyNames().iterator().next());
+       assertEquals(OrderedIndex.OrderDirection.DESC, editor.getDirection());
+   }
+   
+   @Test
+   public void orderDirectionUnknownDefinition() {
+       final String property = "foobar";
+       NodeBuilder definition = EmptyNodeState.EMPTY_NODE.builder();
+       definition.setProperty(IndexConstants.PROPERTY_NAMES, property);
+       definition.setProperty(OrderedIndex.DIRECTION, "bazbaz");
+       OrderedPropertyIndexEditor editor = new OrderedPropertyIndexEditor(definition, null, null);
+       assertNotNull(editor.getPropertyNames());
+       assertEquals(property, editor.getPropertyNames().iterator().next());
+       assertEquals("if we provide a non-valid definition for order the Ascending is expected", OrderedIndex.OrderDirection.ASC, editor.getDirection());
+   }
+   
+   @Test
+   public void strategies() {
+       final String property = "foobar";
+       NodeBuilder definition = EmptyNodeState.EMPTY_NODE.builder();
+       definition.setProperty(IndexConstants.PROPERTY_NAMES, property);
+       definition.setProperty(OrderedIndex.DIRECTION, OrderDirection.ASC.getDirection());
+       OrderedPropertyIndexEditor editor = new OrderedPropertyIndexEditor(definition, null, null);
+       assertEquals(OrderedPropertyIndexEditor.ORDERED_MIRROR, editor.getStrategy(false));
+       
+       definition = EmptyNodeState.EMPTY_NODE.builder();
+       definition.setProperty(IndexConstants.PROPERTY_NAMES, property);
+       definition.setProperty(OrderedIndex.DIRECTION, OrderDirection.DESC.getDirection());
+       editor = new OrderedPropertyIndexEditor(definition, null, null);
+       assertEquals(OrderedPropertyIndexEditor.ORDERED_MIRROR_DESCENDING, editor.getStrategy(false));
+   }
+}
\ No newline at end of file

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=1575892&r1=1575891&r2=1575892&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 Mar 10 10:36:00 2014
@@ -20,138 +20,29 @@ import static junit.framework.Assert.ass
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertTrue;
 import static junit.framework.Assert.fail;
-import static org.apache.jackrabbit.JcrConstants.JCR_PRIMARYTYPE;
 import static org.apache.jackrabbit.JcrConstants.NT_UNSTRUCTURED;
 
-import java.text.DecimalFormat;
-import java.text.NumberFormat;
 import java.text.ParseException;
-import java.util.ArrayList;
-import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
-import java.util.Random;
 
-import javax.annotation.Nonnull;
 import javax.jcr.RepositoryException;
 
-import org.apache.jackrabbit.oak.Oak;
 import org.apache.jackrabbit.oak.api.CommitFailedException;
-import org.apache.jackrabbit.oak.api.ContentRepository;
 import org.apache.jackrabbit.oak.api.PropertyValue;
 import org.apache.jackrabbit.oak.api.ResultRow;
 import org.apache.jackrabbit.oak.api.Tree;
-import org.apache.jackrabbit.oak.api.Type;
 import org.apache.jackrabbit.oak.plugins.index.IndexConstants;
 import org.apache.jackrabbit.oak.plugins.index.IndexUtils;
-import org.apache.jackrabbit.oak.plugins.nodetype.write.InitialContent;
-import org.apache.jackrabbit.oak.query.AbstractQueryTest;
+import org.apache.jackrabbit.oak.plugins.index.property.OrderedIndex.OrderDirection;
 import org.apache.jackrabbit.oak.spi.query.PropertyValues;
-import org.apache.jackrabbit.oak.spi.security.OpenSecurityProvider;
 import org.apache.jackrabbit.oak.util.NodeUtil;
 import org.junit.Test;
 
 import com.google.common.collect.ImmutableMap;
 
-public class OrderedPropertyIndexQueryTest extends AbstractQueryTest {
-    /**
-     * the property used by the index
-     */
-    public static final String ORDERED_PROPERTY = "foo";
-
-    /**
-     * number of nodes to create for testing.
-     * 
-     * It has been found during development that in some cases the order of the nodes creation within the persistence
-     * where the actual expected order.
-     * 
-     * The higher the value the lower the chance for this to happen.
-     */
-    private static final int NUMBER_OF_NODES = 50;
-
-    /**
-     * convenience orderable object that represents a tuple of values and paths
-     * 
-     * where the values are the indexed keys from the index and the paths are the path which hold the key
-     */
-    private class ValuePathTuple implements Comparable<ValuePathTuple> {
-        private final String value;
-        private final String path;
-
-        ValuePathTuple(String value, String path) {
-            this.value = value;
-            this.path = path;
-        }
-
-        @Override
-        public int hashCode() {
-            final int prime = 31;
-            int result = 1;
-            result = prime * result + getOuterType().hashCode();
-            result = prime * result + ((path == null) ? 0 : path.hashCode());
-            result = prime * result + ((value == null) ? 0 : value.hashCode());
-            return result;
-        }
-
-        @Override
-        public boolean equals(Object obj) {
-            if (this == obj){
-                return true;
-            }
-            if (obj == null){
-                return false;
-            }
-            if (getClass() != obj.getClass()){
-                return false;
-            }
-            ValuePathTuple other = (ValuePathTuple) obj;
-            if (!getOuterType().equals(other.getOuterType())){
-                return false;
-            }
-            if (path == null) {
-                if (other.path != null){
-                    return false;
-                }
-            } else if (!path.equals(other.path)){
-                return false;
-            }
-            if (value == null) {
-                if (other.value != null){
-                    return false;
-                }
-            } else if (!value.equals(other.value)){
-                return false;
-            }
-            return true;
-        }
-
-        @Override
-        public int compareTo(ValuePathTuple o) {
-            if (this.equals(o)){
-                return 0;
-            }
-            if (this.value.compareTo(o.value) < 0){
-                return -1;
-            }
-            if (this.value.compareTo(o.value) > 0){
-                return 1;
-            }
-            if (this.path.compareTo(o.path) < 0){
-                return -1;
-            }
-            if (this.path.compareTo(o.path) > 0){
-                return 1;
-            }
-            return 0;
-        }
-
-        private OrderedPropertyIndexQueryTest getOuterType() {
-            return OrderedPropertyIndexQueryTest.this;
-        }
-
-    }
-
+public class OrderedPropertyIndexQueryTest extends BasicOrderedPropertyIndexQueryTest {
     /**
      * testing for asserting the right comparison behaviour of the custom class
      */
@@ -176,79 +67,6 @@ public class OrderedPropertyIndexQueryTe
     }
 
     @Override
-    protected ContentRepository createRepository() {
-        return new Oak().with(new InitialContent())
-            .with(new OpenSecurityProvider())
-            // .with(new PropertyIndexProvider())
-            // .with(new PropertyIndexEditorProvider())
-            .with(new OrderedPropertyIndexProvider()).with(new OrderedPropertyIndexEditorProvider())
-            .createContentRepository();
-    }
-
-    /**
-     * create a child node for the provided father
-     * 
-     * @param father
-     * @param name
-     *            the name of the node to create
-     * @param propName
-     *            the name of the property to assign
-     * @param propValue
-     *            the value of the property to assign
-     * @return
-     */
-    private static Tree child(Tree father, String name, String propName, String propValue) {
-        Tree child = father.addChild(name);
-        child.setProperty(JCR_PRIMARYTYPE, NT_UNSTRUCTURED, Type.NAME);
-        child.setProperty(propName, propValue, Type.STRING);
-        return child;
-    }
-
-    /**
-     * generate a list of values to be used as ordered set. Will return something like
-     * {@code value000, value001, value002, ...}
-     * 
-     * 
-     * @param amount
-     * @return
-     */
-    private static List<String> generateOrderedValues(int amount) {
-        if (amount > 1000){
-            throw new RuntimeException("amount cannot be greater than 100");
-        }
-        List<String> values = new ArrayList<String>(amount);
-        NumberFormat nf = new DecimalFormat("000");
-        for (int i = 0; i < amount; i++){
-            values.add(String.format("value%s", String.valueOf(nf.format(i))));
-        }
-        return values;
-    }
-
-    /**
-     * convenience method that adds a bunch of nodes in random order and return the order in which they should be
-     * presented by the OrderedIndex
-     * 
-     * @param values
-     *            the values of the property that will be indexed
-     * @param father
-     *            the father under which add the nodes
-     * @return
-     */
-    private List<ValuePathTuple> addChildNodes(final List<String> values, final Tree father) {
-        List<ValuePathTuple> nodes = new ArrayList<ValuePathTuple>();
-        Random rnd = new Random();
-        int counter = 0;
-        while (!values.isEmpty()) {
-            String v = values.remove(rnd.nextInt(values.size()));
-            Tree t = child(father, String.format("n%s", counter++), ORDERED_PROPERTY, v);
-            nodes.add(new ValuePathTuple(v, t.getPath()));
-        }
-
-        Collections.sort(nodes);
-        return nodes;
-    }
-
-    @Override
     protected void createTestIndexNode() throws Exception {
         Tree index = root.getTree("/");
         IndexUtils.createIndexDefinition(new NodeUtil(index.getChild(IndexConstants.INDEX_DEFINITIONS_NAME)),
@@ -257,27 +75,6 @@ public class OrderedPropertyIndexQueryTe
     }
 
     /**
-     * assert the right order of the returned resultset
-     * 
-     * @param orderedSequence
-     *            the right order in which the resultset should be returned
-     * @param resultset
-     *            the resultset
-     */
-    private void assertRightOrder(@Nonnull
-    final List<ValuePathTuple> orderedSequence, @Nonnull
-    final Iterator<? extends ResultRow> resultset) {
-        assertTrue("No results returned", resultset.hasNext());
-        int counter = 0;
-        while (resultset.hasNext() && counter < orderedSequence.size()) {
-            ResultRow row = resultset.next();
-            assertEquals(String.format("Wrong path at the element '%d'", counter), orderedSequence.get(counter).path,
-                row.getPath());
-            counter++;
-        }
-    }
-
-    /**
      * Query the index for retrieving all the entries
      * 
      * @throws CommitFailedException
@@ -293,7 +90,7 @@ public class OrderedPropertyIndexQueryTe
 
         Tree rTree = root.getTree("/");
         Tree test = rTree.addChild("test");
-        List<ValuePathTuple> nodes = addChildNodes(generateOrderedValues(NUMBER_OF_NODES), test);
+        List<ValuePathTuple> nodes = addChildNodes(generateOrderedValues(NUMBER_OF_NODES), test, OrderDirection.ASC);
         root.commit();
 
         // querying
@@ -320,19 +117,19 @@ public class OrderedPropertyIndexQueryTe
 
         Tree rTree = root.getTree("/");
         Tree test = rTree.addChild("test");
-        List<ValuePathTuple> nodes = addChildNodes(generateOrderedValues(NUMBER_OF_NODES), test);
+        List<ValuePathTuple> nodes = addChildNodes(generateOrderedValues(NUMBER_OF_NODES), test, OrderDirection.ASC);
         root.commit();
 
-        ValuePathTuple searchfor = nodes.get(NUMBER_OF_NODES / 2); // getting the middle of the random list of
-                                                                         // nodes.
+        // getting the middle of the random list of nodes.
+        ValuePathTuple searchfor = nodes.get(NUMBER_OF_NODES / 2); 
         Map<String, PropertyValue> filter = ImmutableMap
-            .of(ORDERED_PROPERTY, PropertyValues.newString(searchfor.value));
+            .of(ORDERED_PROPERTY, PropertyValues.newString(searchfor.getValue()));
         String query = "SELECT * FROM [%s] WHERE %s=$%s";
         Iterator<? extends ResultRow> results = executeQuery(
             String.format(query, NT_UNSTRUCTURED, ORDERED_PROPERTY, ORDERED_PROPERTY), SQL2, filter).getRows()
             .iterator();
         assertTrue("one element is expected", results.hasNext());
-        assertEquals("wrong path returned", searchfor.path, results.next().getPath());
+        assertEquals("wrong path returned", searchfor.getPath(), results.next().getPath());
         assertFalse("there should be not any more items", results.hasNext());
 
         setTravesalEnabled(true);

Added: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/ValuePathTuple.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/ValuePathTuple.java?rev=1575892&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/ValuePathTuple.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/ValuePathTuple.java Mon Mar 10 10:36:00 2014
@@ -0,0 +1,101 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.oak.plugins.index.property;
+
+
+/**
+ * convenience orderable object that represents a tuple of values and paths
+ * 
+ * where the values are the indexed keys from the index and the paths are the path which hold the
+ * key
+ */
+public class ValuePathTuple implements Comparable<ValuePathTuple> {
+    private String value;
+    private String path;
+
+    ValuePathTuple(String value, String path) {
+        this.value = value;
+        this.path = path;
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + this.getClass().hashCode();
+        result = prime * result + ((path == null) ? 0 : path.hashCode());
+        result = prime * result + ((value == null) ? 0 : value.hashCode());
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        ValuePathTuple other = (ValuePathTuple) obj;
+        if (path == null) {
+            if (other.getPath() != null) {
+                return false;
+            }
+        } else if (!path.equals(other.getPath())) {
+            return false;
+        }
+        if (value == null) {
+            if (other.getValue() != null) {
+                return false;
+            }
+        } else if (!value.equals(other.getValue())) {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public int compareTo(ValuePathTuple o) {
+        if (this.equals(o)) {
+            return 0;
+        }
+        if (this.value.compareTo(o.getValue()) < 0) {
+            return -1;
+        }
+        if (this.value.compareTo(o.getValue()) > 0) {
+            return 1;
+        }
+        if (this.path.compareTo(o.getPath()) < 0) {
+            return -1;
+        }
+        if (this.path.compareTo(o.getPath()) > 0) {
+            return 1;
+        }
+        return 0;
+    }
+
+    public String getValue() {
+        return value;
+    }
+
+    public String getPath() {
+        return path;
+    }
+}
\ No newline at end of file



Mime
View raw message