Author: chetanm
Date: Tue Oct 3 05:05:57 2017
New Revision: 1810629
URL: http://svn.apache.org/viewvc?rev=1810629&view=rev
Log:
OAK-6535 - Synchronous Lucene Property Indexes
PropertyIndexUpdateCallback implements PropertyUpdateCallback and
manages property index updates based on changes.
- For simple property index it implements a bucket storage where current
writes always go to head bucket.
- For unique indexes no bucketing is done
HybridPropertyIndexLookup performs the query part taking into account
for bucket storage
Added:
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/property/
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/property/HybridPropertyIndexLookup.java
(with props)
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/property/HybridPropertyIndexUtil.java
(with props)
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/property/PropertyIndexUpdateCallback.java
(with props)
jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/property/
jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/property/HybridPropertyIndexStorageTest.java
(with props)
Modified:
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/PropertyDefinition.java
Modified:
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/PropertyDefinition.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/PropertyDefinition.java?rev=1810629&r1=1810628&r2=1810629&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/PropertyDefinition.java
(original)
+++
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/PropertyDefinition.java
Tue Oct 3 05:05:57 2017
@@ -42,7 +42,7 @@ import static org.apache.jackrabbit.oak.
import static
org.apache.jackrabbit.oak.plugins.index.lucene.LuceneIndexConstants.PROP_WEIGHT;
import static
org.apache.jackrabbit.oak.plugins.index.lucene.util.ConfigUtil.getOptionalValue;
-class PropertyDefinition {
+public class PropertyDefinition {
private static final Logger log =
LoggerFactory.getLogger(PropertyDefinition.class);
/**
* The default boost: 1.0f.
@@ -106,7 +106,7 @@ class PropertyDefinition {
/**
* For function-based indexes: the function name, in Polish notation.
- */
+ */
final String function;
/**
@@ -114,11 +114,11 @@ class PropertyDefinition {
*/
final String[] functionCode;
- final ValuePattern valuePattern;
+ public final ValuePattern valuePattern;
- final boolean sync;
+ public final boolean sync;
- final boolean unique;
+ public final boolean unique;
public PropertyDefinition(IndexingRule idxDefn, String nodeName, NodeState
defn) {
this.isRegexp = getOptionalValue(defn, PROP_IS_REGEX, false);
Added:
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/property/HybridPropertyIndexLookup.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/property/HybridPropertyIndexLookup.java?rev=1810629&view=auto
==============================================================================
---
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/property/HybridPropertyIndexLookup.java
(added)
+++
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/property/HybridPropertyIndexLookup.java
Tue Oct 3 05:05:57 2017
@@ -0,0 +1,91 @@
+/*
+ * 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.lucene.property;
+
+import java.util.Collections;
+import java.util.Set;
+
+import com.google.common.collect.Iterables;
+import org.apache.jackrabbit.oak.api.PropertyValue;
+import org.apache.jackrabbit.oak.plugins.index.lucene.PropertyDefinition;
+import
org.apache.jackrabbit.oak.plugins.index.property.strategy.ContentMirrorStoreStrategy;
+import
org.apache.jackrabbit.oak.plugins.index.property.strategy.UniqueEntryStoreStrategy;
+import org.apache.jackrabbit.oak.spi.query.Filter;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+
+import static
org.apache.jackrabbit.oak.plugins.index.lucene.property.HybridPropertyIndexUtil.PROPERTY_INDEX;
+import static
org.apache.jackrabbit.oak.plugins.index.lucene.property.HybridPropertyIndexUtil.PROP_HEAD_BUCKET;
+import static
org.apache.jackrabbit.oak.plugins.index.lucene.property.HybridPropertyIndexUtil.PROP_PREVIOUS_BUCKET;
+import static
org.apache.jackrabbit.oak.plugins.index.property.PropertyIndexUtil.encode;
+
+public class HybridPropertyIndexLookup {
+ private final String indexPath;
+ private final NodeState indexState;
+
+ public HybridPropertyIndexLookup(String indexPath, NodeState indexState) {
+ this.indexPath = indexPath;
+ this.indexState = indexState;
+ }
+
+ public Iterable<String> query(Filter filter, PropertyDefinition pd,
+ String propertyName, PropertyValue value) {
+
+ String propIdxNodeName =
HybridPropertyIndexUtil.getNodeName(propertyName);
+ NodeState propIndexRootNode = indexState.getChildNode(PROPERTY_INDEX);
+ NodeState propIndexNode =
propIndexRootNode.getChildNode(propIdxNodeName);
+ if (!propIndexNode.exists()) {
+ return Collections.emptyList();
+ }
+
+ //TODO Check for non root indexes
+
+ String indexName = indexPath + "(" + propertyName + ")";
+ Set<String> values = encode(value, pd.valuePattern);
+ if (pd.unique) {
+ return queryUnique(filter, indexName, propIndexRootNode,
propIdxNodeName, values);
+ } else {
+ return querySimple(filter, indexName, propIndexNode, values);
+ }
+ }
+
+ private static Iterable<String> queryUnique(Filter filter, String
indexName, NodeState propIndexRootNode,
+ String propIdxNodeName, Set<String>
values) {
+ UniqueEntryStoreStrategy s = new
UniqueEntryStoreStrategy(propIdxNodeName);
+ return s.query(filter, indexName, propIndexRootNode, values);
+ }
+
+ private static Iterable<String> querySimple(Filter filter, String
indexName, NodeState propIndexNode,
+ Set<String> values) {
+ return Iterables.concat(
+ queryBucket(filter, indexName, propIndexNode,
PROP_HEAD_BUCKET, values),
+ queryBucket(filter, indexName, propIndexNode,
PROP_PREVIOUS_BUCKET, values)
+ );
+ }
+
+ private static Iterable<String> queryBucket(Filter filter, String
indexName, NodeState propIndexNode,
+ String bucketPropName, Set<String>
values) {
+ String bucketName = propIndexNode.getString(bucketPropName);
+ if (bucketName == null) {
+ return Collections.emptyList();
+ }
+ ContentMirrorStoreStrategy s = new
ContentMirrorStoreStrategy(bucketName);
+ return s.query(filter, indexName, propIndexNode, bucketName, values);
+ }
+}
Propchange:
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/property/HybridPropertyIndexLookup.java
------------------------------------------------------------------------------
svn:eol-style = native
Added:
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/property/HybridPropertyIndexUtil.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/property/HybridPropertyIndexUtil.java?rev=1810629&view=auto
==============================================================================
---
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/property/HybridPropertyIndexUtil.java
(added)
+++
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/property/HybridPropertyIndexUtil.java
Tue Oct 3 05:05:57 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.lucene.property;
+
+class HybridPropertyIndexUtil {
+ /**
+ * Node name under which all property indexes are created
+ */
+ static final String PROPERTY_INDEX = ":property-index";
+
+ /**
+ * Property name referring to 'head' bucket
+ */
+ static final String PROP_HEAD_BUCKET = "head";
+
+ /**
+ * Property name referring to 'previous' bucket
+ */
+ static final String PROP_PREVIOUS_BUCKET = "previous";
+
+ static String getNodeName(String propertyRelativePath) {
+ return propertyRelativePath.replace('/', '_');
+ }
+}
Propchange:
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/property/HybridPropertyIndexUtil.java
------------------------------------------------------------------------------
svn:eol-style = native
Added:
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/property/PropertyIndexUpdateCallback.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/property/PropertyIndexUpdateCallback.java?rev=1810629&view=auto
==============================================================================
---
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/property/PropertyIndexUpdateCallback.java
(added)
+++
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/property/PropertyIndexUpdateCallback.java
Tue Oct 3 05:05:57 2017
@@ -0,0 +1,134 @@
+/*
+ * 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.lucene.property;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.annotation.Nullable;
+import javax.jcr.PropertyType;
+
+import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.plugins.index.lucene.PropertyDefinition;
+import org.apache.jackrabbit.oak.plugins.index.lucene.PropertyUpdateCallback;
+import org.apache.jackrabbit.oak.plugins.index.property.ValuePattern;
+import
org.apache.jackrabbit.oak.plugins.index.property.strategy.ContentMirrorStoreStrategy;
+import
org.apache.jackrabbit.oak.plugins.index.property.strategy.UniqueEntryStoreStrategy;
+import org.apache.jackrabbit.oak.plugins.memory.PropertyValues;
+import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Suppliers.ofInstance;
+import static com.google.common.collect.Sets.newHashSet;
+import static java.util.Collections.emptySet;
+import static
org.apache.jackrabbit.oak.plugins.index.lucene.property.HybridPropertyIndexUtil.PROPERTY_INDEX;
+import static
org.apache.jackrabbit.oak.plugins.index.lucene.property.HybridPropertyIndexUtil.PROP_HEAD_BUCKET;
+import static
org.apache.jackrabbit.oak.plugins.index.property.PropertyIndexUtil.encode;
+
+public class PropertyIndexUpdateCallback implements PropertyUpdateCallback {
+ private static final String DEFAULT_HEAD_BUCKET = String.valueOf(1);
+
+ private final NodeBuilder builder;
+ private final String indexPath;
+
+ public PropertyIndexUpdateCallback(String indexPath, NodeBuilder builder) {
+ this.builder = builder;
+ this.indexPath = indexPath;
+ }
+
+ @Override
+ public void propertyUpdated(String nodePath, String propertyRelativePath,
PropertyDefinition pd,
+ @Nullable PropertyState before, @Nullable
PropertyState after) {
+ if (!pd.sync) {
+ return;
+ }
+
+ Set<String> beforeKeys = getValueKeys(before, pd.valuePattern);
+ Set<String> afterKeys = getValueKeys(after, pd.valuePattern);
+
+ //Remove duplicates
+ Set<String> sharedKeys = newHashSet(beforeKeys);
+ sharedKeys.retainAll(afterKeys);
+ beforeKeys.removeAll(sharedKeys);
+ afterKeys.removeAll(sharedKeys);
+
+ if (!beforeKeys.isEmpty() || !afterKeys.isEmpty()){
+ NodeBuilder indexNode = getIndexNode(propertyRelativePath,
pd.unique);
+
+ if (pd.unique) {
+ UniqueEntryStoreStrategy s = new UniqueEntryStoreStrategy();
+ s.update(ofInstance(indexNode),
+ nodePath,
+ null,
+ null,
+ beforeKeys,
+ afterKeys);
+ //TODO Query to check if unique
+ } else {
+ ContentMirrorStoreStrategy s = new
ContentMirrorStoreStrategy();
+ s.update(ofInstance(indexNode),
+ nodePath,
+ null,
+ null,
+ emptySet(), //Disable pruning with empty before keys
+ afterKeys);
+ }
+ }
+
+ }
+
+ private NodeBuilder getIndexNode(String propertyRelativePath, boolean
unique) {
+ NodeBuilder propertyIndex = builder.child(PROPERTY_INDEX);
+
+ String nodeName =
HybridPropertyIndexUtil.getNodeName(propertyRelativePath);
+ if (unique) {
+ return getUniqueIndexBuilder(propertyIndex, nodeName);
+ } else {
+ return getSimpleIndexBuilder(propertyIndex, nodeName);
+ }
+ }
+
+ private NodeBuilder getSimpleIndexBuilder(NodeBuilder propertyIndex,
String nodeName) {
+ NodeBuilder idx = propertyIndex.child(nodeName);
+ if (idx.isNew()) {
+ idx.setProperty(PROP_HEAD_BUCKET, DEFAULT_HEAD_BUCKET);
+ }
+
+ String headBucketName = idx.getString(PROP_HEAD_BUCKET);
+ checkNotNull(headBucketName, "[%s] property not found in [%s] for
index [%s]",
+ PROP_HEAD_BUCKET, idx, indexPath);
+
+ return idx.child(headBucketName);
+ }
+
+ private static NodeBuilder getUniqueIndexBuilder(NodeBuilder
propertyIndex, String nodeName) {
+ return propertyIndex.child(nodeName);
+ }
+
+ private static Set<String> getValueKeys(PropertyState property,
ValuePattern pattern) {
+ Set<String> keys = new HashSet<>();
+ if (property != null
+ && property.getType().tag() != PropertyType.BINARY
+ && property.count() != 0) {
+ keys.addAll(encode(PropertyValues.create(property), pattern));
+ }
+ return keys;
+ }
+}
Propchange:
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/property/PropertyIndexUpdateCallback.java
------------------------------------------------------------------------------
svn:eol-style = native
Added:
jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/property/HybridPropertyIndexStorageTest.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/property/HybridPropertyIndexStorageTest.java?rev=1810629&view=auto
==============================================================================
---
jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/property/HybridPropertyIndexStorageTest.java
(added)
+++
jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/property/HybridPropertyIndexStorageTest.java
Tue Oct 3 05:05:57 2017
@@ -0,0 +1,180 @@
+/*
+ * 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.lucene.property;
+
+import java.util.List;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterators;
+import org.apache.jackrabbit.oak.api.PropertyValue;
+import org.apache.jackrabbit.oak.plugins.index.Cursors;
+import org.apache.jackrabbit.oak.plugins.index.lucene.IndexDefinition;
+import org.apache.jackrabbit.oak.plugins.index.lucene.PropertyDefinition;
+import
org.apache.jackrabbit.oak.plugins.index.lucene.util.IndexDefinitionBuilder;
+import org.apache.jackrabbit.oak.query.NodeStateNodeTypeInfoProvider;
+import org.apache.jackrabbit.oak.query.QueryEngineSettings;
+import org.apache.jackrabbit.oak.query.ast.NodeTypeInfo;
+import org.apache.jackrabbit.oak.query.ast.NodeTypeInfoProvider;
+import org.apache.jackrabbit.oak.query.ast.SelectorImpl;
+import org.apache.jackrabbit.oak.query.index.FilterImpl;
+import org.apache.jackrabbit.oak.spi.query.Cursor;
+import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+import org.junit.Test;
+
+import static org.apache.jackrabbit.oak.InitialContent.INITIAL_CONTENT;
+import static
org.apache.jackrabbit.oak.plugins.memory.EmptyNodeState.EMPTY_NODE;
+import static
org.apache.jackrabbit.oak.plugins.memory.PropertyStates.createProperty;
+import static
org.apache.jackrabbit.oak.plugins.memory.PropertyValues.newString;
+import static org.hamcrest.Matchers.containsInAnyOrder;
+import static org.hamcrest.Matchers.empty;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThat;
+
+public class HybridPropertyIndexStorageTest {
+ private NodeState root = INITIAL_CONTENT;
+ private NodeBuilder builder = EMPTY_NODE.builder();
+ private IndexDefinitionBuilder defnb = new IndexDefinitionBuilder();
+ private String indexPath = "/oak:index/foo";
+
+ @Test
+ public void nonSyncProp() throws Exception{
+ defnb.indexRule("nt:base").property("foo");
+
+ newCallback().propertyUpdated("/a", "foo", pd("foo"),
+ null, createProperty("foo", "bar"));
+
+ assertFalse(builder.isModified());
+ }
+
+ @Test
+ public void simpleProperty() throws Exception{
+ defnb.indexRule("nt:base").property("foo").sync();
+
+ newCallback().propertyUpdated("/a", "foo", pd("foo"),
+ null, createProperty("foo", "bar"));
+ newCallback().propertyUpdated("/b", "foo", pd("foo"),
+ null, createProperty("foo", "bar2"));
+
+ assertThat(query("foo", newString("bar")), containsInAnyOrder("/a"));
+ assertThat(query("foo", newString("bar2")), containsInAnyOrder("/b"));
+ }
+
+ @Test
+ public void relativeProperty() throws Exception{
+ String propName = "jcr:content/foo";
+ defnb.indexRule("nt:base").property(propName).sync();
+
+ newCallback().propertyUpdated("/a", propName, pd(propName),
+ null, createProperty("foo", "bar"));
+
+ assertThat(query(propName, newString("bar")),
containsInAnyOrder("/a"));
+ }
+
+ @Test
+ public void valuePattern() throws Exception{
+
defnb.indexRule("nt:base").property("foo").sync().valueIncludedPrefixes("bar/");
+
+ newCallback().propertyUpdated("/a", "foo", pd("foo"),
+ null, createProperty("foo", "bar/a"));
+ newCallback().propertyUpdated("/b", "foo", pd("foo"),
+ null, createProperty("foo", "baz/a"));
+
+ assertThat(query("foo", newString("bar/a")), containsInAnyOrder("/a"));
+
+ //As baz pattern is excluded it should not be indexed
+ assertThat(query("foo", newString("baz/a")), empty());
+ }
+
+ @Test
+ public void pruningDisabledForSimpleProperty() throws Exception{
+ defnb.indexRule("nt:base").property("foo").sync();
+
+ newCallback().propertyUpdated("/a", "foo", pd("foo"),
+ null, createProperty("foo", "bar"));
+ newCallback().propertyUpdated("/b", "foo", pd("foo"),
+ null, createProperty("foo", "bar"));
+
+ assertThat(query("foo", newString("bar")), containsInAnyOrder("/a",
"/b"));
+
+ builder = builder.getNodeState().builder();
+ newCallback().propertyUpdated("/b", "foo", pd("foo"),
+ createProperty("foo", "bar"), null);
+
+ // /b would still come as pruning is disabled
+ assertThat(query("foo", newString("bar")), containsInAnyOrder("/a",
"/b"));
+ }
+
+ //~----------------------------------------< unique props >
+
+ @Test
+ public void uniqueProperty() throws Exception{
+ defnb.indexRule("nt:base").property("foo").unique();
+
+ newCallback().propertyUpdated("/a", "foo", pd("foo"),
+ null, createProperty("foo", "bar"));
+ newCallback().propertyUpdated("/b", "foo", pd("foo"),
+ null, createProperty("foo", "bar2"));
+
+ assertThat(query("foo", newString("bar")), containsInAnyOrder("/a"));
+ }
+
+ @Test
+ public void pruningWorkingForUnique() throws Exception{
+ defnb.indexRule("nt:base").property("foo").unique();
+
+ newCallback().propertyUpdated("/a", "foo", pd("foo"),
+ null, createProperty("foo", "bar"));
+
+ assertThat(query("foo", newString("bar")), containsInAnyOrder("/a"));
+
+ builder = builder.getNodeState().builder();
+ newCallback().propertyUpdated("/a", "foo", pd("foo"),
+ createProperty("foo", "bar"), null);
+
+ // /b should not come as pruning is disabled
+ assertThat(query("foo", newString("bar")), empty());
+ }
+
+ private List<String> query(String propertyName, PropertyValue value) {
+ HybridPropertyIndexLookup lookup = new
HybridPropertyIndexLookup(indexPath, builder.getNodeState());
+ FilterImpl filter = createFilter(root, "nt:base");
+ Iterable<String> paths = lookup.query(filter, pd(propertyName),
propertyName, value);
+ Cursor c = Cursors.newPathCursor(paths, new QueryEngineSettings());
+ return ImmutableList.copyOf(Iterators.transform(c, r -> r.getPath()));
+ }
+
+ private PropertyIndexUpdateCallback newCallback(){
+ return new PropertyIndexUpdateCallback(indexPath, builder);
+ }
+
+ private PropertyDefinition pd(String propName){
+ IndexDefinition defn = new IndexDefinition(root, defnb.build(),
indexPath);
+ return defn.getApplicableIndexingRule("nt:base").getConfig(propName);
+ }
+
+ private static FilterImpl createFilter(NodeState root, String
nodeTypeName) {
+ NodeTypeInfoProvider nodeTypes = new
NodeStateNodeTypeInfoProvider(root);
+ NodeTypeInfo type = nodeTypes.getNodeTypeInfo(nodeTypeName);
+ SelectorImpl selector = new SelectorImpl(type, nodeTypeName);
+ return new FilterImpl(selector, "SELECT * FROM [" + nodeTypeName +
"]", new QueryEngineSettings());
+ }
+
+}
\ No newline at end of file
Propchange:
jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/property/HybridPropertyIndexStorageTest.java
------------------------------------------------------------------------------
svn:eol-style = native