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


Reply via email to