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 extends ChildNodeEntry> 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 extends ChildNodeEntry> getChildNodeEntries(@Nonnull final NodeState index, final boolean includeStart){
+ Iterable extends ChildNodeEntry> 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 extends ChildNodeEntry> 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 extends ChildNodeEntry> children = store.getChildNodeEntries(index.getNodeState(), true);
+ assertEquals("Wrong size of Iterable",1,Iterators.size(children.iterator()));
+
+ Iterator extends ChildNodeEntry> it = store.getChildNodeEntries(index.getNodeState(), true).iterator();
+ assertTrue("We should have at least 1 element",it.hasNext());
+ ChildNodeEntry entry = it.next();
+ assertEquals(":start is expected",START,entry.getName());
+ assertEquals("wrong node returned",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 extends ChildNodeEntry> getChildNodeEntries(@Nonnull final NodeState index, final boolean includeStart){
Iterable extends ChildNodeEntry> 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 extends ChildNodeEntry> children = store.getChildNodeEntries(index.getNodeState(), true).iterator();
+ assertEquals("Wrong number of children",1,Iterators.size(children));
+
+ children = store.getChildNodeEntries(index.getNodeState(), true).iterator();
+ assertTrue("at least one item expected",children.hasNext());
+ ChildNodeEntry child = children.next();
+ assertEquals(START,child.getName());
+ assertEquals(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 extends QueryIndex> 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 extends ResultRow> 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 extends ResultRow> 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 extends ResultRow> 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 extends ResultRow> 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 extends ResultRow> 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 extends ResultRow> 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 extends ChildNodeEntry> 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 extends ChildNodeEntry> 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 extends ChildNodeEntry> 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 extends ChildNodeEntry> 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 extends ChildNodeEntry> 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 extends ResultRow> 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 extends ChildNodeEntry> children = store.getChildNodeEntries(index.getNodeState(), true);
- assertEquals("Wrong size of Iterable",1,Iterators.size(children.iterator()));
-
- Iterator extends ChildNodeEntry> it = store.getChildNodeEntries(index.getNodeState(), true).iterator();
- assertTrue("We should have at least 1 element",it.hasNext());
- ChildNodeEntry entry = it.next();
- assertEquals(":start is expected",START,entry.getName());
- assertEquals("wrong node returned",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 extends ChildNodeEntry> children = store.getChildNodeEntries(index.getNodeState(), true).iterator();
- assertEquals("Wrong number of children",1,Iterators.size(children));
-
- children = store.getChildNodeEntries(index.getNodeState(), true).iterator();
- assertTrue("at least one item expected",children.hasNext());
- ChildNodeEntry child = children.next();
- assertEquals(START,child.getName());
- assertEquals(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 extends ChildNodeEntry> children = store.getChildNodeEntries(index.getNodeState(), true);
+ assertEquals("Wrong size of Iterable", 1, Iterators.size(children.iterator()));
+
+ Iterator extends ChildNodeEntry> it = store.getChildNodeEntries(index.getNodeState(), true).iterator();
+ assertTrue("We should have at least 1 element", it.hasNext());
+ ChildNodeEntry entry = it.next();
+ assertEquals(":start is expected", START, entry.getName());
+ assertEquals("wrong node returned", 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 extends ChildNodeEntry> children = store.getChildNodeEntries(index.getNodeState(), true).iterator();
+ assertEquals("Wrong number of children", 1, Iterators.size(children));
+
+ children = store.getChildNodeEntries(index.getNodeState(), true).iterator();
+ assertTrue("at least one item expected", children.hasNext());
+ ChildNodeEntry child = children.next();
+ assertEquals(START, child.getName());
+ assertEquals(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 extends ResultRow> results;
- results = executeQuery(
- String.format("SELECT * from [%s] WHERE foo IS NOT NULL", NT_UNSTRUCTURED),
- SQL2,
- null
- ).getRows().iterator();
- assertRightOrder(nodes, results);
-
- setTravesalEnabled(true);
- }
-
- /**
- * 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 extends ResultRow> resultset){
- assertTrue("No results returned", resultset.hasNext());
- int counter = 0;
- while(resultset.hasNext() && counter < orderedSequence.size()){
- ResultRow row = resultset.next();
- assertEquals(String.format("Wrong path at the element '%d'",counter), orderedSequence.get(counter).path,row.getPath());
- counter++;
- }
- }
+ }
+
+ @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 extends ResultRow> results;
+ results = executeQuery(String.format("SELECT * from [%s] WHERE foo IS NOT NULL", NT_UNSTRUCTURED), SQL2, null)
+ .getRows().iterator();
+ assertRightOrder(nodes, results);
+
+ setTravesalEnabled(true);
+ }
+
+ /**
+ * 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 extends ResultRow> resultset) {
+ assertTrue("No results returned", resultset.hasNext());
+ int counter = 0;
+ while (resultset.hasNext() && counter < orderedSequence.size()) {
+ ResultRow row = resultset.next();
+ assertEquals(String.format("Wrong path at the element '%d'", counter), orderedSequence.get(counter).path,
+ row.getPath());
+ counter++;
+ }
+ }
}
--
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 extends ResultRow> resultset) {
+ assertTrue("No results returned", resultset.hasNext());
+ int counter = 0;
+ while (resultset.hasNext() && counter < orderedSequence.size()) {
+ ResultRow row = resultset.next();
+ assertEquals(String.format("Wrong path at the element '%d'", counter), orderedSequence.get(counter).path,
+ row.getPath());
+ counter++;
+ }
+ }
+
+ /**
* Query the index for retrieving all the entries
*
* @throws CommitFailedException
@@ -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 extends ResultRow> 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 extends ResultRow> resultset) {
- assertTrue("No results returned", resultset.hasNext());
- int counter = 0;
- while (resultset.hasNext() && counter < orderedSequence.size()) {
- ResultRow row = resultset.next();
- assertEquals(String.format("Wrong path at the element '%d'", counter), orderedSequence.get(counter).path,
- row.getPath());
- counter++;
- }
+ @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 extends ResultRow> results = executeQuery(
+ String.format(query,NT_UNSTRUCTURED,ORDERED_PROPERTY,ORDERED_PROPERTY),
+ SQL2,
+ filter
+ ).getRows().iterator();
+ assertTrue("one element is expected",results.hasNext());
+ assertEquals("wrong path returned",searchfor.path,results.next().getPath());
+ 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 extends ResultRow> results = executeQuery(
- String.format(query,NT_UNSTRUCTURED,ORDERED_PROPERTY,ORDERED_PROPERTY),
- SQL2,
- filter
- ).getRows().iterator();
- assertTrue("one element is expected",results.hasNext());
- assertEquals("wrong path returned",searchfor.path,results.next().getPath());
- 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 extends ResultRow> 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 extends ResultRow> 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 extends ChildNodeEntry> 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 extends ChildNodeEntry> 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 extends ChildNodeEntry> 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)