From d8907d3f43bd1ef529af82f22934765ea9e5c69a Mon Sep 17 00:00:00 2001 From: Davide Giannella Date: Mon, 10 Feb 2014 14:06:28 +0000 Subject: [PATCH 01/36] #OAK-1263 initial fileset --- .../index/property/OrderedPropertyIndexEditor.java | 128 +++++ .../OrderedPropertyIndexEditorProvider.java | 22 + .../index/property/PropertyIndexEditor.java | 55 +- .../strategy/ContentMirrorStoreStrategy.java | 43 +- .../OrderedContentMirrorStoreStrategy.java | 237 +++++++++ .../property/OrderedPropertyIndexEditorTest.java | 67 +++ .../OrderedContentMirrorStorageStrategyTest.java | 586 +++++++++++++++++++++ .../java/org/apache/jackrabbit/oak/jcr/Jcr.java | 3 + .../oak/benchmark/BaseOrderByInsertTest.java | 92 ++++ .../jackrabbit/oak/benchmark/BenchmarkRunner.java | 4 + .../oak/benchmark/NoIndexesOrderByInsertTest.java | 30 ++ .../OrderedPropertyIndexOrderByInsertTest.java | 49 ++ .../StardardPropertyIndexOrderByInsertTest.java | 49 ++ .../oak/benchmark/util/OakIndexUtils.java | 26 +- 14 files changed, 1354 insertions(+), 37 deletions(-) create mode 100644 oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexEditor.java create mode 100644 oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexEditorProvider.java create mode 100644 oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/OrderedContentMirrorStoreStrategy.java create mode 100644 oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexEditorTest.java create mode 100644 oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/OrderedContentMirrorStorageStrategyTest.java create mode 100644 oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/BaseOrderByInsertTest.java create mode 100644 oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/NoIndexesOrderByInsertTest.java create mode 100644 oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/OrderedPropertyIndexOrderByInsertTest.java create mode 100644 oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/StardardPropertyIndexOrderByInsertTest.java diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexEditor.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexEditor.java new file mode 100644 index 0000000..e0f330c --- /dev/null +++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexEditor.java @@ -0,0 +1,128 @@ +package org.apache.jackrabbit.oak.plugins.index.property; + +import java.util.Collections; +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.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; +import org.slf4j.LoggerFactory; + +import com.google.common.base.Strings; + +public class OrderedPropertyIndexEditor extends PropertyIndexEditor { + private final static Logger log = LoggerFactory.getLogger(OrderedPropertyIndexEditor.class); + private final static IndexStoreStrategy ORDERED_MIRROR = new OrderedContentMirrorStoreStrategy(); + + private final Set propertyNames; + + private boolean properlyConfigured; + + + public OrderedPropertyIndexEditor(NodeBuilder definition, NodeState root, IndexUpdateCallback callback){ + super(definition,root,callback); + + Set 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."); + }else{ + if(names.isArray()) log.warn("Only single value supported. '{}' only will be used.", value); + pns = Collections.singleton(value); + this.properlyConfigured=true; + } + } + + this.propertyNames = pns; + } + + OrderedPropertyIndexEditor(OrderedPropertyIndexEditor parent, String name) { + super(parent,name); + this.propertyNames = parent.getPropertyNames(); + } + + /** + * Same as {@link PropertyIndexEditor#getStrategy(boolean)} but ignores the boolean flag. + * + * @return the proper index strategy + */ + @Override + IndexStoreStrategy getStrategy(boolean unique) { + return ORDERED_MIRROR; + } + + public boolean isProperlyConfigured() { + return properlyConfigured; + } + + @Override + Set getPropertyNames() { + return propertyNames; + } + + @Override + PropertyIndexEditor getChildIndexEditor(@Nonnull PropertyIndexEditor parent, @Nonnull String name) { + 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); + } +} diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexEditorProvider.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexEditorProvider.java new file mode 100644 index 0000000..43663c9 --- /dev/null +++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexEditorProvider.java @@ -0,0 +1,22 @@ +package org.apache.jackrabbit.oak.plugins.index.property; + +import javax.annotation.CheckForNull; +import javax.annotation.Nonnull; + +import org.apache.jackrabbit.oak.api.CommitFailedException; +import org.apache.jackrabbit.oak.plugins.index.IndexEditorProvider; +import org.apache.jackrabbit.oak.plugins.index.IndexUpdateCallback; +import org.apache.jackrabbit.oak.spi.commit.Editor; +import org.apache.jackrabbit.oak.spi.state.NodeBuilder; +import org.apache.jackrabbit.oak.spi.state.NodeState; + +public class OrderedPropertyIndexEditorProvider implements IndexEditorProvider { + public final static String TYPE = "ordered"; + + @Override + @CheckForNull + public Editor getIndexEditor(@Nonnull String type, @Nonnull NodeBuilder definition, @Nonnull NodeState root, @Nonnull IndexUpdateCallback callback) throws CommitFailedException { + Editor _editor = (TYPE.equals(type)) ? new OrderedPropertyIndexEditor(definition,root,callback) : null; + return _editor; + } +} diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexEditor.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexEditor.java index ddfbe13..16ea189 100644 --- a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexEditor.java +++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexEditor.java @@ -31,6 +31,7 @@ import static org.apache.jackrabbit.oak.plugins.index.property.PropertyIndex.enc import java.util.Set; +import javax.annotation.Nonnull; import javax.jcr.PropertyType; import org.apache.jackrabbit.oak.api.CommitFailedException; @@ -56,7 +57,6 @@ import com.google.common.base.Predicate; * @see PropertyIndexLookup */ class PropertyIndexEditor implements IndexEditor { - /** Index storage strategy */ private static final IndexStoreStrategy MIRROR = new ContentMirrorStoreStrategy(); @@ -79,7 +79,7 @@ class PropertyIndexEditor implements IndexEditor { private final Set propertyNames; - /** Type predicate, or {@code null} if there are no type restrictions */ + /** Type predicate, or {@code null} if there are no type restrictions */ private final Predicate typePredicate; /** @@ -111,6 +111,8 @@ class PropertyIndexEditor implements IndexEditor { this.path = "/"; this.definition = definition; + //initPropertyNames(definition); + // get property names PropertyState names = definition.getProperty(PROPERTY_NAMES); if (names.count() == 1) { @@ -119,7 +121,7 @@ class PropertyIndexEditor implements IndexEditor { } else { this.propertyNames = newHashSet(names.getValue(NAMES)); } - + // get declaring types, and all their subtypes // TODO: should we reindex when type definitions change? if (definition.hasProperty(DECLARING_NODE_TYPES)) { @@ -137,17 +139,26 @@ class PropertyIndexEditor implements IndexEditor { } this.updateCallback = updateCallback; } - - private PropertyIndexEditor(PropertyIndexEditor parent, String name) { + + PropertyIndexEditor(PropertyIndexEditor parent, String name) { this.parent = parent; this.name = name; this.path = null; this.definition = parent.definition; - this.propertyNames = parent.propertyNames; + this.propertyNames = parent.getPropertyNames(); this.typePredicate = parent.typePredicate; this.keysToCheckForUniqueness = parent.keysToCheckForUniqueness; this.updateCallback = parent.updateCallback; } + + /** + * commodity method for allowing extensions + * + * @return + */ + Set getPropertyNames() { + return propertyNames; + } /** * Returns the path of this node, building it lazily when first requested. @@ -193,7 +204,7 @@ class PropertyIndexEditor implements IndexEditor { return keys; } - private static IndexStoreStrategy getStrategy(boolean unique) { + IndexStoreStrategy getStrategy(boolean unique) { return unique ? UNIQUE : MIRROR; } @@ -214,8 +225,8 @@ class PropertyIndexEditor implements IndexEditor { if (typeChanged) { // possible type change, so ignore diff results and // just load all matching values from both states - beforeKeys = getMatchingKeys(before, propertyNames); - afterKeys = getMatchingKeys(after, propertyNames); + beforeKeys = getMatchingKeys(before, getPropertyNames()); + afterKeys = getMatchingKeys(after, getPropertyNames()); } if (beforeKeys != null && !typePredicate.apply(before)) { // the before state doesn't match the type, so clear its values @@ -282,7 +293,7 @@ class PropertyIndexEditor implements IndexEditor { public void propertyAdded(PropertyState after) { String name = after.getName(); typeChanged = typeChanged || isTypeProperty(name); - if (propertyNames.contains(name)) { + if (getPropertyNames().contains(name)) { afterKeys = addValueKeys(afterKeys, after); } } @@ -291,7 +302,7 @@ class PropertyIndexEditor implements IndexEditor { public void propertyChanged(PropertyState before, PropertyState after) { String name = after.getName(); typeChanged = typeChanged || isTypeProperty(name); - if (propertyNames.contains(name)) { + if (getPropertyNames().contains(name)) { beforeKeys = addValueKeys(beforeKeys, before); afterKeys = addValueKeys(afterKeys, after); } @@ -301,25 +312,35 @@ class PropertyIndexEditor implements IndexEditor { public void propertyDeleted(PropertyState before) { String name = before.getName(); typeChanged = typeChanged || isTypeProperty(name); - if (propertyNames.contains(name)) { + if (getPropertyNames().contains(name)) { beforeKeys = addValueKeys(beforeKeys, before); } } + /** + * Retrieve a new index editor associated with the child node to process + * + * @param parent the index editor related to the parent node + * @param name the name of the child node + * @return an instance of the PropertyIndexEditor + */ + PropertyIndexEditor getChildIndexEditor(@Nonnull PropertyIndexEditor parent, @Nonnull String name){ + return new PropertyIndexEditor(parent, name); + } + @Override public Editor childNodeAdded(String name, NodeState after) { - return new PropertyIndexEditor(this, name); + return getChildIndexEditor(this, name); } @Override - public Editor childNodeChanged( - String name, NodeState before, NodeState after) { - return new PropertyIndexEditor(this, name); + public Editor childNodeChanged(String name, NodeState before, NodeState after) { + return getChildIndexEditor(this, name); } @Override public Editor childNodeDeleted(String name, NodeState before) { - return new PropertyIndexEditor(this, name); + return getChildIndexEditor(this, name); } } diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/ContentMirrorStoreStrategy.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/ContentMirrorStoreStrategy.java index 3b0bf7b..7d159a6 100644 --- a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/ContentMirrorStoreStrategy.java +++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/ContentMirrorStoreStrategy.java @@ -24,6 +24,8 @@ import java.util.Deque; import java.util.Iterator; import java.util.Set; +import javax.annotation.Nonnull; + import org.apache.jackrabbit.oak.api.PropertyState; import org.apache.jackrabbit.oak.api.Type; import org.apache.jackrabbit.oak.commons.PathUtils; @@ -78,7 +80,7 @@ public class ContentMirrorStoreStrategy implements IndexStoreStrategy { } } - private static void remove(NodeBuilder index, String key, String value) { + private void remove(NodeBuilder index, String key, String value) { NodeBuilder builder = index.getChildNode(key); if (builder.exists()) { // Collect all builders along the given path @@ -97,24 +99,45 @@ public class ContentMirrorStoreStrategy implements IndexStoreStrategy { } // Prune all index nodes that are no longer needed - for (NodeBuilder node : builders) { - if (node.getBoolean("match") || node.getChildNodeCount(1) > 0) { - return; - } else if (node.exists()) { - node.remove(); - } - } + prune(builders); } } - private static void insert(NodeBuilder index, String key, String value) { - NodeBuilder builder = index.child(key); + /** + * Physically prune a list of nodes from the index + * + * @param builders list of nodes to prune + */ + void prune(final Deque builders){ + for (NodeBuilder node : builders) { + if (node.getBoolean("match") || node.getChildNodeCount(1) > 0) { + return; + } else if (node.exists()) { + node.remove(); + } + } + } + + private void insert(NodeBuilder index, String key, String value) { +// NodeBuilder builder = index.child(key); + NodeBuilder builder = fetchKeyNode(index, key); for (String name : PathUtils.elements(value)) { builder = builder.child(name); } builder.setProperty("match", true); } + /** + * fetch from the index the key node + * + * @param index the current index root + * @param key the 'key' to fetch from the repo + * @return the node representing the key + */ + NodeBuilder fetchKeyNode(@Nonnull NodeBuilder index,@Nonnull String key){ + return index.child(key); + } + public Iterable query(final Filter filter, final String indexName, final NodeState indexMeta, final String indexStorageNodeName, final Iterable values) { diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/OrderedContentMirrorStoreStrategy.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/OrderedContentMirrorStoreStrategy.java new file mode 100644 index 0000000..36b4e67 --- /dev/null +++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/OrderedContentMirrorStoreStrategy.java @@ -0,0 +1,237 @@ +/* + * 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 java.util.Collections; +import java.util.Deque; +import java.util.Iterator; +import java.util.NoSuchElementException; +import java.util.Set; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import org.apache.jackrabbit.oak.spi.state.AbstractChildNodeEntry; +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.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.base.Predicate; +import com.google.common.base.Strings; +import com.google.common.collect.Iterables; +import com.google.common.collect.Iterators; + + +public class OrderedContentMirrorStoreStrategy extends ContentMirrorStoreStrategy { + private final static Logger log = LoggerFactory.getLogger(OrderedContentMirrorStoreStrategy.class); + public final static String NEXT = ":next"; + public final static String START = ":start"; + + @Override + NodeBuilder fetchKeyNode(@Nonnull NodeBuilder index, @Nonnull String key) { + log.debug("fetchKeyNode() - index: {} - key: {}",index,key); + NodeBuilder _key = null; + NodeBuilder start = index.child(START); + + //identifying the right place for insert + String n = start.getString(NEXT); + if(Strings.isNullOrEmpty(n)){ + //new/empty index + _key = index.child(key); + _key.setProperty(NEXT,""); + start.setProperty(NEXT, key); + }else{ + //specific use-case where the item has to be added as first of the list + String nextKey = n; + if(key.compareTo(nextKey)<0){ + _key = index.child(key); + _key.setProperty(NEXT,nextKey); + start.setProperty(NEXT, key); + }else{ + @SuppressWarnings("unchecked") Iterable children = (Iterable)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 + index.getChildNode(child.getName()).setProperty(NEXT, key); + _key = index.child(key); + _key.setProperty(NEXT, ""); + } else { + if(key.compareTo(nextKey)<0){ + index.getChildNode(child.getName()).setProperty(NEXT, key); + _key = index.child(key); + _key.setProperty(NEXT, nextKey); + break; + } + } + } + } + } + + return _key; + } + + @Override + void prune(Deque builders) { + for(NodeBuilder node : builders){ + if(node.hasProperty("match") || node.getChildNodeCount(1) > 0){ + return; + }else if (node.exists()) { + if(node.hasProperty(NEXT)){ + //it's an index key and we have to relink the list + //(1) find the previous element + //(2) find the next element + //(3) re-link the previous to the next + }else{ + node.remove(); + } + } + } + } + + @SuppressWarnings("unchecked") + @Nullable NodeState findPrevious(@Nonnull final NodeState index, @Nonnull final NodeState node){ + ChildNodeEntry previous = null; + ChildNodeEntry current = null; + boolean found = false; + Iterator it = (Iterator) getChildNodeEntries(index).iterator(); + + while(!found && it.hasNext()){ + current = it.next(); + if(previous==null){ + //first iteration + previous = current; + }else{ + found = node.equals(current.getNodeState()); + if(!found) previous = current; + } + } + + return ((found) ? previous.getNodeState() : null); + } + + @Override + public void update(NodeBuilder index, String path, Set beforeKeys, Set 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); + } + + + /** + * retrieve an Iterable for going through the index in the right order without the :start node + * + * @param index the root of the index (:index) + * @return + */ + @Nonnull Iterable getChildNodeEntries(@Nonnull final NodeState index){ + return getChildNodeEntries(index, false); + } + + /** + * retrive an Iterable for going through the index in the right order with potentially the :start node + * + * @param index the root of the index (:index) + * @param includeStart true if :start should be included as first element + * @return + */ + @Nonnull Iterable getChildNodeEntries(@Nonnull final NodeState index, final boolean includeStart){ + Iterable cne = null; + NodeState start = index.getChildNode(START); + + if(!start.exists() || Strings.isNullOrEmpty(start.getString(NEXT))) { + //if the property is not there or is empty it means we're empty + cne = Collections.emptyList(); + }else{ + cne = new Iterable(){ + private NodeState _index = index; + private NodeState _start = _index.getChildNode(START); + private NodeState current = _start; + private boolean _includeStart = includeStart; + + @Override + public Iterator iterator() { + return new Iterator(){ + + @Override + public boolean hasNext() { + return !Strings.isNullOrEmpty(current.getString(NEXT)); + } + + @Override + public ChildNodeEntry next() { + ChildNodeEntry _cne = null; + if(_includeStart && _start.equals(current)){ + _cne = new OrderedChildNodeEntry(START, current); + _includeStart = false; //let's set it to false. We just included it. +// current = _index.getChildNode(current.getString(NEXT)); + } else { + if(hasNext()){ + final String name = current.getString(NEXT); + current = _index.getChildNode(name); + _cne = new OrderedChildNodeEntry(name, current); + }else{ + throw new NoSuchElementException(); + } + } + return _cne; + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + }; + } + return cne; + } + + private final class OrderedChildNodeEntry extends AbstractChildNodeEntry { + private final String name; + private final NodeState state; + + public OrderedChildNodeEntry(@Nonnull final String name,@Nonnull final NodeState state){ + this.name = name; + this.state = state; + } + + /* (non-Javadoc) + * @see org.apache.jackrabbit.oak.spi.state.ChildNodeEntry#getName() + */ + @Override + @Nonnull + public String getName() { + return name; + } + + /* (non-Javadoc) + * @see org.apache.jackrabbit.oak.spi.state.ChildNodeEntry#getNodeState() + */ + @Override + @Nonnull + public NodeState getNodeState() { + return state; + } + } +} diff --git a/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexEditorTest.java b/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexEditorTest.java new file mode 100644 index 0000000..bbe7681 --- /dev/null +++ b/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexEditorTest.java @@ -0,0 +1,67 @@ +package org.apache.jackrabbit.oak.plugins.index.property; + +import static org.easymock.EasyMock.createNiceMock; +import static org.easymock.EasyMock.expect; +import static org.easymock.EasyMock.replay; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +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.spi.state.NodeBuilder; +import org.junit.Test; + +public class OrderedPropertyIndexEditorTest { + + @Test public void isProperlyConfiguredWithPropertyNames(){ + NodeBuilder definition = createNiceMock(NodeBuilder.class); + PropertyState names = createNiceMock(PropertyState.class); + expect(names.count()).andReturn(1); + expect(definition.getProperty(IndexConstants.PROPERTY_NAMES)).andReturn(names).anyTimes(); + replay(names); + replay(definition); + + OrderedPropertyIndexEditor ie = new OrderedPropertyIndexEditor(definition, null, null); + assertFalse("With empty or missing property the index should not work.",ie.isProperlyConfigured()); + } + + @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(definition.getProperty(IndexConstants.PROPERTY_NAMES)).andReturn(names).anyTimes(); + replay(names); + replay(definition); + + OrderedPropertyIndexEditor ie = new OrderedPropertyIndexEditor(definition, null, null); + assertNotNull("With a correct proprety 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(){ + 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(new String[]{"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 proprety 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()); + } +} diff --git a/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/OrderedContentMirrorStorageStrategyTest.java b/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/OrderedContentMirrorStorageStrategyTest.java new file mode 100644 index 0000000..4d31595 --- /dev/null +++ b/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/OrderedContentMirrorStorageStrategyTest.java @@ -0,0 +1,586 @@ +/* + * 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.assertTrue; + +import java.util.Iterator; +import java.util.Set; + +import org.apache.jackrabbit.oak.commons.PathUtils; +import org.apache.jackrabbit.oak.plugins.memory.EmptyNodeState; +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.Assert; +import org.junit.Ignore; +import org.junit.Test; + +import com.google.common.base.Strings; +import com.google.common.collect.Iterables; +import com.google.common.collect.Iterators; + +/** + * + */ +public class OrderedContentMirrorStorageStrategyTest { + /** + * ascending ordered set of keys. Useful for testing + */ + private final static String[] KEYS = new String[]{"donald","goofy", "mickey","minnie"}; + private final static Set EMPTY_KEY_SET = newHashSet(); + + /** + * checks that the fist item/key is inserted with an empty property 'next' + * + * expected structure: + * + * + * :index : { + * :start : { :next=n0 }, + * n0 : { + * :next=, + * foo : { + * bar: { match=true} + * } + * } + * } + * + */ + @Test public void firstAndOnlyItem(){ + final String PATH = "/foo/bar"; + final String[] PATH_NODES = Iterables.toArray(PathUtils.elements(PATH), String.class); + final String N0 = KEYS[0]; + + IndexStoreStrategy store = new OrderedContentMirrorStoreStrategy(); + NodeBuilder index = EmptyNodeState.EMPTY_NODE.builder(); + NodeBuilder node = null; + + store.update(index, PATH, EMPTY_KEY_SET, newHashSet(N0)); + + 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",N0,node.getString(NEXT)); + + node = index.getChildNode(N0); + 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(PATH_NODES[0]); + assertTrue("n0 should contain 'foo'",node.exists()); + node = node.getChildNode(PATH_NODES[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 + * + * + * :index : { + * :start : { :next=n0 }, + * n0 : { :next=n1 }, + * n1 : { :next= } + * } + * + */ + @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; + + store.update(index, PATH, EMPTY_KEY_SET, newHashSet(N0)); //first node arrives + + 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)); + + store.update(index, PATH, EMPTY_KEY_SET, newHashSet(N1)); //second node arrives + + 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 children = (Iterable) store.getChildNodeEntries(index); + + assertNotNull("A returned Iterable cannot be null",children); + } + + /** + * test the iteration of the index with 2 shuffled items + * + * + * :index : { + * :start : { :next=n1 }, + * n0 : { :next= }, + * n1 : { :next=n0 } + * } + * + */ + @SuppressWarnings("unchecked") + @Test public void childNodeEntriesACoupleOfMixedItems(){ + final String N0 = KEYS[1]; + final String N1 = KEYS[0]; + final NodeState NODE_0 = EmptyNodeState.EMPTY_NODE.builder().setProperty(NEXT, "").getNodeState(); + final NodeState NODE_1 = 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, NODE_0); + index.setChildNode(N1, NODE_1); + + NodeState indexState = index.getNodeState(); + + Iterable children = (Iterable)store.getChildNodeEntries(indexState); + assertNotNull("The iterable cannot be null",children); + assertEquals("Expecting 2 items in the index", 2, Iterators.size(children.iterator())); //how many entries do we have? + + //ensuring the right sequence + ChildNodeEntry entry = null; + children = (Iterable)store.getChildNodeEntries(indexState); + Iterator 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",NODE_1,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",NODE_0,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 + * + * + * :index : { + * :start : { :next=n1 }, + * n0 : { :next= }, + * n1 : { :next=n0 } + * } + * + */ + @SuppressWarnings("unchecked") + @Test public void childNodeEntriesACoupleOfMixedItemsNoStart(){ + final String N0 = KEYS[1]; + final String N1 = KEYS[0]; + final NodeState NODE_0 = EmptyNodeState.EMPTY_NODE.builder().setProperty(NEXT, "").getNodeState(); + final NodeState NODE_1 = 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, NODE_0); + index.setChildNode(N1, NODE_1); + + NodeState indexState = index.getNodeState(); + + Iterable children = (Iterable)store.getChildNodeEntries(indexState,false); + assertNotNull("The iterable cannot be null",children); + assertEquals("Expecting 2 items in the index", 2, Iterators.size(children.iterator())); //how many entries do we have? + + //ensuring the right sequence + ChildNodeEntry entry = null; + children = (Iterable)store.getChildNodeEntries(indexState); + Iterator 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",NODE_1,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",NODE_0,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 + * + * + * :index : { + * :start : { :next=n1 }, + * n0 : { :next= }, + * n1 : { :next=n0 } + * } + * + */ + @SuppressWarnings("unchecked") + @Test public void childNodeEntriesACoupleOfMixedItemsWithStart(){ + final String N0 = KEYS[1]; + final String N1 = KEYS[0]; + final NodeState NODE_START = EmptyNodeState.EMPTY_NODE.builder().setProperty(NEXT, N1).getNodeState(); + final NodeState NODE_0 = EmptyNodeState.EMPTY_NODE.builder().setProperty(NEXT, "").getNodeState(); + final NodeState NODE_1 = 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, NODE_START); + index.setChildNode(N0, NODE_0); + index.setChildNode(N1, NODE_1); + + NodeState indexState = index.getNodeState(); + + Iterable children = (Iterable)store.getChildNodeEntries(indexState,true); + assertNotNull("The iterable cannot be null",children); + assertEquals("Expecting 3 items in the index", 3, Iterators.size(children.iterator())); //how many entries do we have? + + //ensuring the right sequence + ChildNodeEntry entry = null; + children = (Iterable)store.getChildNodeEntries(indexState,true); + Iterator 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",NODE_START,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",NODE_1,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",NODE_0,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 + * + */ + @Ignore("easing the merge") @Test public void childNodeEntriesNoItemsWithStart(){ + Assert.fail("develop me"); + } + + + /** + * 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. + */ + @Ignore("easing the merge") @Test public void childNodeEntriesNewIndexWithStart(){ + Assert.fail("develop me"); + } + + /** + * test the insert of two shuffled items + * + * Building final a structure like + * + * + * :index : { + * :start : { :next=n1 }, + * n0 : { :next= }, + * n1 : { :next=n0 } + * } + * + * + * where: + * + * + * Stage 1 + * ======= + * + * :index : { + * :start : { :next = n0 }, + * n0 : { + * :next = + * } + * } + * + * Stage 2 + * ======= + * + * :index : { + * :start : { :next = n1 }, + * n0 : { + * :next = + * }, + * n1 : { + * :next = n0 + * } + * } + * + */ + @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 + * + * + * { + * :start : { :next = n1 }, + * n0 : { + * :next = "" + * }, + * n1 : { + * :next = n2 + * }, + * n2 : { + * :next = n0 + * } + * } + * + * + * where: + * + * + * 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 = } + * } + * + */ + @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 existant + * node/key gets updated by changing the key and the key contains only 1 item. + * + * Where the second key is greater than the first. + * + * + * Stage 1 + * ======= + * + * :index : { + * :start { :next = n0 }, + * n0 : { + * :next =, + * content : { + * foobar : { + * match = true + * } + * } + * } + * } + * + * Stage 2 + * ======= + * + * :index : { + * :start : { :next = n1 }, + * n1 : { + * :next =, + * content : { + * foobar : { + * match = true + * } + * } + * } + * } + * + */ + + @Ignore("easing the merge") @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 cointain /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)); + } + + /** + *

+ * find a previous item given a key in an index with 1 element only + *

+ * + *

it relies on the functionality of the store.update() for creating the index

+ * + * + * :index { + * :start : { :next=n0 }, + * n0 = { :next= } + * } + * + */ + @Ignore("easing the merge") @Test public void findPrevious(){ + final OrderedContentMirrorStoreStrategy store = new OrderedContentMirrorStoreStrategy(); + final String N0 = KEYS[0]; + final NodeState NODE_START = EmptyNodeState.EMPTY_NODE.builder().setProperty(NEXT, N0).getNodeState(); + final NodeState NODE_0 = EmptyNodeState.EMPTY_NODE.builder().setProperty(NEXT,"").getNodeState(); + final NodeBuilder index = EmptyNodeState.EMPTY_NODE.builder(); + + index.setChildNode(START, NODE_START); + index.setChildNode(N0,NODE_0); + + NodeState indexState = index.getNodeState(); + NodeState previous = store.findPrevious( + indexState, + NODE_0 + ); + assertEquals("the :start node is expected",NODE_START,previous); + } +} diff --git a/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/Jcr.java b/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/Jcr.java index f767ece..c826496 100644 --- a/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/Jcr.java +++ b/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/Jcr.java @@ -31,6 +31,7 @@ import org.apache.jackrabbit.oak.plugins.commit.ConflictValidatorProvider; import org.apache.jackrabbit.oak.plugins.commit.JcrConflictHandler; import org.apache.jackrabbit.oak.plugins.index.IndexEditorProvider; import org.apache.jackrabbit.oak.plugins.index.nodetype.NodeTypeIndexProvider; +import org.apache.jackrabbit.oak.plugins.index.property.OrderedPropertyIndexEditorProvider; import org.apache.jackrabbit.oak.plugins.index.property.PropertyIndexEditorProvider; import org.apache.jackrabbit.oak.plugins.index.property.PropertyIndexProvider; import org.apache.jackrabbit.oak.plugins.index.reference.ReferenceEditorProvider; @@ -78,6 +79,8 @@ public class Jcr { with(new PropertyIndexProvider()); with(new NodeTypeIndexProvider()); + + with(new OrderedPropertyIndexEditorProvider()); } public Jcr() { diff --git a/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/BaseOrderByInsertTest.java b/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/BaseOrderByInsertTest.java new file mode 100644 index 0000000..95920b6 --- /dev/null +++ b/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/BaseOrderByInsertTest.java @@ -0,0 +1,92 @@ +/* + * 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.benchmark; + +import java.util.UUID; + +import javax.jcr.Node; +import javax.jcr.RepositoryException; +import javax.jcr.Session; + +/** + * + */ +public abstract class BaseOrderByInsertTest extends AbstractTest { + /** + * type of the created node + */ + private final static String NODE_TYPE = "oak:Unstructured"; + + /** + * property that will be indexed + */ + final static String INDEXED_PROPERTY = "indexedProperty"; + + /** + * the number of nodes created per iteration + */ + private final static int NODES_PER_ITERATION = Integer.parseInt(System.getProperty("nodesPerIteration", "100")); + + /** + * node name below which creating the test data + */ + private final String DUMP_NODE = this.getClass().getSimpleName() + TEST_ID; + + /** + * session used for operations throughout the test + */ + Session session; + + /** + * node under which all the test data will be filled in + */ + private Node dump; + + @Override + protected void beforeTest() throws Exception { + session = loginWriter(); + + //initiate the place for writing child nodes + dump = session.getRootNode().addNode(DUMP_NODE,NODE_TYPE); + session.save(); + } + + @Override + protected void afterTest() throws Exception { + //clean-up our mess + dump.remove(); + session.save(); + session.logout(); + } + + /* (non-Javadoc) + * @see org.apache.jackrabbit.oak.benchmark.AbstractTest#runTest() + */ + @Override + protected void runTest() throws Exception { + try{ + for(int i=0; i Date: Mon, 10 Feb 2014 14:44:30 +0000 Subject: [PATCH 02/36] #OAK-1263 initial idea around the benchmark for queries --- .../jackrabbit/oak/benchmark/OrderByQueryTest.java | 89 ++++++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/OrderByQueryTest.java diff --git a/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/OrderByQueryTest.java b/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/OrderByQueryTest.java new file mode 100644 index 0000000..a6cabdb --- /dev/null +++ b/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/OrderByQueryTest.java @@ -0,0 +1,89 @@ +/* + * 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.benchmark; + +import java.util.Random; +import java.util.UUID; + +import javax.jcr.Node; +import javax.jcr.NodeIterator; +import javax.jcr.RepositoryException; +import javax.jcr.Session; +import javax.jcr.query.Query; +import javax.jcr.query.QueryManager; +import javax.jcr.query.QueryResult; + +/** + * This benchmark measures the read performance of child nodes using + * an ORDER BY query. + *

+ * This is related to OAK-1263. + * + */ +public class OrderByQueryTest extends AbstractTest { + + private static final String NT = "oak:unstructured"; + + private static final String ROOT_NODE_NAME = "test" + TEST_ID; + private static final int NUM_NODES = 10000; + private static final String PROPERTY_NAME = "testProperty"; + private static final Random random = new Random(); // doesn't have to be very secure, just some randomness + + @Override + protected void beforeSuite() throws Exception { + Session session = loginWriter(); + Node rootNode = session.getRootNode(); + if (rootNode.hasNode(ROOT_NODE_NAME)) { + Node root = rootNode.getNode(ROOT_NODE_NAME); + root.remove(); + } + rootNode = session.getRootNode().addNode(ROOT_NODE_NAME, NT); + + for (int i = 0; i < NUM_NODES; i++) { + if (i%1000==0) { + session.save(); + } + Node newNode = rootNode.addNode(UUID.randomUUID().toString(), NT); + newNode.setProperty(PROPERTY_NAME, random.nextLong()); + } + session.save(); + } + + @Override + public void runTest() throws Exception { + final Session session = loginWriter(); + try { + // run the query + final QueryManager qm = session.getWorkspace().getQueryManager(); + + final Query q = + qm.createQuery("SELECT * FROM [oak:unstructured] AS s WHERE " + + "ISDESCENDANTNODE(s, [/"+ROOT_NODE_NAME+"/]) ORDER BY s."+PROPERTY_NAME+"]", + Query.JCR_SQL2); + final QueryResult res = q.execute(); + + final NodeIterator nit = res.getNodes(); +// while(nit.hasNext()) { +// Node node = nit.nextNode(); +//// System.out.println("node: "+node.getPath()+", prop="+node.getProperty(PROPERTY_NAME).getLong()); +// } + } catch (RepositoryException e) { + e.printStackTrace(); + throw new RuntimeException(e); + } + } +} -- 1.8.3.4 (Apple Git-47) From 2fdae8eb895c949e574b4c51956462c7d0c62221 Mon Sep 17 00:00:00 2001 From: Davide Giannella Date: Mon, 10 Feb 2014 16:34:47 +0000 Subject: [PATCH 03/36] #OAK-1263 improved the benchmarks (still to be tested) --- .../oak/benchmark/BaseOrderByInsertTest.java | 21 +++++++++- .../jackrabbit/oak/benchmark/BenchmarkRunner.java | 2 +- .../OrderedPropertyIndexOrderByInsertTest.java | 5 +-- .../StandardPropertyIndexOrderByInsertTest.java | 46 ++++++++++++++++++++ .../StardardPropertyIndexOrderByInsertTest.java | 49 ---------------------- 5 files changed, 67 insertions(+), 56 deletions(-) create mode 100644 oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/StandardPropertyIndexOrderByInsertTest.java delete mode 100644 oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/StardardPropertyIndexOrderByInsertTest.java diff --git a/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/BaseOrderByInsertTest.java b/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/BaseOrderByInsertTest.java index 95920b6..5fa3a23 100644 --- a/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/BaseOrderByInsertTest.java +++ b/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/BaseOrderByInsertTest.java @@ -41,6 +41,8 @@ public abstract class BaseOrderByInsertTest extends AbstractTest { * the number of nodes created per iteration */ private final static int NODES_PER_ITERATION = Integer.parseInt(System.getProperty("nodesPerIteration", "100")); + + private final static int PRE_ADDED_NODES = Integer.parseInt(System.getProperty("preAddedNodes", "0")); /** * node name below which creating the test data @@ -64,6 +66,11 @@ public abstract class BaseOrderByInsertTest extends AbstractTest { //initiate the place for writing child nodes dump = session.getRootNode().addNode(DUMP_NODE,NODE_TYPE); session.save(); + + defineIndex(); + + //pre-adding nodes + fireNodes(PRE_ADDED_NODES); } @Override @@ -79,14 +86,24 @@ public abstract class BaseOrderByInsertTest extends AbstractTest { */ @Override protected void runTest() throws Exception { + fireNodes(NODES_PER_ITERATION); + } + + void fireNodes(int numberOfNodes){ try{ - for(int i=0; i Date: Tue, 11 Feb 2014 16:56:26 +0000 Subject: [PATCH 04/36] #OAK-1263 initial pruning attempt. Doesn't work yet. --- .../property/strategy/ContentMirrorStoreStrategy.java | 5 +++-- .../strategy/OrderedContentMirrorStoreStrategy.java | 16 ++++++++++++++-- .../OrderedContentMirrorStorageStrategyTest.java | 6 +++--- 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/ContentMirrorStoreStrategy.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/ContentMirrorStoreStrategy.java index 7d159a6..293ea09 100644 --- a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/ContentMirrorStoreStrategy.java +++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/ContentMirrorStoreStrategy.java @@ -99,16 +99,17 @@ public class ContentMirrorStoreStrategy implements IndexStoreStrategy { } // Prune all index nodes that are no longer needed - prune(builders); + prune(index, builders); } } /** * Physically prune a list of nodes from the index * + * @param index the current index * @param builders list of nodes to prune */ - void prune(final Deque builders){ + void prune(final NodeBuilder index, final Deque builders){ for (NodeBuilder node : builders) { if (node.getBoolean("match") || node.getChildNodeCount(1) > 0) { return; diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/OrderedContentMirrorStoreStrategy.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/OrderedContentMirrorStoreStrategy.java index 36b4e67..06a07fc 100644 --- a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/OrderedContentMirrorStoreStrategy.java +++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/OrderedContentMirrorStoreStrategy.java @@ -89,16 +89,28 @@ public class OrderedContentMirrorStoreStrategy extends ContentMirrorStoreStrateg } @Override - void prune(Deque builders) { + void prune(final NodeBuilder index, final Deque builders){ for(NodeBuilder node : builders){ if(node.hasProperty("match") || node.getChildNodeCount(1) > 0){ return; }else if (node.exists()) { if(node.hasProperty(NEXT)){ + //TODO this pruning doens't really works because of the chicken-egg problem around node names, NodeState and NodeBuilder. + //TODO Fix me! + //it's an index key and we have to relink the list //(1) find the previous element + NodeState previous = findPrevious(index.getNodeState(), node.getNodeState()); + //(2) find the next element + String next = node.getString(NEXT); + if(next==null) next = ""; + //(3) re-link the previous to the next + previous.builder().setProperty(NEXT, next); + + //(4) remove the current node + node.remove(); }else{ node.remove(); } @@ -111,7 +123,7 @@ public class OrderedContentMirrorStoreStrategy extends ContentMirrorStoreStrateg ChildNodeEntry previous = null; ChildNodeEntry current = null; boolean found = false; - Iterator it = (Iterator) getChildNodeEntries(index).iterator(); + Iterator it = (Iterator) getChildNodeEntries(index,true).iterator(); while(!found && it.hasNext()){ current = it.next(); diff --git a/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/OrderedContentMirrorStorageStrategyTest.java b/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/OrderedContentMirrorStorageStrategyTest.java index 4d31595..003a18a 100644 --- a/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/OrderedContentMirrorStorageStrategyTest.java +++ b/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/OrderedContentMirrorStorageStrategyTest.java @@ -482,7 +482,7 @@ public class OrderedContentMirrorStorageStrategyTest { } /** - * perform a test where the index gets updated if an already existant + * 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. @@ -520,7 +520,7 @@ public class OrderedContentMirrorStorageStrategyTest { * */ - @Ignore("easing the merge") @Test public void singleKeyUpdate(){ + @Test public void singleKeyUpdate(){ final String N0 = KEYS[0]; final String N1 = KEYS[1]; final String PATH = "/content/foobar"; @@ -566,7 +566,7 @@ public class OrderedContentMirrorStorageStrategyTest { * } * */ - @Ignore("easing the merge") @Test public void findPrevious(){ + @Test public void findPrevious1ItemIndex(){ final OrderedContentMirrorStoreStrategy store = new OrderedContentMirrorStoreStrategy(); final String N0 = KEYS[0]; final NodeState NODE_START = EmptyNodeState.EMPTY_NODE.builder().setProperty(NEXT, N0).getNodeState(); -- 1.8.3.4 (Apple Git-47) From 3783ae173406a6d6c8dfcfc85030838d11e5e06e Mon Sep 17 00:00:00 2001 From: Davide Giannella Date: Wed, 12 Feb 2014 08:06:04 +0000 Subject: [PATCH 05/36] pre-merge operations --- .../property/strategy/OrderedContentMirrorStorageStrategyTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/OrderedContentMirrorStorageStrategyTest.java b/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/OrderedContentMirrorStorageStrategyTest.java index 003a18a..c8e0610 100644 --- a/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/OrderedContentMirrorStorageStrategyTest.java +++ b/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/OrderedContentMirrorStorageStrategyTest.java @@ -520,7 +520,7 @@ public class OrderedContentMirrorStorageStrategyTest { * */ - @Test public void singleKeyUpdate(){ + @Ignore("Easying the merge") @Test public void singleKeyUpdate(){ final String N0 = KEYS[0]; final String N1 = KEYS[1]; final String PATH = "/content/foobar"; -- 1.8.3.4 (Apple Git-47) From ef38b113a63399ccd4468c5d886e020d072176a7 Mon Sep 17 00:00:00 2001 From: Davide Giannella Date: Wed, 12 Feb 2014 09:13:55 +0000 Subject: [PATCH 06/36] #OAK-1263 pruning of the index --- .../strategy/OrderedContentMirrorStoreStrategy.java | 10 ++++++---- .../OrderedContentMirrorStorageStrategyTest.java | 17 ++++++++++++++--- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/OrderedContentMirrorStoreStrategy.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/OrderedContentMirrorStoreStrategy.java index 06a07fc..a926a00 100644 --- a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/OrderedContentMirrorStoreStrategy.java +++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/OrderedContentMirrorStoreStrategy.java @@ -100,14 +100,16 @@ public class OrderedContentMirrorStoreStrategy extends ContentMirrorStoreStrateg //it's an index key and we have to relink the list //(1) find the previous element - NodeState previous = findPrevious(index.getNodeState(), node.getNodeState()); + 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 = ""; //(3) re-link the previous to the next - previous.builder().setProperty(NEXT, next); + index.getChildNode(previous.getName()).setProperty(NEXT, next); //(4) remove the current node node.remove(); @@ -119,7 +121,7 @@ public class OrderedContentMirrorStoreStrategy extends ContentMirrorStoreStrateg } @SuppressWarnings("unchecked") - @Nullable NodeState findPrevious(@Nonnull final NodeState index, @Nonnull final NodeState node){ + @Nullable ChildNodeEntry findPrevious(@Nonnull final NodeState index, @Nonnull final NodeState node){ ChildNodeEntry previous = null; ChildNodeEntry current = null; boolean found = false; @@ -136,7 +138,7 @@ public class OrderedContentMirrorStoreStrategy extends ContentMirrorStoreStrateg } } - return ((found) ? previous.getNodeState() : null); + return ((found) ? previous : null); } @Override diff --git a/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/OrderedContentMirrorStorageStrategyTest.java b/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/OrderedContentMirrorStorageStrategyTest.java index c8e0610..d692b3c 100644 --- a/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/OrderedContentMirrorStorageStrategyTest.java +++ b/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/OrderedContentMirrorStorageStrategyTest.java @@ -520,7 +520,7 @@ public class OrderedContentMirrorStorageStrategyTest { * */ - @Ignore("Easying the merge") @Test public void singleKeyUpdate(){ + @Test public void singleKeyUpdate(){ final String N0 = KEYS[0]; final String N1 = KEYS[1]; final String PATH = "/content/foobar"; @@ -550,6 +550,17 @@ public class OrderedContentMirrorStorageStrategyTest { 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")); } /** @@ -577,10 +588,10 @@ public class OrderedContentMirrorStorageStrategyTest { index.setChildNode(N0,NODE_0); NodeState indexState = index.getNodeState(); - NodeState previous = store.findPrevious( + ChildNodeEntry previous = store.findPrevious( indexState, NODE_0 ); - assertEquals("the :start node is expected",NODE_START,previous); + assertEquals("the :start node is expected",NODE_START,previous.getNodeState()); } } -- 1.8.3.4 (Apple Git-47) From 57e55ead8e8fe559a8e482e413325c5e9f04fe35 Mon Sep 17 00:00:00 2001 From: Davide Giannella Date: Wed, 12 Feb 2014 09:50:20 +0000 Subject: [PATCH 07/36] #OAK-1263 improved the Iterable --- .../OrderedContentMirrorStoreStrategy.java | 30 +++++++--------------- .../OrderedContentMirrorStorageStrategyTest.java | 24 +++++++++++++++-- 2 files changed, 31 insertions(+), 23 deletions(-) diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/OrderedContentMirrorStoreStrategy.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/OrderedContentMirrorStoreStrategy.java index a926a00..5cf7554 100644 --- a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/OrderedContentMirrorStoreStrategy.java +++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/OrderedContentMirrorStoreStrategy.java @@ -33,10 +33,7 @@ import org.apache.jackrabbit.oak.spi.state.NodeState; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.common.base.Predicate; import com.google.common.base.Strings; -import com.google.common.collect.Iterables; -import com.google.common.collect.Iterators; public class OrderedContentMirrorStoreStrategy extends ContentMirrorStoreStrategy { @@ -95,24 +92,13 @@ public class OrderedContentMirrorStoreStrategy extends ContentMirrorStoreStrateg return; }else if (node.exists()) { if(node.hasProperty(NEXT)){ - //TODO this pruning doens't really works because of the chicken-egg problem around node names, NodeState and NodeBuilder. - //TODO Fix me! - //it's an index key and we have to relink the list - //(1) find the previous element - ChildNodeEntry previous = findPrevious(index.getNodeState(), node.getNodeState()); - + ChildNodeEntry previous = findPrevious(index.getNodeState(), node.getNodeState()); //(1) find the previous element log.debug("previous: {}",previous); - - //(2) find the next element - String next = node.getString(NEXT); + String next = node.getString(NEXT); //(2) find the next element if(next==null) next = ""; - - //(3) re-link the previous to the next - index.getChildNode(previous.getName()).setProperty(NEXT, next); - - //(4) remove the current node - node.remove(); + index.getChildNode(previous.getName()).setProperty(NEXT, next); //(3) re-link the previous to the next + node.remove(); //(4) remove the current node }else{ node.remove(); } @@ -172,7 +158,7 @@ public class OrderedContentMirrorStoreStrategy extends ContentMirrorStoreStrateg Iterable cne = null; NodeState start = index.getChildNode(START); - if(!start.exists() || Strings.isNullOrEmpty(start.getString(NEXT))) { + if(!start.exists() || Strings.isNullOrEmpty(start.getString(NEXT)) && !includeStart) { //if the property is not there or is empty it means we're empty cne = Collections.emptyList(); }else{ @@ -188,7 +174,10 @@ public class OrderedContentMirrorStoreStrategy extends ContentMirrorStoreStrateg @Override public boolean hasNext() { - return !Strings.isNullOrEmpty(current.getString(NEXT)); + return ( + (_includeStart && _start.equals(current)) || + (!_includeStart && !Strings.isNullOrEmpty(current.getString(NEXT))) + ); } @Override @@ -197,7 +186,6 @@ public class OrderedContentMirrorStoreStrategy extends ContentMirrorStoreStrateg if(_includeStart && _start.equals(current)){ _cne = new OrderedChildNodeEntry(START, current); _includeStart = false; //let's set it to false. We just included it. -// current = _index.getChildNode(current.getString(NEXT)); } else { if(hasNext()){ final String name = current.getString(NEXT); diff --git a/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/OrderedContentMirrorStorageStrategyTest.java b/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/OrderedContentMirrorStorageStrategyTest.java index d692b3c..f0b3cd6 100644 --- a/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/OrderedContentMirrorStorageStrategyTest.java +++ b/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/OrderedContentMirrorStorageStrategyTest.java @@ -302,9 +302,29 @@ public class OrderedContentMirrorStorageStrategyTest { * test the iteration over an empty list when the :start is required. * In this case :start should always be returned * + * + * :index : { + * :start : { :next= } + * } + * */ - @Ignore("easing the merge") @Test public void childNodeEntriesNoItemsWithStart(){ - Assert.fail("develop me"); + @Test public void childNodeEntriesNoItemsWithStart(){ + NodeState NODE_START = EmptyNodeState.EMPTY_NODE.builder().setProperty(NEXT, "").getNodeState(); + NodeBuilder index = EmptyNodeState.EMPTY_NODE.builder(); + OrderedContentMirrorStoreStrategy store = new OrderedContentMirrorStoreStrategy(); + + //setting-up the index + index.setChildNode(START, NODE_START); + + Iterable children = store.getChildNodeEntries(index.getNodeState(), true); + assertEquals("Wrong size of Iterable",1,Iterators.size(children.iterator())); + + Iterator 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",NODE_START,entry.getNodeState()); + assertFalse("We should be at the end of the list",it.hasNext()); } -- 1.8.3.4 (Apple Git-47) From 78991cb5ce5ecf5872f83d042f2188169de4ced3 Mon Sep 17 00:00:00 2001 From: Davide Giannella Date: Wed, 12 Feb 2014 10:49:55 +0000 Subject: [PATCH 08/36] #OAK-1263 added coverage of iteration through new index while requiring :start --- .../OrderedContentMirrorStoreStrategy.java | 24 +++++++++++++++++----- .../OrderedContentMirrorStorageStrategyTest.java | 16 +++++++++++++-- 2 files changed, 33 insertions(+), 7 deletions(-) diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/OrderedContentMirrorStoreStrategy.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/OrderedContentMirrorStoreStrategy.java index 5cf7554..c3df9d5 100644 --- a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/OrderedContentMirrorStoreStrategy.java +++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/OrderedContentMirrorStoreStrategy.java @@ -26,6 +26,7 @@ import java.util.Set; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.apache.jackrabbit.oak.plugins.memory.EmptyNodeState; import org.apache.jackrabbit.oak.spi.state.AbstractChildNodeEntry; import org.apache.jackrabbit.oak.spi.state.ChildNodeEntry; import org.apache.jackrabbit.oak.spi.state.NodeBuilder; @@ -38,9 +39,22 @@ import com.google.common.base.Strings; public class OrderedContentMirrorStoreStrategy extends ContentMirrorStoreStrategy { private final static Logger log = LoggerFactory.getLogger(OrderedContentMirrorStoreStrategy.class); + + /** + * the property linking to the next node + */ public final static String NEXT = ":next"; + + /** + * node that works as root of the index (start point or 0 element) + */ public final static String START = ":start"; - + + /** + * a NodeState used for easy creating of an empty :start + */ + public final static NodeState EMPTY_START_NODE = EmptyNodeState.EMPTY_NODE.builder().setProperty(NEXT, "").getNodeState(); + @Override NodeBuilder fetchKeyNode(@Nonnull NodeBuilder index, @Nonnull String key) { log.debug("fetchKeyNode() - index: {} - key: {}",index,key); @@ -148,7 +162,7 @@ public class OrderedContentMirrorStoreStrategy extends ContentMirrorStoreStrateg } /** - * retrive an Iterable for going through the index in the right order with potentially the :start node + * Retrieve an Iterable for going through the index in the right order with potentially the :start node * * @param index the root of the index (:index) * @param includeStart true if :start should be included as first element @@ -156,15 +170,15 @@ public class OrderedContentMirrorStoreStrategy extends ContentMirrorStoreStrateg */ @Nonnull Iterable getChildNodeEntries(@Nonnull final NodeState index, final boolean includeStart){ Iterable cne = null; - NodeState start = index.getChildNode(START); + final NodeState start = index.getChildNode(START); - if(!start.exists() || Strings.isNullOrEmpty(start.getString(NEXT)) && !includeStart) { + if((!start.exists() || Strings.isNullOrEmpty(start.getString(NEXT))) && !includeStart) { //if the property is not there or is empty it means we're empty cne = Collections.emptyList(); }else{ cne = new Iterable(){ private NodeState _index = index; - private NodeState _start = _index.getChildNode(START); + private NodeState _start = ((includeStart && !start.exists())?EMPTY_START_NODE:start); private NodeState current = _start; private boolean _includeStart = includeStart; diff --git a/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/OrderedContentMirrorStorageStrategyTest.java b/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/OrderedContentMirrorStorageStrategyTest.java index f0b3cd6..4b12176 100644 --- a/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/OrderedContentMirrorStorageStrategyTest.java +++ b/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/OrderedContentMirrorStorageStrategyTest.java @@ -333,8 +333,20 @@ public class OrderedContentMirrorStorageStrategyTest { * a brand new index. In this case :start doesn't exists but if * we ask for it we should return it. */ - @Ignore("easing the merge") @Test public void childNodeEntriesNewIndexWithStart(){ - Assert.fail("develop me"); + @Test public void childNodeEntriesNewIndexWithStart(){ + NodeState NODE_START = EmptyNodeState.EMPTY_NODE.builder().setProperty(NEXT, "").getNodeState(); + NodeBuilder index = EmptyNodeState.EMPTY_NODE.builder(); + OrderedContentMirrorStoreStrategy store = new OrderedContentMirrorStoreStrategy(); + + Iterator 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(NODE_START,child.getNodeState()); + assertFalse(children.hasNext()); } /** -- 1.8.3.4 (Apple Git-47) From 14686e56a3a440512a5c10d88f2246d5ed2f815d Mon Sep 17 00:00:00 2001 From: Davide Giannella Date: Wed, 12 Feb 2014 12:01:05 +0000 Subject: [PATCH 09/36] #OAK-1263 added coverage when changing an indexed property of an existing already indexed document --- .../OrderedContentMirrorStorageStrategyTest.java | 113 ++++++++++++++++++++- 1 file changed, 111 insertions(+), 2 deletions(-) diff --git a/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/OrderedContentMirrorStorageStrategyTest.java b/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/OrderedContentMirrorStorageStrategyTest.java index 4b12176..e6a3172 100644 --- a/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/OrderedContentMirrorStorageStrategyTest.java +++ b/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/OrderedContentMirrorStorageStrategyTest.java @@ -28,13 +28,13 @@ import static org.junit.Assert.assertTrue; import java.util.Iterator; import java.util.Set; +import junit.framework.Assert; + import org.apache.jackrabbit.oak.commons.PathUtils; import org.apache.jackrabbit.oak.plugins.memory.EmptyNodeState; 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.Assert; -import org.junit.Ignore; import org.junit.Test; import com.google.common.base.Strings; @@ -626,4 +626,113 @@ public class OrderedContentMirrorStorageStrategyTest { ); assertEquals("the :start node is expected",NODE_START,previous.getNodeState()); } + + /** + * test the use case where a document change the indexed property. For example document that change author. + * + *

it relies on the functionality of the store.update() for creating the index

+ * + * + * 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 } + * } + * } + * } + * + */ + @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); + node = index.getChildNode(N0).getChildNode(path[0]);//we know both the documents share the same /content + 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")); + } } -- 1.8.3.4 (Apple Git-47) From 76c69c1c819ff2fbe8b64e9099a40c1bcc8cb43f Mon Sep 17 00:00:00 2001 From: Davide Giannella Date: Wed, 12 Feb 2014 13:50:54 +0000 Subject: [PATCH 10/36] #OAK-1263 added coverage or removing the only document under the only key in index --- .../OrderedContentMirrorStorageStrategyTest.java | 61 +++++++++++++++++++++- 1 file changed, 59 insertions(+), 2 deletions(-) diff --git a/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/OrderedContentMirrorStorageStrategyTest.java b/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/OrderedContentMirrorStorageStrategyTest.java index e6a3172..c10cbe9 100644 --- a/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/OrderedContentMirrorStorageStrategyTest.java +++ b/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/OrderedContentMirrorStorageStrategyTest.java @@ -18,8 +18,7 @@ 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.apache.jackrabbit.oak.plugins.index.property.strategy.OrderedContentMirrorStoreStrategy.*; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; @@ -35,6 +34,7 @@ import org.apache.jackrabbit.oak.plugins.memory.EmptyNodeState; 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.Ignore; import org.junit.Test; import com.google.common.base.Strings; @@ -735,4 +735,61 @@ public class OrderedContentMirrorStorageStrategyTest { assertTrue(node.exists()); assertTrue(node.getBoolean("match")); } + + /** + * test when a document is deleted and is the only one under the indexed key + * + *

it relies on the functionality of the store.update() for creating the index

+ * + * + * Stage 1 + * ======= + * + * :index : { + * :start : { :next = n0 }, + * n0 : { + * :next = , + * sampledoc : { match = true } + * } + * } + * + * Stage 2 + * ======= + * + * :index : { + * :start : { :next = } + * } + * + */ + @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 + */ + @Ignore @Test public void deleteOneOfTheDocuments(){ + Assert.fail(); + } + + /** + * test when the only document is deleted from an indexed key but there're still some keys left in the index + */ + @Ignore @Test public void deleteTheOnlyDocumentInMultiKeysIndex(){ + Assert.fail(); + } } -- 1.8.3.4 (Apple Git-47) From 96d9a46501e6fbe9ea771783c11787191d36bd0a Mon Sep 17 00:00:00 2001 From: Davide Giannella Date: Wed, 12 Feb 2014 14:18:47 +0000 Subject: [PATCH 11/36] #OAK-1263 added coverage of removing a document from a key when other documents are left --- .../OrderedContentMirrorStorageStrategyTest.java | 53 +++++++++++++++++++++- 1 file changed, 51 insertions(+), 2 deletions(-) diff --git a/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/OrderedContentMirrorStorageStrategyTest.java b/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/OrderedContentMirrorStorageStrategyTest.java index c10cbe9..6d73756 100644 --- a/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/OrderedContentMirrorStorageStrategyTest.java +++ b/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/OrderedContentMirrorStorageStrategyTest.java @@ -781,9 +781,58 @@ public class OrderedContentMirrorStorageStrategyTest { /** * test when the document is deleted but there're still some documents left under the indexed key + * + *

it relies on the functionality of the store.update() for creating the index

+ * + * + * 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 } + * } + * } + * */ - @Ignore @Test public void deleteOneOfTheDocuments(){ - Assert.fail(); + @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")); } /** -- 1.8.3.4 (Apple Git-47) From bdebf623ea8b8d0ed8b52afd55ab17703230d4b4 Mon Sep 17 00:00:00 2001 From: Davide Giannella Date: Wed, 12 Feb 2014 16:01:20 +0000 Subject: [PATCH 12/36] #OAK-1263 added coverage of removing the only document when the are more than one item left in the index --- .../OrderedContentMirrorStorageStrategyTest.java | 99 ++++++++++++++++++++-- 1 file changed, 93 insertions(+), 6 deletions(-) diff --git a/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/OrderedContentMirrorStorageStrategyTest.java b/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/OrderedContentMirrorStorageStrategyTest.java index 6d73756..0bf31cf 100644 --- a/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/OrderedContentMirrorStorageStrategyTest.java +++ b/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/OrderedContentMirrorStorageStrategyTest.java @@ -18,7 +18,8 @@ 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.*; +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; @@ -27,14 +28,11 @@ import static org.junit.Assert.assertTrue; import java.util.Iterator; import java.util.Set; -import junit.framework.Assert; - import org.apache.jackrabbit.oak.commons.PathUtils; import org.apache.jackrabbit.oak.plugins.memory.EmptyNodeState; 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.Ignore; import org.junit.Test; import com.google.common.base.Strings; @@ -837,8 +835,97 @@ public class OrderedContentMirrorStorageStrategyTest { /** * test when the only document is deleted from an indexed key but there're still some keys left in the index + * + *

it relies on the functionality of the store.update() for creating the index

+ * + * + * 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 } + * } + * } + * } + * + * */ - @Ignore @Test public void deleteTheOnlyDocumentInMultiKeysIndex(){ - Assert.fail(); + @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")); } } -- 1.8.3.4 (Apple Git-47) From 697935b18bfee38bdbceca1396ea0743a8155916 Mon Sep 17 00:00:00 2001 From: Davide Giannella Date: Tue, 18 Feb 2014 15:47:46 +0000 Subject: [PATCH 13/36] #OAK-1263 initial refactoring for hosting the polymorphism used for the Ordered aspect of a ContentStoreStrategy --- .../oak/plugins/index/property/OrderedIndex.java | 25 ++++++++++++ .../index/property/OrderedPropertyIndex.java | 36 ++++++++++++++++++ .../OrderedPropertyIndexEditorProvider.java | 3 +- .../index/property/OrderedPropertyIndexLookup.java | 39 +++++++++++++++++++ .../property/OrderedPropertyIndexProvider.java | 44 ++++++++++++++++++++++ .../oak/plugins/index/property/PropertyIndex.java | 15 ++++++-- .../index/property/PropertyIndexLookup.java | 2 +- 7 files changed, 158 insertions(+), 6 deletions(-) create mode 100644 oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedIndex.java create mode 100644 oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndex.java create mode 100644 oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexLookup.java create mode 100644 oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexProvider.java diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedIndex.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedIndex.java new file mode 100644 index 0000000..19870f0 --- /dev/null +++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedIndex.java @@ -0,0 +1,25 @@ +/* + * 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; + +/** + * interface for shared constants around different actors: QueryIndex, IndexEditors, IndexEditorProviders, ... + */ +public interface OrderedIndex { + public final static String TYPE = "ordered"; +} diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndex.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndex.java new file mode 100644 index 0000000..9d9a4e4 --- /dev/null +++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndex.java @@ -0,0 +1,36 @@ +/* + * 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 org.apache.jackrabbit.oak.spi.state.NodeState; + +/** + * + */ +public class OrderedPropertyIndex extends PropertyIndex implements OrderedIndex { + + @Override + public String getIndexName() { + return TYPE; + } + + @Override + PropertyIndexLookup getLookup(NodeState root) { + return new OrderedPropertyIndexLookup(root); + } +} diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexEditorProvider.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexEditorProvider.java index 43663c9..27f55d7 100644 --- a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexEditorProvider.java +++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexEditorProvider.java @@ -10,8 +10,7 @@ import org.apache.jackrabbit.oak.spi.commit.Editor; import org.apache.jackrabbit.oak.spi.state.NodeBuilder; import org.apache.jackrabbit.oak.spi.state.NodeState; -public class OrderedPropertyIndexEditorProvider implements IndexEditorProvider { - public final static String TYPE = "ordered"; +public class OrderedPropertyIndexEditorProvider implements IndexEditorProvider, OrderedIndex { @Override @CheckForNull diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexLookup.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexLookup.java new file mode 100644 index 0000000..ff63de9 --- /dev/null +++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexLookup.java @@ -0,0 +1,39 @@ +/* + * 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 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.state.NodeState; + +/** + * + */ +public class OrderedPropertyIndexLookup extends PropertyIndexLookup { + + private final static IndexStoreStrategy STORE = new OrderedContentMirrorStoreStrategy(); + + public OrderedPropertyIndexLookup(NodeState root) { + super(root); + } + + @Override + IndexStoreStrategy getStrategy(NodeState indexMeta) { + return STORE; + } +} diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexProvider.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexProvider.java new file mode 100644 index 0000000..15daaed --- /dev/null +++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexProvider.java @@ -0,0 +1,44 @@ +/* + * 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 javax.annotation.Nonnull; + +import org.apache.felix.scr.annotations.Component; +import org.apache.felix.scr.annotations.Service; +import org.apache.jackrabbit.oak.spi.query.QueryIndex; +import org.apache.jackrabbit.oak.spi.query.QueryIndexProvider; +import org.apache.jackrabbit.oak.spi.state.NodeState; + +import com.google.common.collect.ImmutableList; + +@Component +@Service(QueryIndexProvider.class) +public class OrderedPropertyIndexProvider implements QueryIndexProvider { + + /* (non-Javadoc) + * @see org.apache.jackrabbit.oak.spi.query.QueryIndexProvider#getQueryIndexes(org.apache.jackrabbit.oak.spi.state.NodeState) + */ + @Override + @Nonnull + public List getQueryIndexes(NodeState nodeState) { + return ImmutableList.of(new OrderedPropertyIndex()); + } +} diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndex.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndex.java index e7c19c0..dfe7bc8 100644 --- a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndex.java +++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndex.java @@ -120,6 +120,15 @@ class PropertyIndex implements QueryIndex { return "property"; } + /** + * return the proper implementaion of the Lookup + * @param root + * @return + */ + PropertyIndexLookup getLookup(NodeState root){ + return new PropertyIndexLookup(root); + } + @Override public double getCost(Filter filter, NodeState root) { if (filter.getFullTextConstraint() != null) { @@ -127,7 +136,7 @@ class PropertyIndex implements QueryIndex { return Double.POSITIVE_INFINITY; } - PropertyIndexLookup lookup = new PropertyIndexLookup(root); + PropertyIndexLookup lookup = getLookup(root); for (PropertyRestriction pr : filter.getPropertyRestrictions()) { String propertyName = PathUtils.getName(pr.propertyName); // TODO support indexes on a path @@ -157,7 +166,7 @@ class PropertyIndex implements QueryIndex { public Cursor query(Filter filter, NodeState root) { Iterable paths = null; - PropertyIndexLookup lookup = new PropertyIndexLookup(root); + PropertyIndexLookup lookup = getLookup(root); int depth = 1; for (PropertyRestriction pr : filter.getPropertyRestrictions()) { String propertyName = PathUtils.getName(pr.propertyName); @@ -202,7 +211,7 @@ class PropertyIndex implements QueryIndex { public String getPlan(Filter filter, NodeState root) { StringBuilder buff = new StringBuilder("property"); StringBuilder notIndexed = new StringBuilder(); - PropertyIndexLookup lookup = new PropertyIndexLookup(root); + PropertyIndexLookup lookup = getLookup(root); for (PropertyRestriction pr : filter.getPropertyRestrictions()) { String propertyName = PathUtils.getName(pr.propertyName); // TODO support indexes on a path diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexLookup.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexLookup.java index 271e101..eebcea7 100644 --- a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexLookup.java +++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexLookup.java @@ -118,7 +118,7 @@ public class PropertyIndexLookup { return getStrategy(indexMeta).query(filter, propertyName, indexMeta, encode(value)); } - private static IndexStoreStrategy getStrategy(NodeState indexMeta) { + IndexStoreStrategy getStrategy(NodeState indexMeta) { if (indexMeta.getBoolean(IndexConstants.UNIQUE_PROPERTY_NAME)) { return UNIQUE; } -- 1.8.3.4 (Apple Git-47) From 1104d2be3528bc8825157fd040adbae7e1caa825 Mon Sep 17 00:00:00 2001 From: Davide Giannella Date: Wed, 19 Feb 2014 18:41:36 +0100 Subject: [PATCH 14/36] #OAK-1263 initial set-up of testing around query --- .../property/OrderedPropertyIndexQueryTest.java | 240 +++++++++++++++++++++ 1 file changed, 240 insertions(+) create mode 100644 oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexQueryTest.java diff --git a/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexQueryTest.java b/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexQueryTest.java new file mode 100644 index 0000000..275c7ae --- /dev/null +++ b/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexQueryTest.java @@ -0,0 +1,240 @@ +/* + * 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.*; + +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.jcr.RepositoryException; + +import net.sf.cglib.proxy.ProxyRefDispatcher; + +import org.apache.jackrabbit.commons.webdav.NodeTypeConstants; +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.PropertyState; +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 static org.apache.jackrabbit.oak.plugins.nodetype.NodeTypeConstants.*; + +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.spi.query.PropertyValues; +import org.apache.jackrabbit.oak.spi.security.OpenSecurityProvider; +import org.apache.jackrabbit.oak.util.NodeUtil; +import org.h2.index.IndexCondition; +import org.junit.Test; + +import com.google.common.collect.ImmutableMap; + +public class OrderedPropertyIndexQueryTest extends AbstractQueryTest { + public final static String ORDERED_PROPERTY = "foo"; + + private class ValuePathTuple implements Comparable{ + 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 + 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; + } + + } + + @Test public void valuePathTupleComparison(){ + try{ + new ValuePathTuple("value", "path").compareTo(null); + fail("It should have raisen a NPE"); + }catch(NullPointerException e){ + //so far so good + } + assertEquals(0,(new ValuePathTuple("value", "path")).compareTo(new ValuePathTuple("value", "path"))); + assertEquals(-1,(new ValuePathTuple("value", "path")).compareTo(new ValuePathTuple("value1", "path"))); + assertEquals(-1,(new ValuePathTuple("value1", "path")).compareTo(new ValuePathTuple("value1", "path1"))); + assertEquals(1,(new ValuePathTuple("value1", "path")).compareTo(new ValuePathTuple("value", "path"))); + assertEquals(1,(new ValuePathTuple("value1", "path1")).compareTo(new ValuePathTuple("value1", "path"))); + + assertEquals(-1,(new ValuePathTuple("value000", "/test/n1")).compareTo(new ValuePathTuple("value001", "/test/n0"))); + assertEquals(1,(new ValuePathTuple("value001", "/test/n0")).compareTo(new ValuePathTuple("value000", "/test/n1"))); +} + + @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 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 + * @param amount + * @return + */ + private List generateOrderedValues(int amount){ + if(amount>1000) throw new RuntimeException("amount cannot be greater than 100"); + + List values = new ArrayList(amount); + NumberFormat nf = new DecimalFormat("000"); + for(int i=0;i addChildNodes(final List values, final Tree father){ + List nodes = new ArrayList(); + Random rnd = new Random(); + int counter = 0; + while(values.size()>0){ + 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; + } + + @Test public void query() throws CommitFailedException, ParseException, RepositoryException{ + setTravesalEnabled(false); + + Tree tree = root.getTree("/"); + + IndexUtils.createIndexDefinition( + new NodeUtil(tree.getChild(IndexConstants.INDEX_DEFINITIONS_NAME)), + "indextest", + false, + new String[] { ORDERED_PROPERTY }, + null + ); + root.commit(); + + Tree test = tree.addChild("test"); + List nodes = addChildNodes(generateOrderedValues(10),test); + for(ValuePathTuple n : nodes){ + System.out.println(String.format("%s - %s",n.value,n.path)); + } + + +//// Tree forth = child(test,"d",ORDERED_PROPERTY,ORDERED_VALUES[3]); +// Tree second = child(test,"b",ORDERED_PROPERTY,ORDERED_VALUES[1]); +// root.commit(); +//// Tree third = child(test,"c",ORDERED_PROPERTY,ORDERED_VALUES[2]); +// Tree first = child(test,"a",ORDERED_PROPERTY,ORDERED_VALUES[0]); +// root.commit(); +// +// //querying +// Iterator results; +// results = executeQuery( +// String.format("SELECT * from [%s] WHERE foo IS NOT NULL", NT_UNSTRUCTURED , ORDERED_PROPERTY), +//// String.format("SELECT * from [%s] WHERE foo IS NOT NULL ORDER BY %s", NT_UNSTRUCTURED , ORDERED_PROPERTY), +// SQL2, +// null +// ).getRows().iterator(); +// assertTrue("two elements expected",results.hasNext()); +// System.out.println(first.getPath()); +// assertEquals("Wrong item found. Order not respected.",first.getPath(),results.next().getPath()); + + + setTravesalEnabled(true); + } +} -- 1.8.3.4 (Apple Git-47) From fd48c9091ab9b1497186d654737615046785f7a8 Mon Sep 17 00:00:00 2001 From: Davide Giannella Date: Wed, 19 Feb 2014 22:41:14 +0100 Subject: [PATCH 15/36] #OAK-1263 assessed the the PropertyIndex returns a non-ordered set --- .../property/OrderedPropertyIndexQueryTest.java | 71 ++++++++++++++-------- 1 file changed, 46 insertions(+), 25 deletions(-) diff --git a/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexQueryTest.java b/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexQueryTest.java index 275c7ae..7de4bae 100644 --- a/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexQueryTest.java +++ b/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexQueryTest.java @@ -28,6 +28,7 @@ import java.util.List; import java.util.Map; import java.util.Random; +import javax.annotation.Nonnull; import javax.jcr.RepositoryException; import net.sf.cglib.proxy.ProxyRefDispatcher; @@ -59,6 +60,12 @@ import com.google.common.collect.ImmutableMap; public class OrderedPropertyIndexQueryTest extends AbstractQueryTest { public final static String ORDERED_PROPERTY = "foo"; + /** + * convenience orderable object that represet 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{ private String value; private String path; @@ -120,7 +127,10 @@ public class OrderedPropertyIndexQueryTest extends AbstractQueryTest { } } - + + /** + * testing for asserting the right comparison behaviour of the custom class + */ @Test public void valuePathTupleComparison(){ try{ new ValuePathTuple("value", "path").compareTo(null); @@ -136,7 +146,7 @@ public class OrderedPropertyIndexQueryTest extends AbstractQueryTest { assertEquals(-1,(new ValuePathTuple("value000", "/test/n1")).compareTo(new ValuePathTuple("value001", "/test/n0"))); assertEquals(1,(new ValuePathTuple("value001", "/test/n0")).compareTo(new ValuePathTuple("value000", "/test/n1"))); -} + } @Override protected ContentRepository createRepository() { @@ -167,6 +177,7 @@ public class OrderedPropertyIndexQueryTest extends AbstractQueryTest { /** * generate a list of values to be used as ordered set + * * @param amount * @return */ @@ -180,6 +191,14 @@ public class OrderedPropertyIndexQueryTest extends AbstractQueryTest { 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 ndoes + * @return + */ private List addChildNodes(final List values, final Tree father){ List nodes = new ArrayList(); Random rnd = new Random(); @@ -210,31 +229,33 @@ public class OrderedPropertyIndexQueryTest extends AbstractQueryTest { Tree test = tree.addChild("test"); List nodes = addChildNodes(generateOrderedValues(10),test); - for(ValuePathTuple n : nodes){ - System.out.println(String.format("%s - %s",n.value,n.path)); - } - - -//// Tree forth = child(test,"d",ORDERED_PROPERTY,ORDERED_VALUES[3]); -// Tree second = child(test,"b",ORDERED_PROPERTY,ORDERED_VALUES[1]); -// root.commit(); -//// Tree third = child(test,"c",ORDERED_PROPERTY,ORDERED_VALUES[2]); -// Tree first = child(test,"a",ORDERED_PROPERTY,ORDERED_VALUES[0]); -// root.commit(); -// -// //querying -// Iterator results; -// results = executeQuery( -// String.format("SELECT * from [%s] WHERE foo IS NOT NULL", NT_UNSTRUCTURED , ORDERED_PROPERTY), -//// String.format("SELECT * from [%s] WHERE foo IS NOT NULL ORDER BY %s", NT_UNSTRUCTURED , ORDERED_PROPERTY), -// SQL2, -// null -// ).getRows().iterator(); -// assertTrue("two elements expected",results.hasNext()); -// System.out.println(first.getPath()); -// assertEquals("Wrong item found. Order not respected.",first.getPath(),results.next().getPath()); + root.commit(); + //querying + Iterator results; + results = executeQuery( + String.format("SELECT * from [%s] WHERE foo IS NOT NULL", NT_UNSTRUCTURED , ORDERED_PROPERTY), +// String.format("SELECT * from [%s] WHERE foo IS NOT NULL ORDER BY %s", NT_UNSTRUCTURED , ORDERED_PROPERTY), + SQL2, + null + ).getRows().iterator(); + assertRightOrder(nodes, results); setTravesalEnabled(true); } + + /** + * assert the right order of the returned resultset + * + * @param orderedSequence the right order in which the resultset should be returned + * @param resultset the resultes + */ + private void assertRightOrder(@Nonnull final List orderedSequence, @Nonnull final Iterator resultset){ + assertTrue(resultset.hasNext()); + int counter = 0; + while(resultset.hasNext() && counter < orderedSequence.size()){ + ResultRow row = resultset.next(); + assertEquals(orderedSequence.get(counter++).path,row.getPath()); + } + } } -- 1.8.3.4 (Apple Git-47) From b949c6732bd3bd65e2fb4abf616b158234550609 Mon Sep 17 00:00:00 2001 From: Davide Giannella Date: Wed, 19 Feb 2014 23:01:17 +0100 Subject: [PATCH 16/36] #OAK-1263 overload of method for allowing the creation of a specific PropertyIndex type --- .../jackrabbit/oak/plugins/index/IndexUtils.java | 190 ++++++++++++--------- 1 file changed, 105 insertions(+), 85 deletions(-) diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/IndexUtils.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/IndexUtils.java index 7c4e19f..ec920c1 100644 --- a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/IndexUtils.java +++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/IndexUtils.java @@ -49,95 +49,115 @@ import org.apache.jackrabbit.oak.util.NodeUtil; */ public class IndexUtils { - public static NodeBuilder getOrCreateOakIndex(NodeBuilder root) { - NodeBuilder index; - if (!root.hasChildNode(INDEX_DEFINITIONS_NAME)) { - index = root.child(INDEX_DEFINITIONS_NAME); - // TODO: use property node type name - index.setProperty(JCR_PRIMARYTYPE, NT_UNSTRUCTURED, NAME); - } else { - index = root.child(INDEX_DEFINITIONS_NAME); - } - return index; - } + public static NodeBuilder getOrCreateOakIndex(NodeBuilder root) { + NodeBuilder index; + if (!root.hasChildNode(INDEX_DEFINITIONS_NAME)) { + index = root.child(INDEX_DEFINITIONS_NAME); + // TODO: use property node type name + index.setProperty(JCR_PRIMARYTYPE, NT_UNSTRUCTURED, NAME); + } else { + index = root.child(INDEX_DEFINITIONS_NAME); + } + return index; + } - /** - * Create a new property index definition below the given {@code indexNode}. - * - * @param index The oak:index node builder - * @param indexDefName The name of the new property index. - * @param reindex {@code true} if the the reindex flag should be turned on. - * @param unique {@code true} if the index is expected the assert property - * uniqueness. - * @param propertyNames The property names that should be indexed. - * @param declaringNodeTypeNames The declaring node type names or {@code null}. - * @return the NodeBuilder of the new index definition. - */ - public static NodeBuilder createIndexDefinition(@Nonnull NodeBuilder index, - @Nonnull String indexDefName, - boolean reindex, - boolean unique, - @Nonnull Collection propertyNames, - @Nullable Collection declaringNodeTypeNames) { - NodeBuilder entry = index.child(indexDefName) - .setProperty(JCR_PRIMARYTYPE, INDEX_DEFINITIONS_NODE_TYPE, NAME) - .setProperty(TYPE_PROPERTY_NAME, PropertyIndexEditorProvider.TYPE) - .setProperty(REINDEX_PROPERTY_NAME, reindex); - if (unique) { - entry.setProperty(UNIQUE_PROPERTY_NAME, unique); - } - entry.setProperty(PropertyStates.createProperty(PROPERTY_NAMES, propertyNames, NAMES)); - if (declaringNodeTypeNames != null && !declaringNodeTypeNames.isEmpty()) { - entry.setProperty(PropertyStates.createProperty(DECLARING_NODE_TYPES, declaringNodeTypeNames, NAMES)); - } - return entry; - } + /** + * Create a new property index definition below the given {@code indexNode}. + * + * @param index The oak:index node builder + * @param indexDefName The name of the new property index. + * @param reindex {@code true} if the the reindex flag should be turned on. + * @param unique {@code true} if the index is expected the assert property + * uniqueness. + * @param propertyNames The property names that should be indexed. + * @param declaringNodeTypeNames The declaring node type names or {@code null}. + * @return the NodeBuilder of the new index definition. + */ + public static NodeBuilder createIndexDefinition(@Nonnull NodeBuilder index, + @Nonnull String indexDefName, + boolean reindex, + boolean unique, + @Nonnull Collection propertyNames, + @Nullable Collection declaringNodeTypeNames) { + NodeBuilder entry = index.child(indexDefName) + .setProperty(JCR_PRIMARYTYPE, INDEX_DEFINITIONS_NODE_TYPE, NAME) + .setProperty(TYPE_PROPERTY_NAME, PropertyIndexEditorProvider.TYPE) + .setProperty(REINDEX_PROPERTY_NAME, reindex); + if (unique) { + entry.setProperty(UNIQUE_PROPERTY_NAME, unique); + } + entry.setProperty(PropertyStates.createProperty(PROPERTY_NAMES, propertyNames, NAMES)); + if (declaringNodeTypeNames != null && !declaringNodeTypeNames.isEmpty()) { + entry.setProperty(PropertyStates.createProperty(DECLARING_NODE_TYPES, declaringNodeTypeNames, NAMES)); + } + return entry; + } - /** - * Create a new property2 index definition below the given {@code indexNode}. - * - * @param indexNode - * @param indexDefName - * @param unique - * @param propertyNames - * @param declaringNodeTypeNames - */ - public static void createIndexDefinition(@Nonnull NodeUtil indexNode, - @Nonnull String indexDefName, - boolean unique, - @Nonnull String[] propertyNames, - @Nullable String[] declaringNodeTypeNames) throws RepositoryException { - NodeUtil entry = indexNode.getOrAddChild(indexDefName, INDEX_DEFINITIONS_NODE_TYPE); - entry.setString(TYPE_PROPERTY_NAME, PropertyIndexEditorProvider.TYPE); - 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); - } + /** + * Create a new property2 index definition below the given {@code indexNode}. + * + * @param indexNode + * @param indexDefName + * @param unique + * @param propertyNames + * @param declaringNodeTypeNames + */ + public static void createIndexDefinition(@Nonnull NodeUtil indexNode, + @Nonnull String indexDefName, + boolean unique, + @Nonnull String[] propertyNames, + @Nullable String[] declaringNodeTypeNames) throws RepositoryException { + + createIndexDefinition(indexNode, indexDefName, unique, propertyNames, declaringNodeTypeNames, PropertyIndexEditorProvider.TYPE); + } - public static void createReferenceIndex(@Nonnull NodeBuilder index) { - index.child(NodeReferenceConstants.NAME) - .setProperty(JCR_PRIMARYTYPE, INDEX_DEFINITIONS_NODE_TYPE, NAME) - .setProperty(TYPE_PROPERTY_NAME, NodeReferenceConstants.TYPE); - } + /** + * 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 + * @throws RepositoryException + */ + public static void createIndexDefinition(@Nonnull NodeUtil indexNode, + @Nonnull String indexDefName, + boolean unique, + @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); + } - public static boolean isIndexNodeType(NodeState state) { - PropertyState ps = state.getProperty(JCR_PRIMARYTYPE); - return ps != null - && ps.getValue(STRING).equals(INDEX_DEFINITIONS_NODE_TYPE); - } + public static void createReferenceIndex(@Nonnull NodeBuilder index) { + index.child(NodeReferenceConstants.NAME) + .setProperty(JCR_PRIMARYTYPE, INDEX_DEFINITIONS_NODE_TYPE, NAME) + .setProperty(TYPE_PROPERTY_NAME, NodeReferenceConstants.TYPE); + } - public static boolean isIndexNodeType(NodeState state, String typeIn) { - if (!isIndexNodeType(state)) { - return false; - } - PropertyState type = state.getProperty(TYPE_PROPERTY_NAME); - return type != null && !type.isArray() - && type.getValue(Type.STRING).equals(typeIn); - } + public static boolean isIndexNodeType(NodeState state) { + PropertyState ps = state.getProperty(JCR_PRIMARYTYPE); + return ps != null + && ps.getValue(STRING).equals(INDEX_DEFINITIONS_NODE_TYPE); + } + + public static boolean isIndexNodeType(NodeState state, String typeIn) { + if (!isIndexNodeType(state)) { + return false; + } + PropertyState type = state.getProperty(TYPE_PROPERTY_NAME); + return type != null && !type.isArray() + && type.getValue(Type.STRING).equals(typeIn); + } } -- 1.8.3.4 (Apple Git-47) From 1da3236dfac80ff991ccaf2cce560a0998a4a2f6 Mon Sep 17 00:00:00 2001 From: Davide Giannella Date: Wed, 19 Feb 2014 23:33:54 +0100 Subject: [PATCH 17/36] #OAK-1263 started implementing/debugging the OrderedIndexLookup --- .../property/OrderedPropertyIndexQueryTest.java | 36 +++++++++++++++++----- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexQueryTest.java b/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexQueryTest.java index 7de4bae..84dbccf 100644 --- a/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexQueryTest.java +++ b/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexQueryTest.java @@ -53,12 +53,26 @@ 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.h2.index.IndexCondition; +import org.junit.Ignore; import org.junit.Test; import com.google.common.collect.ImmutableMap; public class OrderedPropertyIndexQueryTest extends AbstractQueryTest { + /** + * the property used by the index + */ public final static 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 final static int NUMBER_OF_NODES = 50; /** * convenience orderable object that represet a tuple of values and paths @@ -216,29 +230,34 @@ public class OrderedPropertyIndexQueryTest extends AbstractQueryTest { @Test public void query() throws CommitFailedException, ParseException, RepositoryException{ setTravesalEnabled(false); - Tree tree = root.getTree("/"); + Tree rTree = root.getTree("/"); IndexUtils.createIndexDefinition( - new NodeUtil(tree.getChild(IndexConstants.INDEX_DEFINITIONS_NAME)), + new NodeUtil(rTree.getChild(IndexConstants.INDEX_DEFINITIONS_NAME)), "indextest", false, new String[] { ORDERED_PROPERTY }, - null + null, + OrderedIndex.TYPE ); root.commit(); - Tree test = tree.addChild("test"); - List nodes = addChildNodes(generateOrderedValues(10),test); + Tree test = rTree.addChild("test"); + List nodes = addChildNodes(generateOrderedValues(NUMBER_OF_NODES),test); root.commit(); + System.out.println(root.getTree("/oak:index/indextest")); + //querying Iterator results; results = executeQuery( - String.format("SELECT * from [%s] WHERE foo IS NOT NULL", NT_UNSTRUCTURED , ORDERED_PROPERTY), + String.format("SELECT * from [%s] WHERE foo IS NOT NULL", NT_UNSTRUCTURED), +// String.format("EXPLAIN SELECT * from [%s] WHERE foo IS NOT NULL", NT_UNSTRUCTURED), // String.format("SELECT * from [%s] WHERE foo IS NOT NULL ORDER BY %s", NT_UNSTRUCTURED , ORDERED_PROPERTY), SQL2, null ).getRows().iterator(); +// System.out.println(results.next()); assertRightOrder(nodes, results); setTravesalEnabled(true); @@ -251,11 +270,12 @@ public class OrderedPropertyIndexQueryTest extends AbstractQueryTest { * @param resultset the resultes */ private void assertRightOrder(@Nonnull final List orderedSequence, @Nonnull final Iterator resultset){ - assertTrue(resultset.hasNext()); + assertTrue("No results returned", resultset.hasNext()); int counter = 0; while(resultset.hasNext() && counter < orderedSequence.size()){ ResultRow row = resultset.next(); - assertEquals(orderedSequence.get(counter++).path,row.getPath()); + assertEquals(String.format("Wrong path at the element '%d'",counter), orderedSequence.get(counter).path,row.getPath()); + counter++; } } } -- 1.8.3.4 (Apple Git-47) From d830eabef988b9d3026d2916c281161dfd243e43 Mon Sep 17 00:00:00 2001 From: Davide Giannella Date: Thu, 20 Feb 2014 12:07:50 +0100 Subject: [PATCH 18/36] OAK-1263 implemented query the whole index --- .../index/property/OrderedPropertyIndexLookup.java | 5 + .../index/property/PropertyIndexLookup.java | 13 +- .../strategy/ContentMirrorStoreStrategy.java | 696 +++++++++++---------- .../property/OrderedPropertyIndexQueryTest.java | 89 +-- 4 files changed, 411 insertions(+), 392 deletions(-) diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexLookup.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexLookup.java index ff63de9..6b71f3d 100644 --- a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexLookup.java +++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexLookup.java @@ -36,4 +36,9 @@ public class OrderedPropertyIndexLookup extends PropertyIndexLookup { IndexStoreStrategy getStrategy(NodeState indexMeta) { return STORE; } + + @Override + String getType() { + return OrderedIndex.TYPE; + } } diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexLookup.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexLookup.java index eebcea7..4915962 100644 --- a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexLookup.java +++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexLookup.java @@ -146,7 +146,7 @@ public class PropertyIndexLookup { * node was found */ @Nullable - private static NodeState getIndexNode( + private NodeState getIndexNode( NodeState node, String propertyName, Filter filter) { // keep a fallback to a matching index def that has *no* node type constraints // (initially, there is no fallback) @@ -156,7 +156,7 @@ public class PropertyIndexLookup { for (ChildNodeEntry entry : state.getChildNodeEntries()) { NodeState index = entry.getNodeState(); PropertyState type = index.getProperty(TYPE_PROPERTY_NAME); - if (type == null || type.isArray() || !TYPE.equals(type.getValue(Type.STRING))) { + if (type == null || type.isArray() || !getType().equals(type.getValue(Type.STRING))) { continue; } if (contains(index.getNames(PROPERTY_NAMES), propertyName)) { @@ -185,6 +185,15 @@ public class PropertyIndexLookup { return fallback; } + /** + * retrieve the type of the index + * + * @return + */ + String getType(){ + return TYPE; + } + private static Set getSuperTypes(Filter filter) { if (filter != null && !filter.matchesAllTypes()) { return filter.getSupertypes(); diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/ContentMirrorStoreStrategy.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/ContentMirrorStoreStrategy.java index 0c58585..95b640c 100644 --- a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/ContentMirrorStoreStrategy.java +++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/ContentMirrorStoreStrategy.java @@ -67,366 +67,370 @@ import com.google.common.collect.Sets; */ public class ContentMirrorStoreStrategy implements IndexStoreStrategy { - static final Logger LOG = LoggerFactory.getLogger(ContentMirrorStoreStrategy.class); - - @Override - public void update( - NodeBuilder index, String path, - Set beforeKeys, Set afterKeys) { - for (String key : beforeKeys) { - remove(index, key, path); - } - for (String key : afterKeys) { - insert(index, key, path); - } - } - - private void remove(NodeBuilder index, String key, String value) { - NodeBuilder builder = index.getChildNode(key); - if (builder.exists()) { - // Collect all builders along the given path - Deque builders = newArrayDeque(); + static final Logger LOG = LoggerFactory.getLogger(ContentMirrorStoreStrategy.class); + + @Override + public void update( + NodeBuilder index, String path, + Set beforeKeys, Set afterKeys) { + for (String key : beforeKeys) { + remove(index, key, path); + } + for (String key : afterKeys) { + insert(index, key, path); + } + } + + private void remove(NodeBuilder index, String key, String value) { + NodeBuilder builder = index.getChildNode(key); + if (builder.exists()) { + // Collect all builders along the given path + Deque builders = newArrayDeque(); + builders.addFirst(builder); + + // Descend to the correct location in the index tree + for (String name : PathUtils.elements(value)) { + builder = builder.getChildNode(name); builders.addFirst(builder); + } - // Descend to the correct location in the index tree - for (String name : PathUtils.elements(value)) { - builder = builder.getChildNode(name); - builders.addFirst(builder); - } + // Drop the match value, if present + if (builder.exists()) { + builder.removeProperty("match"); + } - // Drop the match value, if present - if (builder.exists()) { - builder.removeProperty("match"); - } + // Prune all index nodes that are no longer needed + prune(index, builders); + } + } - // Prune all index nodes that are no longer needed - prune(index, builders); - } - } - - /** - * Physically prune a list of nodes from the index - * - * @param index the current index - * @param builders list of nodes to prune - */ - void prune(final NodeBuilder index, final Deque builders){ - for (NodeBuilder node : builders) { - if (node.getBoolean("match") || node.getChildNodeCount(1) > 0) { - return; - } else if (node.exists()) { - node.remove(); - } + /** + * Physically prune a list of nodes from the index + * + * @param index the current index + * @param builders list of nodes to prune + */ + void prune(final NodeBuilder index, final Deque builders){ + for (NodeBuilder node : builders) { + if (node.getBoolean("match") || node.getChildNodeCount(1) > 0) { + return; + } else if (node.exists()) { + node.remove(); + } } - } - - private void insert(NodeBuilder index, String key, String value) { -// NodeBuilder builder = index.child(key); - NodeBuilder builder = fetchKeyNode(index, key); - for (String name : PathUtils.elements(value)) { - builder = builder.child(name); - } - builder.setProperty("match", true); - } - - /** - * fetch from the index the key node - * - * @param index the current index root - * @param key the 'key' to fetch from the repo - * @return the node representing the key - */ - NodeBuilder fetchKeyNode(@Nonnull NodeBuilder index,@Nonnull String key){ - return index.child(key); - } - - public Iterable query(final Filter filter, final String indexName, - final NodeState indexMeta, final String indexStorageNodeName, - final Iterable values) { - final NodeState index = indexMeta.getChildNode(indexStorageNodeName); - return new Iterable() { - @Override - public Iterator iterator() { - PathIterator it = new PathIterator(filter, indexName); - if (values == null) { - it.setPathContainsValue(true); - it.enqueue(index.getChildNodeEntries().iterator()); - } else { - for (String p : values) { - NodeState property = index.getChildNode(p); - if (property.exists()) { - // we have an entry for this value, so use it - it.enqueue(Iterators.singletonIterator( - new MemoryChildNodeEntry("", property))); - } - } - } - return it; - } - }; - } - - @Override - public Iterable query(final Filter filter, final String indexName, - final NodeState indexMeta, final Iterable values) { - return query(filter, indexName, indexMeta, INDEX_CONTENT_NODE_NAME, values); - } - - @Override - public long count(NodeState indexMeta, Set values, int max) { - return count(indexMeta, INDEX_CONTENT_NODE_NAME, values, max); - } - - public long count(NodeState indexMeta, final String indexStorageNodeName, - Set values, int max) { - NodeState index = indexMeta.getChildNode(indexStorageNodeName); - int count = 0; - if (values == null) { - PropertyState ec = indexMeta.getProperty(ENTRY_COUNT_PROPERTY_NAME); - if (ec != null) { - return ec.getValue(Type.LONG); - } - CountingNodeVisitor v = new CountingNodeVisitor(max); - v.visit(index); - count = v.getEstimatedCount(); - // "is not null" queries typically read more data - count *= 10; - } else { - int size = values.size(); - if (size == 0) { - return 0; - } - max = Math.max(10, max / size); - int i = 0; - for (String p : values) { - if (count > max && i > 3) { - // the total count is extrapolated from the the number - // of values counted so far to the total number of values - count = count * size / i; - break; - } - NodeState s = index.getChildNode(p); - if (s.exists()) { - CountingNodeVisitor v = new CountingNodeVisitor(max); - v.visit(s); - count += v.getEstimatedCount(); - } - i++; - } - } - return count; - } - - /** - * An iterator over paths within an index node. - */ - static class PathIterator implements Iterator { - - private final Filter filter; - private final String indexName; - private final Deque> nodeIterators = - Queues.newArrayDeque(); - private int readCount; - private boolean init; - private boolean closed; - private String parentPath; - private String currentPath; - private boolean pathContainsValue; - - /** - * Keep the returned path, to avoid returning duplicate entries. - */ - private final Set knownPaths = Sets.newHashSet(); - - PathIterator(Filter filter, String indexName) { - this.filter = filter; - this.indexName = indexName; - parentPath = ""; - currentPath = "/"; - } - - void enqueue(Iterator it) { - nodeIterators.addLast(it); - } - - void setPathContainsValue(boolean pathContainsValue) { - if (init) { - throw new IllegalStateException("This iterator is already initialized"); - } - this.pathContainsValue = pathContainsValue; - } - - @Override - public boolean hasNext() { - if (!closed && !init) { - fetchNext(); - init = true; + } + + private void insert(NodeBuilder index, String key, String value) { + // NodeBuilder builder = index.child(key); + NodeBuilder builder = fetchKeyNode(index, key); + for (String name : PathUtils.elements(value)) { + builder = builder.child(name); + } + builder.setProperty("match", true); + } + + /** + * fetch from the index the key node + * + * @param index the current index root + * @param key the 'key' to fetch from the repo + * @return the node representing the key + */ + NodeBuilder fetchKeyNode(@Nonnull NodeBuilder index,@Nonnull String key){ + return index.child(key); + } + + public Iterable query(final Filter filter, final String indexName, + final NodeState indexMeta, final String indexStorageNodeName, + final Iterable values) { + final NodeState index = indexMeta.getChildNode(indexStorageNodeName); + return new Iterable() { + @Override + public Iterator iterator() { + PathIterator it = new PathIterator(filter, indexName); + if (values == null) { + it.setPathContainsValue(true); + it.enqueue(getChildNodeEntries(index).iterator()); + } else { + for (String p : values) { + NodeState property = index.getChildNode(p); + if (property.exists()) { + // we have an entry for this value, so use it + it.enqueue(Iterators.singletonIterator( + new MemoryChildNodeEntry("", property))); + } + } } - return !closed; - } - - private void fetchNext() { - while (true) { - fetchNextPossiblyDuplicate(); - if (closed) { - return; - } - if (pathContainsValue) { - String value = PathUtils.elements(currentPath).iterator().next(); - currentPath = PathUtils.relativize(value, currentPath); - // don't return duplicate paths: - // Set.add returns true if the entry was new, - // so if it returns false, it was already known - if (!knownPaths.add(currentPath)) { - continue; - } - } - break; + return it; + } + }; + } + + @Nonnull Iterable getChildNodeEntries(@Nonnull final NodeState index){ + return index.getChildNodeEntries(); + } + + @Override + public Iterable query(final Filter filter, final String indexName, + final NodeState indexMeta, final Iterable values) { + return query(filter, indexName, indexMeta, INDEX_CONTENT_NODE_NAME, values); + } + + @Override + public long count(NodeState indexMeta, Set values, int max) { + return count(indexMeta, INDEX_CONTENT_NODE_NAME, values, max); + } + + public long count(NodeState indexMeta, final String indexStorageNodeName, + Set values, int max) { + NodeState index = indexMeta.getChildNode(indexStorageNodeName); + int count = 0; + if (values == null) { + PropertyState ec = indexMeta.getProperty(ENTRY_COUNT_PROPERTY_NAME); + if (ec != null) { + return ec.getValue(Type.LONG); + } + CountingNodeVisitor v = new CountingNodeVisitor(max); + v.visit(index); + count = v.getEstimatedCount(); + // "is not null" queries typically read more data + count *= 10; + } else { + int size = values.size(); + if (size == 0) { + return 0; + } + max = Math.max(10, max / size); + int i = 0; + for (String p : values) { + if (count > max && i > 3) { + // the total count is extrapolated from the the number + // of values counted so far to the total number of values + count = count * size / i; + break; } - } - - private void fetchNextPossiblyDuplicate() { - while (!nodeIterators.isEmpty()) { - Iterator iterator = nodeIterators.getLast(); - if (iterator.hasNext()) { - ChildNodeEntry entry = iterator.next(); - - readCount++; - if (readCount % 1000 == 0) { - FilterIterators.checkReadLimit(readCount); - LOG.warn("Traversed " + readCount + " nodes using index " + indexName + " with filter " + filter); - } - - NodeState node = entry.getNodeState(); - - String name = entry.getName(); - if (NodeStateUtils.isHidden(name)) { - continue; - } - currentPath = PathUtils.concat(parentPath, name); - - nodeIterators.addLast(node.getChildNodeEntries().iterator()); - parentPath = currentPath; - - if (node.getBoolean("match")) { - return; - } - - } else { - nodeIterators.removeLast(); - parentPath = PathUtils.getParentPath(parentPath); - } + NodeState s = index.getChildNode(p); + if (s.exists()) { + CountingNodeVisitor v = new CountingNodeVisitor(max); + v.visit(s); + count += v.getEstimatedCount(); } - currentPath = null; - closed = true; - } + i++; + } + } + return count; + } + + /** + * An iterator over paths within an index node. + */ + static class PathIterator implements Iterator { + + private final Filter filter; + private final String indexName; + private final Deque> nodeIterators = + Queues.newArrayDeque(); + private int readCount; + private boolean init; + private boolean closed; + private String parentPath; + private String currentPath; + private boolean pathContainsValue; + + /** + * Keep the returned path, to avoid returning duplicate entries. + */ + private final Set knownPaths = Sets.newHashSet(); + + PathIterator(Filter filter, String indexName) { + this.filter = filter; + this.indexName = indexName; + parentPath = ""; + currentPath = "/"; + } + + void enqueue(Iterator it) { + nodeIterators.addLast(it); + } + + void setPathContainsValue(boolean pathContainsValue) { + if (init) { + throw new IllegalStateException("This iterator is already initialized"); + } + this.pathContainsValue = pathContainsValue; + } + + @Override + public boolean hasNext() { + if (!closed && !init) { + fetchNext(); + init = true; + } + return !closed; + } - @Override - public String next() { + private void fetchNext() { + while (true) { + fetchNextPossiblyDuplicate(); if (closed) { - throw new IllegalStateException("This iterator is closed"); + return; } - if (!init) { - fetchNext(); - init = true; + if (pathContainsValue) { + String value = PathUtils.elements(currentPath).iterator().next(); + currentPath = PathUtils.relativize(value, currentPath); + // don't return duplicate paths: + // Set.add returns true if the entry was new, + // so if it returns false, it was already known + if (!knownPaths.add(currentPath)) { + continue; + } } - String result = currentPath; - fetchNext(); - return result; - } - - @Override - public void remove() { - throw new UnsupportedOperationException(); - } - - } - - /** - * A node visitor to recursively traverse a number of nodes. - */ - interface NodeVisitor { - void visit(NodeState state); - } - - /** - * A node visitor that counts the number of matching nodes up to a given - * maximum, in order to estimate the number of matches. - */ - static class CountingNodeVisitor implements NodeVisitor { - - /** - * The maximum number of matching nodes to count. - */ - final int maxCount; - - /** - * The current count of matching nodes. - */ - int count; - - /** - * The current depth (number of parent nodes). - */ - int depth; - - /** - * The sum of the depth of all matching nodes. This value is used to - * calculate the average depth. - */ - long depthTotal; - - CountingNodeVisitor(int maxCount) { - this.maxCount = maxCount; - } - - @Override - public void visit(NodeState state) { - if (state.hasProperty("match")) { - count++; - depthTotal += depth; + break; + } + } + + private void fetchNextPossiblyDuplicate() { + while (!nodeIterators.isEmpty()) { + Iterator iterator = nodeIterators.getLast(); + if (iterator.hasNext()) { + ChildNodeEntry entry = iterator.next(); + + readCount++; + if (readCount % 1000 == 0) { + FilterIterators.checkReadLimit(readCount); + LOG.warn("Traversed " + readCount + " nodes using index " + indexName + " with filter " + filter); + } + + NodeState node = entry.getNodeState(); + + String name = entry.getName(); + if (NodeStateUtils.isHidden(name)) { + continue; + } + currentPath = PathUtils.concat(parentPath, name); + + nodeIterators.addLast(node.getChildNodeEntries().iterator()); + parentPath = currentPath; + + if (node.getBoolean("match")) { + return; + } + + } else { + nodeIterators.removeLast(); + parentPath = PathUtils.getParentPath(parentPath); } - if (count < maxCount) { - depth++; - for (ChildNodeEntry entry : state.getChildNodeEntries()) { - if (count >= maxCount) { - break; - } - visit(entry.getNodeState()); - } - depth--; + } + currentPath = null; + closed = true; + } + + @Override + public String next() { + if (closed) { + throw new IllegalStateException("This iterator is closed"); + } + if (!init) { + fetchNext(); + init = true; + } + String result = currentPath; + fetchNext(); + return result; + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + + } + + /** + * A node visitor to recursively traverse a number of nodes. + */ + interface NodeVisitor { + void visit(NodeState state); + } + + /** + * A node visitor that counts the number of matching nodes up to a given + * maximum, in order to estimate the number of matches. + */ + static class CountingNodeVisitor implements NodeVisitor { + + /** + * The maximum number of matching nodes to count. + */ + final int maxCount; + + /** + * The current count of matching nodes. + */ + int count; + + /** + * The current depth (number of parent nodes). + */ + int depth; + + /** + * The sum of the depth of all matching nodes. This value is used to + * calculate the average depth. + */ + long depthTotal; + + CountingNodeVisitor(int maxCount) { + this.maxCount = maxCount; + } + + @Override + public void visit(NodeState state) { + if (state.hasProperty("match")) { + count++; + depthTotal += depth; + } + if (count < maxCount) { + depth++; + for (ChildNodeEntry entry : state.getChildNodeEntries()) { + if (count >= maxCount) { + break; + } + visit(entry.getNodeState()); } - } - - /** - * The number of matches (at most the maximum count). - * - * @return the match count - */ - int getCount() { + depth--; + } + } + + /** + * The number of matches (at most the maximum count). + * + * @return the match count + */ + int getCount() { + return count; + } + + /** + * The number of estimated matches. This value might be higher than the + * number of counted matches, if the maximum number of matches has been + * reached. It is based on the average depth of matches, and the average + * number of child nodes. + * + * @return the estimated matches + */ + int getEstimatedCount() { + if (count < maxCount) { return count; - } - - /** - * The number of estimated matches. This value might be higher than the - * number of counted matches, if the maximum number of matches has been - * reached. It is based on the average depth of matches, and the average - * number of child nodes. - * - * @return the estimated matches - */ - int getEstimatedCount() { - if (count < maxCount) { - return count; - } - double averageDepth = (int) (depthTotal / count); - // the number of estimated matches is higher - // the higher the average depth of the first hits - long estimatedNodes = (long) (count * Math.pow(1.1, averageDepth)); - estimatedNodes = Math.min(estimatedNodes, Integer.MAX_VALUE); - return Math.max(count, (int) estimatedNodes); - } - - } + } + double averageDepth = (int) (depthTotal / count); + // the number of estimated matches is higher + // the higher the average depth of the first hits + long estimatedNodes = (long) (count * Math.pow(1.1, averageDepth)); + estimatedNodes = Math.min(estimatedNodes, Integer.MAX_VALUE); + return Math.max(count, (int) estimatedNodes); + } + + } } diff --git a/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexQueryTest.java b/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexQueryTest.java index 84dbccf..9b6505b 100644 --- a/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexQueryTest.java +++ b/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexQueryTest.java @@ -16,7 +16,11 @@ */ package org.apache.jackrabbit.oak.plugins.index.property; -import static junit.framework.Assert.*; +import static junit.framework.Assert.assertEquals; +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; @@ -25,45 +29,31 @@ 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 net.sf.cglib.proxy.ProxyRefDispatcher; - -import org.apache.jackrabbit.commons.webdav.NodeTypeConstants; 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.PropertyState; -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 static org.apache.jackrabbit.oak.plugins.nodetype.NodeTypeConstants.*; - 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.spi.query.PropertyValues; import org.apache.jackrabbit.oak.spi.security.OpenSecurityProvider; import org.apache.jackrabbit.oak.util.NodeUtil; -import org.h2.index.IndexCondition; -import org.junit.Ignore; import org.junit.Test; -import com.google.common.collect.ImmutableMap; - public class OrderedPropertyIndexQueryTest extends AbstractQueryTest { /** * the property used by the index */ public final static String ORDERED_PROPERTY = "foo"; - + /** * number of nodes to create for testing. * @@ -83,12 +73,12 @@ public class OrderedPropertyIndexQueryTest extends AbstractQueryTest { private class ValuePathTuple implements Comparable{ 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; @@ -126,20 +116,20 @@ public class OrderedPropertyIndexQueryTest extends AbstractQueryTest { @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; } - + } /** @@ -161,13 +151,13 @@ public class OrderedPropertyIndexQueryTest extends AbstractQueryTest { assertEquals(-1,(new ValuePathTuple("value000", "/test/n1")).compareTo(new ValuePathTuple("value001", "/test/n0"))); assertEquals(1,(new ValuePathTuple("value001", "/test/n0")).compareTo(new ValuePathTuple("value000", "/test/n1"))); } - + @Override protected ContentRepository createRepository() { return new Oak().with(new InitialContent()) .with(new OpenSecurityProvider()) - .with(new PropertyIndexProvider()) - .with(new PropertyIndexEditorProvider()) + // .with(new PropertyIndexProvider()) + // .with(new PropertyIndexEditorProvider()) .with(new OrderedPropertyIndexProvider()) .with(new OrderedPropertyIndexEditorProvider()) .createContentRepository(); @@ -190,18 +180,20 @@ public class OrderedPropertyIndexQueryTest extends AbstractQueryTest { } /** - * generate a list of values to be used as ordered set + * generate a list of values to be used as ordered set. Will return something like + * {@code value000, value001, value002, ...} + * * * @param amount * @return */ private List generateOrderedValues(int amount){ if(amount>1000) throw new RuntimeException("amount cannot be greater than 100"); - + List values = new ArrayList(amount); NumberFormat nf = new DecimalFormat("000"); for(int i=0;i nodes = addChildNodes(generateOrderedValues(NUMBER_OF_NODES),test); root.commit(); - - System.out.println(root.getTree("/oak:index/indextest")); - + //querying Iterator results; results = executeQuery( String.format("SELECT * from [%s] WHERE foo IS NOT NULL", NT_UNSTRUCTURED), -// String.format("EXPLAIN SELECT * from [%s] WHERE foo IS NOT NULL", NT_UNSTRUCTURED), -// String.format("SELECT * from [%s] WHERE foo IS NOT NULL ORDER BY %s", NT_UNSTRUCTURED , ORDERED_PROPERTY), SQL2, null - ).getRows().iterator(); -// System.out.println(results.next()); + ).getRows().iterator(); assertRightOrder(nodes, results); setTravesalEnabled(true); } - + /** * assert the right order of the returned resultset * -- 1.8.3.4 (Apple Git-47) From 00510b0678af5f9ab89aa0613c1c04943b5d673e Mon Sep 17 00:00:00 2001 From: Davide Giannella Date: Thu, 20 Feb 2014 15:03:39 +0100 Subject: [PATCH 19/36] OAK-1263 applied the formatter --- .../OrderedContentMirrorStorageStrategyTest.java | 1828 ++++++++++---------- 1 file changed, 943 insertions(+), 885 deletions(-) diff --git a/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/OrderedContentMirrorStorageStrategyTest.java b/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/OrderedContentMirrorStorageStrategyTest.java index 0bf31cf..deb5562 100644 --- a/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/OrderedContentMirrorStorageStrategyTest.java +++ b/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/OrderedContentMirrorStorageStrategyTest.java @@ -43,889 +43,947 @@ import com.google.common.collect.Iterators; * */ public class OrderedContentMirrorStorageStrategyTest { - /** - * ascending ordered set of keys. Useful for testing - */ - private final static String[] KEYS = new String[]{"donald","goofy", "mickey","minnie"}; - private final static Set EMPTY_KEY_SET = newHashSet(); - - /** - * checks that the fist item/key is inserted with an empty property 'next' - * - * expected structure: - * - * - * :index : { - * :start : { :next=n0 }, - * n0 : { - * :next=, - * foo : { - * bar: { match=true} - * } - * } - * } - * - */ - @Test public void firstAndOnlyItem(){ - final String PATH = "/foo/bar"; - final String[] PATH_NODES = Iterables.toArray(PathUtils.elements(PATH), String.class); - final String N0 = KEYS[0]; - - IndexStoreStrategy store = new OrderedContentMirrorStoreStrategy(); - NodeBuilder index = EmptyNodeState.EMPTY_NODE.builder(); - NodeBuilder node = null; - - store.update(index, PATH, EMPTY_KEY_SET, newHashSet(N0)); - - 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",N0,node.getString(NEXT)); - - node = index.getChildNode(N0); - 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(PATH_NODES[0]); - assertTrue("n0 should contain 'foo'",node.exists()); - node = node.getChildNode(PATH_NODES[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 - * - * - * :index : { - * :start : { :next=n0 }, - * n0 : { :next=n1 }, - * n1 : { :next= } - * } - * - */ - @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; - - store.update(index, PATH, EMPTY_KEY_SET, newHashSet(N0)); //first node arrives - - 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)); - - store.update(index, PATH, EMPTY_KEY_SET, newHashSet(N1)); //second node arrives - - 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 children = (Iterable) store.getChildNodeEntries(index); - - assertNotNull("A returned Iterable cannot be null",children); - } - - /** - * test the iteration of the index with 2 shuffled items - * - * - * :index : { - * :start : { :next=n1 }, - * n0 : { :next= }, - * n1 : { :next=n0 } - * } - * - */ - @SuppressWarnings("unchecked") - @Test public void childNodeEntriesACoupleOfMixedItems(){ - final String N0 = KEYS[1]; - final String N1 = KEYS[0]; - final NodeState NODE_0 = EmptyNodeState.EMPTY_NODE.builder().setProperty(NEXT, "").getNodeState(); - final NodeState NODE_1 = 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, NODE_0); - index.setChildNode(N1, NODE_1); - - NodeState indexState = index.getNodeState(); - - Iterable children = (Iterable)store.getChildNodeEntries(indexState); - assertNotNull("The iterable cannot be null",children); - assertEquals("Expecting 2 items in the index", 2, Iterators.size(children.iterator())); //how many entries do we have? - - //ensuring the right sequence - ChildNodeEntry entry = null; - children = (Iterable)store.getChildNodeEntries(indexState); - Iterator 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",NODE_1,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",NODE_0,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 - * - * - * :index : { - * :start : { :next=n1 }, - * n0 : { :next= }, - * n1 : { :next=n0 } - * } - * - */ - @SuppressWarnings("unchecked") - @Test public void childNodeEntriesACoupleOfMixedItemsNoStart(){ - final String N0 = KEYS[1]; - final String N1 = KEYS[0]; - final NodeState NODE_0 = EmptyNodeState.EMPTY_NODE.builder().setProperty(NEXT, "").getNodeState(); - final NodeState NODE_1 = 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, NODE_0); - index.setChildNode(N1, NODE_1); - - NodeState indexState = index.getNodeState(); - - Iterable children = (Iterable)store.getChildNodeEntries(indexState,false); - assertNotNull("The iterable cannot be null",children); - assertEquals("Expecting 2 items in the index", 2, Iterators.size(children.iterator())); //how many entries do we have? - - //ensuring the right sequence - ChildNodeEntry entry = null; - children = (Iterable)store.getChildNodeEntries(indexState); - Iterator 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",NODE_1,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",NODE_0,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 - * - * - * :index : { - * :start : { :next=n1 }, - * n0 : { :next= }, - * n1 : { :next=n0 } - * } - * - */ - @SuppressWarnings("unchecked") - @Test public void childNodeEntriesACoupleOfMixedItemsWithStart(){ - final String N0 = KEYS[1]; - final String N1 = KEYS[0]; - final NodeState NODE_START = EmptyNodeState.EMPTY_NODE.builder().setProperty(NEXT, N1).getNodeState(); - final NodeState NODE_0 = EmptyNodeState.EMPTY_NODE.builder().setProperty(NEXT, "").getNodeState(); - final NodeState NODE_1 = 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, NODE_START); - index.setChildNode(N0, NODE_0); - index.setChildNode(N1, NODE_1); - - NodeState indexState = index.getNodeState(); - - Iterable children = (Iterable)store.getChildNodeEntries(indexState,true); - assertNotNull("The iterable cannot be null",children); - assertEquals("Expecting 3 items in the index", 3, Iterators.size(children.iterator())); //how many entries do we have? - - //ensuring the right sequence - ChildNodeEntry entry = null; - children = (Iterable)store.getChildNodeEntries(indexState,true); - Iterator 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",NODE_START,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",NODE_1,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",NODE_0,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 - * - * - * :index : { - * :start : { :next= } - * } - * - */ - @Test public void childNodeEntriesNoItemsWithStart(){ - NodeState NODE_START = EmptyNodeState.EMPTY_NODE.builder().setProperty(NEXT, "").getNodeState(); - NodeBuilder index = EmptyNodeState.EMPTY_NODE.builder(); - OrderedContentMirrorStoreStrategy store = new OrderedContentMirrorStoreStrategy(); - - //setting-up the index - index.setChildNode(START, NODE_START); - - Iterable children = store.getChildNodeEntries(index.getNodeState(), true); - assertEquals("Wrong size of Iterable",1,Iterators.size(children.iterator())); - - Iterator 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",NODE_START,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 NODE_START = EmptyNodeState.EMPTY_NODE.builder().setProperty(NEXT, "").getNodeState(); - NodeBuilder index = EmptyNodeState.EMPTY_NODE.builder(); - OrderedContentMirrorStoreStrategy store = new OrderedContentMirrorStoreStrategy(); - - Iterator 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(NODE_START,child.getNodeState()); - assertFalse(children.hasNext()); - } - - /** - * test the insert of two shuffled items - * - * Building final a structure like - * - * - * :index : { - * :start : { :next=n1 }, - * n0 : { :next= }, - * n1 : { :next=n0 } - * } - * - * - * where: - * - * - * Stage 1 - * ======= - * - * :index : { - * :start : { :next = n0 }, - * n0 : { - * :next = - * } - * } - * - * Stage 2 - * ======= - * - * :index : { - * :start : { :next = n1 }, - * n0 : { - * :next = - * }, - * n1 : { - * :next = n0 - * } - * } - * - */ - @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 - * - * - * { - * :start : { :next = n1 }, - * n0 : { - * :next = "" - * }, - * n1 : { - * :next = n2 - * }, - * n2 : { - * :next = n0 - * } - * } - * - * - * where: - * - * - * 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 = } - * } - * - */ - @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. - * - * - * Stage 1 - * ======= - * - * :index : { - * :start { :next = n0 }, - * n0 : { - * :next =, - * content : { - * foobar : { - * match = true - * } - * } - * } - * } - * - * Stage 2 - * ======= - * - * :index : { - * :start : { :next = n1 }, - * n1 : { - * :next =, - * content : { - * foobar : { - * match = true - * } - * } - * } - * } - * - */ - - @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 cointain /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")); - } - - /** - *

- * find a previous item given a key in an index with 1 element only - *

- * - *

it relies on the functionality of the store.update() for creating the index

- * - * - * :index { - * :start : { :next=n0 }, - * n0 = { :next= } - * } - * - */ - @Test public void findPrevious1ItemIndex(){ - final OrderedContentMirrorStoreStrategy store = new OrderedContentMirrorStoreStrategy(); - final String N0 = KEYS[0]; - final NodeState NODE_START = EmptyNodeState.EMPTY_NODE.builder().setProperty(NEXT, N0).getNodeState(); - final NodeState NODE_0 = EmptyNodeState.EMPTY_NODE.builder().setProperty(NEXT,"").getNodeState(); - final NodeBuilder index = EmptyNodeState.EMPTY_NODE.builder(); - - index.setChildNode(START, NODE_START); - index.setChildNode(N0,NODE_0); - - NodeState indexState = index.getNodeState(); - ChildNodeEntry previous = store.findPrevious( - indexState, - NODE_0 - ); - assertEquals("the :start node is expected",NODE_START,previous.getNodeState()); - } - - /** - * test the use case where a document change the indexed property. For example document that change author. - * - *

it relies on the functionality of the store.update() for creating the index

- * - * - * 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 } - * } - * } - * } - * - */ - @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); - node = index.getChildNode(N0).getChildNode(path[0]);//we know both the documents share the same /content - 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 - * - *

it relies on the functionality of the store.update() for creating the index

- * - * - * Stage 1 - * ======= - * - * :index : { - * :start : { :next = n0 }, - * n0 : { - * :next = , - * sampledoc : { match = true } - * } - * } - * - * Stage 2 - * ======= - * - * :index : { - * :start : { :next = } - * } - * - */ - @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 - * - *

it relies on the functionality of the store.update() for creating the index

- * - * - * 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 } - * } - * } - * - */ - @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 - * - *

it relies on the functionality of the store.update() for creating the index

- * - * - * 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 } - * } - * } - * } - * - * - */ - @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")); - } + /** + * ascending ordered set of keys. Useful for testing + */ + private final static String[] KEYS = new String[] { "donald", "goofy", "mickey", "minnie" }; + private final static Set EMPTY_KEY_SET = newHashSet(); + + /** + * checks that the fist item/key is inserted with an empty property 'next' + * + * expected structure: + * + * + * :index : { + * :start : { :next=n0 }, + * n0 : { + * :next=, + * foo : { + * bar: { match=true} + * } + * } + * } + * + */ + @Test + public void firstAndOnlyItem() { + final String PATH = "/foo/bar"; + final String[] PATH_NODES = Iterables.toArray(PathUtils.elements(PATH), String.class); + final String N0 = KEYS[0]; + + IndexStoreStrategy store = new OrderedContentMirrorStoreStrategy(); + NodeBuilder index = EmptyNodeState.EMPTY_NODE.builder(); + NodeBuilder node = null; + + store.update(index, PATH, EMPTY_KEY_SET, newHashSet(N0)); + + 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", N0, node.getString(NEXT)); + + node = index.getChildNode(N0); + 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(PATH_NODES[0]); + assertTrue("n0 should contain 'foo'", node.exists()); + node = node.getChildNode(PATH_NODES[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 + * + * + * :index : { + * :start : { :next=n0 }, + * n0 : { :next=n1 }, + * n1 : { :next= } + * } + * + */ + @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; + + store.update(index, PATH, EMPTY_KEY_SET, newHashSet(N0)); // first node + // arrives + + 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)); + + store.update(index, PATH, EMPTY_KEY_SET, newHashSet(N1)); // second node + // arrives + + 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 children = (Iterable) store.getChildNodeEntries(index); + + assertNotNull("A returned Iterable cannot be null", children); + } + + /** + * test the iteration of the index with 2 shuffled items + * + * + * :index : { + * :start : { :next=n1 }, + * n0 : { :next= }, + * n1 : { :next=n0 } + * } + * + */ + @SuppressWarnings("unchecked") + @Test + public void childNodeEntriesACoupleOfMixedItems() { + final String N0 = KEYS[1]; + final String N1 = KEYS[0]; + final NodeState NODE_0 = EmptyNodeState.EMPTY_NODE.builder().setProperty(NEXT, "").getNodeState(); + final NodeState NODE_1 = 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, NODE_0); + index.setChildNode(N1, NODE_1); + + NodeState indexState = index.getNodeState(); + + Iterable children = (Iterable) store.getChildNodeEntries(indexState); + assertNotNull("The iterable cannot be null", children); + assertEquals("Expecting 2 items in the index", 2, Iterators.size(children.iterator())); // how + // many + // entries + // do + // we + // have? + + // ensuring the right sequence + ChildNodeEntry entry = null; + children = (Iterable) store.getChildNodeEntries(indexState); + Iterator 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", NODE_1, 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", NODE_0, 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 + * + * + * :index : { + * :start : { :next=n1 }, + * n0 : { :next= }, + * n1 : { :next=n0 } + * } + * + */ + @SuppressWarnings("unchecked") + @Test + public void childNodeEntriesACoupleOfMixedItemsNoStart() { + final String N0 = KEYS[1]; + final String N1 = KEYS[0]; + final NodeState NODE_0 = EmptyNodeState.EMPTY_NODE.builder().setProperty(NEXT, "").getNodeState(); + final NodeState NODE_1 = 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, NODE_0); + index.setChildNode(N1, NODE_1); + + NodeState indexState = index.getNodeState(); + + Iterable children = (Iterable) store.getChildNodeEntries(indexState, false); + assertNotNull("The iterable cannot be null", children); + assertEquals("Expecting 2 items in the index", 2, Iterators.size(children.iterator())); // how + // many + // entries + // do + // we + // have? + + // ensuring the right sequence + ChildNodeEntry entry = null; + children = (Iterable) store.getChildNodeEntries(indexState); + Iterator 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", NODE_1, 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", NODE_0, 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 + * + * + * :index : { + * :start : { :next=n1 }, + * n0 : { :next= }, + * n1 : { :next=n0 } + * } + * + */ + @SuppressWarnings("unchecked") + @Test + public void childNodeEntriesACoupleOfMixedItemsWithStart() { + final String N0 = KEYS[1]; + final String N1 = KEYS[0]; + final NodeState NODE_START = EmptyNodeState.EMPTY_NODE.builder().setProperty(NEXT, N1).getNodeState(); + final NodeState NODE_0 = EmptyNodeState.EMPTY_NODE.builder().setProperty(NEXT, "").getNodeState(); + final NodeState NODE_1 = 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, NODE_START); + index.setChildNode(N0, NODE_0); + index.setChildNode(N1, NODE_1); + + NodeState indexState = index.getNodeState(); + + Iterable children = (Iterable) store.getChildNodeEntries(indexState, true); + assertNotNull("The iterable cannot be null", children); + assertEquals("Expecting 3 items in the index", 3, Iterators.size(children.iterator())); // how + // many + // entries + // do + // we + // have? + + // ensuring the right sequence + ChildNodeEntry entry = null; + children = (Iterable) store.getChildNodeEntries(indexState, true); + Iterator 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", NODE_START, 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", NODE_1, 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", NODE_0, 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 + * + * + * :index : { + * :start : { :next= } + * } + * + */ + @Test + public void childNodeEntriesNoItemsWithStart() { + NodeState NODE_START = EmptyNodeState.EMPTY_NODE.builder().setProperty(NEXT, "").getNodeState(); + NodeBuilder index = EmptyNodeState.EMPTY_NODE.builder(); + OrderedContentMirrorStoreStrategy store = new OrderedContentMirrorStoreStrategy(); + + // setting-up the index + index.setChildNode(START, NODE_START); + + Iterable children = store.getChildNodeEntries(index.getNodeState(), true); + assertEquals("Wrong size of Iterable", 1, Iterators.size(children.iterator())); + + Iterator 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", NODE_START, 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 NODE_START = EmptyNodeState.EMPTY_NODE.builder().setProperty(NEXT, "").getNodeState(); + NodeBuilder index = EmptyNodeState.EMPTY_NODE.builder(); + OrderedContentMirrorStoreStrategy store = new OrderedContentMirrorStoreStrategy(); + + Iterator 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(NODE_START, child.getNodeState()); + assertFalse(children.hasNext()); + } + + /** + * test the insert of two shuffled items + * + * Building final a structure like + * + * + * :index : { + * :start : { :next=n1 }, + * n0 : { :next= }, + * n1 : { :next=n0 } + * } + * + * + * where: + * + * + * Stage 1 + * ======= + * + * :index : { + * :start : { :next = n0 }, + * n0 : { + * :next = + * } + * } + * + * Stage 2 + * ======= + * + * :index : { + * :start : { :next = n1 }, + * n0 : { + * :next = + * }, + * n1 : { + * :next = n0 + * } + * } + * + */ + @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 + * + * + * { + * :start : { :next = n1 }, + * n0 : { + * :next = "" + * }, + * n1 : { + * :next = n2 + * }, + * n2 : { + * :next = n0 + * } + * } + * + * + * where: + * + * + * 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 = } + * } + * + */ + @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. + * + * + * Stage 1 + * ======= + * + * :index : { + * :start { :next = n0 }, + * n0 : { + * :next =, + * content : { + * foobar : { + * match = true + * } + * } + * } + * } + * + * Stage 2 + * ======= + * + * :index : { + * :start : { :next = n1 }, + * n1 : { + * :next =, + * content : { + * foobar : { + * match = true + * } + * } + * } + * } + * + */ + + @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 cointain /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")); + } + + /** + *

+ * find a previous item given a key in an index with 1 element only + *

+ * + *

+ * it relies on the functionality of the store.update() for creating the + * index + *

+ * + * + * :index { + * :start : { :next=n0 }, + * n0 = { :next= } + * } + * + */ + @Test + public void findPrevious1ItemIndex() { + final OrderedContentMirrorStoreStrategy store = new OrderedContentMirrorStoreStrategy(); + final String N0 = KEYS[0]; + final NodeState NODE_START = EmptyNodeState.EMPTY_NODE.builder().setProperty(NEXT, N0).getNodeState(); + final NodeState NODE_0 = EmptyNodeState.EMPTY_NODE.builder().setProperty(NEXT, "").getNodeState(); + final NodeBuilder index = EmptyNodeState.EMPTY_NODE.builder(); + + index.setChildNode(START, NODE_START); + index.setChildNode(N0, NODE_0); + + NodeState indexState = index.getNodeState(); + ChildNodeEntry previous = store.findPrevious(indexState, NODE_0); + assertEquals("the :start node is expected", NODE_START, previous.getNodeState()); + } + + /** + * test the use case where a document change the indexed property. For + * example document that change author. + * + *

+ * it relies on the functionality of the store.update() for creating the + * index + *

+ * + * + * 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 } + * } + * } + * } + * + */ + @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); + node = index.getChildNode(N0).getChildNode(path[0]);// we know both the + // documents share + // the same /content + 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 + * + *

+ * it relies on the functionality of the store.update() for creating the + * index + *

+ * + * + * Stage 1 + * ======= + * + * :index : { + * :start : { :next = n0 }, + * n0 : { + * :next = , + * sampledoc : { match = true } + * } + * } + * + * Stage 2 + * ======= + * + * :index : { + * :start : { :next = } + * } + * + */ + @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 + * + *

+ * it relies on the functionality of the store.update() for creating the + * index + *

+ * + * + * 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 } + * } + * } + * + */ + @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 + * + *

+ * it relies on the functionality of the store.update() for creating the + * index + *

+ * + * + * 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 } + * } + * } + * } + * + * + */ + @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")); + } } -- 1.8.3.4 (Apple Git-47) From 4b7b352258c4d712ae21c9d74ad2d466e62729bb Mon Sep 17 00:00:00 2001 From: Davide Giannella Date: Thu, 20 Feb 2014 15:04:35 +0100 Subject: [PATCH 20/36] OAK-1263 applied the formatter --- .../property/OrderedPropertyIndexQueryTest.java | 468 +++++++++++---------- 1 file changed, 239 insertions(+), 229 deletions(-) diff --git a/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexQueryTest.java b/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexQueryTest.java index 9b6505b..6935c7c 100644 --- a/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexQueryTest.java +++ b/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexQueryTest.java @@ -49,234 +49,244 @@ import org.apache.jackrabbit.oak.util.NodeUtil; import org.junit.Test; public class OrderedPropertyIndexQueryTest extends AbstractQueryTest { - /** - * the property used by the index - */ - public final static 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 final static int NUMBER_OF_NODES = 50; - - /** - * convenience orderable object that represet 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{ - 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 + 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) + /** + * the property used by the index + */ + public final static 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 final static int NUMBER_OF_NODES = 50; + + /** + * convenience orderable object that represet 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 { + 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 + 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; - 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; - } - - } - - /** - * testing for asserting the right comparison behaviour of the custom class - */ - @Test public void valuePathTupleComparison(){ - try{ - new ValuePathTuple("value", "path").compareTo(null); - fail("It should have raisen a NPE"); - }catch(NullPointerException e){ - //so far so good - } - assertEquals(0,(new ValuePathTuple("value", "path")).compareTo(new ValuePathTuple("value", "path"))); - assertEquals(-1,(new ValuePathTuple("value", "path")).compareTo(new ValuePathTuple("value1", "path"))); - assertEquals(-1,(new ValuePathTuple("value1", "path")).compareTo(new ValuePathTuple("value1", "path1"))); - assertEquals(1,(new ValuePathTuple("value1", "path")).compareTo(new ValuePathTuple("value", "path"))); - assertEquals(1,(new ValuePathTuple("value1", "path1")).compareTo(new ValuePathTuple("value1", "path"))); - - assertEquals(-1,(new ValuePathTuple("value000", "/test/n1")).compareTo(new ValuePathTuple("value001", "/test/n0"))); - assertEquals(1,(new ValuePathTuple("value001", "/test/n0")).compareTo(new ValuePathTuple("value000", "/test/n1"))); - } - - @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 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 List generateOrderedValues(int amount){ - if(amount>1000) throw new RuntimeException("amount cannot be greater than 100"); - - List values = new ArrayList(amount); - NumberFormat nf = new DecimalFormat("000"); - for(int i=0;i addChildNodes(final List values, final Tree father){ - List nodes = new ArrayList(); - Random rnd = new Random(); - int counter = 0; - while(values.size()>0){ - 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)), - TEST_INDEX_NAME, - false, - new String[] { ORDERED_PROPERTY }, - null, - OrderedIndex.TYPE - ); - 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 nodes = addChildNodes(generateOrderedValues(NUMBER_OF_NODES),test); - root.commit(); - - //querying - Iterator results; - results = executeQuery( - String.format("SELECT * from [%s] WHERE foo IS NOT NULL", NT_UNSTRUCTURED), - SQL2, - null - ).getRows().iterator(); - assertRightOrder(nodes, results); - - setTravesalEnabled(true); - } - - /** - * assert the right order of the returned resultset - * - * @param orderedSequence the right order in which the resultset should be returned - * @param resultset the resultes - */ - private void assertRightOrder(@Nonnull final List orderedSequence, @Nonnull final Iterator 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++; - } - } + } + + @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; + } + + } + + /** + * testing for asserting the right comparison behaviour of the custom class + */ + @Test + public void valuePathTupleComparison() { + try { + new ValuePathTuple("value", "path").compareTo(null); + fail("It should have raisen a NPE"); + } catch (NullPointerException e) { + // so far so good + } + assertEquals(0, (new ValuePathTuple("value", "path")).compareTo(new ValuePathTuple("value", "path"))); + assertEquals(-1, (new ValuePathTuple("value", "path")).compareTo(new ValuePathTuple("value1", "path"))); + assertEquals(-1, (new ValuePathTuple("value1", "path")).compareTo(new ValuePathTuple("value1", "path1"))); + assertEquals(1, (new ValuePathTuple("value1", "path")).compareTo(new ValuePathTuple("value", "path"))); + assertEquals(1, (new ValuePathTuple("value1", "path1")).compareTo(new ValuePathTuple("value1", "path"))); + + assertEquals(-1, (new ValuePathTuple("value000", "/test/n1")).compareTo(new ValuePathTuple("value001", + "/test/n0"))); + assertEquals(1, (new ValuePathTuple("value001", "/test/n0")).compareTo(new ValuePathTuple("value000", + "/test/n1"))); + } + + @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 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 List generateOrderedValues(int amount) { + if (amount > 1000) + throw new RuntimeException("amount cannot be greater than 100"); + + List values = new ArrayList(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 ndoes + * @return + */ + private List addChildNodes(final List values, final Tree father) { + List nodes = new ArrayList(); + Random rnd = new Random(); + int counter = 0; + while (values.size() > 0) { + 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)), + TEST_INDEX_NAME, false, new String[] { ORDERED_PROPERTY }, null, OrderedIndex.TYPE); + 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 nodes = addChildNodes(generateOrderedValues(NUMBER_OF_NODES), test); + root.commit(); + + // querying + Iterator results; + results = executeQuery(String.format("SELECT * from [%s] WHERE foo IS NOT NULL", NT_UNSTRUCTURED), SQL2, null) + .getRows().iterator(); + assertRightOrder(nodes, results); + + setTravesalEnabled(true); + } + + /** + * assert the right order of the returned resultset + * + * @param orderedSequence + * the right order in which the resultset should be returned + * @param resultset + * the resultes + */ + private void assertRightOrder(@Nonnull + final List orderedSequence, @Nonnull + final Iterator 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++; + } + } } -- 1.8.3.4 (Apple Git-47) From 8303238beeee00c92e66b56649470d831fedd102 Mon Sep 17 00:00:00 2001 From: Davide Giannella Date: Thu, 20 Feb 2014 15:27:25 +0100 Subject: [PATCH 21/36] OAK-1263 added coverage for 1 key only filter --- .../property/OrderedPropertyIndexQueryTest.java | 104 ++++++++++++++------- 1 file changed, 71 insertions(+), 33 deletions(-) diff --git a/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexQueryTest.java b/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexQueryTest.java index 6935c7c..66f5258 100644 --- a/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexQueryTest.java +++ b/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexQueryTest.java @@ -16,9 +16,7 @@ */ package org.apache.jackrabbit.oak.plugins.index.property; -import static junit.framework.Assert.assertEquals; -import static junit.framework.Assert.assertTrue; -import static junit.framework.Assert.fail; +import static junit.framework.Assert.*; import static org.apache.jackrabbit.JcrConstants.JCR_PRIMARYTYPE; import static org.apache.jackrabbit.JcrConstants.NT_UNSTRUCTURED; @@ -29,14 +27,17 @@ 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.derby.iapi.sql.execute.ExecIndexRow; 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; @@ -44,10 +45,13 @@ 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.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 @@ -154,20 +158,20 @@ public class OrderedPropertyIndexQueryTest extends AbstractQueryTest { assertEquals(1, (new ValuePathTuple("value1", "path")).compareTo(new ValuePathTuple("value", "path"))); assertEquals(1, (new ValuePathTuple("value1", "path1")).compareTo(new ValuePathTuple("value1", "path"))); - assertEquals(-1, (new ValuePathTuple("value000", "/test/n1")).compareTo(new ValuePathTuple("value001", - "/test/n0"))); - assertEquals(1, (new ValuePathTuple("value001", "/test/n0")).compareTo(new ValuePathTuple("value000", - "/test/n1"))); + assertEquals(-1, + (new ValuePathTuple("value000", "/test/n1")).compareTo(new ValuePathTuple("value001", "/test/n0"))); + assertEquals(1, + (new ValuePathTuple("value001", "/test/n0")).compareTo(new ValuePathTuple("value000", "/test/n1"))); } @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(); + .with(new OpenSecurityProvider()) + // .with(new PropertyIndexProvider()) + // .with(new PropertyIndexEditorProvider()) + .with(new OrderedPropertyIndexProvider()).with(new OrderedPropertyIndexEditorProvider()) + .createContentRepository(); } /** @@ -237,11 +241,32 @@ public class OrderedPropertyIndexQueryTest extends AbstractQueryTest { 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); + TEST_INDEX_NAME, false, new String[] { ORDERED_PROPERTY }, null, OrderedIndex.TYPE); root.commit(); } /** + * assert the right order of the returned resultset + * + * @param orderedSequence + * the right order in which the resultset should be returned + * @param resultset + * the resultes + */ + private void assertRightOrder(@Nonnull + final List orderedSequence, @Nonnull + final Iterator 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 @@ -252,8 +277,8 @@ public class OrderedPropertyIndexQueryTest extends AbstractQueryTest { public void queryAllEntries() throws CommitFailedException, ParseException, RepositoryException { setTravesalEnabled(false); - // index automatically created by the framework: {@code - // createTestIndexNode()} + // index automatically created by the framework: + // {@code createTestIndexNode()} Tree rTree = root.getTree("/"); Tree test = rTree.addChild("test"); @@ -263,30 +288,43 @@ public class OrderedPropertyIndexQueryTest extends AbstractQueryTest { // querying Iterator results; results = executeQuery(String.format("SELECT * from [%s] WHERE foo IS NOT NULL", NT_UNSTRUCTURED), SQL2, null) - .getRows().iterator(); + .getRows().iterator(); assertRightOrder(nodes, results); setTravesalEnabled(true); } /** - * assert the right order of the returned resultset - * - * @param orderedSequence - * the right order in which the resultset should be returned - * @param resultset - * the resultes + * test the index for returning the items related to a single key + * @throws CommitFailedException + * @throws ParseException */ - private void assertRightOrder(@Nonnull - final List orderedSequence, @Nonnull - final Iterator 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++; - } + @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 nodes = addChildNodes(generateOrderedValues(NUMBER_OF_NODES), test); + root.commit(); + + ValuePathTuple searchfor = nodes.get((int)NUMBER_OF_NODES/2); //getting the middle of the random list of nodes. + Map filter = ImmutableMap.of( + ORDERED_PROPERTY, PropertyValues.newString(searchfor.value) + ); + String query = "SELECT * FROM [%s] WHERE %s=$%s"; + Iterator 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()); + assertFalse("there should be not any more items",results.hasNext()); + + setTravesalEnabled(true); } } -- 1.8.3.4 (Apple Git-47) From 1212afe6ff0c174b3ddb7d449108ee93439b74ee Mon Sep 17 00:00:00 2001 From: Davide Giannella Date: Thu, 20 Feb 2014 15:47:30 +0100 Subject: [PATCH 22/36] OAK-1263 added coverage for 2 keys filter --- .../property/OrderedPropertyIndexQueryTest.java | 92 +++++++++++++++------- 1 file changed, 65 insertions(+), 27 deletions(-) diff --git a/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexQueryTest.java b/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexQueryTest.java index 66f5258..369fce2 100644 --- a/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexQueryTest.java +++ b/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexQueryTest.java @@ -16,7 +16,10 @@ */ package org.apache.jackrabbit.oak.plugins.index.property; -import static junit.framework.Assert.*; +import static junit.framework.Assert.assertEquals; +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; @@ -33,7 +36,6 @@ import java.util.Random; import javax.annotation.Nonnull; import javax.jcr.RepositoryException; -import org.apache.derby.iapi.sql.execute.ExecIndexRow; import org.apache.jackrabbit.oak.Oak; import org.apache.jackrabbit.oak.api.CommitFailedException; import org.apache.jackrabbit.oak.api.ContentRepository; @@ -61,8 +63,8 @@ public class OrderedPropertyIndexQueryTest extends AbstractQueryTest { /** * 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. + * 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. */ @@ -71,8 +73,7 @@ public class OrderedPropertyIndexQueryTest extends AbstractQueryTest { /** * convenience orderable object that represet 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 + * 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 { private String value; @@ -194,8 +195,8 @@ public class OrderedPropertyIndexQueryTest extends AbstractQueryTest { } /** - * generate a list of values to be used as ordered set. Will return - * something like {@code value000, value001, value002, ...} + * generate a list of values to be used as ordered set. Will return something like + * {@code value000, value001, value002, ...} * * * @param amount @@ -214,8 +215,8 @@ public class OrderedPropertyIndexQueryTest extends AbstractQueryTest { } /** - * convenience method that adds a bunch of nodes in random order and return - * the order in which they should be presented by the OrderedIndex + * 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 @@ -296,35 +297,72 @@ public class OrderedPropertyIndexQueryTest extends AbstractQueryTest { /** * test the index for returning the items related to a single key - * @throws CommitFailedException - * @throws ParseException + * + * @throws CommitFailedException + * @throws ParseException */ @Test - public void queryOneKey() throws CommitFailedException, ParseException{ + 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 nodes = addChildNodes(generateOrderedValues(NUMBER_OF_NODES), test); root.commit(); - - ValuePathTuple searchfor = nodes.get((int)NUMBER_OF_NODES/2); //getting the middle of the random list of nodes. - Map filter = ImmutableMap.of( - ORDERED_PROPERTY, PropertyValues.newString(searchfor.value) - ); + + ValuePathTuple searchfor = nodes.get((int) NUMBER_OF_NODES / 2); // getting the middle of the random list of + // nodes. + Map filter = ImmutableMap + .of(ORDERED_PROPERTY, PropertyValues.newString(searchfor.value)); String query = "SELECT * FROM [%s] WHERE %s=$%s"; Iterator 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()); - assertFalse("there should be not any more items",results.hasNext()); - + 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()); + assertFalse("there should be not any more items", results.hasNext()); + + setTravesalEnabled(true); + } + + /** + * Querying two properties they should be returned in the right order + * + * @throws CommitFailedException + * @throws ParseException + */ + @Test + public void queryTwoProperties() throws CommitFailedException, ParseException { + setTravesalEnabled(false); + + // index automatically created by the framework: + // {@code createTestIndexNode()} + + Tree rTree = root.getTree("/"); + Tree test = rTree.addChild("test"); + List nodes = addChildNodes(generateOrderedValues(NUMBER_OF_NODES), test); + root.commit(); + + // picking up two random elements from the list of added nodes + Random rnd = new Random(); + List searchfor = new ArrayList(); + searchfor.add(nodes.remove(rnd.nextInt(nodes.size()))); + searchfor.add(nodes.remove(rnd.nextInt(nodes.size()))); + + String query = "SELECT * FROM [%s] WHERE %s = $%s OR %s = $%s1"; + Map filter = ImmutableMap.of(ORDERED_PROPERTY, + PropertyValues.newString(searchfor.get(0).value), ORDERED_PROPERTY + 1, + PropertyValues.newString(searchfor.get(1).value)); + Iterator results = executeQuery( + String.format(query, NT_UNSTRUCTURED, ORDERED_PROPERTY, ORDERED_PROPERTY, ORDERED_PROPERTY, + ORDERED_PROPERTY), SQL2, filter).getRows().iterator(); + + Collections.sort(searchfor); // sorting them for having the right expected order + assertRightOrder(searchfor, results); + setTravesalEnabled(true); } } -- 1.8.3.4 (Apple Git-47) From 20f530a45aaeea66a6aa3aa2dda2f1ec0941f84e Mon Sep 17 00:00:00 2001 From: Davide Giannella Date: Fri, 21 Feb 2014 09:34:25 +0100 Subject: [PATCH 23/36] initial refactoring for benchmarking --- .../oak/benchmark/BaseOrderByInsertTest.java | 109 --------------------- .../oak/benchmark/BaseOrderedIndexTest.java | 109 +++++++++++++++++++++ .../benchmark/OrderedPropertyIndexInsertTest.java | 46 +++++++++ .../OrderedPropertyIndexOrderByInsertTest.java | 46 --------- 4 files changed, 155 insertions(+), 155 deletions(-) delete mode 100644 oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/BaseOrderByInsertTest.java create mode 100644 oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/BaseOrderedIndexTest.java create mode 100644 oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/OrderedPropertyIndexInsertTest.java delete mode 100644 oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/OrderedPropertyIndexOrderByInsertTest.java diff --git a/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/BaseOrderByInsertTest.java b/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/BaseOrderByInsertTest.java deleted file mode 100644 index 5fa3a23..0000000 --- a/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/BaseOrderByInsertTest.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * 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.benchmark; - -import java.util.UUID; - -import javax.jcr.Node; -import javax.jcr.RepositoryException; -import javax.jcr.Session; - -/** - * - */ -public abstract class BaseOrderByInsertTest extends AbstractTest { - /** - * type of the created node - */ - private final static String NODE_TYPE = "oak:Unstructured"; - - /** - * property that will be indexed - */ - final static String INDEXED_PROPERTY = "indexedProperty"; - - /** - * the number of nodes created per iteration - */ - private final static int NODES_PER_ITERATION = Integer.parseInt(System.getProperty("nodesPerIteration", "100")); - - private final static int PRE_ADDED_NODES = Integer.parseInt(System.getProperty("preAddedNodes", "0")); - - /** - * node name below which creating the test data - */ - private final String DUMP_NODE = this.getClass().getSimpleName() + TEST_ID; - - /** - * session used for operations throughout the test - */ - Session session; - - /** - * node under which all the test data will be filled in - */ - private Node dump; - - @Override - protected void beforeTest() throws Exception { - session = loginWriter(); - - //initiate the place for writing child nodes - dump = session.getRootNode().addNode(DUMP_NODE,NODE_TYPE); - session.save(); - - defineIndex(); - - //pre-adding nodes - fireNodes(PRE_ADDED_NODES); - } - - @Override - protected void afterTest() throws Exception { - //clean-up our mess - dump.remove(); - session.save(); - session.logout(); - } - - /* (non-Javadoc) - * @see org.apache.jackrabbit.oak.benchmark.AbstractTest#runTest() - */ - @Override - protected void runTest() throws Exception { - fireNodes(NODES_PER_ITERATION); - } - - void fireNodes(int numberOfNodes){ - try{ - for(int i=0; i Date: Fri, 21 Feb 2014 09:34:56 +0100 Subject: [PATCH 24/36] initial refactoring for benchmarking --- .../org/apache/jackrabbit/oak/benchmark/BaseOrderedIndexTest.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/BaseOrderedIndexTest.java b/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/BaseOrderedIndexTest.java index 5fa3a23..1a68770 100644 --- a/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/BaseOrderedIndexTest.java +++ b/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/BaseOrderedIndexTest.java @@ -23,14 +23,16 @@ import javax.jcr.Node; import javax.jcr.RepositoryException; import javax.jcr.Session; +import org.apache.jackrabbit.oak.plugins.nodetype.NodeTypeConstants; + /** * */ -public abstract class BaseOrderByInsertTest extends AbstractTest { +public abstract class BaseOrderedIndexTest extends AbstractTest { /** * type of the created node */ - private final static String NODE_TYPE = "oak:Unstructured"; + private final static String NODE_TYPE = NodeTypeConstants.NT_OAK_UNSTRUCTURED; /** * property that will be indexed -- 1.8.3.4 (Apple Git-47) From d9b42f65e51f30cd17e849c09c4a7da83392550b Mon Sep 17 00:00:00 2001 From: Davide Giannella Date: Fri, 21 Feb 2014 09:36:13 +0100 Subject: [PATCH 25/36] initial refactoring for benchmarking --- .../main/java/org/apache/jackrabbit/oak/benchmark/BenchmarkRunner.java | 2 +- .../org/apache/jackrabbit/oak/benchmark/NoIndexesOrderByInsertTest.java | 2 +- .../apache/jackrabbit/oak/benchmark/OrderedPropertyIndexInsertTest.java | 2 +- .../oak/benchmark/StandardPropertyIndexOrderByInsertTest.java | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/BenchmarkRunner.java b/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/BenchmarkRunner.java index 88d769a..bb661a8 100644 --- a/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/BenchmarkRunner.java +++ b/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/BenchmarkRunner.java @@ -99,7 +99,7 @@ public class BenchmarkRunner { base.value(options), 256, cacheSize, mmap.value(options)) }; Benchmark[] allBenchmarks = new Benchmark[] { - new OrderedPropertyIndexOrderByInsertTest(), + new OrderedPropertyIndexInsertTest(), new StandardPropertyIndexOrderByInsertTest(), new NoIndexesOrderByInsertTest(), new OrderByQueryTest(), diff --git a/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/NoIndexesOrderByInsertTest.java b/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/NoIndexesOrderByInsertTest.java index 51a2537..c4a6c55 100644 --- a/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/NoIndexesOrderByInsertTest.java +++ b/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/NoIndexesOrderByInsertTest.java @@ -17,7 +17,7 @@ package org.apache.jackrabbit.oak.benchmark; -public class NoIndexesOrderByInsertTest extends BaseOrderByInsertTest { +public class NoIndexesOrderByInsertTest extends BaseOrderedIndexTest { /* * this class is empty extending a base only because during benchmark testing * it was found that an extended class had an overhead of 4-6ms even if it was diff --git a/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/OrderedPropertyIndexInsertTest.java b/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/OrderedPropertyIndexInsertTest.java index 17f95d7..f585920 100644 --- a/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/OrderedPropertyIndexInsertTest.java +++ b/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/OrderedPropertyIndexInsertTest.java @@ -26,7 +26,7 @@ import org.apache.jackrabbit.oak.plugins.index.property.OrderedPropertyIndexEdit /** * */ -public class OrderedPropertyIndexOrderByInsertTest extends BaseOrderByInsertTest { +public class OrderedPropertyIndexInsertTest extends BaseOrderedIndexTest { private Node index = null; @Override diff --git a/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/StandardPropertyIndexOrderByInsertTest.java b/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/StandardPropertyIndexOrderByInsertTest.java index b0e4a85..9a12195 100644 --- a/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/StandardPropertyIndexOrderByInsertTest.java +++ b/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/StandardPropertyIndexOrderByInsertTest.java @@ -26,7 +26,7 @@ import org.apache.jackrabbit.oak.plugins.index.property.PropertyIndexEditorProvi /** * */ -public class StandardPropertyIndexOrderByInsertTest extends BaseOrderByInsertTest { +public class StandardPropertyIndexOrderByInsertTest extends BaseOrderedIndexTest { private Node index = null; @Override -- 1.8.3.4 (Apple Git-47) From ecd55b5c9a306d3b80947e6d06983575fd8abe7c Mon Sep 17 00:00:00 2001 From: Davide Giannella Date: Mon, 24 Feb 2014 15:56:00 +0100 Subject: [PATCH 26/36] OAK-1263 refactored to better naming and host new tests --- .../oak/benchmark/BaseOrderedIndexTest.java | 111 --------------------- .../jackrabbit/oak/benchmark/BenchmarkRunner.java | 6 +- .../oak/benchmark/NoIndexesOrderByInsertTest.java | 30 ------ .../oak/benchmark/OrderedIndexBaseTest.java | 111 +++++++++++++++++++++ .../benchmark/OrderedIndexInsertNoIndexTest.java | 30 ++++++ .../OrderedIndexInsertOrderedPropertyTest.java | 46 +++++++++ .../OrderedIndexInsertStandardPropertyTest.java | 46 +++++++++ .../benchmark/OrderedPropertyIndexInsertTest.java | 46 --------- .../StandardPropertyIndexOrderByInsertTest.java | 46 --------- 9 files changed, 236 insertions(+), 236 deletions(-) delete mode 100644 oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/BaseOrderedIndexTest.java delete mode 100644 oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/NoIndexesOrderByInsertTest.java create mode 100644 oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/OrderedIndexBaseTest.java create mode 100644 oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/OrderedIndexInsertNoIndexTest.java create mode 100644 oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/OrderedIndexInsertOrderedPropertyTest.java create mode 100644 oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/OrderedIndexInsertStandardPropertyTest.java delete mode 100644 oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/OrderedPropertyIndexInsertTest.java delete mode 100644 oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/StandardPropertyIndexOrderByInsertTest.java diff --git a/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/BaseOrderedIndexTest.java b/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/BaseOrderedIndexTest.java deleted file mode 100644 index 1a68770..0000000 --- a/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/BaseOrderedIndexTest.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * 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.benchmark; - -import java.util.UUID; - -import javax.jcr.Node; -import javax.jcr.RepositoryException; -import javax.jcr.Session; - -import org.apache.jackrabbit.oak.plugins.nodetype.NodeTypeConstants; - -/** - * - */ -public abstract class BaseOrderedIndexTest extends AbstractTest { - /** - * type of the created node - */ - private final static String NODE_TYPE = NodeTypeConstants.NT_OAK_UNSTRUCTURED; - - /** - * property that will be indexed - */ - final static String INDEXED_PROPERTY = "indexedProperty"; - - /** - * the number of nodes created per iteration - */ - private final static int NODES_PER_ITERATION = Integer.parseInt(System.getProperty("nodesPerIteration", "100")); - - private final static int PRE_ADDED_NODES = Integer.parseInt(System.getProperty("preAddedNodes", "0")); - - /** - * node name below which creating the test data - */ - private final String DUMP_NODE = this.getClass().getSimpleName() + TEST_ID; - - /** - * session used for operations throughout the test - */ - Session session; - - /** - * node under which all the test data will be filled in - */ - private Node dump; - - @Override - protected void beforeTest() throws Exception { - session = loginWriter(); - - //initiate the place for writing child nodes - dump = session.getRootNode().addNode(DUMP_NODE,NODE_TYPE); - session.save(); - - defineIndex(); - - //pre-adding nodes - fireNodes(PRE_ADDED_NODES); - } - - @Override - protected void afterTest() throws Exception { - //clean-up our mess - dump.remove(); - session.save(); - session.logout(); - } - - /* (non-Javadoc) - * @see org.apache.jackrabbit.oak.benchmark.AbstractTest#runTest() - */ - @Override - protected void runTest() throws Exception { - fireNodes(NODES_PER_ITERATION); - } - - void fireNodes(int numberOfNodes){ - try{ - for(int i=0; i Date: Mon, 24 Feb 2014 17:53:04 +0100 Subject: [PATCH 27/36] OAK-1263 benchmark for query without index --- .../jackrabbit/oak/benchmark/BenchmarkRunner.java | 1 + .../oak/benchmark/OrderedIndexBaseTest.java | 57 ++++++---------------- .../oak/benchmark/OrderedIndexInsertBaseTest.java | 52 ++++++++++++++++++++ .../benchmark/OrderedIndexInsertNoIndexTest.java | 2 +- .../OrderedIndexInsertOrderedPropertyTest.java | 2 +- .../OrderedIndexInsertStandardPropertyTest.java | 2 +- .../benchmark/OrderedIndexQueryNoIndexTest.java | 56 +++++++++++++++++++++ 7 files changed, 127 insertions(+), 45 deletions(-) create mode 100644 oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/OrderedIndexInsertBaseTest.java create mode 100644 oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/OrderedIndexQueryNoIndexTest.java diff --git a/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/BenchmarkRunner.java b/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/BenchmarkRunner.java index 347944e..b268fdf 100644 --- a/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/BenchmarkRunner.java +++ b/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/BenchmarkRunner.java @@ -99,6 +99,7 @@ public class BenchmarkRunner { base.value(options), 256, cacheSize, mmap.value(options)) }; Benchmark[] allBenchmarks = new Benchmark[] { + new OrderedIndexQueryNoIndexTest(), new OrderedIndexInsertOrderedPropertyTest(), new OrderedIndexInsertStandardPropertyTest(), new OrderedIndexInsertNoIndexTest(), diff --git a/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/OrderedIndexBaseTest.java b/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/OrderedIndexBaseTest.java index 86057ce..b55bcd2 100644 --- a/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/OrderedIndexBaseTest.java +++ b/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/OrderedIndexBaseTest.java @@ -29,10 +29,20 @@ import org.apache.jackrabbit.oak.plugins.nodetype.NodeTypeConstants; * */ public abstract class OrderedIndexBaseTest extends AbstractTest { - /** + /** + * the number of nodes created per iteration + */ + final static int NODES_PER_ITERATION = Integer.parseInt(System.getProperty("nodesPerIteration", "100")); + + /** + * number of nodes that has to be added before performing the actual test + */ + final static int PRE_ADDED_NODES = Integer.parseInt(System.getProperty("preAddedNodes", "0")); + + /** * type of the created node */ - private final static String NODE_TYPE = NodeTypeConstants.NT_OAK_UNSTRUCTURED; + final static String NODE_TYPE = NodeTypeConstants.NT_OAK_UNSTRUCTURED; /** * property that will be indexed @@ -40,16 +50,9 @@ public abstract class OrderedIndexBaseTest extends AbstractTest { final static String INDEXED_PROPERTY = "indexedProperty"; /** - * the number of nodes created per iteration - */ - private final static int NODES_PER_ITERATION = Integer.parseInt(System.getProperty("nodesPerIteration", "100")); - - private final static int PRE_ADDED_NODES = Integer.parseInt(System.getProperty("preAddedNodes", "0")); - - /** * node name below which creating the test data */ - private final String DUMP_NODE = this.getClass().getSimpleName() + TEST_ID; + final String DUMP_NODE = this.getClass().getSimpleName() + TEST_ID; /** * session used for operations throughout the test @@ -59,39 +62,9 @@ public abstract class OrderedIndexBaseTest extends AbstractTest { /** * node under which all the test data will be filled in */ - private Node dump; - - @Override - protected void beforeTest() throws Exception { - session = loginWriter(); - - //initiate the place for writing child nodes - dump = session.getRootNode().addNode(DUMP_NODE,NODE_TYPE); - session.save(); + Node dump; - defineIndex(); - - //pre-adding nodes - fireNodes(PRE_ADDED_NODES); - } - - @Override - protected void afterTest() throws Exception { - //clean-up our mess - dump.remove(); - session.save(); - session.logout(); - } - - /* (non-Javadoc) - * @see org.apache.jackrabbit.oak.benchmark.AbstractTest#runTest() - */ - @Override - protected void runTest() throws Exception { - fireNodes(NODES_PER_ITERATION); - } - - void fireNodes(int numberOfNodes){ + void insertRandomNodes(int numberOfNodes){ try{ for(int i=0; i Date: Tue, 25 Feb 2014 11:22:56 +0100 Subject: [PATCH 28/36] OAK-1263 benchmark for query with standard property index --- .../jackrabbit/oak/benchmark/BenchmarkRunner.java | 1 + .../oak/benchmark/OrderedIndexBaseTest.java | 11 ++++ .../oak/benchmark/OrderedIndexInsertBaseTest.java | 2 +- .../OrderedIndexInsertStandardPropertyTest.java | 9 +--- .../oak/benchmark/OrderedIndexQueryBaseTest.java | 58 ++++++++++++++++++++++ .../benchmark/OrderedIndexQueryNoIndexTest.java | 33 ++---------- .../OrderedIndexQueryStandardIndexTest.java | 34 +++++++++++++ 7 files changed, 109 insertions(+), 39 deletions(-) create mode 100644 oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/OrderedIndexQueryBaseTest.java create mode 100644 oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/OrderedIndexQueryStandardIndexTest.java diff --git a/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/BenchmarkRunner.java b/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/BenchmarkRunner.java index b268fdf..e6b8923 100644 --- a/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/BenchmarkRunner.java +++ b/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/BenchmarkRunner.java @@ -99,6 +99,7 @@ public class BenchmarkRunner { base.value(options), 256, cacheSize, mmap.value(options)) }; Benchmark[] allBenchmarks = new Benchmark[] { + new OrderedIndexQueryStandardIndexTest(), new OrderedIndexQueryNoIndexTest(), new OrderedIndexInsertOrderedPropertyTest(), new OrderedIndexInsertStandardPropertyTest(), diff --git a/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/OrderedIndexBaseTest.java b/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/OrderedIndexBaseTest.java index b55bcd2..ce13a35 100644 --- a/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/OrderedIndexBaseTest.java +++ b/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/OrderedIndexBaseTest.java @@ -23,6 +23,9 @@ import javax.jcr.Node; import javax.jcr.RepositoryException; import javax.jcr.Session; +import org.apache.jackrabbit.oak.benchmark.util.OakIndexUtils; +import org.apache.jackrabbit.oak.plugins.index.IndexConstants; +import org.apache.jackrabbit.oak.plugins.index.property.PropertyIndexEditorProvider; import org.apache.jackrabbit.oak.plugins.nodetype.NodeTypeConstants; /** @@ -81,4 +84,12 @@ public abstract class OrderedIndexBaseTest extends AbstractTest { */ void defineIndex() throws Exception{ } + + static Node defineStandardPropertyIndex(Session session) throws Exception { + Node index = new OakIndexUtils.PropertyIndex().property(INDEXED_PROPERTY).create(session); + if(index == null) throw new RuntimeException("Error while creating the index definition. index node is null."); + if(!PropertyIndexEditorProvider.TYPE.equals(index.getProperty(IndexConstants.TYPE_PROPERTY_NAME).getString())) throw new RuntimeException("The type of the index does not match the expected"); + session.save(); + return index; + } } diff --git a/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/OrderedIndexInsertBaseTest.java b/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/OrderedIndexInsertBaseTest.java index 2855ca7..03e6360 100644 --- a/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/OrderedIndexInsertBaseTest.java +++ b/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/OrderedIndexInsertBaseTest.java @@ -19,7 +19,7 @@ package org.apache.jackrabbit.oak.benchmark; /** * intermediate class for the testing of Inserts of nodes. */ -public class OrderedIndexInsertBaseTest extends OrderedIndexBaseTest { +public abstract class OrderedIndexInsertBaseTest extends OrderedIndexBaseTest { @Override protected void beforeTest() throws Exception { session = loginWriter(); diff --git a/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/OrderedIndexInsertStandardPropertyTest.java b/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/OrderedIndexInsertStandardPropertyTest.java index d8f6ea0..d35809e 100644 --- a/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/OrderedIndexInsertStandardPropertyTest.java +++ b/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/OrderedIndexInsertStandardPropertyTest.java @@ -19,10 +19,6 @@ package org.apache.jackrabbit.oak.benchmark; import javax.jcr.Node; -import org.apache.jackrabbit.oak.benchmark.util.OakIndexUtils; -import org.apache.jackrabbit.oak.plugins.index.IndexConstants; -import org.apache.jackrabbit.oak.plugins.index.property.PropertyIndexEditorProvider; - /** * */ @@ -31,10 +27,7 @@ public class OrderedIndexInsertStandardPropertyTest extends OrderedIndexInsertBa @Override void defineIndex() throws Exception { - index = new OakIndexUtils.PropertyIndex().property(INDEXED_PROPERTY).create(session); - if(index == null) throw new RuntimeException("Error while creating the index definition. index node is null."); - if(!PropertyIndexEditorProvider.TYPE.equals(index.getProperty(IndexConstants.TYPE_PROPERTY_NAME).getString())) throw new RuntimeException("The type of the index does not match the expected"); - session.save(); + index = defineStandardPropertyIndex(session); } @Override diff --git a/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/OrderedIndexQueryBaseTest.java b/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/OrderedIndexQueryBaseTest.java new file mode 100644 index 0000000..7a63215 --- /dev/null +++ b/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/OrderedIndexQueryBaseTest.java @@ -0,0 +1,58 @@ +/* + * 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.benchmark; + +import javax.jcr.query.Query; +import javax.jcr.query.QueryManager; +import javax.jcr.query.QueryResult; + +/** + * Benchmark the query performance of an ORDER BY clause when No index are involved + */ +public abstract class OrderedIndexQueryBaseTest extends OrderedIndexBaseTest { + /** + * query to execute with the ORDER BY statement + */ + public final static String QUERY_WITH_ORDER = String.format( + "SELECT * FROM [%s] WHERE %s IS NOT NULL ORDER BY %s", NODE_TYPE, INDEXED_PROPERTY, INDEXED_PROPERTY); + + @Override + protected void beforeSuite() throws Exception { + session = loginWriter(); + dump = session.getRootNode().addNode(DUMP_NODE, NODE_TYPE); + session.save(); + defineIndex(); + insertRandomNodes(PRE_ADDED_NODES); + } + + @Override + protected void afterSuite() throws Exception { + dump.remove(); + session.save(); + session.logout(); + } + + @Override + protected void runTest() throws Exception { + QueryManager qm = session.getWorkspace().getQueryManager(); + Query q = qm.createQuery(QUERY_WITH_ORDER, Query.JCR_SQL2); + QueryResult r = q.execute(); + r.getNodes(); + } + + abstract String getQuery(); +} diff --git a/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/OrderedIndexQueryNoIndexTest.java b/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/OrderedIndexQueryNoIndexTest.java index ae7405c..d057676 100644 --- a/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/OrderedIndexQueryNoIndexTest.java +++ b/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/OrderedIndexQueryNoIndexTest.java @@ -16,41 +16,14 @@ */ package org.apache.jackrabbit.oak.benchmark; -import javax.jcr.query.Query; -import javax.jcr.query.QueryManager; -import javax.jcr.query.QueryResult; /** * Benchmark the query performance of an ORDER BY clause when No index are involved */ -public class OrderedIndexQueryNoIndexTest extends OrderedIndexBaseTest { - /** - * query to execute with the ORDER BY statement - */ - public final static String QUERY_WITH_ORDER = String.format( - "SELECT * FROM [%s] WHERE %s IS NOT NULL ORDER BY %s", NODE_TYPE, INDEXED_PROPERTY, INDEXED_PROPERTY); +public class OrderedIndexQueryNoIndexTest extends OrderedIndexQueryBaseTest { @Override - protected void beforeSuite() throws Exception { - session = loginWriter(); - dump = session.getRootNode().addNode(DUMP_NODE, NODE_TYPE); - session.save(); - defineIndex(); - insertRandomNodes(PRE_ADDED_NODES); - } - - @Override - protected void afterSuite() throws Exception { - dump.remove(); - session.save(); - session.logout(); - } - - @Override - protected void runTest() throws Exception { - QueryManager qm = session.getWorkspace().getQueryManager(); - Query q = qm.createQuery(QUERY_WITH_ORDER, Query.JCR_SQL2); - QueryResult r = q.execute(); - r.getNodes(); + String getQuery() { + return QUERY_WITH_ORDER; } } diff --git a/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/OrderedIndexQueryStandardIndexTest.java b/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/OrderedIndexQueryStandardIndexTest.java new file mode 100644 index 0000000..a415cf4 --- /dev/null +++ b/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/OrderedIndexQueryStandardIndexTest.java @@ -0,0 +1,34 @@ +/* + * 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.benchmark; + + +/** + * Benchmark the query performance of an ORDER BY clause when No index are involved + */ +public class OrderedIndexQueryStandardIndexTest extends OrderedIndexQueryBaseTest { + + @Override + void defineIndex() throws Exception { + defineStandardPropertyIndex(session); + } + + @Override + String getQuery() { + return QUERY_WITH_ORDER; + } +} -- 1.8.3.4 (Apple Git-47) From c17dc93057bdf58bd136fbb20239465fdfada39b Mon Sep 17 00:00:00 2001 From: Davide Giannella Date: Tue, 25 Feb 2014 12:29:17 +0100 Subject: [PATCH 29/36] OAK-1263 benchmark for query with ordered property index --- .../jackrabbit/oak/benchmark/BenchmarkRunner.java | 1 + .../oak/benchmark/OrderedIndexBaseTest.java | 11 ++++++- .../OrderedIndexInsertOrderedPropertyTest.java | 9 +----- .../oak/benchmark/OrderedIndexQueryBaseTest.java | 12 +++++++- .../OrderedIndexQueryOrderedIndexTest.java | 34 ++++++++++++++++++++++ .../OrderedIndexQueryStandardIndexTest.java | 2 +- 6 files changed, 58 insertions(+), 11 deletions(-) create mode 100644 oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/OrderedIndexQueryOrderedIndexTest.java diff --git a/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/BenchmarkRunner.java b/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/BenchmarkRunner.java index e6b8923..93efc71 100644 --- a/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/BenchmarkRunner.java +++ b/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/BenchmarkRunner.java @@ -99,6 +99,7 @@ public class BenchmarkRunner { base.value(options), 256, cacheSize, mmap.value(options)) }; Benchmark[] allBenchmarks = new Benchmark[] { + new OrderedIndexQueryOrderedIndexTest(), new OrderedIndexQueryStandardIndexTest(), new OrderedIndexQueryNoIndexTest(), new OrderedIndexInsertOrderedPropertyTest(), diff --git a/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/OrderedIndexBaseTest.java b/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/OrderedIndexBaseTest.java index ce13a35..143d8d0 100644 --- a/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/OrderedIndexBaseTest.java +++ b/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/OrderedIndexBaseTest.java @@ -25,6 +25,7 @@ import javax.jcr.Session; import org.apache.jackrabbit.oak.benchmark.util.OakIndexUtils; import org.apache.jackrabbit.oak.plugins.index.IndexConstants; +import org.apache.jackrabbit.oak.plugins.index.property.OrderedPropertyIndexEditorProvider; import org.apache.jackrabbit.oak.plugins.index.property.PropertyIndexEditorProvider; import org.apache.jackrabbit.oak.plugins.nodetype.NodeTypeConstants; @@ -85,11 +86,19 @@ public abstract class OrderedIndexBaseTest extends AbstractTest { void defineIndex() throws Exception{ } - static Node defineStandardPropertyIndex(Session session) throws Exception { + Node defineStandardPropertyIndex(Session session) throws Exception { Node index = new OakIndexUtils.PropertyIndex().property(INDEXED_PROPERTY).create(session); if(index == null) throw new RuntimeException("Error while creating the index definition. index node is null."); if(!PropertyIndexEditorProvider.TYPE.equals(index.getProperty(IndexConstants.TYPE_PROPERTY_NAME).getString())) throw new RuntimeException("The type of the index does not match the expected"); session.save(); return index; } + + Node defineOrderedPropertyIndex(Session session) throws Exception { + Node index = new OakIndexUtils.PropertyIndex().property(INDEXED_PROPERTY).create(session,OrderedPropertyIndexEditorProvider.TYPE); + if(index == null) throw new RuntimeException("Error while creating the index definition. index node is null."); + if(!OrderedPropertyIndexEditorProvider.TYPE.equals(index.getProperty(IndexConstants.TYPE_PROPERTY_NAME).getString())) throw new RuntimeException("The index type does not match the expected"); + session.save(); + return index; + } } diff --git a/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/OrderedIndexInsertOrderedPropertyTest.java b/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/OrderedIndexInsertOrderedPropertyTest.java index 8b77a6e..ebe2782 100644 --- a/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/OrderedIndexInsertOrderedPropertyTest.java +++ b/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/OrderedIndexInsertOrderedPropertyTest.java @@ -19,10 +19,6 @@ package org.apache.jackrabbit.oak.benchmark; import javax.jcr.Node; -import org.apache.jackrabbit.oak.benchmark.util.OakIndexUtils; -import org.apache.jackrabbit.oak.plugins.index.IndexConstants; -import org.apache.jackrabbit.oak.plugins.index.property.OrderedPropertyIndexEditorProvider; - /** * */ @@ -31,10 +27,7 @@ public class OrderedIndexInsertOrderedPropertyTest extends OrderedIndexInsertBas @Override void defineIndex() throws Exception{ - index = new OakIndexUtils.PropertyIndex().property(INDEXED_PROPERTY).create(session,OrderedPropertyIndexEditorProvider.TYPE); - if(index == null) throw new RuntimeException("Error while creating the index definition. index node is null."); - if(!OrderedPropertyIndexEditorProvider.TYPE.equals(index.getProperty(IndexConstants.TYPE_PROPERTY_NAME).getString())) throw new RuntimeException("The index type does not match the expected"); - session.save(); + index = defineOrderedPropertyIndex(session); } @Override diff --git a/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/OrderedIndexQueryBaseTest.java b/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/OrderedIndexQueryBaseTest.java index 7a63215..4cd51dd 100644 --- a/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/OrderedIndexQueryBaseTest.java +++ b/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/OrderedIndexQueryBaseTest.java @@ -16,6 +16,7 @@ */ package org.apache.jackrabbit.oak.benchmark; +import javax.jcr.Node; import javax.jcr.query.Query; import javax.jcr.query.QueryManager; import javax.jcr.query.QueryResult; @@ -24,12 +25,20 @@ import javax.jcr.query.QueryResult; * Benchmark the query performance of an ORDER BY clause when No index are involved */ public abstract class OrderedIndexQueryBaseTest extends OrderedIndexBaseTest { + Node index; + /** * query to execute with the ORDER BY statement */ public final static String QUERY_WITH_ORDER = String.format( "SELECT * FROM [%s] WHERE %s IS NOT NULL ORDER BY %s", NODE_TYPE, INDEXED_PROPERTY, INDEXED_PROPERTY); + /** + * query to execute WITHOUT the ORDER BY clause + */ + public final static String QUERY_WITHOUT_ORDER = String.format( + "SELECT * FROM [%s] WHERE %s IS NOT NULL", NODE_TYPE, INDEXED_PROPERTY); + @Override protected void beforeSuite() throws Exception { session = loginWriter(); @@ -42,6 +51,7 @@ public abstract class OrderedIndexQueryBaseTest extends OrderedIndexBaseTest { @Override protected void afterSuite() throws Exception { dump.remove(); + if(index!=null) index.remove(); session.save(); session.logout(); } @@ -49,7 +59,7 @@ public abstract class OrderedIndexQueryBaseTest extends OrderedIndexBaseTest { @Override protected void runTest() throws Exception { QueryManager qm = session.getWorkspace().getQueryManager(); - Query q = qm.createQuery(QUERY_WITH_ORDER, Query.JCR_SQL2); + Query q = qm.createQuery(getQuery(), Query.JCR_SQL2); QueryResult r = q.execute(); r.getNodes(); } diff --git a/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/OrderedIndexQueryOrderedIndexTest.java b/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/OrderedIndexQueryOrderedIndexTest.java new file mode 100644 index 0000000..ce556f8 --- /dev/null +++ b/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/OrderedIndexQueryOrderedIndexTest.java @@ -0,0 +1,34 @@ +/* + * 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.benchmark; + + +/** + * Benchmark the query performance of an ORDER BY clause when No index are involved + */ +public class OrderedIndexQueryOrderedIndexTest extends OrderedIndexQueryBaseTest { + + @Override + void defineIndex() throws Exception { + index = defineOrderedPropertyIndex(session); + } + + @Override + String getQuery() { + return QUERY_WITHOUT_ORDER; + } +} diff --git a/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/OrderedIndexQueryStandardIndexTest.java b/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/OrderedIndexQueryStandardIndexTest.java index a415cf4..c5351e6 100644 --- a/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/OrderedIndexQueryStandardIndexTest.java +++ b/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/OrderedIndexQueryStandardIndexTest.java @@ -24,7 +24,7 @@ public class OrderedIndexQueryStandardIndexTest extends OrderedIndexQueryBaseTes @Override void defineIndex() throws Exception { - defineStandardPropertyIndex(session); + index = defineStandardPropertyIndex(session); } @Override -- 1.8.3.4 (Apple Git-47) From b6e2a773d987e13f55451318975a6e1b5e8bf06c Mon Sep 17 00:00:00 2001 From: Davide Giannella Date: Tue, 25 Feb 2014 22:27:33 +0100 Subject: [PATCH 30/36] OAK-1263 iterated over the first 100 records for avoiding laziness --- .../oak/benchmark/OrderedIndexQueryBaseTest.java | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/OrderedIndexQueryBaseTest.java b/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/OrderedIndexQueryBaseTest.java index 4cd51dd..3d4df25 100644 --- a/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/OrderedIndexQueryBaseTest.java +++ b/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/OrderedIndexQueryBaseTest.java @@ -17,6 +17,7 @@ package org.apache.jackrabbit.oak.benchmark; import javax.jcr.Node; +import javax.jcr.NodeIterator; import javax.jcr.query.Query; import javax.jcr.query.QueryManager; import javax.jcr.query.QueryResult; @@ -32,7 +33,12 @@ public abstract class OrderedIndexQueryBaseTest extends OrderedIndexBaseTest { */ public final static String QUERY_WITH_ORDER = String.format( "SELECT * FROM [%s] WHERE %s IS NOT NULL ORDER BY %s", NODE_TYPE, INDEXED_PROPERTY, INDEXED_PROPERTY); - + + /** + * constant used to identify how many nodes will be fetched after the query execution + */ + public final static int FETCH_NODES = 100; + /** * query to execute WITHOUT the ORDER BY clause */ @@ -61,7 +67,11 @@ public abstract class OrderedIndexQueryBaseTest extends OrderedIndexBaseTest { QueryManager qm = session.getWorkspace().getQueryManager(); Query q = qm.createQuery(getQuery(), Query.JCR_SQL2); QueryResult r = q.execute(); - r.getNodes(); + NodeIterator nodes = r.getNodes(); + int counter = 0; + while(nodes.hasNext() && counter++ Date: Wed, 26 Feb 2014 10:51:54 +0100 Subject: [PATCH 31/36] OAK-1263 added dependency for allowing javax.annotations compile --- oak-run/pom.xml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/oak-run/pom.xml b/oak-run/pom.xml index 00c3fcb..ea23235 100644 --- a/oak-run/pom.xml +++ b/oak-run/pom.xml @@ -192,6 +192,9 @@ junit test + + com.google.code.findbugs + jsr305 + - -- 1.8.3.4 (Apple Git-47) From 3b5e0902a123805342d4546c23397c37b7a73805 Mon Sep 17 00:00:00 2001 From: Davide Giannella Date: Wed, 26 Feb 2014 12:17:51 +0100 Subject: [PATCH 32/36] OAK-1263 deleted wrong test --- .../property/OrderedPropertyIndexQueryTest.java | 38 ---------------------- 1 file changed, 38 deletions(-) diff --git a/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexQueryTest.java b/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexQueryTest.java index 369fce2..a024021 100644 --- a/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexQueryTest.java +++ b/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexQueryTest.java @@ -327,42 +327,4 @@ public class OrderedPropertyIndexQueryTest extends AbstractQueryTest { setTravesalEnabled(true); } - - /** - * Querying two properties they should be returned in the right order - * - * @throws CommitFailedException - * @throws ParseException - */ - @Test - public void queryTwoProperties() throws CommitFailedException, ParseException { - setTravesalEnabled(false); - - // index automatically created by the framework: - // {@code createTestIndexNode()} - - Tree rTree = root.getTree("/"); - Tree test = rTree.addChild("test"); - List nodes = addChildNodes(generateOrderedValues(NUMBER_OF_NODES), test); - root.commit(); - - // picking up two random elements from the list of added nodes - Random rnd = new Random(); - List searchfor = new ArrayList(); - searchfor.add(nodes.remove(rnd.nextInt(nodes.size()))); - searchfor.add(nodes.remove(rnd.nextInt(nodes.size()))); - - String query = "SELECT * FROM [%s] WHERE %s = $%s OR %s = $%s1"; - Map filter = ImmutableMap.of(ORDERED_PROPERTY, - PropertyValues.newString(searchfor.get(0).value), ORDERED_PROPERTY + 1, - PropertyValues.newString(searchfor.get(1).value)); - Iterator results = executeQuery( - String.format(query, NT_UNSTRUCTURED, ORDERED_PROPERTY, ORDERED_PROPERTY, ORDERED_PROPERTY, - ORDERED_PROPERTY), SQL2, filter).getRows().iterator(); - - Collections.sort(searchfor); // sorting them for having the right expected order - assertRightOrder(searchfor, results); - - setTravesalEnabled(true); - } } -- 1.8.3.4 (Apple Git-47) From f8bcd698a010ac5687ac772f8e64eab790bc586b Mon Sep 17 00:00:00 2001 From: Davide Giannella Date: Thu, 27 Feb 2014 09:03:30 +0100 Subject: [PATCH 33/36] OAK-1263 OSGi-fied the EditorProvider --- .../plugins/index/property/OrderedPropertyIndexEditorProvider.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexEditorProvider.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexEditorProvider.java index 27f55d7..75c439d 100644 --- a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexEditorProvider.java +++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexEditorProvider.java @@ -3,6 +3,8 @@ package org.apache.jackrabbit.oak.plugins.index.property; import javax.annotation.CheckForNull; import javax.annotation.Nonnull; +import org.apache.felix.scr.annotations.Component; +import org.apache.felix.scr.annotations.Service; import org.apache.jackrabbit.oak.api.CommitFailedException; import org.apache.jackrabbit.oak.plugins.index.IndexEditorProvider; import org.apache.jackrabbit.oak.plugins.index.IndexUpdateCallback; @@ -10,6 +12,8 @@ import org.apache.jackrabbit.oak.spi.commit.Editor; import org.apache.jackrabbit.oak.spi.state.NodeBuilder; import org.apache.jackrabbit.oak.spi.state.NodeState; +@Component +@Service(IndexEditorProvider.class) public class OrderedPropertyIndexEditorProvider implements IndexEditorProvider, OrderedIndex { @Override -- 1.8.3.4 (Apple Git-47) From 499f5f1711674a02067f0f23edb40569ba7a70dd Mon Sep 17 00:00:00 2001 From: Davide Giannella Date: Thu, 27 Feb 2014 14:09:18 +0100 Subject: [PATCH 34/36] OAK-1263 code clean-up --- .../oak/plugins/index/property/OrderedIndex.java | 2 +- .../index/property/OrderedPropertyIndex.java | 19 +++--- .../index/property/OrderedPropertyIndexEditor.java | 8 ++- .../index/property/OrderedPropertyIndexLookup.java | 26 ++++---- .../oak/plugins/index/property/PropertyIndex.java | 42 ++++++------- .../index/property/PropertyIndexLookup.java | 73 ++++++++++------------ 6 files changed, 83 insertions(+), 87 deletions(-) diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedIndex.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedIndex.java index 19870f0..38fa6df 100644 --- a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedIndex.java +++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedIndex.java @@ -21,5 +21,5 @@ package org.apache.jackrabbit.oak.plugins.index.property; * interface for shared constants around different actors: QueryIndex, IndexEditors, IndexEditorProviders, ... */ public interface OrderedIndex { - public final static String TYPE = "ordered"; + String TYPE = "ordered"; } diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndex.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndex.java index 9d9a4e4..9a94445 100644 --- a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndex.java +++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndex.java @@ -18,19 +18,20 @@ package org.apache.jackrabbit.oak.plugins.index.property; import org.apache.jackrabbit.oak.spi.state.NodeState; +import static org.apache.jackrabbit.oak.plugins.index.property.OrderedIndex.*; /** * */ -public class OrderedPropertyIndex extends PropertyIndex implements OrderedIndex { +public class OrderedPropertyIndex extends PropertyIndex { - @Override - public String getIndexName() { - return TYPE; - } + @Override + public String getIndexName() { + return TYPE; + } - @Override - PropertyIndexLookup getLookup(NodeState root) { - return new OrderedPropertyIndexLookup(root); - } + @Override + PropertyIndexLookup getLookup(NodeState root) { + return new OrderedPropertyIndexLookup(root); + } } diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexEditor.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexEditor.java index e0f330c..46807f7 100644 --- a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexEditor.java +++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexEditor.java @@ -21,8 +21,8 @@ import org.slf4j.LoggerFactory; import com.google.common.base.Strings; public class OrderedPropertyIndexEditor extends PropertyIndexEditor { - private final static Logger log = LoggerFactory.getLogger(OrderedPropertyIndexEditor.class); - private final static IndexStoreStrategy ORDERED_MIRROR = new OrderedContentMirrorStoreStrategy(); + private static final Logger log = LoggerFactory.getLogger(OrderedPropertyIndexEditor.class); + private static final IndexStoreStrategy ORDERED_MIRROR = new OrderedContentMirrorStoreStrategy(); private final Set propertyNames; @@ -40,7 +40,9 @@ public class OrderedPropertyIndexEditor extends PropertyIndexEditor { if(Strings.isNullOrEmpty(value)){ 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); + if(names.isArray()){ + log.warn("Only single value supported. '{}' only will be used.", value); + } pns = Collections.singleton(value); this.properlyConfigured=true; } diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexLookup.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexLookup.java index 6b71f3d..9a84e4f 100644 --- a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexLookup.java +++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexLookup.java @@ -26,19 +26,19 @@ import org.apache.jackrabbit.oak.spi.state.NodeState; */ public class OrderedPropertyIndexLookup extends PropertyIndexLookup { - private final static IndexStoreStrategy STORE = new OrderedContentMirrorStoreStrategy(); - - public OrderedPropertyIndexLookup(NodeState root) { - super(root); - } + private static final IndexStoreStrategy STORE = new OrderedContentMirrorStoreStrategy(); - @Override - IndexStoreStrategy getStrategy(NodeState indexMeta) { - return STORE; - } + public OrderedPropertyIndexLookup(NodeState root) { + super(root); + } - @Override - String getType() { - return OrderedIndex.TYPE; - } + @Override + IndexStoreStrategy getStrategy(NodeState indexMeta) { + return STORE; + } + + @Override + String getType() { + return OrderedIndex.TYPE; + } } diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndex.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndex.java index dfe7bc8..dd36ba6 100644 --- a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndex.java +++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndex.java @@ -36,10 +36,9 @@ import com.google.common.collect.Iterables; /** * Provides a QueryIndex that does lookups against a property index - * + * *

- * To define a property index on a subtree you have to add an oak:index node. - *
+ * To define a property index on a subtree you have to add an oak:index node.
* Next (as a child node) follows the index definition node that: *

    *
  • must be of type oak:QueryIndexDefinition
  • @@ -49,15 +48,17 @@ import com.google.common.collect.Iterables; *

    *

    * Optionally you can specify - *

      - *
    • a uniqueness constraint on a property index by setting the unique flag to true
    • - *
    • that the property index only applies to a certain node type by setting the declaringNodeTypes property
    • + *
        + *
      • a uniqueness constraint on a property index by setting the unique flag to true
      • + *
      • that the property index only applies to a certain node type by setting the declaringNodeTypes + * property
      • *
      *

      *

      * Notes: *

        - *
      • propertyNames can be a list of properties, and it is optional.in case it is missing, the node name will be used as a property name reference value
      • + *
      • propertyNames can be a list of properties, and it is optional.in case it is missing, the node name + * will be used as a property name reference value
      • *
      • reindex is a property that when set to true, triggers a full content reindex.
      • *
      *

      @@ -113,7 +114,7 @@ class PropertyIndex implements QueryIndex { return values; } - //--------------------------------------------------------< QueryIndex >-- + // --------------------------------------------------------< QueryIndex >-- @Override public String getIndexName() { @@ -121,14 +122,15 @@ class PropertyIndex implements QueryIndex { } /** - * return the proper implementaion of the Lookup + * return the proper implementation of the Lookup + * * @param root * @return */ - PropertyIndexLookup getLookup(NodeState root){ - return new PropertyIndexLookup(root); + PropertyIndexLookup getLookup(NodeState root) { + return new PropertyIndexLookup(root); } - + @Override public double getCost(Filter filter, NodeState root) { if (filter.getFullTextConstraint() != null) { @@ -142,8 +144,7 @@ class PropertyIndex implements QueryIndex { // TODO support indexes on a path // currently, only indexes on the root node are supported if (lookup.isIndexed(propertyName, "/", filter)) { - if (pr.firstIncluding && pr.lastIncluding - && pr.first != null && pr.first.equals(pr.last)) { + if (pr.firstIncluding && pr.lastIncluding && pr.first != null && pr.first.equals(pr.last)) { // "[property] = $value" return lookup.getCost(filter, propertyName, pr.first); } else if (pr.list != null) { @@ -175,8 +176,7 @@ class PropertyIndex implements QueryIndex { // currently, only indexes on the root node are supported if (lookup.isIndexed(propertyName, "/", filter)) { // equality - if (pr.firstIncluding && pr.lastIncluding - && pr.first != null && pr.first.equals(pr.last)) { + if (pr.firstIncluding && pr.lastIncluding && pr.first != null && pr.first.equals(pr.last)) { // "[property] = $value" paths = lookup.query(filter, propertyName, pr.first); break; @@ -198,7 +198,8 @@ class PropertyIndex implements QueryIndex { } } if (paths == null) { - throw new IllegalStateException("Property index is used even when no index is available for filter " + filter); + throw new IllegalStateException("Property index is used even when no index is available for filter " + + filter); } Cursor c = Cursors.newPathCursor(paths); if (depth > 1) { @@ -206,7 +207,7 @@ class PropertyIndex implements QueryIndex { } return c; } - + @Override public String getPlan(Filter filter, NodeState root) { StringBuilder buff = new StringBuilder("property"); @@ -217,8 +218,7 @@ class PropertyIndex implements QueryIndex { // TODO support indexes on a path // currently, only indexes on the root node are supported if (lookup.isIndexed(propertyName, "/", filter)) { - if (pr.firstIncluding && pr.lastIncluding - && pr.first != null && pr.first.equals(pr.last)) { + if (pr.firstIncluding && pr.lastIncluding && pr.first != null && pr.first.equals(pr.last)) { buff.append(' ').append(propertyName).append('=').append(pr.first); } else { buff.append(' ').append(propertyName); @@ -246,4 +246,4 @@ class PropertyIndex implements QueryIndex { return buff.toString(); } -} \ No newline at end of file +} diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexLookup.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexLookup.java index 4915962..a2ae9d1 100644 --- a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexLookup.java +++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexLookup.java @@ -18,14 +18,13 @@ package org.apache.jackrabbit.oak.plugins.index.property; import static com.google.common.collect.Iterables.contains; import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.DECLARING_NODE_TYPES; +import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.INDEX_CONTENT_NODE_NAME; import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.INDEX_DEFINITIONS_NAME; import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.PROPERTY_NAMES; import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.TYPE_PROPERTY_NAME; -import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.INDEX_CONTENT_NODE_NAME; -import static org.apache.jackrabbit.oak.plugins.index.property.PropertyIndexEditorProvider.TYPE; import static org.apache.jackrabbit.oak.plugins.index.property.PropertyIndex.encode; +import static org.apache.jackrabbit.oak.plugins.index.property.PropertyIndexEditorProvider.TYPE; -import java.util.Iterator; import java.util.Set; import javax.annotation.Nullable; @@ -43,10 +42,8 @@ import org.apache.jackrabbit.oak.spi.state.ChildNodeEntry; import org.apache.jackrabbit.oak.spi.state.NodeState; /** - * Is responsible for querying the property index content. - *
      - * This class can be used directly on a subtree where there is an index defined - * by supplying a {@link NodeState} root. + * Is responsible for querying the property index content.
      + * This class can be used directly on a subtree where there is an index defined by supplying a {@link NodeState} root. * *
        * 
      @@ -64,19 +61,17 @@ public class PropertyIndexLookup {
            * The cost overhead to use the index in number of read operations.
            */
           private static final int COST_OVERHEAD = 2;
      -    
      +
           /**
            * The maximum cost when the index can be used.
            */
           private static final int MAX_COST = 100;
       
           /** Index storage strategy */
      -    private static final IndexStoreStrategy MIRROR =
      -            new ContentMirrorStoreStrategy();
      +    private static final IndexStoreStrategy MIRROR = new ContentMirrorStoreStrategy();
       
           /** Index storage strategy */
      -    private static final IndexStoreStrategy UNIQUE =
      -            new UniqueEntryStoreStrategy();
      +    private static final IndexStoreStrategy UNIQUE = new UniqueEntryStoreStrategy();
       
           private final NodeState root;
       
      @@ -85,13 +80,15 @@ public class PropertyIndexLookup {
           }
       
           /**
      -     * Checks whether the named property is indexed somewhere along the given
      -     * path. Lookup starts at the current path (at the root of this object) and
      -     * traverses down the path.
      +     * Checks whether the named property is indexed somewhere along the given path. Lookup starts at the current path
      +     * (at the root of this object) and traverses down the path.
            * 
      -     * @param propertyName property name
      -     * @param path lookup path
      -     * @param filter for the node type restriction (null if no node type restriction)
      +     * @param propertyName
      +     *            property name
      +     * @param path
      +     *            lookup path
      +     * @param filter
      +     *            for the node type restriction (null if no node type restriction)
            * @return true if the property is indexed
            */
           public boolean isIndexed(String propertyName, String path, Filter filter) {
      @@ -100,12 +97,11 @@ public class PropertyIndexLookup {
               }
       
               NodeState node = root;
      -        Iterator it = PathUtils.elements(path).iterator();
      -        while (it.hasNext()) {
      +        for(String s : PathUtils.elements(path)){
                   if (getIndexNode(node, propertyName, filter) != null) {
                       return true;
                   }
      -            node = node.getChildNode(it.next());
      +            node = node.getChildNode(s);
               }
               return false;
           }
      @@ -117,7 +113,7 @@ public class PropertyIndexLookup {
               }
               return getStrategy(indexMeta).query(filter, propertyName, indexMeta, encode(value));
           }
      -        
      +
           IndexStoreStrategy getStrategy(NodeState indexMeta) {
               if (indexMeta.getBoolean(IndexConstants.UNIQUE_PROPERTY_NAME)) {
                   return UNIQUE;
      @@ -130,24 +126,21 @@ public class PropertyIndexLookup {
               if (indexMeta == null) {
                   return Double.POSITIVE_INFINITY;
               }
      -        return COST_OVERHEAD + 
      -                getStrategy(indexMeta).count(indexMeta, encode(value), MAX_COST);
      +        return COST_OVERHEAD + getStrategy(indexMeta).count(indexMeta, encode(value), MAX_COST);
           }
       
           /**
      -     * Get the node with the index definition for the given property, if there
      -     * is an applicable index with data.
      +     * Get the node with the index definition for the given property, if there is an applicable index with data.
            * 
      -     * @param propertyName the property name
      -     * @param filter the filter (which contains information of all supertypes,
      -     *            unless the filter matches all types)
      -     * @return the node where the index definition (metadata) is stored (the
      -     *         parent of ":index"), or null if no index definition or index data
      -     *         node was found
      +     * @param propertyName
      +     *            the property name
      +     * @param filter
      +     *            the filter (which contains information of all supertypes, unless the filter matches all types)
      +     * @return the node where the index definition (metadata) is stored (the parent of ":index"), or null if no index
      +     *         definition or index data node was found
            */
           @Nullable
      -    private NodeState getIndexNode(
      -            NodeState node, String propertyName, Filter filter) {
      +    private NodeState getIndexNode(NodeState node, String propertyName, Filter filter) {
               // keep a fallback to a matching index def that has *no* node type constraints
               // (initially, there is no fallback)
               NodeState fallback = null;
      @@ -184,16 +177,16 @@ public class PropertyIndexLookup {
               }
               return fallback;
           }
      -    
      +
           /**
      -     * retrieve the type of the index 
      +     * retrieve the type of the index
            * 
      -     * @return 
      +     * @return
            */
      -    String getType(){
      -       return TYPE;
      +    String getType() {
      +        return TYPE;
           }
      -    
      +
           private static Set getSuperTypes(Filter filter) {
               if (filter != null && !filter.matchesAllTypes()) {
                   return filter.getSupertypes();
      -- 
      1.8.3.4 (Apple Git-47)
      
      
      From d13d9f34a612f6411028e54bd94dbd9184dc4d1c Mon Sep 17 00:00:00 2001
      From: Davide Giannella 
      Date: Fri, 28 Feb 2014 09:12:01 +0100
      Subject: [PATCH 35/36] OAK-1263 code clean-up
      
      ---
       .../OrderedContentMirrorStoreStrategy.java         | 24 +++++++++++++---------
       .../property/OrderedPropertyIndexEditorTest.java   |  6 +++---
       2 files changed, 17 insertions(+), 13 deletions(-)
      
      diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/OrderedContentMirrorStoreStrategy.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/OrderedContentMirrorStoreStrategy.java
      index c3df9d5..22d7717 100644
      --- a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/OrderedContentMirrorStoreStrategy.java
      +++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/OrderedContentMirrorStoreStrategy.java
      @@ -38,22 +38,22 @@ import com.google.common.base.Strings;
       
       
       public class OrderedContentMirrorStoreStrategy extends ContentMirrorStoreStrategy {
      -   private final static Logger log = LoggerFactory.getLogger(OrderedContentMirrorStoreStrategy.class);
      +   private static final Logger log = LoggerFactory.getLogger(OrderedContentMirrorStoreStrategy.class);
          
          /**
           * the property linking to the next node
           */
      -   public final static String NEXT = ":next";
      +   public static final String NEXT = ":next";
          
          /**
           * node that works as root of the index (start point or 0 element)
           */
      -   public final static String START = ":start";
      +   public static final String START = ":start";
          
          /**
           * a NodeState used for easy creating of an empty :start
           */
      -   public final static NodeState EMPTY_START_NODE = EmptyNodeState.EMPTY_NODE.builder().setProperty(NEXT, "").getNodeState(); 
      +   public static final NodeState EMPTY_START_NODE = EmptyNodeState.EMPTY_NODE.builder().setProperty(NEXT, "").getNodeState(); 
          
          @Override
          NodeBuilder fetchKeyNode(@Nonnull NodeBuilder index, @Nonnull String key) {
      @@ -76,7 +76,7 @@ public class OrderedContentMirrorStoreStrategy extends ContentMirrorStoreStrateg
                   _key.setProperty(NEXT,nextKey);
                   start.setProperty(NEXT, key);
                }else{
      -            @SuppressWarnings("unchecked") Iterable children = (Iterable)getChildNodeEntries(index.getNodeState());
      +            Iterable children = getChildNodeEntries(index.getNodeState());
                   for(ChildNodeEntry child : children){
                      nextKey = child.getNodeState().getString(NEXT);
                      if(Strings.isNullOrEmpty(nextKey)){
      @@ -110,7 +110,9 @@ public class OrderedContentMirrorStoreStrategy extends ContentMirrorStoreStrateg
                      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
      -               if(next==null) next = "";
      +               if(next==null) {
      +                   next = "";
      +               }
                      index.getChildNode(previous.getName()).setProperty(NEXT, next); //(3) re-link the previous to the next
                      node.remove(); //(4) remove the current node
                   }else{
      @@ -120,12 +122,11 @@ public class OrderedContentMirrorStoreStrategy extends ContentMirrorStoreStrateg
             }
          }
       
      -   @SuppressWarnings("unchecked")
          @Nullable ChildNodeEntry findPrevious(@Nonnull final NodeState index, @Nonnull final NodeState node){
             ChildNodeEntry previous = null;
             ChildNodeEntry current = null; 
             boolean found = false;
      -      Iterator it = (Iterator) getChildNodeEntries(index,true).iterator();
      +      Iterator it = getChildNodeEntries(index,true).iterator();
             
             while(!found && it.hasNext()){
                current = it.next();
      @@ -134,7 +135,9 @@ public class OrderedContentMirrorStoreStrategy extends ContentMirrorStoreStrateg
                   previous = current;
                }else{
                   found = node.equals(current.getNodeState());
      -            if(!found) previous = current;
      +            if(!found) {
      +                previous = current;
      +            }
                }
             }
             
      @@ -157,6 +160,7 @@ public class OrderedContentMirrorStoreStrategy extends ContentMirrorStoreStrateg
           * @param index the root of the index (:index)
           * @return
           */
      +   @Override
          @Nonnull Iterable getChildNodeEntries(@Nonnull final NodeState index){
             return getChildNodeEntries(index, false);
          }
      @@ -223,7 +227,7 @@ public class OrderedContentMirrorStoreStrategy extends ContentMirrorStoreStrateg
             return cne;   
          }
          
      -   private final class OrderedChildNodeEntry extends AbstractChildNodeEntry {
      +   private static final class OrderedChildNodeEntry extends AbstractChildNodeEntry {
             private final String name;
             private final NodeState state;
             
      diff --git a/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexEditorTest.java b/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexEditorTest.java
      index bbe7681..34f4562 100644
      --- a/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexEditorTest.java
      +++ b/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexEditorTest.java
      @@ -40,7 +40,7 @@ public class OrderedPropertyIndexEditorTest {
             replay(definition);
             
             OrderedPropertyIndexEditor ie = new OrderedPropertyIndexEditor(definition, null, null);
      -      assertNotNull("With a correct proprety set 'propertyNames' can't be null",ie.getPropertyNames());
      +      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());
      @@ -53,13 +53,13 @@ public class OrderedPropertyIndexEditorTest {
             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(new String[]{"jcr:lastModified","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 proprety set 'propertyNames' can't be null",ie.getPropertyNames());
      +      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());
      -- 
      1.8.3.4 (Apple Git-47)
      
      
      From ef6e0419b65d332304dc3c50180eddbe64490cc7 Mon Sep 17 00:00:00 2001
      From: Davide Giannella 
      Date: Fri, 28 Feb 2014 10:25:24 +0100
      Subject: [PATCH 36/36] OAK-1263 code clean-up
      
      ---
       .../property/OrderedPropertyIndexQueryTest.java    | 74 ++++++++++++----------
       .../OrderedContentMirrorStorageStrategyTest.java   |  7 +-
       .../oak/benchmark/OrderedIndexBaseTest.java        | 24 ++++---
       .../oak/benchmark/OrderedIndexQueryBaseTest.java   | 10 +--
       4 files changed, 68 insertions(+), 47 deletions(-)
      
      diff --git a/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexQueryTest.java b/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexQueryTest.java
      index a024021..5329c4c 100644
      --- a/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexQueryTest.java
      +++ b/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/OrderedPropertyIndexQueryTest.java
      @@ -58,7 +58,7 @@ public class OrderedPropertyIndexQueryTest extends AbstractQueryTest {
           /**
            * the property used by the index
            */
      -    public final static String ORDERED_PROPERTY = "foo";
      +    public static final String ORDERED_PROPERTY = "foo";
       
           /**
            * number of nodes to create for testing.
      @@ -68,16 +68,16 @@ public class OrderedPropertyIndexQueryTest extends AbstractQueryTest {
            * 
            * The higher the value the lower the chance for this to happen.
            */
      -    private final static int NUMBER_OF_NODES = 50;
      +    private static final int NUMBER_OF_NODES = 50;
       
           /**
      -     * convenience orderable object that represet a tuple of values and paths
      +     * 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 {
      -        private String value;
      -        private String path;
      +        private final String value;
      +        private final String path;
       
               ValuePathTuple(String value, String path) {
                   this.value = value;
      @@ -96,43 +96,53 @@ public class OrderedPropertyIndexQueryTest extends AbstractQueryTest {
       
               @Override
               public boolean equals(Object obj) {
      -            if (this == obj)
      +            if (this == obj){
                       return true;
      -            if (obj == null)
      +            }
      +            if (obj == null){
                       return false;
      -            if (getClass() != obj.getClass())
      +            }
      +            if (getClass() != obj.getClass()){
                       return false;
      +            }
                   ValuePathTuple other = (ValuePathTuple) obj;
      -            if (!getOuterType().equals(other.getOuterType()))
      +            if (!getOuterType().equals(other.getOuterType())){
                       return false;
      +            }
                   if (path == null) {
      -                if (other.path != null)
      +                if (other.path != null){
                           return false;
      -            } else if (!path.equals(other.path))
      +                }
      +            } else if (!path.equals(other.path)){
                       return false;
      +            }
                   if (value == null) {
      -                if (other.value != null)
      +                if (other.value != null){
                           return false;
      -            } else if (!value.equals(other.value))
      +                }
      +            } else if (!value.equals(other.value)){
                       return false;
      +            }
                   return true;
               }
       
               @Override
               public int compareTo(ValuePathTuple o) {
      -            if (this.equals(o))
      +            if (this.equals(o)){
                       return 0;
      -
      -            if (this.value.compareTo(o.value) < 0)
      +            }
      +            if (this.value.compareTo(o.value) < 0){
                       return -1;
      -            if (this.value.compareTo(o.value) > 0)
      +            }
      +            if (this.value.compareTo(o.value) > 0){
                       return 1;
      -
      -            if (this.path.compareTo(o.path) < 0)
      +            }
      +            if (this.path.compareTo(o.path) < 0){
                       return -1;
      -            if (this.path.compareTo(o.path) > 0)
      +            }
      +            if (this.path.compareTo(o.path) > 0){
                       return 1;
      -
      +            }
                   return 0;
               }
       
      @@ -149,7 +159,7 @@ public class OrderedPropertyIndexQueryTest extends AbstractQueryTest {
           public void valuePathTupleComparison() {
               try {
                   new ValuePathTuple("value", "path").compareTo(null);
      -            fail("It should have raisen a NPE");
      +            fail("It should have raised a NPE");
               } catch (NullPointerException e) {
                   // so far so good
               }
      @@ -187,7 +197,7 @@ public class OrderedPropertyIndexQueryTest extends AbstractQueryTest {
            *            the value of the property to assign
            * @return
            */
      -    private Tree child(Tree father, String name, String propName, String propValue) {
      +    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);
      @@ -202,15 +212,15 @@ public class OrderedPropertyIndexQueryTest extends AbstractQueryTest {
            * @param amount
            * @return
            */
      -    private List generateOrderedValues(int amount) {
      -        if (amount > 1000)
      +    private static List generateOrderedValues(int amount) {
      +        if (amount > 1000){
                   throw new RuntimeException("amount cannot be greater than 100");
      -
      +        }
               List values = new ArrayList(amount);
               NumberFormat nf = new DecimalFormat("000");
      -        for (int i = 0; i < amount; i++)
      +        for (int i = 0; i < amount; i++){
                   values.add(String.format("value%s", String.valueOf(nf.format(i))));
      -
      +        }
               return values;
           }
       
      @@ -221,14 +231,14 @@ public class OrderedPropertyIndexQueryTest extends AbstractQueryTest {
            * @param values
            *            the values of the property that will be indexed
            * @param father
      -     *            the father under which add the ndoes
      +     *            the father under which add the nodes
            * @return
            */
           private List addChildNodes(final List values, final Tree father) {
               List nodes = new ArrayList();
               Random rnd = new Random();
               int counter = 0;
      -        while (values.size() > 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()));
      @@ -252,7 +262,7 @@ public class OrderedPropertyIndexQueryTest extends AbstractQueryTest {
            * @param orderedSequence
            *            the right order in which the resultset should be returned
            * @param resultset
      -     *            the resultes
      +     *            the resultset
            */
           private void assertRightOrder(@Nonnull
           final List orderedSequence, @Nonnull
      @@ -313,7 +323,7 @@ public class OrderedPropertyIndexQueryTest extends AbstractQueryTest {
               List nodes = addChildNodes(generateOrderedValues(NUMBER_OF_NODES), test);
               root.commit();
       
      -        ValuePathTuple searchfor = nodes.get((int) NUMBER_OF_NODES / 2); // getting the middle of the random list of
      +        ValuePathTuple searchfor = nodes.get(NUMBER_OF_NODES / 2); // getting the middle of the random list of
                                                                                // nodes.
               Map filter = ImmutableMap
                   .of(ORDERED_PROPERTY, PropertyValues.newString(searchfor.value));
      diff --git a/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/OrderedContentMirrorStorageStrategyTest.java b/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/OrderedContentMirrorStorageStrategyTest.java
      index deb5562..527edcc 100644
      --- a/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/OrderedContentMirrorStorageStrategyTest.java
      +++ b/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/OrderedContentMirrorStorageStrategyTest.java
      @@ -46,8 +46,8 @@ public class OrderedContentMirrorStorageStrategyTest {
           /**
            * ascending ordered set of keys. Useful for testing
            */
      -    private final static String[] KEYS = new String[] { "donald", "goofy", "mickey", "minnie" };
      -    private final static Set EMPTY_KEY_SET = newHashSet();
      +    private static final String[] KEYS = new String[] { "donald", "goofy", "mickey", "minnie" };
      +    private static final Set EMPTY_KEY_SET = newHashSet();
       
           /**
            * checks that the fist item/key is inserted with an empty property 'next'
      @@ -607,7 +607,7 @@ public class OrderedContentMirrorStorageStrategyTest {
               assertTrue("n0 should have /content", node.exists());
       
               node = node.getChildNode(NODES[1]);
      -        assertTrue("/content should cointain /foobar", node.exists());
      +        assertTrue("/content should contain /foobar", node.exists());
               assertTrue("/foobar should have match=true", node.getBoolean("match"));
       
               // Stage 2
      @@ -657,6 +657,7 @@ public class OrderedContentMirrorStorageStrategyTest {
       
               NodeState indexState = index.getNodeState();
               ChildNodeEntry previous = store.findPrevious(indexState, NODE_0);
      +        assertNotNull(previous);
               assertEquals("the :start node is expected", NODE_START, previous.getNodeState());
           }
       
      diff --git a/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/OrderedIndexBaseTest.java b/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/OrderedIndexBaseTest.java
      index 143d8d0..674ab89 100644
      --- a/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/OrderedIndexBaseTest.java
      +++ b/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/OrderedIndexBaseTest.java
      @@ -36,22 +36,22 @@ public abstract class OrderedIndexBaseTest extends AbstractTest {
           /**
            * the number of nodes created per iteration
            */
      -    final static int NODES_PER_ITERATION = Integer.parseInt(System.getProperty("nodesPerIteration", "100"));
      +    static final int NODES_PER_ITERATION = Integer.parseInt(System.getProperty("nodesPerIteration", "100"));
           
           /**
            * number of nodes that has to be added before performing the actual test
            */
      -    final static int PRE_ADDED_NODES = Integer.parseInt(System.getProperty("preAddedNodes", "0"));
      +    static final int PRE_ADDED_NODES = Integer.parseInt(System.getProperty("preAddedNodes", "0"));
       
           /**
           * type of the created node
           */
      -   final static String NODE_TYPE = NodeTypeConstants.NT_OAK_UNSTRUCTURED;
      +   static final String NODE_TYPE = NodeTypeConstants.NT_OAK_UNSTRUCTURED;
             
          /**
           * property that will be indexed
           */
      -   final static String INDEXED_PROPERTY = "indexedProperty";
      +   static final String INDEXED_PROPERTY = "indexedProperty";
          
          /**
           * node name below which creating the test data
      @@ -88,16 +88,24 @@ public abstract class OrderedIndexBaseTest extends AbstractTest {
          
          Node defineStandardPropertyIndex(Session session) throws Exception {
              Node index = new OakIndexUtils.PropertyIndex().property(INDEXED_PROPERTY).create(session);
      -       if(index == null) throw new RuntimeException("Error while creating the index definition. index node is null.");
      -       if(!PropertyIndexEditorProvider.TYPE.equals(index.getProperty(IndexConstants.TYPE_PROPERTY_NAME).getString())) throw new RuntimeException("The type of the index does not match the expected");
      +       if(index == null) {
      +           throw new RuntimeException("Error while creating the index definition. index node is null.");
      +       }
      +       if(!PropertyIndexEditorProvider.TYPE.equals(index.getProperty(IndexConstants.TYPE_PROPERTY_NAME).getString())) {
      +           throw new RuntimeException("The type of the index does not match the expected");
      +       }
              session.save();
              return index;
          }
          
          Node defineOrderedPropertyIndex(Session session) throws Exception {
              Node index = new OakIndexUtils.PropertyIndex().property(INDEXED_PROPERTY).create(session,OrderedPropertyIndexEditorProvider.TYPE);
      -       if(index == null) throw new RuntimeException("Error while creating the index definition. index node is null.");
      -       if(!OrderedPropertyIndexEditorProvider.TYPE.equals(index.getProperty(IndexConstants.TYPE_PROPERTY_NAME).getString())) throw new RuntimeException("The index type does not match the expected");
      +       if(index == null) {
      +           throw new RuntimeException("Error while creating the index definition. index node is null.");
      +       }
      +       if(!OrderedPropertyIndexEditorProvider.TYPE.equals(index.getProperty(IndexConstants.TYPE_PROPERTY_NAME).getString())) {
      +           throw new RuntimeException("The index type does not match the expected");
      +       }
              session.save();
              return index;
          }
      diff --git a/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/OrderedIndexQueryBaseTest.java b/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/OrderedIndexQueryBaseTest.java
      index 3d4df25..28338ff 100644
      --- a/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/OrderedIndexQueryBaseTest.java
      +++ b/oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/OrderedIndexQueryBaseTest.java
      @@ -31,18 +31,18 @@ public abstract class OrderedIndexQueryBaseTest extends OrderedIndexBaseTest {
           /**
            * query to execute with the ORDER BY statement
            */
      -    public final static String QUERY_WITH_ORDER = String.format(
      +    public static final String QUERY_WITH_ORDER = String.format(
               "SELECT * FROM [%s] WHERE %s IS NOT NULL ORDER BY %s", NODE_TYPE, INDEXED_PROPERTY, INDEXED_PROPERTY);
           
           /**
            * constant used to identify how many nodes will be fetched after the query execution
            */
      -    public final static int FETCH_NODES = 100;
      +    public static final int FETCH_NODES = 100;
           
           /**
            * query to execute WITHOUT the ORDER BY clause
            */
      -    public final static String QUERY_WITHOUT_ORDER = String.format(
      +    public static final String QUERY_WITHOUT_ORDER = String.format(
               "SELECT * FROM [%s] WHERE %s IS NOT NULL", NODE_TYPE, INDEXED_PROPERTY);
       
           @Override
      @@ -57,7 +57,9 @@ public abstract class OrderedIndexQueryBaseTest extends OrderedIndexBaseTest {
           @Override
           protected void afterSuite() throws Exception {
               dump.remove();
      -        if(index!=null) index.remove();
      +        if(index!=null) {
      +            index.remove();
      +        }
               session.save();
               session.logout();
           }
      -- 
      1.8.3.4 (Apple Git-47)