Author: thomasm
Date: Fri Apr 28 14:23:28 2017
New Revision: 1793097
URL: http://svn.apache.org/viewvc?rev=1793097&view=rev
Log:
OAK-4637 Property index: include/exclude key pattern list
Added:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/ValuePattern.java
Modified:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/IndexConstants.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndex.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexEditor.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexLookup.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexPlan.java
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexTest.java
Modified:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/IndexConstants.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/IndexConstants.java?rev=1793097&r1=1793096&r2=1793097&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/IndexConstants.java
(original)
+++
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/IndexConstants.java
Fri Apr 28 14:23:28 2017
@@ -46,6 +46,11 @@ public interface IndexConstants {
String ENTRY_COUNT_PROPERTY_NAME = "entryCount";
String KEY_COUNT_PROPERTY_NAME = "keyCount";
+
+ /**
+ * The regular expression pattern of the values to be indexes.
+ */
+ String VALUE_PATTERN = "valuePattern";
/**
* Marks a unique property index.
Modified:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndex.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndex.java?rev=1793097&r1=1793096&r2=1793097&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndex.java
(original)
+++
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndex.java
Fri Apr 28 14:23:28 2017
@@ -110,7 +110,7 @@ class PropertyIndex implements QueryInde
this.mountInfoProvider = mountInfoProvider;
}
- static Set<String> encode(PropertyValue value) {
+ static Set<String> encode(PropertyValue value, ValuePattern pattern) {
if (value == null) {
return null;
}
@@ -120,6 +120,9 @@ class PropertyIndex implements QueryInde
if (v.length() > MAX_STRING_LENGTH) {
v = v.substring(0, MAX_STRING_LENGTH);
}
+ if (!pattern.matches(v)) {
+ continue;
+ }
if (v.isEmpty()) {
v = EMPTY_TOKEN;
} else {
Modified:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexEditor.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexEditor.java?rev=1793097&r1=1793096&r2=1793097&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexEditor.java
(original)
+++
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexEditor.java
Fri Apr 28 14:23:28 2017
@@ -74,7 +74,9 @@ class PropertyIndexEditor implements Ind
/** Root node state */
private final NodeState root;
- private final Set<String> propertyNames;
+ private final Set<String> propertyNames;
+
+ private final ValuePattern valuePattern;
/** Type predicate, or {@code null} if there are no type restrictions */
private final Predicate<NodeState> typePredicate;
@@ -128,6 +130,7 @@ class PropertyIndexEditor implements Ind
} else {
this.propertyNames = newHashSet(names.getValue(NAMES));
}
+ this.valuePattern = new
ValuePattern(definition.getString(IndexConstants.VALUE_PATTERN));
// get declaring types, and all their subtypes
// TODO: should we reindex when type definitions change?
@@ -155,6 +158,7 @@ class PropertyIndexEditor implements Ind
this.definition = parent.definition;
this.root = parent.root;
this.propertyNames = parent.getPropertyNames();
+ this.valuePattern = parent.valuePattern;
this.typePredicate = parent.typePredicate;
this.keysToCheckForUniqueness = parent.keysToCheckForUniqueness;
this.updateCallback = parent.updateCallback;
@@ -193,24 +197,24 @@ class PropertyIndexEditor implements Ind
* @return set of encoded values, possibly initialized
*/
private static Set<String> addValueKeys(
- Set<String> keys, PropertyState property) {
+ Set<String> keys, PropertyState property, ValuePattern pattern) {
if (property.getType().tag() != PropertyType.BINARY
&& property.count() > 0) {
if (keys == null) {
keys = newHashSet();
}
- keys.addAll(encode(PropertyValues.create(property)));
+ keys.addAll(encode(PropertyValues.create(property), pattern));
}
return keys;
}
private static Set<String> getMatchingKeys(
- NodeState state, Iterable<String> propertyNames) {
+ NodeState state, Iterable<String> propertyNames, ValuePattern
pattern) {
Set<String> keys = null;
for (String propertyName : propertyNames) {
PropertyState property = state.getProperty(propertyName);
if (property != null) {
- keys = addValueKeys(keys, property);
+ keys = addValueKeys(keys, property, pattern);
}
}
return keys;
@@ -248,8 +252,8 @@ class PropertyIndexEditor implements Ind
if (typeChanged) {
// possible type change, so ignore diff results and
// just load all matching values from both states
- beforeKeys = getMatchingKeys(before, getPropertyNames());
- afterKeys = getMatchingKeys(after, getPropertyNames());
+ beforeKeys = getMatchingKeys(before, getPropertyNames(),
valuePattern);
+ afterKeys = getMatchingKeys(after, getPropertyNames(),
valuePattern);
}
if (beforeKeys != null && !typePredicate.apply(before)) {
// the before state doesn't match the type, so clear its values
@@ -375,7 +379,7 @@ class PropertyIndexEditor implements Ind
String name = after.getName();
typeChanged = typeChanged || isTypeProperty(name);
if (getPropertyNames().contains(name)) {
- afterKeys = addValueKeys(afterKeys, after);
+ afterKeys = addValueKeys(afterKeys, after, valuePattern);
}
}
@@ -384,8 +388,8 @@ class PropertyIndexEditor implements Ind
String name = after.getName();
typeChanged = typeChanged || isTypeProperty(name);
if (getPropertyNames().contains(name)) {
- beforeKeys = addValueKeys(beforeKeys, before);
- afterKeys = addValueKeys(afterKeys, after);
+ beforeKeys = addValueKeys(beforeKeys, before, valuePattern);
+ afterKeys = addValueKeys(afterKeys, after, valuePattern);
}
}
@@ -394,7 +398,7 @@ class PropertyIndexEditor implements Ind
String name = before.getName();
typeChanged = typeChanged || isTypeProperty(name);
if (getPropertyNames().contains(name)) {
- beforeKeys = addValueKeys(beforeKeys, before);
+ beforeKeys = addValueKeys(beforeKeys, before, valuePattern);
}
}
Modified:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexLookup.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexLookup.java?rev=1793097&r1=1793096&r2=1793097&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexLookup.java
(original)
+++
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexLookup.java
Fri Apr 28 14:23:28 2017
@@ -123,9 +123,10 @@ public class PropertyIndexLookup {
throw new IllegalArgumentException("No index for " + propertyName);
}
List<Iterable<String>> iterables = Lists.newArrayList();
+ ValuePattern pattern = new
ValuePattern(indexMeta.getString(IndexConstants.VALUE_PATTERN));
for (IndexStoreStrategy s : getStrategies(indexMeta)) {
iterables.add(s.query(filter, propertyName, indexMeta,
- encode(value)));
+ encode(value, pattern)));
}
return Iterables.concat(iterables);
}
@@ -143,9 +144,10 @@ public class PropertyIndexLookup {
return Double.POSITIVE_INFINITY;
}
Set<IndexStoreStrategy> strategies = getStrategies(indexMeta);
+ ValuePattern pattern = new
ValuePattern(indexMeta.getString(IndexConstants.VALUE_PATTERN));
double cost = strategies.isEmpty() ? MAX_COST : COST_OVERHEAD;
for (IndexStoreStrategy s : strategies) {
- cost += s.count(filter, root, indexMeta, encode(value), MAX_COST);
+ cost += s.count(filter, root, indexMeta, encode(value, pattern),
MAX_COST);
}
return cost;
}
Modified:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexPlan.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexPlan.java?rev=1793097&r1=1793096&r2=1793097&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexPlan.java
(original)
+++
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexPlan.java
Fri Apr 28 14:23:28 2017
@@ -84,6 +84,8 @@ public class PropertyIndexPlan {
private final PathFilter pathFilter;
private final boolean unique;
+
+ private final ValuePattern valuePattern;
PropertyIndexPlan(String name, NodeState root, NodeState definition,
Filter filter){
@@ -96,6 +98,7 @@ public class PropertyIndexPlan {
this.unique =
definition.getBoolean(IndexConstants.UNIQUE_PROPERTY_NAME);
this.definition = definition;
this.properties = newHashSet(definition.getNames(PROPERTY_NAMES));
+ this.valuePattern = new
ValuePattern(definition.getString(IndexConstants.VALUE_PATTERN));
pathFilter = PathFilter.from(definition.builder());
this.strategies = getStrategies(definition, mountInfoProvider);
this.filter = filter;
@@ -142,7 +145,28 @@ public class PropertyIndexPlan {
// of the child node (well, we could, for some node
types)
continue;
}
- Set<String> values = getValues(restriction);
+ Set<String> values = getValues(restriction, new
ValuePattern(null));
+ if (valuePattern.matchesAll()) {
+ // matches all values: not a problem
+ } else if (values == null) {
+ // "is not null" condition, but we have a value pattern
+ // that doesn't match everything
+ // so we can't use that index
+ continue;
+ } else {
+ boolean allValuesMatches = true;
+ for (String v : values) {
+ if (!valuePattern.matches(v)) {
+ allValuesMatches = false;
+ break;
+ }
+ }
+ // we have a value pattern, for example (a|b),
+ // but we search (also) for 'c': can't match
+ if (!allValuesMatches) {
+ continue;
+ }
+ }
double cost = strategies.isEmpty() ? MAX_COST : 0;
for (IndexStoreStrategy strategy : strategies) {
cost += strategy.count(filter, root, definition,
@@ -172,18 +196,18 @@ public class PropertyIndexPlan {
this.cost = COST_OVERHEAD + bestCost;
}
- private static Set<String> getValues(PropertyRestriction restriction) {
+ private static Set<String> getValues(PropertyRestriction restriction,
ValuePattern pattern) {
if (restriction.firstIncluding
&& restriction.lastIncluding
&& restriction.first != null
&& restriction.first.equals(restriction.last)) {
// "[property] = $value"
- return encode(restriction.first);
+ return encode(restriction.first, pattern);
} else if (restriction.list != null) {
// "[property] IN (...)
Set<String> values = newLinkedHashSet(); // keep order for testing
for (PropertyValue value : restriction.list) {
- values.addAll(encode(value));
+ values.addAll(encode(value, pattern));
}
return values;
} else {
Added:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/ValuePattern.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/ValuePattern.java?rev=1793097&view=auto
==============================================================================
---
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/ValuePattern.java
(added)
+++
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/ValuePattern.java
Fri Apr 28 14:23:28 2017
@@ -0,0 +1,41 @@
+/*
+ * 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.regex.Pattern;
+
+/**
+ * A value pattern.
+ */
+public class ValuePattern {
+
+ private final Pattern pattern;
+
+ public ValuePattern(String pattern) {
+ Pattern p = pattern == null ? null : Pattern.compile(pattern);
+ this.pattern = p;
+ }
+
+ public boolean matches(String v) {
+ return pattern == null || pattern.matcher(v).matches();
+ }
+
+ public boolean matchesAll() {
+ return pattern == null;
+ }
+
+}
Modified:
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexTest.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexTest.java?rev=1793097&r1=1793096&r2=1793097&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexTest.java
(original)
+++
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexTest.java
Fri Apr 28 14:23:28 2017
@@ -36,11 +36,14 @@ import static org.junit.Assert.assertFal
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Set;
import org.apache.jackrabbit.oak.api.CommitFailedException;
+import org.apache.jackrabbit.oak.api.PropertyValue;
import org.apache.jackrabbit.oak.api.Type;
+import org.apache.jackrabbit.oak.plugins.index.IndexConstants;
import org.apache.jackrabbit.oak.plugins.index.IndexUpdateProvider;
import
org.apache.jackrabbit.oak.plugins.index.property.strategy.ContentMirrorStoreStrategy;
import org.apache.jackrabbit.oak.plugins.memory.EmptyNodeState;
@@ -454,6 +457,64 @@ public class PropertyIndexTest {
// expected: no index for "pqr"
}
}
+
+ @Test
+ public void valuePattern() throws Exception {
+ NodeState root = EMPTY_NODE;
+
+ // Add index definitions
+ NodeBuilder builder = root.builder();
+ NodeBuilder index = builder.child(INDEX_DEFINITIONS_NAME);
+ NodeBuilder indexDef = createIndexDefinition(
+ index, "fooIndex", true, false,
+ ImmutableSet.of("foo"), null);
+ indexDef.setProperty(IndexConstants.VALUE_PATTERN, "(a.*|b)");
+ NodeState before = builder.getNodeState();
+
+ // Add some content and process it through the property index hook
+ builder.child("a")
+ .setProperty(JCR_PRIMARYTYPE, NT_UNSTRUCTURED, Type.NAME)
+ .setProperty("foo", "a");
+ builder.child("a1")
+ .setProperty(JCR_PRIMARYTYPE, NT_UNSTRUCTURED, Type.NAME)
+ .setProperty("foo", "a1");
+ builder.child("b")
+ .setProperty(JCR_PRIMARYTYPE, NT_UNSTRUCTURED, Type.NAME)
+ .setProperty("foo", "b");
+ builder.child("c")
+ .setProperty(JCR_PRIMARYTYPE, NT_UNSTRUCTURED, Type.NAME)
+ .setProperty("foo", "c");
+ NodeState after = builder.getNodeState();
+
+ // Add an index
+ NodeState indexed = HOOK.processCommit(before, after,
CommitInfo.EMPTY);
+
+ FilterImpl f = createFilter(after, NT_UNSTRUCTURED);
+
+ // Query the index
+ PropertyIndexLookup lookup = new PropertyIndexLookup(indexed);
+ PropertyIndex pIndex = new
PropertyIndex(Mounts.defaultMountInfoProvider());
+ assertEquals(ImmutableSet.of("a"), find(lookup, "foo", "a", f));
+ assertEquals(ImmutableSet.of("a1"), find(lookup, "foo", "a1", f));
+ assertEquals(ImmutableSet.of("b"), find(lookup, "foo", "b", f));
+
+ // expected: no index for "is not null"
+ assertTrue(pIndex.getCost(f, indexed) == Double.POSITIVE_INFINITY);
+
+ ArrayList<PropertyValue> list = new ArrayList<PropertyValue>();
+ list.add(PropertyValues.newString("c"));
+ f.restrictPropertyAsList("foo", list);
+ // expected: no index for value c
+ assertTrue(pIndex.getCost(f, indexed) == Double.POSITIVE_INFINITY);
+
+ f = createFilter(after, NT_UNSTRUCTURED);
+ list = new ArrayList<PropertyValue>();
+ list.add(PropertyValues.newString("a"));
+ f.restrictPropertyAsList("foo", list);
+ // expected: no index for value a
+ assertTrue(pIndex.getCost(f, indexed) < Double.POSITIVE_INFINITY);
+
+ }
@Test(expected = CommitFailedException.class)
public void testUnique() throws Exception {