diff --git a/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexDefinition.java b/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexDefinition.java
index 29f9173..b4b0fd5 100644
--- a/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexDefinition.java
+++ b/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexDefinition.java
@@ -99,6 +99,16 @@
 
     private static final Logger log = LoggerFactory.getLogger(IndexDefinition.class);
 
+    public static final boolean DISABLE_STORED_INDEX_DEFINITION =
+            Boolean.getBoolean("oak.lucene.disableStoredIndexDefinition");
+
+    static {
+        if (DISABLE_STORED_INDEX_DEFINITION){
+            log.info("Feature to ensure that index definition match the index state is set to be disabled. Change in " +
+                    "index definition would now effect query plans and might lead to inconsistent results");
+        }
+    }
+
     /**
      * Default number of seconds after which to delete actively. Default is -1, meaning disabled.
      * The plan is to use 3600 (1 hour) in the future.
@@ -129,6 +139,12 @@
     static final String INDEX_VERSION = ":version";
 
     /**
+     * Hidden node under index definition which is used to store the index definition
+     * nodestate as it was at time of reindexing
+     */
+    static final String INDEX_DEFINITION_NODE = ":index-definition";
+
+    /**
      * Hidden node under index definition which is used to store meta info
      */
     static final String STATUS_NODE = ":status";
@@ -299,7 +315,7 @@ public IndexDefinition build(){
     }
 
     public IndexDefinition(NodeState root, NodeState defn, String indexPath) {
-        this(root, defn, determineIndexFormatVersion(defn), determineUniqueId(defn), indexPath);
+        this(root, getIndexDefinitionState(defn), determineIndexFormatVersion(defn), determineUniqueId(defn), indexPath);
     }
 
     private IndexDefinition(NodeState root, NodeState defn, IndexFormatVersion version, String uid, String indexPath) {
@@ -1678,4 +1694,12 @@ private static boolean supportsIndexingMode(NodeBuilder defn, String mode) {
         return Iterables.contains(async.getValue(Type.STRINGS), mode);
     }
 
+    private static NodeState getIndexDefinitionState(NodeState defn) {
+        if (DISABLE_STORED_INDEX_DEFINITION){
+            return defn;
+        }
+        NodeState storedState = defn.getChildNode(INDEX_DEFINITION_NODE);
+        return storedState.exists() ? storedState : defn;
+    }
+
 }
diff --git a/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexTracker.java b/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexTracker.java
index 9885baa..8c80dad 100644
--- a/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexTracker.java
+++ b/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexTracker.java
@@ -199,6 +199,10 @@ BadIndexTracker getBadIndexTracker() {
         return badIndexTracker;
     }
 
+    NodeState getRoot() {
+        return root;
+    }
+
     private synchronized IndexNode findIndexNode(String path) {
         // Retry the lookup from acquireIndexNode now that we're
         // synchronized. The acquire() call is guaranteed to succeed
diff --git a/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexConstants.java b/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexConstants.java
index b543f18..c3a8ca2 100644
--- a/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexConstants.java
+++ b/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexConstants.java
@@ -361,4 +361,9 @@ public static IndexingMode from(String indexingMode){
      * String property: the function to index, for function-based index
      */
     String PROP_FUNCTION = "function";
+
+    /**
+     * Boolean property which signal LuceneIndexEditor to refresh the stored index definition
+     */
+    String PROP_REFRESH_DEFN = "refresh";
 }
diff --git a/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexEditorContext.java b/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexEditorContext.java
index e4f07ed..cae7de1 100644
--- a/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexEditorContext.java
+++ b/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexEditorContext.java
@@ -58,6 +58,8 @@
     private static final PerfLogger PERF_LOGGER =
             new PerfLogger(LoggerFactory.getLogger(LuceneIndexEditorContext.class.getName() + ".perf"));
 
+
+
     private FacetsConfig facetsConfig;
 
     private static final Parser defaultParser = createDefaultParser();
@@ -98,6 +100,8 @@
     //Set for testing ONLY
     private static Clock clock = Clock.SIMPLE;
 
+    private final boolean indexDefnRewritten;
+
     LuceneIndexEditorContext(NodeState root, NodeBuilder definition,
                              @Nullable IndexDefinition indexDefinition,
                              IndexUpdateCallback updateCallback,
@@ -110,14 +114,17 @@
         this.definitionBuilder = definition;
         this.indexWriterFactory = indexWriterFactory;
         this.definition = indexDefinition != null ? indexDefinition :
-                new IndexDefinition(root, definition.getBaseState(),indexingContext.getIndexPath());
+                createIndexDefinition(root, definition, indexingContext, asyncIndexing);
         this.indexedNodes = 0;
         this.updateCallback = updateCallback;
         this.extractedTextCache = extractedTextCache;
         this.augmentorFactory = augmentorFactory;
         this.asyncIndexing = asyncIndexing;
         if (this.definition.isOfOldFormat()){
+            indexDefnRewritten = true;
             IndexDefinition.updateDefinition(definition, indexingContext.getIndexPath());
+        } else {
+            indexDefnRewritten = false;
         }
     }
 
@@ -181,11 +188,19 @@ public void enableReindexMode(){
         reindex = true;
         IndexFormatVersion version = IndexDefinition.determineVersionForFreshIndex(definitionBuilder);
         definitionBuilder.setProperty(IndexDefinition.INDEX_VERSION, version.getVersion());
+
+        //Avoid obtaining the latest NodeState from builder as that would force purge of current transient state
+        //as index definition does not get modified as part of IndexUpdate run in most case we rely on base state
+        //For case where index definition is rewritten there we get fresh state
+        NodeState defnState = indexDefnRewritten ? definitionBuilder.getNodeState() : definitionBuilder.getBaseState();
+        if (!IndexDefinition.DISABLE_STORED_INDEX_DEFINITION) {
+            definitionBuilder.setChildNode(IndexDefinition.INDEX_DEFINITION_NODE, NodeStateCloner.cloneVisibleState(defnState));
+        }
         String uid = configureUniqueId(definitionBuilder);
 
         //Refresh the index definition based on update builder state
         definition = IndexDefinition
-                .newBuilder(root, definitionBuilder.getBaseState())
+                .newBuilder(root, defnState)
                 .indexPath(indexingContext.getIndexPath())
                 .version(version)
                 .uid(uid)
@@ -264,6 +279,17 @@ public static String configureUniqueId(NodeBuilder definition) {
         return uid;
     }
 
+    private static IndexDefinition createIndexDefinition(NodeState root, NodeBuilder definition, IndexingContext
+            indexingContext, boolean asyncIndexing) {
+        NodeState defnState = definition.getBaseState();
+        if (definition.getBoolean(LuceneIndexConstants.PROP_REFRESH_DEFN) && asyncIndexing){
+            definition.removeProperty(LuceneIndexConstants.PROP_REFRESH_DEFN);
+            definition.setChildNode(IndexDefinition.INDEX_DEFINITION_NODE, NodeStateCloner.cloneVisibleState(defnState));
+            log.info("Refreshed the index definition for [{}]", indexingContext.getIndexPath());
+        }
+        return new IndexDefinition(root, defnState,indexingContext.getIndexPath());
+    }
+
     private static Parser initializeTikaParser(IndexDefinition definition) {
         ClassLoader current = Thread.currentThread().getContextClassLoader();
         try {
diff --git a/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexMBean.java b/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexMBean.java
index 7cf2612..c752101 100644
--- a/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexMBean.java
+++ b/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexMBean.java
@@ -54,4 +54,7 @@
             int maxPathCount
             ) throws IOException;
 
+    @Description("Returns the stored index definition for index at given path in string form")
+    String getStoredIndexDefinition(@Name("indexPath") String indexPath);
+
 }
diff --git a/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexMBeanImpl.java b/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexMBeanImpl.java
index 749b81d..d555db2 100644
--- a/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexMBeanImpl.java
+++ b/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexMBeanImpl.java
@@ -43,8 +43,11 @@
 import com.google.common.collect.TreeTraverser;
 import org.apache.jackrabbit.oak.commons.PathUtils;
 import org.apache.jackrabbit.oak.commons.jmx.AnnotatedStandardMBean;
+import org.apache.jackrabbit.oak.commons.jmx.Name;
 import org.apache.jackrabbit.oak.plugins.index.lucene.BadIndexTracker.BadIndexInfo;
 import org.apache.jackrabbit.oak.plugins.index.lucene.LucenePropertyIndex.PathStoredFieldVisitor;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+import org.apache.jackrabbit.oak.spi.state.NodeStateUtils;
 import org.apache.lucene.index.DirectoryReader;
 import org.apache.lucene.index.IndexReader;
 import org.apache.lucene.search.BooleanClause;
@@ -63,6 +66,7 @@
 
 import static com.google.common.base.Preconditions.checkNotNull;
 import static org.apache.jackrabbit.oak.commons.IOUtils.humanReadableByteCount;
+import static org.apache.jackrabbit.oak.plugins.index.lucene.IndexDefinition.INDEX_DEFINITION_NODE;
 import static org.apache.jackrabbit.oak.plugins.index.lucene.TermFactory.newAncestorTerm;
 
 public class LuceneIndexMBeanImpl extends AnnotatedStandardMBean implements LuceneIndexMBean {
@@ -178,6 +182,22 @@ public boolean isFailing() {
         return new String[0];
     }
 
+    @Override
+    public String getStoredIndexDefinition(@Name("indexPath") String indexPath) {
+        IndexDefinition defn = indexTracker.getIndexDefinition(indexPath);
+        NodeState state;
+        if (defn != null){
+            state = defn.getDefinitionNodeState();
+        } else {
+            state = NodeStateUtils.getNode(indexTracker.getRoot(), indexPath + "/" + INDEX_DEFINITION_NODE);
+        }
+
+        if (state.exists()){
+            return NodeStateUtils.toString(state);
+        }
+        return "No index found at given path";
+    }
+
     public void dumpIndexContent(String sourcePath, String destPath) throws IOException {
         IndexNode indexNode = null;
         try {
diff --git a/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/NodeStateCloner.java b/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/NodeStateCloner.java
new file mode 100644
index 0000000..2439593
--- /dev/null
+++ b/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/NodeStateCloner.java
@@ -0,0 +1,51 @@
+/*
+ * 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;
+
+import org.apache.jackrabbit.oak.spi.state.ApplyDiff;
+import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+import org.apache.jackrabbit.oak.spi.state.NodeStateUtils;
+
+import static org.apache.jackrabbit.oak.plugins.memory.EmptyNodeState.EMPTY_NODE;
+
+class NodeStateCloner {
+
+    public static NodeState cloneVisibleState(NodeState state){
+        NodeBuilder builder = EMPTY_NODE.builder();
+        new ApplyVisibleDiff(builder).apply(state);
+        return builder.getNodeState();
+    }
+
+    private static class ApplyVisibleDiff extends ApplyDiff {
+        public ApplyVisibleDiff(NodeBuilder builder) {
+            super(builder);
+        }
+
+        @Override
+        public boolean childNodeAdded(String name, NodeState after) {
+            if (NodeStateUtils.isHidden(name)){
+                return true;
+            }
+            return after.compareAgainstBaseState(
+                    EMPTY_NODE, new ApplyVisibleDiff(builder.child(name)));
+        }
+    }
+}
diff --git a/oak-lucene/src/test/java/org/apache/jackrabbit/oak/jcr/query/FacetTest.java b/oak-lucene/src/test/java/org/apache/jackrabbit/oak/jcr/query/FacetTest.java
index 793ba9a..6ac82be 100644
--- a/oak-lucene/src/test/java/org/apache/jackrabbit/oak/jcr/query/FacetTest.java
+++ b/oak-lucene/src/test/java/org/apache/jackrabbit/oak/jcr/query/FacetTest.java
@@ -33,6 +33,8 @@
 import org.junit.After;
 import org.junit.Before;
 
+import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.REINDEX_PROPERTY_NAME;
+
 /**
  * Test for faceting capabilities via JCR API
  */
@@ -48,6 +50,7 @@ protected void setUp() throws Exception {
         if (!superuser.itemExists(FACET_CONFING_PROP_PATH)) {
             Node node = superuser.getNode("/oak:index/luceneGlobal/indexRules/nt:base/properties/allProps");
             node.setProperty(LuceneIndexConstants.PROP_FACETS, true);
+            markIndexForReindex();
             superuser.save();
             superuser.refresh(true);
         }
@@ -55,6 +58,7 @@ protected void setUp() throws Exception {
         if (!superuser.nodeExists(FACET_CONFING_NODE_PATH)) {
             Node node = superuser.getNode(INDEX_CONFING_NODE_PATH);
             node.addNode(LuceneIndexConstants.FACETS);
+            markIndexForReindex();
             superuser.save();
             superuser.refresh(true);
         }
@@ -80,6 +84,8 @@ protected void tearDown() throws Exception {
     public void testFacetsNA() throws Exception {
         if (superuser.itemExists(FACET_CONFING_PROP_PATH)) {
             superuser.getItem(FACET_CONFING_PROP_PATH).remove();
+            markIndexForReindex();
+            superuser.save();
         }
         Session session = superuser;
         QueryManager qm = session.getWorkspace().getQueryManager();
@@ -364,6 +370,7 @@ public void testFacetRetrievalNumberOfFacetsConfiguredHigherThanDefault() throws
 
         Node facetsConfig = superuser.getNode(FACET_CONFING_NODE_PATH);
         facetsConfig.setProperty(LuceneIndexConstants.PROP_FACETS_TOP_CHILDREN, 11);
+        markIndexForReindex();
         superuser.save();
         superuser.refresh(true);
 
@@ -414,6 +421,7 @@ public void testFacetRetrievalNumberOfFacetsConfiguredLowerThanDefault() throws
 
         Node facetsConfig = superuser.getNode(FACET_CONFING_NODE_PATH);
         facetsConfig.setProperty(LuceneIndexConstants.PROP_FACETS_TOP_CHILDREN, 7);
+        markIndexForReindex();
         superuser.save();
         superuser.refresh(true);
 
@@ -459,4 +467,8 @@ public void testFacetRetrievalNumberOfFacetsConfiguredLowerThanDefault() throws
         assertNotNull(facets);
         assertEquals(7, facets.size());
     }
+
+    private void markIndexForReindex() throws RepositoryException {
+        superuser.getNode("/oak:index/luceneGlobal").setProperty(REINDEX_PROPERTY_NAME, true);
+    }
 }
\ No newline at end of file
diff --git a/oak-lucene/src/test/java/org/apache/jackrabbit/oak/jcr/query/SuggestTest.java b/oak-lucene/src/test/java/org/apache/jackrabbit/oak/jcr/query/SuggestTest.java
index 264822c..2e8a63e 100644
--- a/oak-lucene/src/test/java/org/apache/jackrabbit/oak/jcr/query/SuggestTest.java
+++ b/oak-lucene/src/test/java/org/apache/jackrabbit/oak/jcr/query/SuggestTest.java
@@ -33,6 +33,7 @@
 import org.junit.After;
 import org.junit.Before;
 
+import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.REINDEX_PROPERTY_NAME;
 import static org.apache.jackrabbit.oak.plugins.index.lucene.LuceneIndexConstants.SUGGESTION_CONFIG;
 import static org.apache.jackrabbit.oak.plugins.index.lucene.LuceneIndexConstants.SUGGEST_UPDATE_FREQUENCY_MINUTES;
 
@@ -48,6 +49,7 @@ public void setUp() throws Exception {
         // change suggester update frequency
         superuser.getNode("/oak:index/luceneGlobal/" + SUGGESTION_CONFIG)
                 .setProperty(SUGGEST_UPDATE_FREQUENCY_MINUTES, 0);
+        superuser.getNode("/oak:index/luceneGlobal").setProperty(REINDEX_PROPERTY_NAME, true);
     }
 
     @After
diff --git a/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LucenePropertyIndexTest.java b/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LucenePropertyIndexTest.java
index 6ab8777..6aff7a9 100644
--- a/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LucenePropertyIndexTest.java
+++ b/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LucenePropertyIndexTest.java
@@ -138,6 +138,7 @@
 import org.junit.Test;
 import org.junit.rules.TemporaryFolder;
 
+@SuppressWarnings("ArraysAsListWithZeroOrOneArgument")
 public class LucenePropertyIndexTest extends AbstractQueryTest {
     /**
      * Set the size to twice the batch size to test the pagination with sorting
@@ -158,6 +159,8 @@
 
     private NodeStore nodeStore;
 
+    private LuceneIndexProvider provider;
+
     @After
     public void after() {
         new ExecutorCloser(executorService).close();
@@ -172,7 +175,7 @@ protected void createTestIndexNode() throws Exception {
     protected ContentRepository createRepository() {
         IndexCopier copier = createIndexCopier();
         editorProvider = new LuceneIndexEditorProvider(copier, new ExtractedTextCache(10* FileUtils.ONE_MB, 100));
-        LuceneIndexProvider provider = new LuceneIndexProvider(copier);
+        provider = new LuceneIndexProvider(copier);
         nodeStore = new MemoryNodeStore();
         return new Oak(nodeStore)
                 .with(new InitialContent())
@@ -804,6 +807,7 @@ public void wildcardQueryToLookupUnanalyzedText() throws Exception {
         //set propb def to be node scope indexed
         propTree = root.getTree(idx.getPath() + "/indexRules/nt:base/properties/propb");
         propTree.setProperty(PROP_NODE_SCOPE_INDEX, true);
+        root.getTree(idx.getPath()).setProperty(REINDEX_PROPERTY_NAME, true);
         root.commit();
 
         Tree rootTree = root.getTree("/");
@@ -2424,6 +2428,70 @@ public void subNodeTypes() throws Exception{
         assertPlanAndQuery("select * from [oak:TestMixA]", "lucene:test1(/oak:index/test1)", asList("/b", "/c"));
     }
 
+    @Test
+    public void indexDefinitionModifiedPostReindex() throws Exception{
+        IndexDefinitionBuilder idxb = new IndexDefinitionBuilder().noAsync();
+        idxb.indexRule("nt:base").property("foo").propertyIndex();
+        Tree idx = root.getTree("/").getChild("oak:index").addChild("test1");
+        idxb.build(idx);
+
+        Tree rootTree = root.getTree("/");
+        rootTree.addChild("a").setProperty("foo", "bar");
+        rootTree.addChild("b").setProperty("bar", "bar");
+        root.commit();
+
+        String query = "select * from [nt:base] where [foo] = 'bar'";
+        assertPlanAndQuery(query, "lucene:test1(/oak:index/test1)", asList("/a"));
+
+        Tree barProp = root.getTree("/oak:index/test1/indexRules/nt:base/properties").addChild("bar");
+        barProp.setProperty("name", "bar");
+        barProp.setProperty("propertyIndex", true);
+        root.commit();
+
+        query = "select * from [nt:base] where [bar] = 'bar'";
+        assertThat(explain(query), not(containsString("lucene:test1(/oak:index/test1)")));
+
+        root.getTree("/oak:index/test1").setProperty(REINDEX_PROPERTY_NAME, true);
+        root.commit();
+
+        assertPlanAndQuery(query, "lucene:test1(/oak:index/test1)", asList("/b"));
+    }
+
+    @Test
+    public void refreshIndexDefinition() throws Exception{
+        IndexDefinitionBuilder idxb = new IndexDefinitionBuilder().noAsync();
+        idxb.indexRule("nt:base").property("foo").propertyIndex();
+        Tree idx = root.getTree("/").getChild("oak:index").addChild("test1");
+        idxb.build(idx);
+
+        Tree rootTree = root.getTree("/");
+        rootTree.addChild("a").setProperty("foo", "bar");
+        rootTree.addChild("b").setProperty("bar", "bar");
+        root.commit();
+
+        String query = "select * from [nt:base] where [foo] = 'bar'";
+        assertPlanAndQuery(query, "lucene:test1(/oak:index/test1)", asList("/a"));
+
+        Tree barProp = root.getTree("/oak:index/test1/indexRules/nt:base/properties").addChild("bar");
+        barProp.setProperty("name", "bar");
+        barProp.setProperty("propertyIndex", true);
+        root.commit();
+
+        query = "select * from [nt:base] where [bar] = 'bar'";
+        assertThat(explain(query), not(containsString("lucene:test1(/oak:index/test1)")));
+
+        //Instead of reindex just refresh the index definition so that new index definition gets picked up
+        root.getTree("/oak:index/test1").setProperty(LuceneIndexConstants.PROP_REFRESH_DEFN, true);
+        root.commit();
+
+        //Plan would reflect new defintion
+        assertThat(explain(query), containsString("lucene:test1(/oak:index/test1)"));
+        assertFalse(root.getTree("/oak:index/test1").hasProperty(LuceneIndexConstants.PROP_REFRESH_DEFN));
+
+        //However as reindex was not done query would result in empty set
+        assertPlanAndQuery(query, "lucene:test1(/oak:index/test1)", Collections.<String>emptyList());
+    }
+
     private void assertPlanAndQuery(String query, String planExpectation, List<String> paths){
         assertThat(explain(query), containsString(planExpectation));
         assertQuery(query, paths);
diff --git a/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/NodeStateClonerTest.java b/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/NodeStateClonerTest.java
new file mode 100644
index 0000000..3298945
--- /dev/null
+++ b/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/NodeStateClonerTest.java
@@ -0,0 +1,64 @@
+/*
+ * 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;
+
+import org.apache.jackrabbit.oak.plugins.index.lucene.NodeStateCloner;
+import org.apache.jackrabbit.oak.spi.state.EqualsDiff;
+import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+import org.apache.jackrabbit.oak.spi.state.NodeStateUtils;
+import org.junit.Test;
+
+import static org.apache.jackrabbit.oak.plugins.memory.EmptyNodeState.EMPTY_NODE;
+import static org.junit.Assert.*;
+
+public class NodeStateClonerTest {
+
+    @Test
+    public void simple() throws Exception{
+        NodeBuilder builder = EMPTY_NODE.builder();
+        builder.child("a").child("b");
+        builder.child("a").setProperty("foo", "bar");
+
+        NodeState state = builder.getNodeState();
+        assertTrue(EqualsDiff.equals(state, NodeStateCloner.cloneVisibleState(state)));
+    }
+
+    @Test
+    public void excludeHidden() throws Exception{
+        NodeBuilder builder = EMPTY_NODE.builder();
+        builder.child("a").child(":b").child("e");
+        builder.child("a").child("c").child(":d");
+        builder.child("a").setProperty("foo", "bar");
+
+        NodeState source = builder.getNodeState();
+        NodeState cloned = NodeStateCloner.cloneVisibleState(source);
+
+        assertFalse(NodeStateUtils.getNode(cloned, "/a/:b").exists());
+        assertFalse(NodeStateUtils.getNode(cloned, "/a/:b/e").exists());
+        assertFalse(NodeStateUtils.getNode(cloned, "/a/c/:d").exists());
+
+        assertTrue(NodeStateUtils.getNode(cloned, "/a/c").exists());
+        assertTrue(NodeStateUtils.getNode(cloned, "/a").hasProperty("foo"));
+
+        assertFalse(EqualsDiff.equals(source, cloned));
+    }
+
+}
\ No newline at end of file
