Author: thomasm
Date: Thu Sep  6 13:30:19 2018
New Revision: 1840222

URL: http://svn.apache.org/viewvc?rev=1840222&view=rev
Log:
OAK-7739: Use an index only if a certain node or property exists

Added:
    
jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/property/LuceneIndexDisabledTest.java
Modified:
    
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexDefinition.java
    
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexPlanner.java

Modified: 
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexDefinition.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexDefinition.java?rev=1840222&r1=1840221&r2=1840222&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexDefinition.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexDefinition.java
 Thu Sep  6 13:30:19 2018
@@ -270,6 +270,8 @@ public final class IndexDefinition imple
 
     private final boolean syncPropertyIndexes;
 
+    private final String useIfExists;
+
     //~--------------------------------------------------------< Builder >
 
     public static Builder newBuilder(NodeState root, NodeState defn, String 
indexPath){
@@ -408,6 +410,7 @@ public final class IndexDefinition imple
         this.nrtIndexMode = supportsNRTIndexing(defn);
         this.syncIndexMode = supportsSyncIndexing(defn);
         this.syncPropertyIndexes = definedRules.stream().anyMatch(ir -> 
!ir.syncProps.isEmpty());
+        this.useIfExists = getOptionalValue(defn, 
IndexConstants.USE_IF_EXISTS, null);
     }
 
     public NodeState getDefinitionNodeState() {
@@ -1640,6 +1643,23 @@ public final class IndexDefinition imple
         return mergePolicy;
     }
 
+    public boolean isEnabled() {
+        if (useIfExists == null) {
+            return true;
+        }
+        NodeState nodeState = root;
+        for (String element : PathUtils.elements(useIfExists)) {
+            if (element.startsWith("@")) {
+                return nodeState.hasProperty(element.substring(1));
+            }
+            nodeState = nodeState.getChildNode(element);
+            if (!nodeState.exists()) {
+                return false;
+            }
+        }
+        return true;
+    }
+
     private static Set<String> getMultiProperty(NodeState definition, String 
propName){
         PropertyState pse = definition.getProperty(propName);
         return pse != null ? ImmutableSet.copyOf(pse.getValue(Type.STRINGS)) : 
Collections.<String>emptySet();

Modified: 
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexPlanner.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexPlanner.java?rev=1840222&r1=1840221&r2=1840222&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexPlanner.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexPlanner.java
 Thu Sep  6 13:30:19 2018
@@ -333,6 +333,9 @@ class IndexPlanner {
     private boolean wrongIndex() {
         // REMARK: similar code is used in oak-core, PropertyIndex
         // skip index if "option(index ...)" doesn't match
+        if (!definition.isEnabled()) {
+            return true;
+        }
         PropertyRestriction indexName = 
filter.getPropertyRestriction(IndexConstants.INDEX_NAME_OPTION);
         boolean wrong = false;
         if (indexName != null && indexName.first != null) {

Added: 
jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/property/LuceneIndexDisabledTest.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/property/LuceneIndexDisabledTest.java?rev=1840222&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/property/LuceneIndexDisabledTest.java
 (added)
+++ 
jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/property/LuceneIndexDisabledTest.java
 Thu Sep  6 13:30:19 2018
@@ -0,0 +1,135 @@
+/*
+ * 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 static org.apache.jackrabbit.JcrConstants.NT_BASE;
+import static 
org.apache.jackrabbit.oak.plugins.index.IndexConstants.INDEX_DEFINITIONS_NAME;
+import static 
org.apache.jackrabbit.oak.plugins.index.lucene.util.LuceneIndexHelper.newLucenePropertyIndexDefinition;
+import static org.apache.jackrabbit.oak.spi.commit.CommitInfo.EMPTY;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import org.apache.jackrabbit.oak.InitialContentHelper;
+import org.apache.jackrabbit.oak.plugins.index.IndexConstants;
+import org.apache.jackrabbit.oak.plugins.index.IndexUpdateProvider;
+import org.apache.jackrabbit.oak.plugins.index.lucene.IndexTracker;
+import 
org.apache.jackrabbit.oak.plugins.index.lucene.LuceneIndexEditorProvider;
+import org.apache.jackrabbit.oak.plugins.index.lucene.LucenePropertyIndex;
+import org.apache.jackrabbit.oak.plugins.memory.EmptyNodeState;
+import org.apache.jackrabbit.oak.plugins.memory.PropertyValues;
+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.Operator;
+import org.apache.jackrabbit.oak.query.ast.SelectorImpl;
+import org.apache.jackrabbit.oak.query.index.FilterImpl;
+import org.apache.jackrabbit.oak.spi.commit.EditorHook;
+import org.apache.jackrabbit.oak.spi.query.QueryIndex.IndexPlan;
+import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.google.common.collect.ImmutableSet;
+
+/**
+ * Test the Property2 index mechanism.
+ */
+public class LuceneIndexDisabledTest {
+
+    private static final int MANY = 100;
+
+    private NodeState root;
+    private NodeBuilder rootBuilder;
+    private static final EditorHook HOOK = new EditorHook(
+            new IndexUpdateProvider(new LuceneIndexEditorProvider()));
+
+    @Before
+    public void setup() throws Exception {
+        root = EmptyNodeState.EMPTY_NODE;
+        rootBuilder = InitialContentHelper.INITIAL_CONTENT.builder();
+        commit();
+    }
+
+    @Test
+    public void disabled() throws Exception {
+        NodeBuilder index = newLucenePropertyIndexDefinition(
+                rootBuilder.child(INDEX_DEFINITIONS_NAME),
+                "foo", ImmutableSet.of("foo"), null);
+        index.setProperty(IndexConstants.USE_IF_EXISTS, "/");
+        commit();
+        for (int i = 0; i < MANY; i++) {
+            rootBuilder.child("test").child("n" + i).setProperty("foo", "x" + 
i % 20);
+        }
+        commit();
+        FilterImpl f = createFilter(root, NT_BASE);
+        f.restrictProperty("foo", Operator.EQUAL, 
PropertyValues.newString("x10"));
+
+        IndexTracker tracker = new IndexTracker();
+        tracker.update(root);
+        LucenePropertyIndex luceneIndex = new LucenePropertyIndex(tracker);
+        IndexPlan plan = luceneIndex.getPlans(f, null, root).iterator().next();
+        assertTrue(plan.getCostPerExecution() != Double.POSITIVE_INFINITY);
+        assertEquals("/oak:index/foo", plan.getPlanName());
+
+        // now test with a node that doesn't exist
+        index = rootBuilder.child(INDEX_DEFINITIONS_NAME).child("foo");
+        index.setProperty(IndexConstants.USE_IF_EXISTS, "/doesNotExist");
+        index.setProperty("refresh", Boolean.TRUE);
+        commit();
+        // need to create a new one - otherwise the cached plan is used
+        tracker.update(root);
+        luceneIndex = new LucenePropertyIndex(tracker);
+        assertTrue(luceneIndex.getPlans(f, null, root).isEmpty());
+
+        // test with a property that does exist
+        index = rootBuilder.child(INDEX_DEFINITIONS_NAME).child("foo");
+        index.setProperty(IndexConstants.USE_IF_EXISTS, 
"/oak:index/@jcr:primaryType");
+        index.setProperty("refresh", Boolean.TRUE);
+        commit();
+        // need to create a new one - otherwise the cached plan is used
+        tracker.update(root);
+        luceneIndex = new LucenePropertyIndex(tracker);
+        plan = luceneIndex.getPlans(f, null, root).iterator().next();
+        assertTrue(plan.getCostPerExecution() != Double.POSITIVE_INFINITY);
+        assertEquals("/oak:index/foo", plan.getPlanName());
+
+        // test with a property that does not exist
+        index = rootBuilder.child(INDEX_DEFINITIONS_NAME).child("foo");
+        index.setProperty(IndexConstants.USE_IF_EXISTS, 
"/oak:index/@unknownProperty");
+        index.setProperty("refresh", Boolean.TRUE);
+        commit();
+        // need to create a new one - otherwise the cached plan is used
+        tracker.update(root);
+        luceneIndex = new LucenePropertyIndex(tracker);
+        assertTrue(luceneIndex.getPlans(f, null, root).isEmpty());
+    }
+
+    private void commit() throws Exception {
+        root = HOOK.processCommit(rootBuilder.getBaseState(), 
rootBuilder.getNodeState(), EMPTY);
+        rootBuilder = root.builder();
+    }
+
+    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());
+    }
+
+}


Reply via email to