Package org.apache.jackrabbit.oak.plugins.index.property.strategy

Source Code of org.apache.jackrabbit.oak.plugins.index.property.strategy.OrderedContentMirrorStorageStrategyTest

/*
* 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.strategy;

import static com.google.common.collect.Sets.newHashSet;
import static org.apache.jackrabbit.oak.plugins.index.property.strategy.OrderedContentMirrorStoreStrategy.NEXT;
import static org.apache.jackrabbit.oak.plugins.index.property.strategy.OrderedContentMirrorStoreStrategy.START;
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.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.Iterator;
import java.util.Set;

import javax.jcr.RepositoryException;

import org.apache.jackrabbit.oak.commons.PathUtils;
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;
import org.apache.jackrabbit.oak.plugins.index.property.OrderedIndex.OrderDirection;
import org.apache.jackrabbit.oak.plugins.memory.EmptyNodeState;
import org.apache.jackrabbit.oak.spi.query.Filter;
import org.apache.jackrabbit.oak.spi.query.PropertyValues;
import org.apache.jackrabbit.oak.spi.state.ChildNodeEntry;
import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
import org.apache.jackrabbit.oak.spi.state.NodeState;
import org.junit.Test;

import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
import com.google.common.collect.Iterators;

/**
*
*/
public class OrderedContentMirrorStorageStrategyTest {
    /**
     * ascending ordered set of keys. Useful for testing
     */
    private static final String[] KEYS = new String[] { "donald", "goofy", "mickey", "minnie" };
    private static final Set<String> EMPTY_KEY_SET = newHashSet();
    private static final NumberFormat NF = new DecimalFormat("00000");

    /**
     * checks that the fist item/key is inserted with an empty property 'next'
     *
     * expected structure:
     *
     * <code>
     * :index : {
     *    :start : { :next=n0 },
     *    n0 : {
     *       :next=,
     *       foo : {
     *          bar: { match=true}
     *       }
     *    }
     * }
     * </code>
     */
    @Test
    public void firstAndOnlyItem() {
        final String path = "/foo/bar";
        final String[] pathNodes = Iterables.toArray(PathUtils.elements(path), String.class);
        final String no = KEYS[0];

        IndexStoreStrategy store = new OrderedContentMirrorStoreStrategy();
        NodeBuilder index = EmptyNodeState.EMPTY_NODE.builder();
        NodeBuilder node = null;

        store.update(index, path, EMPTY_KEY_SET, newHashSet(no));

        assertFalse(":index should be left alone with not changes", index.hasProperty(NEXT));
        node = index.getChildNode(START);
        assertTrue(":index should have the :start node", node.exists());
        assertEquals(":start should point to n0", no, node.getString(NEXT));

        node = index.getChildNode(no);
        assertTrue("n0 should exists in the index", node.exists());
        assertEquals("n0 should point nowhere as it's the last (and only) element", "", node.getString(NEXT));

        // checking content structure below n0
        node = node.getChildNode(pathNodes[0]);
        assertTrue("n0 should contain 'foo'", node.exists());
        node = node.getChildNode(pathNodes[1]);
        assertTrue("'foo' should contain 'bar'", node.exists());
        assertTrue("the 'foo' node should have 'match=true'", node.getBoolean("match"));
    }

    /**
     * test the saving of 2 new keys that comes already ordered
     *
     * final state of the index will be
     *
     * <code>
     *    :index : {
     *       :start : { :next=n0 },
     *       n0 : { :next=n1 },
     *       n1 : { :next= }
     *    }
     * </code>
     */
    @Test
    public void first2newKeysAlreadyOrdered() {
        final String path = "/foo/bar";
        final String n0 = KEYS[0];
        final String n1 = KEYS[1];

        IndexStoreStrategy store = new OrderedContentMirrorStoreStrategy();
        NodeBuilder index = EmptyNodeState.EMPTY_NODE.builder();
        NodeBuilder node = null;

        // first node arrives
        store.update(index, path, EMPTY_KEY_SET, newHashSet(n0));

        node = index.getChildNode(START);
        assertTrue(":index should have :start", node.exists());
        assertEquals(":start should point to n0", n0, node.getString(NEXT));

        node = index.getChildNode(n0);
        assertTrue(":index should have n0", node.exists());
        assertEquals("n0 should point nowhere at this stage", "", node.getString(NEXT));

        // second node arrives
        store.update(index, path, EMPTY_KEY_SET, newHashSet(n1));

        node = index.getChildNode(START);
        assertTrue(":index should still have :start", node.exists());
        assertEquals(":start should still point to n0", n0, node.getString(NEXT));

        node = index.getChildNode(n0);
        assertTrue("n0 should still exists", node.exists());
        assertEquals("n0 should point to n1", n1, node.getString(NEXT));

        node = index.getChildNode(n1);
        assertTrue("n1 should exists", node.exists());
        assertEquals("n1 should point nowhere", "", node.getString(NEXT));
    }

    /**
     * Test the iteration of an empty index
     */
    @Test
    public void childNodeEntriesEmptyIndex() {
        OrderedContentMirrorStoreStrategy store = new OrderedContentMirrorStoreStrategy();
        NodeState index = EmptyNodeState.EMPTY_NODE;

        @SuppressWarnings("unchecked")
        Iterable<ChildNodeEntry> children = (Iterable<ChildNodeEntry>) store.getChildNodeEntries(index);

        assertNotNull("A returned Iterable cannot be null", children);
    }

    /**
     * test the iteration of the index with 2 shuffled items
     *
     * <code>
     *    :index : {
     *       :start : { :next=n1 },
     *       n0 : { :next= },
     *       n1 : { :next=n0 }
     *    }
     * </code>
     */
    @SuppressWarnings("unchecked")
    @Test
    public void childNodeEntriesACoupleOfMixedItems() {
        final String n0 = KEYS[1];
        final String n1 = KEYS[0];
        final NodeState node0 = EmptyNodeState.EMPTY_NODE.builder().setProperty(NEXT, "").getNodeState();
        final NodeState node1 = EmptyNodeState.EMPTY_NODE.builder().setProperty(NEXT, n0).getNodeState();
        OrderedContentMirrorStoreStrategy store = new OrderedContentMirrorStoreStrategy();
        NodeBuilder index = EmptyNodeState.EMPTY_NODE.builder();

        // setting-up the index structure
        index.child(START).setProperty(NEXT, n1);
        index.setChildNode(n0, node0);
        index.setChildNode(n1, node1);

        NodeState indexState = index.getNodeState();

        Iterable<ChildNodeEntry> children = (Iterable<ChildNodeEntry>) store.getChildNodeEntries(indexState);
        assertNotNull("The iterable cannot be null", children);
        assertEquals("Expecting 2 items in the index", 2, Iterators.size(children.iterator()));

        // ensuring the right sequence
        ChildNodeEntry entry = null;
        children = (Iterable<ChildNodeEntry>) store.getChildNodeEntries(indexState);
        Iterator<ChildNodeEntry> it = children.iterator();
        assertTrue("We should have 2 elements left to loop through", it.hasNext());
        entry = it.next();
        assertEquals("The first element should be n1", n1, entry.getName());
        assertEquals("Wrong entry returned", node1, entry.getNodeState());
        assertTrue("We should have 1 elements left to loop through", it.hasNext());
        entry = it.next();
        assertEquals("The second element should be n0", n0, entry.getName());
        assertEquals("Wrong entry returned", node0, entry.getNodeState());
        assertFalse("We should have be at the end of the list", it.hasNext());
    }

    /**
     * test the iteration of the index with 2 shuffled items without the :start
     * node
     *
     * <code>
     *    :index : {
     *       :start : { :next=n1 },
     *       n0 : { :next= },
     *       n1 : { :next=n0 }
     *    }
     * </code>
     */
    @SuppressWarnings("unchecked")
    @Test
    public void childNodeEntriesACoupleOfMixedItemsNoStart() {
        final String n0 = KEYS[1];
        final String n1 = KEYS[0];
        final NodeState node0 = EmptyNodeState.EMPTY_NODE.builder().setProperty(NEXT, "").getNodeState();
        final NodeState node1 = EmptyNodeState.EMPTY_NODE.builder().setProperty(NEXT, n0).getNodeState();
        OrderedContentMirrorStoreStrategy store = new OrderedContentMirrorStoreStrategy();
        NodeBuilder index = EmptyNodeState.EMPTY_NODE.builder();

        // setting-up the index structure
        index.child(START).setProperty(NEXT, n1);
        index.setChildNode(n0, node0);
        index.setChildNode(n1, node1);

        NodeState indexState = index.getNodeState();

        Iterable<ChildNodeEntry> children = (Iterable<ChildNodeEntry>) store.getChildNodeEntries(indexState, false);
        assertNotNull("The iterable cannot be null", children);
        assertEquals("Expecting 2 items in the index", 2, Iterators.size(children.iterator()));

        // ensuring the right sequence
        ChildNodeEntry entry = null;
        children = (Iterable<ChildNodeEntry>) store.getChildNodeEntries(indexState);
        Iterator<ChildNodeEntry> it = children.iterator();
        assertTrue("We should have 2 elements left to loop through", it.hasNext());
        entry = it.next();
        assertEquals("The first element should be n1", n1, entry.getName());
        assertEquals("Wrong entry returned", node1, entry.getNodeState());
        assertTrue("We should have 1 elements left to loop through", it.hasNext());
        entry = it.next();
        assertEquals("The second element should be n0", n0, entry.getName());
        assertEquals("Wrong entry returned", node0, entry.getNodeState());
        assertFalse("We should have be at the end of the list", it.hasNext());
    }

    /**
     * test the iteration of the index with 2 shuffled items including the
     * :start node as first
     *
     * <code>
     *    :index : {
     *       :start : { :next=n1 },
     *       n0 : { :next= },
     *       n1 : { :next=n0 }
     *    }
     * </code>
     */
    @SuppressWarnings("unchecked")
    @Test
    public void childNodeEntriesACoupleOfMixedItemsWithStart() {
        final String n0 = KEYS[1];
        final String n1 = KEYS[0];
        final NodeState nodeStart = EmptyNodeState.EMPTY_NODE.builder().setProperty(NEXT, n1).getNodeState();
        final NodeState node0 = EmptyNodeState.EMPTY_NODE.builder().setProperty(NEXT, "").getNodeState();
        final NodeState node1 = EmptyNodeState.EMPTY_NODE.builder().setProperty(NEXT, n0).getNodeState();
        OrderedContentMirrorStoreStrategy store = new OrderedContentMirrorStoreStrategy();
        NodeBuilder index = EmptyNodeState.EMPTY_NODE.builder();

        // setting-up the index structure
        index.setChildNode(START, nodeStart);
        index.setChildNode(n0, node0);
        index.setChildNode(n1, node1);

        NodeState indexState = index.getNodeState();

        Iterable<ChildNodeEntry> children = (Iterable<ChildNodeEntry>) store.getChildNodeEntries(indexState, true);
        assertNotNull("The iterable cannot be null", children);
        assertEquals("Expecting 3 items in the index", 3, Iterators.size(children.iterator()));

        // ensuring the right sequence
        ChildNodeEntry entry = null;
        children = (Iterable<ChildNodeEntry>) store.getChildNodeEntries(indexState, true);
        Iterator<ChildNodeEntry> it = children.iterator();
        assertTrue("We should still have elements left to loop through", it.hasNext());
        entry = it.next();
        assertEquals("The first element should be :start", START, entry.getName());
        assertEquals("Wrong entry returned", nodeStart, entry.getNodeState());
        assertTrue("We should still have elements left to loop through", it.hasNext());
        entry = it.next();
        assertEquals("The second element should be n1", n1, entry.getName());
        assertEquals("Wrong entry returned", node1, entry.getNodeState());
        assertTrue("We should still have elements left to loop through", it.hasNext());
        entry = it.next();
        assertEquals("The third element should be n0", n0, entry.getName());
        assertEquals("Wrong entry returned", node0, entry.getNodeState());
        assertFalse("We should be at the end of the list", it.hasNext());
    }

    /**
     * test the iteration over an empty list when the :start is required. In
     * this case :start should always be returned
     *
     * <code>
     *    :index : {
     *       :start : { :next= }
     *    }
     * </code>
     */
    @Test
    public void childNodeEntriesNoItemsWithStart() {
        NodeState nodeStart = EmptyNodeState.EMPTY_NODE.builder().setProperty(NEXT, "").getNodeState();
        NodeBuilder index = EmptyNodeState.EMPTY_NODE.builder();
        OrderedContentMirrorStoreStrategy store = new OrderedContentMirrorStoreStrategy();

        // setting-up the index
        index.setChildNode(START, nodeStart);

        Iterable<? extends ChildNodeEntry> children = store.getChildNodeEntries(index.getNodeState(), true);
        assertEquals("Wrong size of Iterable", 1, Iterators.size(children.iterator()));

        Iterator<? extends ChildNodeEntry> it = store.getChildNodeEntries(index.getNodeState(), true).iterator();
        assertTrue("We should have at least 1 element", it.hasNext());
        ChildNodeEntry entry = it.next();
        assertEquals(":start is expected", START, entry.getName());
        assertEquals("wrong node returned", nodeStart, entry.getNodeState());
        assertFalse("We should be at the end of the list", it.hasNext());
    }

    /**
     * test the case where we want an iterator for the children of a brand new
     * index. In this case :start doesn't exists but if we ask for it we should
     * return it.
     */
    @Test
    public void childNodeEntriesNewIndexWithStart() {
        NodeState nodeStart = EmptyNodeState.EMPTY_NODE.builder().setProperty(NEXT, "").getNodeState();
        NodeBuilder index = EmptyNodeState.EMPTY_NODE.builder();
        OrderedContentMirrorStoreStrategy store = new OrderedContentMirrorStoreStrategy();

        Iterator<? extends ChildNodeEntry> children = store.getChildNodeEntries(index.getNodeState(), true).iterator();
        assertEquals("Wrong number of children", 1, Iterators.size(children));

        children = store.getChildNodeEntries(index.getNodeState(), true).iterator();
        assertTrue("at least one item expected", children.hasNext());
        ChildNodeEntry child = children.next();
        assertEquals(START, child.getName());
        assertEquals(nodeStart, child.getNodeState());
        assertFalse(children.hasNext());
    }

    /**
     * test the insert of two shuffled items
     *
     * Building final a structure like
     *
     * <code>
     *    :index : {
     *       :start : { :next=n1 },
     *       n0 : { :next= },
     *       n1 : { :next=n0 }
     *    }
     * </code>
     *
     * where:
     *
     * <code>
     *    Stage 1
     *    =======
     *
     *    :index : {
     *       :start : { :next = n0 },
     *       n0 : {
     *          :next =
     *       }
     *    }
     *
     *    Stage 2
     *    =======
     *
     *    :index : {
     *       :start : { :next = n1 },
     *       n0 : {
     *          :next =
     *       },
     *       n1 : {
     *          :next = n0
     *       }
     *    }
     * </code>
     */
    @Test
    public void twoShuffledItems() {
        IndexStoreStrategy store = new OrderedContentMirrorStoreStrategy();
        NodeState root = EmptyNodeState.EMPTY_NODE;
        NodeBuilder index = root.builder();
        String key1st = KEYS[1];
        String key2nd = KEYS[0];
        NodeState ns = null;

        // Stage 1
        store.update(index, "/foo/bar", EMPTY_KEY_SET, newHashSet(key1st));
        ns = index.getChildNode(START).getNodeState();
        assertEquals(":start is expected to point to the 1st node", key1st, ns.getString(NEXT));
        ns = index.getChildNode(key1st).getNodeState();
        assertTrue("At Stage 1 the first node is expected to point nowhere as it's the last",
                        Strings.isNullOrEmpty(ns.getString(NEXT)));

        // Stage 2
        store.update(index, "/foo/bar", EMPTY_KEY_SET, newHashSet(key2nd));
        ns = index.getChildNode(START).getNodeState();
        assertEquals(":start is expected to point to the 2nd node", key2nd, ns.getString(NEXT));
        ns = index.getChildNode(key1st).getNodeState();
        assertTrue("At stage 2 the first element should point nowhere as it's the last",
                        Strings.isNullOrEmpty(ns.getString(NEXT)));
        ns = index.getChildNode(key2nd).getNodeState();
        assertEquals("At Stage 2 the second element should point to the first one", key1st, ns.getString(NEXT));
    }

    /**
     * test the insert of shuffled items
     *
     * Building a final structure like
     *
     * <code>
     *    {
     *       :start : { :next = n1 },
     *       n0 : {
     *          :next = ""
     *       },
     *       n1 : {
     *          :next = n2
     *       },
     *       n2 : {
     *          :next = n0
     *       }
     *    }
     * </code>
     *
     * where:
     *
     * <code>
     *    Stage 1
     *    =======
     *
     *    {
     *       :start : { :next = n0 },
     *       n0 : {
     *          :next =
     *       }
     *    }
     *
     *    Stage 2
     *    =======
     *
     *    {
     *       :start : { :next = n1 },
     *       n0 : { :next = },
     *       n1 : { :next = n0 }
     *    }
     *
     *    Stage 3
     *    =======
     *
     *    {
     *       :start : { :next = n1 },
     *       n0 : { :next = },
     *       n1 : { :next = n2 },
     *       n2 : { :next = n0 }
     *    }
     *
     *    Stage 4
     *    =======
     *
     *    {
     *       :start : { :next = n1 },
     *       n0 : { :next = n3 },
     *       n1 : { :next = n2 },
     *       n2 : { :next = n0 },
     *       n3 : { :next = }
     *    }
     * </code>
     */
    @Test
    public void fourShuffledElements() {
        IndexStoreStrategy store = new OrderedContentMirrorStoreStrategy();
        NodeBuilder index = EmptyNodeState.EMPTY_NODE.builder();
        String n0 = KEYS[2];
        String n1 = KEYS[0];
        String n2 = KEYS[1];
        String n3 = KEYS[3];

        // Stage 1
        store.update(index, "/a/b", EMPTY_KEY_SET, newHashSet(n0));
        assertEquals(":start should point to the first node", n0, index.getChildNode(START).getString(NEXT));
        assertTrue("the first node should point nowhere", Strings.isNullOrEmpty(index.getChildNode(n0).getString(NEXT)));

        // Stage 2
        store.update(index, "/a/b", EMPTY_KEY_SET, newHashSet(n1));
        assertEquals(":start should point to n1", n1, index.getChildNode(START).getString(NEXT));
        assertEquals("'n1' should point to 'n0'", n0, index.getChildNode(n1).getString(NEXT));
        assertTrue("n0 should still be point nowhere", Strings.isNullOrEmpty(index.getChildNode(n0).getString(NEXT)));

        // Stage 3
        store.update(index, "/a/b", EMPTY_KEY_SET, newHashSet(n2));
        assertEquals(":start should point to n1", n1, index.getChildNode(START).getString(NEXT));
        assertEquals("n1 should be pointing to n2", n2, index.getChildNode(n1).getString(NEXT));
        assertEquals("n2 should be pointing to n0", n0, index.getChildNode(n2).getString(NEXT));
        assertTrue("n0 should still be the last item of the list",
                        Strings.isNullOrEmpty(index.getChildNode(n0).getString(NEXT)));

        // Stage 4
        store.update(index, "/a/b", EMPTY_KEY_SET, newHashSet(n3));
        assertEquals(":start should point to n1", n1, index.getChildNode(START).getString(NEXT));
        assertEquals("n1 should be pointing to n2", n2, index.getChildNode(n1).getString(NEXT));
        assertEquals("n2 should be pointing to n0", n0, index.getChildNode(n2).getString(NEXT));
        assertEquals("n0 should be pointing to n3", n3, index.getChildNode(n0).getString(NEXT));
        assertTrue("n3 should be the last element", Strings.isNullOrEmpty(index.getChildNode(n3).getString(NEXT)));
    }

    /**
     * perform a test where the index gets updated if an already existent
     * node/key gets updated by changing the key and the key contains only 1
     * item.
     *
     * Where the second key is greater than the first.
     *
     * <code>
     *    Stage 1
     *    =======
     *
     *    :index : {
     *       :start { :next = n0 },
     *       n0 : {
     *          :next =,
     *          content : {
     *             foobar : {
     *                match = true
     *             }
     *          }
     *       }
     *    }
     *
     *    Stage 2
     *    =======
     *
     *    :index : {
     *       :start : { :next = n1 },
     *       n1 : {
     *          :next =,
     *          content : {
     *             foobar : {
     *                match = true
     *             }
     *          }
     *       }
     *    }
     * </code>
     */

    @Test
    public void singleKeyUpdate() {
        final String n0 = KEYS[0];
        final String n1 = KEYS[1];
        final String path = "/content/foobar";
        final String[] nodes = Iterables.toArray(PathUtils.elements(path), String.class);
        IndexStoreStrategy store = new OrderedContentMirrorStoreStrategy();
        NodeBuilder index = EmptyNodeState.EMPTY_NODE.builder();
        NodeBuilder node = null;

        // Stage 1
        store.update(index, path, EMPTY_KEY_SET, newHashSet(n0));
        node = index.getChildNode(START);
        assertTrue(":start should exists", node.exists());
        assertEquals(":start should point to n0", n0, node.getString(NEXT));

        node = index.getChildNode(n0);
        assertTrue(":index should have n0", node.exists());
        assertTrue("n0 should point nowhere", Strings.isNullOrEmpty(node.getString(NEXT)));

        node = node.getChildNode(nodes[0]);
        assertTrue("n0 should have /content", node.exists());

        node = node.getChildNode(nodes[1]);
        assertTrue("/content should contain /foobar", node.exists());
        assertTrue("/foobar should have match=true", node.getBoolean("match"));

        // Stage 2
        store.update(index, path, newHashSet(n0), newHashSet(n1));
        node = index.getChildNode(START);
        assertEquals(":start should now point to n1", n1, node.getString(NEXT));

        node = index.getChildNode(n1);
        assertTrue("n1 should exists", node.exists());
        assertTrue("n1 should point nowhere", Strings.isNullOrEmpty(node.getString(NEXT)));

        node = node.getChildNode(nodes[0]);
        assertTrue("n1 should have /content", node.exists());

        node = node.getChildNode(nodes[1]);
        assertTrue("/content should contain /foobar", node.exists());
        assertTrue("/foobar should have match=true", node.getBoolean("match"));
    }

    /**
     * <p>
     * find a previous item given a key in an index with 1 element only
     * </p>
     *
     * <p>
     * <i>it relies on the functionality of the store.update() for creating the
     * index</i>
     * </p>
     *
     * <code>
     *    :index {
     *       :start : { :next=n0 },
     *       n0 = { :next= }
     *    }
     * </code>
     */
    @Test
    public void findPrevious1ItemIndex() {
        final OrderedContentMirrorStoreStrategy store = new OrderedContentMirrorStoreStrategy();
        final String n0 = KEYS[0];
        final NodeState nodeStart = EmptyNodeState.EMPTY_NODE.builder().setProperty(NEXT, n0).getNodeState();
        final NodeState node0 = EmptyNodeState.EMPTY_NODE.builder().setProperty(NEXT, "").getNodeState();
        final NodeBuilder index = EmptyNodeState.EMPTY_NODE.builder();

        index.setChildNode(START, nodeStart);
        index.setChildNode(n0, node0);

        NodeState indexState = index.getNodeState();
        ChildNodeEntry previous = store.findPrevious(indexState, node0);
        assertNotNull(previous);
        assertEquals("the :start node is expected", nodeStart, previous.getNodeState());
    }

    /**
     * test the use case where a document change the indexed property. For
     * example document that change author.
     *
     * <p>
     * <i>it relies on the functionality of the store.update() for creating the
     * index</i>
     * </p>
     *
     * <code>
     *    Stage 1
     *    =======
     *
     *    :index : {
     *       :start : { :next = n0 },
     *       n0 : {
     *          :next = ,
     *          content : {
     *             one { match=true },
     *             two { match=true }
     *          }
     *       }
     *    }
     *
     *    Stage 2
     *    =======
     *
     *    :index : {
     *       :start : { :next = n0 },
     *       n0 : {
     *          :next = n1,
     *          content : {
     *             one : { match = true }
     *          }
     *       },
     *       n1 : {
     *          :next = ,
     *          content : {
     *             two : { match = true }
     *          }
     *       }
     *    }
     * </code>
     */
    @Test
    public void documentChangingKey() {
        final String path0 = "/content/one";
        final String path1 = "/content/two";
        final String n0 = KEYS[0];
        final String n1 = KEYS[1];
        NodeBuilder index = EmptyNodeState.EMPTY_NODE.builder();
        IndexStoreStrategy store = new OrderedContentMirrorStoreStrategy();

        // Stage 1 - initialising the index
        store.update(index, path0, EMPTY_KEY_SET, newHashSet(n0));
        store.update(index, path1, EMPTY_KEY_SET, newHashSet(n0));

        // ensuring the right structure
        assertTrue(index.hasChildNode(START));
        assertTrue(index.hasChildNode(n0));
        assertFalse(index.hasChildNode(n1));

        NodeBuilder node = index.getChildNode(START);
        assertEquals(":start pointing to wrong node", n0, node.getString(NEXT));

        node = index.getChildNode(n0);
        assertTrue("n0 should go nowhere", Strings.isNullOrEmpty(node.getString(NEXT)));

        // checking the first document
        String[] path = Iterables.toArray(PathUtils.elements(path0), String.class);
        node = node.getChildNode(path[0]);
        assertTrue(node.exists());
        node = node.getChildNode(path[1]);
        assertTrue(node.exists());
        assertTrue(node.getBoolean("match"));

        path = Iterables.toArray(PathUtils.elements(path0), String.class);
        node = index.getChildNode(n0).getChildNode(path[0]);
        assertTrue(node.exists());
        node = node.getChildNode(path[1]);
        assertTrue(node.exists());
        assertTrue(node.getBoolean("match"));

        // Stage 2
        store.update(index, path1, newHashSet(n0), newHashSet(n1));
        assertTrue(index.hasChildNode(START));
        assertTrue(index.hasChildNode(n0));
        assertTrue(index.hasChildNode(n1));

        node = index.getChildNode(START);
        assertEquals(":start pointing to wrong node", n0, node.getString(NEXT));

        node = index.getChildNode(n0);
        assertEquals(n1, node.getString(NEXT));
        path = Iterables.toArray(PathUtils.elements(path0), String.class);
        node = node.getChildNode(path[0]);
        assertTrue(node.exists());
        node = node.getChildNode(path[1]);
        assertTrue(node.exists());
        assertTrue(node.getBoolean("match"));
        path = Iterables.toArray(PathUtils.elements(path1), String.class);
        // we know both the documents share the same /content
        node = index.getChildNode(n0).getChildNode(path[0]);
        assertFalse("/content/two should no longer be under n0", node.hasChildNode(path[1]));

        node = index.getChildNode(n1);
        assertTrue("n1 should point nowhere", Strings.isNullOrEmpty(node.getString(NEXT)));
        path = Iterables.toArray(PathUtils.elements(path1), String.class);
        node = node.getChildNode(path[0]);
        assertTrue(node.exists());
        node = node.getChildNode(path[1]);
        assertTrue(node.exists());
        assertTrue(node.getBoolean("match"));
    }

    /**
     * test when a document is deleted and is the only one under the indexed key
     *
     * <p>
     * <i>it relies on the functionality of the store.update() for creating the
     * index</i>
     * </p>
     *
     * <code>
     *    Stage 1
     *    =======
     *
     *    :index : {
     *       :start : { :next = n0 },
     *       n0 : {
     *          :next = ,
     *          sampledoc : { match = true }
     *       }
     *    }
     *
     *    Stage 2
     *    =======
     *
     *    :index : {
     *       :start : { :next = }
     *    }
     * </code>
     */
    @Test
    public void deleteTheOnlyDocument() {
        final String n0 = KEYS[0];
        final String path = "/sampledoc";
        NodeBuilder index = EmptyNodeState.EMPTY_NODE.builder();
        IndexStoreStrategy store = new OrderedContentMirrorStoreStrategy();

        // Stage 1 - initialising the index
        store.update(index, path, EMPTY_KEY_SET, newHashSet(n0));

        // we assume it works and therefore not checking the status of the index
        // let's go straight to Stage 2

        // Stage 2
        store.update(index, path, newHashSet(n0), EMPTY_KEY_SET);
        assertFalse("The node should have been removed", index.hasChildNode(n0));
        assertTrue("as the index should be empty, :start should point nowhere",
                        Strings.isNullOrEmpty(index.getChildNode(START).getString(NEXT)));
    }

    /**
     * test when the document is deleted but there're still some documents left
     * under the indexed key
     *
     * <p>
     * <i>it relies on the functionality of the store.update() for creating the
     * index</i>
     * </p>
     *
     * <code>
     *    Stage 1
     *    =======
     *
     *    :index : {
     *       :start : { :next = n0 },
     *       n0 : {
     *          :next = ,
     *          doc1 : { match=true },
     *          doc2 : { match=true }
     *       }
     *    }
     *
     *    Stage 2
     *    =======
     *
     *    :index : {
     *       :start : { :next = n0 },
     *       n0 : {
     *          :next  =,
     *          doc2 : { match = true }
     *       }
     *    }
     * </code>
     */
    @Test
    public void deleteOneOfTheDocuments() {
        final String n0 = KEYS[0];
        final String doc1 = "doc1";
        final String doc2 = "doc2";
        final String path1 = "/" + doc1;
        final String path2 = "/" + doc2;
        NodeBuilder index = EmptyNodeState.EMPTY_NODE.builder();
        IndexStoreStrategy store = new OrderedContentMirrorStoreStrategy();

        store.update(index, path1, EMPTY_KEY_SET, newHashSet(n0));
        store.update(index, path2, EMPTY_KEY_SET, newHashSet(n0));

        // we trust the store at this point and skip a double-check. Let's move
        // to Stage 2!

        store.update(index, path1, newHashSet(n0), EMPTY_KEY_SET);

        assertTrue(index.hasChildNode(START));
        assertTrue(index.hasChildNode(n0));
        assertEquals(":start should still point to n0", n0, index.getChildNode(START).getString(NEXT));
        assertTrue("n0 should point nowhere", Strings.isNullOrEmpty(index.getChildNode(n0).getString(NEXT)));

        assertFalse(index.getChildNode(n0).hasChildNode(doc1));
        assertTrue(index.getChildNode(n0).hasChildNode(doc2));
        assertTrue(index.getChildNode(n0).getChildNode(doc2).getBoolean("match"));
    }

    /**
     * test when the only document is deleted from an indexed key but there're
     * still some keys left in the index
     *
     * <p>
     * <i>it relies on the functionality of the store.update() for creating the
     * index</i>
     * </p>
     *
     * <code>
     *    Stage 1
     *    =======
     *
     *    :index : {
     *       :start : { :next = n1 },
     *       n0 : {
     *          :next = ,
     *          content : {
     *             doc0 : { match = true }
     *          }
     *       },
     *       n1 : {
     *          :next = n2,
     *          content : {
     *             doc1 : { match = true }
     *          }
     *       }
     *       n2 : {
     *          :next = n0,
     *          content : {
     *             doc2 : { match = true }
     *          }
     *       }
     *    }
     *
     *    Stage 2
     *    =======
     *
     *    :index : {
     *       :start : { :next = n1 },
     *       n0 : {
     *          :next = ,
     *          content : {
     *             doc0 : { match = true }
     *          }
     *       },
     *       n1 : {
     *          :next = n0,
     *          content : {
     *             doc1 : { match = true }
     *          }
     *       }
     *    }
     *
     * </code>
     */
    @Test
    public void deleteTheOnlyDocumentInMultiKeysIndex() {
        final String path0 = "/content/doc0";
        final String path1 = "/content/doc1";
        final String path2 = "/content/doc2";
        final String n0 = KEYS[2];
        final String n1 = KEYS[0];
        final String n2 = KEYS[1];

        NodeBuilder index = EmptyNodeState.EMPTY_NODE.builder();
        IndexStoreStrategy store = new OrderedContentMirrorStoreStrategy();

        // Stage 1
        store.update(index, path0, EMPTY_KEY_SET, newHashSet(n0));
        store.update(index, path1, EMPTY_KEY_SET, newHashSet(n1));
        store.update(index, path2, EMPTY_KEY_SET, newHashSet(n2));

        // as we trust the store we skip the check and goes straight to Stage 2.

        // Stage 2
        store.update(index, path2, newHashSet(n2), EMPTY_KEY_SET);

        // checking key nodes
        assertTrue(index.hasChildNode(START));
        assertTrue(index.hasChildNode(n0));
        assertTrue(index.hasChildNode(n1));
        assertFalse(index.hasChildNode(n2));

        // checking pointers
        assertEquals(n1, index.getChildNode(START).getString(NEXT));
        assertEquals(n0, index.getChildNode(n1).getString(NEXT));
        assertTrue(Strings.isNullOrEmpty(index.getChildNode(n0).getString(NEXT)));

        // checking sub-nodes
        String[] subNodes = Iterables.toArray(PathUtils.elements(path0), String.class);
        assertTrue(index.getChildNode(n0).hasChildNode(subNodes[0]));
        assertTrue(index.getChildNode(n0).getChildNode(subNodes[0]).hasChildNode(subNodes[1]));
        assertTrue(index.getChildNode(n0).getChildNode(subNodes[0]).getChildNode(subNodes[1]).getBoolean("match"));

        subNodes = Iterables.toArray(PathUtils.elements(path1), String.class);
        assertTrue(index.getChildNode(n1).hasChildNode(subNodes[0]));
        assertTrue(index.getChildNode(n1).getChildNode(subNodes[0]).hasChildNode(subNodes[1]));
        assertTrue(index.getChildNode(n1).getChildNode(subNodes[0]).getChildNode(subNodes[1]).getBoolean("match"));
    }

    /**
     * <p>test the insertion of 2 already ordered items</p>
     *
     * <p>expected</p>
     *
     *  <code>
     *      :index : {
     *          :start : { :next=n0 },
     *          n0 : { :next=n1 },
     *          n1 : { :next=}
     *      }
     *  </code>
     */
    @Test
    public void descendingOrderInsert2itemsAlreadyOrdered() {
        IndexStoreStrategy store = new OrderedContentMirrorStoreStrategy(OrderDirection.DESC);
        NodeBuilder index = EmptyNodeState.EMPTY_NODE.builder();
        String n0 = KEYS[1];
        String n1 = KEYS[0];

        store.update(index, "/a/b", EMPTY_KEY_SET, newHashSet(n0));
        store.update(index, "/a/b", EMPTY_KEY_SET, newHashSet(n1));
        assertEquals(":start should point to the first node", n0, index.getChildNode(START)
                                                                       .getString(NEXT));
        assertEquals("n0 should point to n1", n1, index.getChildNode(n0).getString(NEXT));
        assertTrue("n1 should point nowhere",
                   Strings.isNullOrEmpty(index.getChildNode(n1).getString(NEXT)));
    }

    /**
     * Tests the insert of 4 items that will always have to be added at the beginning of the list.
     * Just to simulate the use-case of lastModified DESC.
     *
     * <code>
     *      Stage 1
     *      =======
     *
     *      :index : {
     *          :start : { :next=n0 },
     *          n0 : { :next= }
     *      }
     *
     *      Stage 2
     *      =======
     *
     *      :index : {
     *          :start : { :next=n1 },
     *          n0 : { :next= },
     *          n1 : { :next=n0 }
     *      }
     *
     *      Stage 3
     *      =======
     *
     *      :index : {
     *          :start : { :next=n2 },
     *          n0 : { :next= },
     *          n1 : { :next=n0 },
     *          n2 : { :next=n1 }
     *      }
     *
     *      Stage 4
     *      =======
     *
     *      :index : {
     *          :start : { :next=n3 },
     *          n0 : { :next= },
     *          n1 : { :next=n0 },
     *          n2 : { :next=n1 },
     *          n3 : { :next=n2 }
     *      }
     * </code>
     */
    @Test
    public void descendingOrder4StagedInsertsAlwaysGreater() {
        IndexStoreStrategy store = new OrderedContentMirrorStoreStrategy(OrderDirection.DESC);
        NodeBuilder index = EmptyNodeState.EMPTY_NODE.builder();
        String n0 = KEYS[0];
        String n1 = KEYS[1];
        String n2 = KEYS[2];
        String n3 = KEYS[3];

        // Stage 1
        store.update(index, "/a/b", EMPTY_KEY_SET, newHashSet(n0));
        assertEquals(":start should point to n0", n0, index.getChildNode(START).getString(NEXT));
        assertTrue("n0 should point nowhere",
                   Strings.isNullOrEmpty(index.getChildNode(n0).getString(NEXT)));

        // Stage 2
        store.update(index, "/a/b", EMPTY_KEY_SET, newHashSet(n1));
        assertEquals(":start should point to n1", n1, index.getChildNode(START).getString(NEXT));
        assertEquals("n1 should point to n0", n0, index.getChildNode(n1).getString(NEXT));
        assertTrue("n0 should point nowhere",
                   Strings.isNullOrEmpty(index.getChildNode(n0).getString(NEXT)));

        // Stage 3
        store.update(index, "/a/b", EMPTY_KEY_SET, newHashSet(n2));
        assertEquals(":start should point to n2", n2, index.getChildNode(START).getString(NEXT));
        assertEquals("n2 should point to n1", n1, index.getChildNode(n2).getString(NEXT));
        assertEquals("n1 should point to n0", n0, index.getChildNode(n1).getString(NEXT));
        assertTrue("n0 should point nowhere",
                   Strings.isNullOrEmpty(index.getChildNode(n0).getString(NEXT)));

        // Stage 4
        store.update(index, "/a/b", EMPTY_KEY_SET, newHashSet(n3));
        assertEquals(":start should point to n3", n3, index.getChildNode(START).getString(NEXT));
        assertEquals("n3 should point to n2", n2, index.getChildNode(n3).getString(NEXT));
        assertEquals("n2 should point to n1", n1, index.getChildNode(n2).getString(NEXT));
        assertEquals("n1 should point to n0", n0, index.getChildNode(n1).getString(NEXT));
        assertTrue("n0 should point nowhere",
                   Strings.isNullOrEmpty(index.getChildNode(n0).getString(NEXT)));
    }

    /**
     * test finding a previous item in a descending ordered index.
     *
     * <code>
     *      Stage 1
     *      =======
     *
     *      :index {
     *          :start : { :next=n0 },
     *          n0 : { :next= }
     *      }
     *
     *      findPrevious(n0)=:start
     *
     *      Stage 2
     *      =======
     *
     *      :index {
     *          :start : { :next=n1 },
     *          n0 : { :next= }
     *          n1 : { :next=n0 }
     *      }
     *
     *      findPrevious(n0)=n1;
     * </code>
     */
    @Test
    public void descendingOrderFindPrevious() {
        OrderedContentMirrorStoreStrategy store = new OrderedContentMirrorStoreStrategy(OrderDirection.DESC);
        NodeBuilder index = EmptyNodeState.EMPTY_NODE.builder();
        String n0 = KEYS[0];
        String n1 = KEYS[1];
        NodeState indexState;
        NodeState previous;
        NodeState node;

        //Stage 1
        store.update(index, "/a/b", EMPTY_KEY_SET, newHashSet(n0));
        indexState = index.getNodeState();
        node = indexState.getChildNode(n0);
        previous = indexState.getChildNode(START);
        assertEquals(previous, store.findPrevious(indexState, node).getNodeState());

        //Stage 2
        store.update(index, "/a/b", EMPTY_KEY_SET, newHashSet(n1));
        indexState = index.getNodeState();
        node = indexState.getChildNode(n0);
        previous = indexState.getChildNode(n1);
        assertEquals(previous, store.findPrevious(indexState, node).getNodeState());
    }

    /**
     * test the insert of 1 item in a descending order index. it should not really matter but just
     * checking we don't break anything
     *
     * expecting
     *
     * <code>
     *  :index : {
     *      :start : { :next = n0 },
     *      n0 : { :next = }
     *  }
     * </code>
     */
    @Test
    public void descendingOrderInsert1item() {
        IndexStoreStrategy store = new OrderedContentMirrorStoreStrategy(OrderDirection.DESC);
        NodeBuilder index = EmptyNodeState.EMPTY_NODE.builder();
        String n0 = KEYS[1];

        store.update(index, "/a/b", EMPTY_KEY_SET, newHashSet(n0));
        assertEquals(":start should point to the first node", n0, index.getChildNode(START)
                                                                       .getString(NEXT));
        assertTrue("the first node should point nowhere",
                   Strings.isNullOrEmpty(index.getChildNode(n0).getString(NEXT)));
    }

    /**
     * test the insert of 4 shuffled items in a descending ordered index
     *
     * expected:
     *
     * <code>
     *      :index : {
     *          :start : { :next= n1},
     *          n0 : { :next= n3},
     *          n1 : { :next= n2},
     *          n2: { :next= n0},
     *          n3 : { :next= },
     *      }
     * </code>
     */
    @Test
    public void descendingOrderInsert4ShuffledItems() {
        IndexStoreStrategy store = new OrderedContentMirrorStoreStrategy(OrderDirection.DESC);
        NodeBuilder index = EmptyNodeState.EMPTY_NODE.builder();
        String n0 = KEYS[1];
        String n1 = KEYS[3];
        String n2 = KEYS[2];
        String n3 = KEYS[0];

        store.update(index, "/a/b", EMPTY_KEY_SET, newHashSet(n0));
        store.update(index, "/a/b", EMPTY_KEY_SET, newHashSet(n1));
        store.update(index, "/a/b", EMPTY_KEY_SET, newHashSet(n2));
        store.update(index, "/a/b", EMPTY_KEY_SET, newHashSet(n3));
        assertEquals(":start should point to n1", n1, index.getChildNode(START).getString(NEXT));
        assertEquals("n0 should point to n3", n3, index.getChildNode(n0).getString(NEXT));
        assertEquals("n1 should point to n2", n2, index.getChildNode(n1).getString(NEXT));
        assertEquals("n2 should point to n1", n0, index.getChildNode(n2).getString(NEXT));
        assertTrue("n3 should point nowhere",
                   Strings.isNullOrEmpty(index.getChildNode(n3).getString(NEXT)));
    }

    /**
     * test the iteration of the descending index with 2 shuffled items
     *
     * <code>
     *    :index : {
     *       :start : { :next=n1 },
     *       n0 : { :next= },
     *       n1 : { :next=n0 }
     *    }
     * </code>
     */
    @Test
    public void descendingOrderChildNodeEntriesACoupleOfMixedItems() {
        OrderedContentMirrorStoreStrategy store = new OrderedContentMirrorStoreStrategy(
                                                                                        OrderDirection.DESC);
        NodeBuilder index = EmptyNodeState.EMPTY_NODE.builder();
        final String n0 = KEYS[0];
        final String n1 = KEYS[1];

        // setting-up the index structure
        store.update(index, "/a/b", EMPTY_KEY_SET, newHashSet(n0));
        store.update(index, "/a/b", EMPTY_KEY_SET, newHashSet(n1));

        NodeState indexState = index.getNodeState();
        NodeState node0 = indexState.getChildNode(n0);
        NodeState node1 = indexState.getChildNode(n1);

        Iterable<? extends ChildNodeEntry> children = store.getChildNodeEntries(indexState);
        assertNotNull("The iterable cannot be null", children);
        assertEquals("Expecting 2 items in the index", 2, Iterators.size(children.iterator()));

        // ensuring the right sequence
        ChildNodeEntry entry = null;
        children = store.getChildNodeEntries(indexState);
        Iterator<? extends ChildNodeEntry> it = children.iterator();
        assertTrue("We should have 2 elements left to loop through", it.hasNext());
        entry = it.next();
        assertEquals("The first element should be n1", n1, entry.getName());
        assertEquals("Wrong entry returned", node1, entry.getNodeState());
        assertTrue("We should have 1 elements left to loop through", it.hasNext());
        entry = it.next();
        assertEquals("The second element should be n0", n0, entry.getName());
        assertEquals("Wrong entry returned", node0, entry.getNodeState());
        assertFalse("We should have be at the end of the list", it.hasNext());
    }

    @Test
    public void count() throws IllegalArgumentException, RepositoryException {
        OrderedContentMirrorStoreStrategy store = new OrderedContentMirrorStoreStrategy();
        OrderedContentMirrorStoreStrategy descendingStore = new OrderedContentMirrorStoreStrategy(OrderDirection.DESC);
        final String orderedProperty = "fooprop";
        final String testAscendingName = "testascending";
        final String testDescendingName = "testdescending";
        final int numberOfNodes = 1000;
        final int maxNodeCount = 100;

        NodeBuilder builder = EmptyNodeState.EMPTY_NODE.builder();

        IndexUtils.createIndexDefinition(builder.child(IndexConstants.INDEX_DEFINITIONS_NAME),
            testAscendingName, false, ImmutableList.of(orderedProperty), null, OrderedIndex.TYPE,
            ImmutableMap.<String, String> of());
        IndexUtils.createIndexDefinition(builder.child(IndexConstants.INDEX_DEFINITIONS_NAME),
            testDescendingName, false, ImmutableList.of(orderedProperty), null, OrderedIndex.TYPE,
            ImmutableMap.<String, String> of(OrderedIndex.DIRECTION, OrderDirection.DESC.getDirection()));

        NodeBuilder ascendingContent = builder.getChildNode(IndexConstants.INDEX_DEFINITIONS_NAME)
            .getChildNode(testAscendingName).child(IndexConstants.INDEX_CONTENT_NODE_NAME);
        NodeBuilder descendingContent = builder.getChildNode(IndexConstants.INDEX_DEFINITIONS_NAME)
            .getChildNode(testDescendingName).child(IndexConstants.INDEX_CONTENT_NODE_NAME);

        // adding some content under the index
        for (int i = 0; i < numberOfNodes; i++) {
            store.update(ascendingContent, "/foo/bar", EMPTY_KEY_SET, newHashSet("x" + NF.format(i)));
            descendingStore.update(descendingContent, "/foo/bar", EMPTY_KEY_SET, newHashSet("x" + NF.format(i)));
        }

        assertEquals("wrong number of nodes encountered", numberOfNodes,
            Iterables.size(store.getChildNodeEntries(ascendingContent.getNodeState())));
        assertEquals("wrong number of nodes encountered", numberOfNodes,
            Iterables.size(descendingStore.getChildNodeEntries(descendingContent.getNodeState())));

        NodeState ascendingMeta = builder.getChildNode(IndexConstants.INDEX_DEFINITIONS_NAME)
            .getChildNode(testAscendingName).getNodeState();
        NodeState descendingMeta = builder.getChildNode(IndexConstants.INDEX_DEFINITIONS_NAME)
            .getChildNode(testDescendingName).getNodeState();

        Filter.PropertyRestriction pr = null;

        // equality
        String value = "x" + NF.format(11);
        pr = new Filter.PropertyRestriction();
        pr.first = PropertyValues.newString(value);
        pr.last = PropertyValues.newString(value);
        pr.firstIncluding = true;
        pr.lastIncluding = true;
        assertEquals(1, store.count(ascendingMeta, pr, maxNodeCount));
        assertEquals(1, descendingStore.count(descendingMeta, pr, maxNodeCount));

        // property not null
        pr = new Filter.PropertyRestriction();
        pr.first = null;
        pr.last = null;
        pr.firstIncluding = false;
        pr.lastIncluding = false;
        // don't care about the actual results as long as we have something. We're reusing existing
        // code
        assertTrue(store.count(ascendingMeta, pr, maxNodeCount) > 0);
        assertEquals(store.count(ascendingMeta, pr, maxNodeCount),
            descendingStore.count(descendingMeta, pr, maxNodeCount));

        // '>'
        pr = new Filter.PropertyRestriction();
        pr.first = PropertyValues.newString(value);
        pr.last = null;
        pr.firstIncluding = false;
        pr.lastIncluding = false;
        // don't care about the actual results as long as we have something. We're reusing existing
        // code
        assertTrue(store.count(ascendingMeta, pr, maxNodeCount) > 0);
        assertEquals(0, descendingStore.count(descendingMeta, pr, maxNodeCount));

        // '>='
        pr = new Filter.PropertyRestriction();
        pr.first = PropertyValues.newString(value);
        pr.last = null;
        pr.firstIncluding = true;
        pr.lastIncluding = false;
        // don't care about the actual results as long as we have something. We're reusing existing
        // code
        assertTrue(store.count(ascendingMeta, pr, maxNodeCount) > 0);
        assertEquals(0, descendingStore.count(descendingMeta, pr, maxNodeCount));

        // '<'
        pr = new Filter.PropertyRestriction();
        pr.first = null;
        pr.last = PropertyValues.newString(value);
        pr.firstIncluding = false;
        pr.lastIncluding = false;
        // don't care about the actual results as long as we have something. We're reusing existing
        // code
        assertTrue(descendingStore.count(descendingMeta, pr, maxNodeCount) > 0);
        assertEquals(0, store.count(ascendingMeta, pr, maxNodeCount));

        // when no conditions has been asked but just an ORDER BY
        pr = null;
        assertTrue(store.count(ascendingMeta, pr, maxNodeCount) > 0);
        assertEquals(store.count(ascendingMeta, pr, maxNodeCount),
            descendingStore.count(descendingMeta, pr, maxNodeCount));
    }
   
    @Test
    public void seekEqualsNotFound() {
        OrderedContentMirrorStoreStrategy store = new OrderedContentMirrorStoreStrategy();
        NodeBuilder index = EmptyNodeState.EMPTY_NODE.builder();
        String n0 = KEYS[1];
        String n1 = KEYS[3];
        String n2 = KEYS[2];
        String n3 = KEYS[0];
        String nonExisting = "dsrfgdrtfhg";

        // initialising the store
        store.update(index, "/a/b", EMPTY_KEY_SET, newHashSet(n0));
        store.update(index, "/a/b", EMPTY_KEY_SET, newHashSet(n1));
        store.update(index, "/a/b", EMPTY_KEY_SET, newHashSet(n2));
        store.update(index, "/a/b", EMPTY_KEY_SET, newHashSet(n3));

        assertNull("The item should have not been found", OrderedContentMirrorStoreStrategy.seek(
            index.getNodeState(),
            new OrderedContentMirrorStoreStrategy.PredicateEquals(nonExisting)));
    }

    @Test
    public void seekEquals() {
        OrderedContentMirrorStoreStrategy store = new OrderedContentMirrorStoreStrategy();
        NodeBuilder index = EmptyNodeState.EMPTY_NODE.builder();
        String n0 = KEYS[1];
        String n1 = KEYS[3];
        String n2 = KEYS[2];
        String n3 = KEYS[0];

        // initialising the store
        store.update(index, "/a/b", EMPTY_KEY_SET, newHashSet(n0));
        store.update(index, "/a/b", EMPTY_KEY_SET, newHashSet(n1));
        store.update(index, "/a/b", EMPTY_KEY_SET, newHashSet(n2));
        store.update(index, "/a/b", EMPTY_KEY_SET, newHashSet(n3));

        String searchFor = n1;

        ChildNodeEntry item = OrderedContentMirrorStoreStrategy.seek(index.getNodeState(),
            new OrderedContentMirrorStoreStrategy.PredicateEquals(searchFor));

        assertNotNull("we should have found an item", item);
        assertEquals(searchFor, item.getName());
    }

    @Test
    public void seekGreaterThanNotFound() {
        OrderedContentMirrorStoreStrategy store = new OrderedContentMirrorStoreStrategy();
        NodeBuilder index = EmptyNodeState.EMPTY_NODE.builder();
        String n0 = KEYS[1];
        String n1 = KEYS[3];
        String n2 = KEYS[2];
        String n3 = KEYS[0];

        // initialising the store
        store.update(index, "/a/b", EMPTY_KEY_SET, newHashSet(n0));
        store.update(index, "/a/b", EMPTY_KEY_SET, newHashSet(n1));
        store.update(index, "/a/b", EMPTY_KEY_SET, newHashSet(n2));
        store.update(index, "/a/b", EMPTY_KEY_SET, newHashSet(n3));

        String searchFor = n1;

        ChildNodeEntry item = OrderedContentMirrorStoreStrategy.seek(index.getNodeState(),
            new OrderedContentMirrorStoreStrategy.PredicateGreaterThan(searchFor));

        assertNull("no item should have been found", item);
    }

    @Test
    public void seekGreaterThan() {
        OrderedContentMirrorStoreStrategy store = new OrderedContentMirrorStoreStrategy();
        NodeBuilder index = EmptyNodeState.EMPTY_NODE.builder();
        String n0 = KEYS[1];
        String n1 = KEYS[3];
        String n2 = KEYS[2];
        String n3 = KEYS[0];

        // initialising the store
        store.update(index, "/a/b", EMPTY_KEY_SET, newHashSet(n0));
        store.update(index, "/a/b", EMPTY_KEY_SET, newHashSet(n1));
        store.update(index, "/a/b", EMPTY_KEY_SET, newHashSet(n2));
        store.update(index, "/a/b", EMPTY_KEY_SET, newHashSet(n3));

        String searchFor = n2;

        ChildNodeEntry item = OrderedContentMirrorStoreStrategy.seek(index.getNodeState(),
            new OrderedContentMirrorStoreStrategy.PredicateGreaterThan(searchFor));

        assertNotNull("we should have found an item", item);
        assertEquals(n1, item.getName());
    }

    @Test
    public void seekGreaterThanEqualsNotFound() {
        OrderedContentMirrorStoreStrategy store = new OrderedContentMirrorStoreStrategy();
        NodeBuilder index = EmptyNodeState.EMPTY_NODE.builder();
        String n0 = KEYS[1];
        String n2 = KEYS[2];
        String n3 = KEYS[0];

        // initialising the store
        store.update(index, "/a/b", EMPTY_KEY_SET, newHashSet(n0));
        store.update(index, "/a/b", EMPTY_KEY_SET, newHashSet(n2));
        store.update(index, "/a/b", EMPTY_KEY_SET, newHashSet(n3));

        String searchFor = KEYS[3];

        ChildNodeEntry item = OrderedContentMirrorStoreStrategy.seek(index.getNodeState(),
            new OrderedContentMirrorStoreStrategy.PredicateGreaterThan(searchFor, true));

        assertNull("we should have not found an item", item);
    }

    @Test
    public void seekGreaterThanEquals() {
        OrderedContentMirrorStoreStrategy store = new OrderedContentMirrorStoreStrategy();
        NodeBuilder index = EmptyNodeState.EMPTY_NODE.builder();
        String n0 = KEYS[1];
        String n2 = KEYS[2];
        String n3 = KEYS[0];

        // initialising the store
        store.update(index, "/a/b", EMPTY_KEY_SET, newHashSet(n0));
        store.update(index, "/a/b", EMPTY_KEY_SET, newHashSet(n2));
        store.update(index, "/a/b", EMPTY_KEY_SET, newHashSet(n3));

        String searchFor = n2;

        ChildNodeEntry item = OrderedContentMirrorStoreStrategy.seek(index.getNodeState(),
            new OrderedContentMirrorStoreStrategy.PredicateGreaterThan(searchFor, true));

        assertNotNull("we should have found an item", item);
        assertEquals(n2, item.getName());
    }

    @Test
    public void seekLessThanNotFound() {
        OrderedContentMirrorStoreStrategy store = new OrderedContentMirrorStoreStrategy(
            OrderDirection.DESC);
        NodeBuilder index = EmptyNodeState.EMPTY_NODE.builder();
        String n0 = KEYS[1];
        String n2 = KEYS[2];
        String n3 = KEYS[0];

        // initialising the store
        store.update(index, "/a/b", EMPTY_KEY_SET, newHashSet(n0));
        store.update(index, "/a/b", EMPTY_KEY_SET, newHashSet(n2));
        store.update(index, "/a/b", EMPTY_KEY_SET, newHashSet(n3));

        String searchFor = n3;

        ChildNodeEntry item = OrderedContentMirrorStoreStrategy.seek(index.getNodeState(),
            new OrderedContentMirrorStoreStrategy.PredicateLessThan(searchFor));

        assertNull("we should have not found an item", item);
    }

    @Test
    public void seekLessThan() {
        OrderedContentMirrorStoreStrategy store = new OrderedContentMirrorStoreStrategy(
            OrderDirection.DESC);
        NodeBuilder index = EmptyNodeState.EMPTY_NODE.builder();
        String n0 = KEYS[1];
        String n2 = KEYS[2];
        String n3 = KEYS[0];

        // initialising the store
        store.update(index, "/a/b", EMPTY_KEY_SET, newHashSet(n0));
        store.update(index, "/a/b", EMPTY_KEY_SET, newHashSet(n2));
        store.update(index, "/a/b", EMPTY_KEY_SET, newHashSet(n3));

        String searchFor = n2;

        ChildNodeEntry item = OrderedContentMirrorStoreStrategy.seek(index.getNodeState(),
            new OrderedContentMirrorStoreStrategy.PredicateLessThan(searchFor));

        assertNotNull("we should have found an item", item);
        assertEquals(n0, item.getName());
    }

    @Test
    public void seekLessThanEqualNotFound() {
        OrderedContentMirrorStoreStrategy store = new OrderedContentMirrorStoreStrategy(
            OrderDirection.DESC);
        NodeBuilder index = EmptyNodeState.EMPTY_NODE.builder();
        String n0 = KEYS[1];
        String n1 = KEYS[3];
        String n2 = KEYS[2];

        // initialising the store
        store.update(index, "/a/b", EMPTY_KEY_SET, newHashSet(n0));
        store.update(index, "/a/b", EMPTY_KEY_SET, newHashSet(n1));
        store.update(index, "/a/b", EMPTY_KEY_SET, newHashSet(n2));

        String searchFor = KEYS[0];

        ChildNodeEntry item = OrderedContentMirrorStoreStrategy.seek(index.getNodeState(),
            new OrderedContentMirrorStoreStrategy.PredicateLessThan(searchFor, true));

        assertNull("we should have not found an item", item);
    }

    @Test
    public void seekLessThanEqual() {
        OrderedContentMirrorStoreStrategy store = new OrderedContentMirrorStoreStrategy(
            OrderDirection.DESC);
        NodeBuilder index = EmptyNodeState.EMPTY_NODE.builder();
        String n0 = KEYS[1];
        String n1 = KEYS[3];
        String n2 = KEYS[2];

        // initialising the store
        store.update(index, "/a/b", EMPTY_KEY_SET, newHashSet(n0));
        store.update(index, "/a/b", EMPTY_KEY_SET, newHashSet(n1));
        store.update(index, "/a/b", EMPTY_KEY_SET, newHashSet(n2));

        String searchFor = n2;

        ChildNodeEntry item = OrderedContentMirrorStoreStrategy.seek(index.getNodeState(),
            new OrderedContentMirrorStoreStrategy.PredicateLessThan(searchFor, true));

        assertNotNull("we should have found an item", item);
        assertEquals(n2, item.getName());
    }
}
TOP

Related Classes of org.apache.jackrabbit.oak.plugins.index.property.strategy.OrderedContentMirrorStorageStrategyTest

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.