Author: catholicon
Date: Wed Nov  1 08:49:52 2017
New Revision: 1813955

URL: http://svn.apache.org/viewvc?rev=1813955&view=rev
Log:
OAK-6735: Lucene Index: improved cost estimation by using document count per 
field

Added:
    
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexStatistics.java
   (with props)
    
jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexStatisticsTest.java
   (with props)
Modified:
    
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexNode.java
    
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexNodeManager.java
    
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexPlanner.java
    
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndex.java
    
jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexPlannerTest.java
    
jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/MultiplexingLucenePropertyIndexTest.java

Modified: 
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexNode.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexNode.java?rev=1813955&r1=1813954&r2=1813955&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexNode.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexNode.java
 Wed Nov  1 08:49:52 2017
@@ -36,6 +36,8 @@ public interface IndexNode {
 
     IndexSearcher getSearcher();
 
+    IndexStatistics getIndexStatistics();
+
     IndexDefinition getDefinition();
 
     List<LuceneIndexReader> getPrimaryReaders();

Modified: 
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexNodeManager.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexNodeManager.java?rev=1813955&r1=1813954&r2=1813955&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexNodeManager.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexNodeManager.java
 Wed Nov  1 08:49:52 2017
@@ -275,10 +275,16 @@ public class IndexNodeManager {
         final IndexSearcher searcher;
         final List<LuceneIndexReader> nrtReaders;
         final int searcherId = SEARCHER_ID_COUNTER.incrementAndGet();
+        final IndexStatistics indexStatistics;
 
         public SearcherHolder(IndexSearcher searcher, List<LuceneIndexReader> 
nrtReaders) {
             this.searcher = searcher;
             this.nrtReaders = nrtReaders;
+            this.indexStatistics = new 
IndexStatistics(searcher.getIndexReader());
+        }
+
+        public IndexStatistics getIndexStatistics() {
+            return indexStatistics;
         }
     }
 
@@ -308,6 +314,11 @@ public class IndexNodeManager {
         }
 
         @Override
+        public IndexStatistics getIndexStatistics() {
+            return holder.getIndexStatistics();
+        }
+
+        @Override
         public IndexDefinition getDefinition() {
             return definition;
         }

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=1813955&r1=1813954&r2=1813955&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
 Wed Nov  1 08:49:52 2017
@@ -48,7 +48,6 @@ import org.apache.jackrabbit.oak.spi.que
 import org.apache.jackrabbit.oak.spi.query.fulltext.FullTextVisitor;
 import org.apache.jackrabbit.oak.spi.query.Filter;
 import org.apache.jackrabbit.oak.spi.query.QueryConstants;
-import org.apache.lucene.index.IndexReader;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -255,26 +254,14 @@ class IndexPlanner {
         boolean canSort = canSortByProperty(sortOrder);
         if (!indexedProps.isEmpty() || canSort || ft != null
                 || evalPathRestrictions || evalNodeTypeRestrictions || 
canEvalNodeNameRestriction) {
-            //TODO Need a way to have better cost estimate to indicate that
-            //this index can evaluate more propertyRestrictions natively (if 
more props are indexed)
-            //For now we reduce cost per entry
-
-            //Use propDefns instead of indexedProps as it determines true 
count of property restrictions
-            //which are evaluated by this index
-            int costPerEntryFactor = result.propDefns.size();
+            int costPerEntryFactor = 1;
             costPerEntryFactor += sortOrder.size();
 
-            //this index can evaluate more propertyRestrictions natively (if 
more props are indexed)
-            //For now we reduce cost per entry
             IndexPlan.Builder plan = defaultPlan();
             if (!sortOrder.isEmpty()) {
                 plan.setSortOrder(sortOrder);
             }
 
-            if (costPerEntryFactor == 0){
-                costPerEntryFactor = 1;
-            }
-
             if (facetFields.size() > 0) {
                 plan.setAttribute(FacetHelper.ATTR_FACET_FIELDS, facetFields);
             }
@@ -291,6 +278,11 @@ class IndexPlanner {
                 result.enableNodeNameRestriction();
             }
 
+            // Set a index based guess here. Unique would set its own value 
below
+            if (useActualEntryCount && !definition.isEntryCountDefined()) {
+                
plan.setEstimatedEntryCount(getMaxPossibleNumDocs(result.propDefns));
+            }
+
             if (sortOrder.isEmpty() && ft == null) {
                 boolean uniqueIndexFound = planForSyncIndexes(indexingRule);
                 if (uniqueIndexFound) {
@@ -717,7 +709,7 @@ class IndexPlanner {
     }
 
     private long estimatedEntryCount() {
-        int numOfDocs = getReader().numDocs();
+        int numOfDocs = getNumDocs();
         if (useActualEntryCount) {
             return definition.isEntryCountDefined() ? 
definition.getEntryCount() : numOfDocs;
         } else {
@@ -742,8 +734,37 @@ class IndexPlanner {
         return PathUtils.denotesRoot(parentPath) ? "" : parentPath;
     }
 
-    private IndexReader getReader() {
-        return indexNode.getSearcher().getIndexReader();
+    private int getNumDocs() {
+        return indexNode.getIndexStatistics().numDocs();
+    }
+
+    private int getMaxPossibleNumDocs(Map<String, PropertyDefinition> 
propDefns) {
+        IndexStatistics indexStatistics = indexNode.getIndexStatistics();
+        int minNumDocs = indexStatistics.numDocs();
+        for (Map.Entry<String, PropertyDefinition> propDef : 
propDefns.entrySet()) {
+            int docCntForField = 
indexStatistics.getDocCountFor(propDef.getKey());
+            if (docCntForField == -1) {
+                continue;
+            }
+
+            int weight = propDef.getValue().weight;
+
+            if (weight > 1) {
+                // use it to scale down the doc count - in broad strokes, we 
can think of weight
+                // as number of terms for the field with all terms getting 
equal share of
+                // the documents in this field
+                double scaledDocCnt = Math.ceil((double) docCntForField / 
weight);
+                if (minNumDocs < scaledDocCnt) {
+                    continue;
+                }
+                // since, we've already taken care that scaled cost is lower 
than minCost,
+                // we can safely cast without risking overflow
+                minNumDocs = (int)scaledDocCnt;
+            } else if (docCntForField < minNumDocs) {
+                minNumDocs = docCntForField;
+            }
+        }
+        return minNumDocs;
     }
 
     private List<OrderEntry> createSortOrder(IndexingRule rule) {

Added: 
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexStatistics.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexStatistics.java?rev=1813955&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexStatistics.java
 (added)
+++ 
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexStatistics.java
 Wed Nov  1 08:49:52 2017
@@ -0,0 +1,104 @@
+/*
+ * 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 com.google.common.collect.Maps;
+import org.apache.lucene.index.Fields;
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.MultiFields;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.Map;
+
+/**
+ * This class would populate some statistics from a reader. We want to be 
careful here such that
+ * we only collect statistics which don't incur reads from the index i.e. we 
would only collect
+ * stats that lucene would already have read into memory when the reader was 
opened.
+ */
+public class IndexStatistics {
+    static final Logger LOG = LoggerFactory.getLogger(IndexStatistics.class);
+    private final int numDocs;
+    private final Map<String, Integer> numDocsForField;
+
+    /**
+     * @param reader {@link IndexReader} for which statistics need to be 
collected.
+     */
+    public IndexStatistics(IndexReader reader) {
+        numDocs = reader.numDocs();
+
+        Map<String, Integer> numDocsForField = Maps.newHashMap();
+
+        Fields fields = null;
+        try {
+            fields = MultiFields.getFields(reader);
+        } catch (IOException e) {
+            LOG.warn("Couldn't open fields for reader ({}). Won't extract doc 
count per field", reader);
+            numDocsForField = null;
+        }
+
+        if (fields != null) {
+            for(String f : fields) {
+                if (isPropertyField(f)) {
+                    int docCntForField = numDocs;
+                    try {
+                        docCntForField = reader.getDocCount(f);
+                    } catch (IOException e) {
+                        LOG.warn("Couldn't read doc count for field {} via 
reader ({}). Would use numDocs for this field");
+                    }
+                    numDocsForField.put(f, docCntForField);
+                }
+            }
+        }
+
+        if (numDocsForField != null) {
+            this.numDocsForField = 
Collections.unmodifiableMap(numDocsForField);
+        } else {
+            this.numDocsForField = null;
+        }
+    }
+
+    /**
+     * @return number of documents in the index
+     */
+    public int numDocs() {
+        return numDocs;
+    }
+
+    /**
+     * @param field Index field for which number of indexed documents are to 
be return
+     * @return number of indexed documents (without subtracting potentially 
deleted ones)
+     *         for the given {@code field}.
+     */
+    public int getDocCountFor(String field) {
+        int docCntForField = isPropertyField(field) ? 0 : -1;
+        if (numDocsForField.containsKey(field)) {
+            docCntForField = numDocsForField.get(field);
+        }
+
+        return docCntForField;
+    }
+
+    private boolean isPropertyField(String field) {
+        return !field.startsWith(FieldNames.ANALYZED_FIELD_PREFIX)
+                && !field.startsWith(FieldNames.FULLTEXT_RELATIVE_NODE)
+                && !field.startsWith(":")
+                && !field.endsWith("_facet");
+    }
+}

Propchange: 
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexStatistics.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: 
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndex.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndex.java?rev=1813955&r1=1813954&r2=1813955&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndex.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndex.java
 Wed Nov  1 08:49:52 2017
@@ -211,7 +211,7 @@ public class LuceneIndex implements Adva
             if (node != null){
                 IndexDefinition defn = node.getDefinition();
                 return Collections.singletonList(planBuilder(filter)
-                        
.setEstimatedEntryCount(defn.getFulltextEntryCount(node.getSearcher().getIndexReader().numDocs()))
+                        
.setEstimatedEntryCount(defn.getFulltextEntryCount(node.getIndexStatistics().numDocs()))
                         .setCostPerExecution(defn.getCostPerExecution())
                         .setCostPerEntry(defn.getCostPerEntry())
                         .setAttribute(ATTR_INDEX_PATH, indexPath)

Modified: 
jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexPlannerTest.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexPlannerTest.java?rev=1813955&r1=1813954&r2=1813955&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexPlannerTest.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexPlannerTest.java
 Wed Nov  1 08:49:52 2017
@@ -30,9 +30,12 @@ import static org.apache.jackrabbit.oak.
 import static 
org.apache.jackrabbit.oak.plugins.index.lucene.LuceneIndexConstants.INDEX_DATA_CHILD_NAME;
 import static 
org.apache.jackrabbit.oak.plugins.index.lucene.LuceneIndexConstants.INDEX_RULES;
 import static 
org.apache.jackrabbit.oak.plugins.index.lucene.LuceneIndexConstants.ORDERED_PROP_NAMES;
+import static 
org.apache.jackrabbit.oak.plugins.index.lucene.LuceneIndexConstants.PROP_FUNCTION;
 import static 
org.apache.jackrabbit.oak.plugins.index.lucene.LuceneIndexConstants.VERSION;
 import static org.apache.jackrabbit.oak.plugins.index.lucene.TestUtil.NT_TEST;
+import static org.apache.jackrabbit.oak.plugins.index.lucene.TestUtil.child;
 import static 
org.apache.jackrabbit.oak.plugins.index.lucene.TestUtil.registerTestNodeType;
+import static 
org.apache.jackrabbit.oak.plugins.index.lucene.util.FunctionIndexProcessor.*;
 import static 
org.apache.jackrabbit.oak.plugins.index.lucene.util.LuceneIndexHelper.newLuceneIndexDefinition;
 import static 
org.apache.jackrabbit.oak.plugins.index.lucene.util.LuceneIndexHelper.newLucenePropertyIndexDefinition;
 import static 
org.apache.jackrabbit.oak.plugins.memory.EmptyNodeState.EMPTY_NODE;
@@ -54,6 +57,8 @@ import java.util.List;
 
 import javax.annotation.Nonnull;
 
+import com.google.common.collect.Lists;
+import org.apache.jackrabbit.oak.api.Tree;
 import org.apache.jackrabbit.oak.api.Type;
 import org.apache.jackrabbit.oak.commons.PathUtils;
 import org.apache.jackrabbit.oak.plugins.index.IndexConstants;
@@ -61,6 +66,7 @@ import org.apache.jackrabbit.oak.plugins
 import 
org.apache.jackrabbit.oak.plugins.index.lucene.reader.DefaultIndexReader;
 import org.apache.jackrabbit.oak.plugins.index.lucene.reader.LuceneIndexReader;
 import 
org.apache.jackrabbit.oak.plugins.index.lucene.reader.LuceneIndexReaderFactory;
+import 
org.apache.jackrabbit.oak.plugins.index.lucene.util.FunctionIndexProcessor;
 import 
org.apache.jackrabbit.oak.plugins.index.lucene.util.IndexDefinitionBuilder;
 import org.apache.jackrabbit.oak.plugins.memory.PropertyValues;
 import org.apache.jackrabbit.oak.query.NodeStateNodeTypeInfoProvider;
@@ -519,20 +525,20 @@ public class IndexPlannerTest {
     @Test
     public void indexedButZeroWeightProps() throws Exception{
         IndexDefinitionBuilder defnb = new IndexDefinitionBuilder();
-        defnb.indexRule("nt:base").property("foo").propertyIndex().weight(0);
-        defnb.indexRule("nt:base").property("bar").propertyIndex();
+        defnb.indexRule("nt:base").property("foo").propertyIndex();
+        defnb.indexRule("nt:base").property("bar").propertyIndex().weight(0);
 
         IndexDefinition defn = new IndexDefinition(root, defnb.build(), 
"/foo");
         IndexNode node = createIndexNode(defn);
 
         FilterImpl filter = createFilter("nt:base");
-        filter.restrictProperty("foo", Operator.EQUAL, 
PropertyValues.newString("a"));
+        filter.restrictProperty("bar", Operator.EQUAL, 
PropertyValues.newString("a"));
         IndexPlanner planner = new IndexPlanner(node, "/foo", filter, 
Collections.<OrderEntry>emptyList());
         //Even though foo is indexed it would not be considered for a query 
involving just foo
         assertNull(planner.getPlan());
 
         filter = createFilter("nt:base");
-        filter.restrictProperty("bar", Operator.EQUAL, 
PropertyValues.newString("a"));
+        filter.restrictProperty("foo", Operator.EQUAL, 
PropertyValues.newString("a"));
         planner = new IndexPlanner(node, "/foo", filter, 
Collections.<OrderEntry>emptyList());
         QueryIndex.IndexPlan plan1 = planner.getPlan();
         assertNotNull(plan1);
@@ -544,9 +550,10 @@ public class IndexPlannerTest {
         QueryIndex.IndexPlan plan2 = planner.getPlan();
         assertNotNull(plan2);
 
-        //For plan2 as 2 props are indexed its costPerEntry should be less 
than plan1 which
-        //indexes only one prop
-        assertThat(plan2.getCostPerEntry(), lessThan(plan1.getCostPerEntry()));
+
+        // Since, the index has no entries for "bar", estimated entry count 
for plan2 would be 0
+        assertEquals(0, plan2.getEstimatedEntryCount());
+        assertThat(plan2.getEstimatedEntryCount(), 
lessThan(plan1.getEstimatedEntryCount()));
 
         assertTrue(pr(plan2).hasProperty("foo"));
         assertTrue(pr(plan2).hasProperty("bar"));
@@ -1251,6 +1258,316 @@ public class IndexPlannerTest {
     }
     //------ END - Suggestion/spellcheck plan tests
 
+    //------ Cost via doc count per field plan tests
+    @Test
+    public void noRestrictionWithSingleSortableField() throws Exception{
+        NodeBuilder defn = newLucenePropertyIndexDefinition(builder, "test", 
of("foo"), "async");
+        defn.setProperty(createProperty(ORDERED_PROP_NAMES, of("foo"), 
STRINGS));
+        IndexDefinition definition = new IndexDefinition(root, 
defn.getNodeState(), "/test");
+        IndexNode node = createIndexNode(definition);
+        IndexPlanner planner = new IndexPlanner(node, "/test", 
createFilter("nt:base"),
+                ImmutableList.of(new OrderEntry("foo", Type.LONG, 
OrderEntry.Order.ASCENDING),
+                        new OrderEntry("bar", Type.LONG, 
OrderEntry.Order.ASCENDING)));
+
+        assertNotNull(planner.getPlan());
+        assertEquals(1, planner.getPlan().getEstimatedEntryCount());
+        assertEquals(definition.getCostPerEntry()/2, 
planner.getPlan().getCostPerEntry(), 0.0001);
+    }
+
+    @Test
+    public void noRestrictionWithTwoSortableFields() throws Exception{
+        NodeBuilder defn = newLucenePropertyIndexDefinition(builder, "test", 
of("foo", "bar"), "async");
+        defn.setProperty(createProperty(ORDERED_PROP_NAMES, of("foo", "bar"), 
STRINGS));
+        IndexDefinition definition = new IndexDefinition(root, 
defn.getNodeState(), "/test");
+        IndexNode node = createIndexNode(definition);
+        IndexPlanner planner = new IndexPlanner(node, "/test", 
createFilter("nt:base"),
+                ImmutableList.of(new OrderEntry("foo", Type.LONG, 
OrderEntry.Order.ASCENDING),
+                        new OrderEntry("bar", Type.LONG, 
OrderEntry.Order.ASCENDING)));
+
+        assertNotNull(planner.getPlan());
+        assertEquals(1, planner.getPlan().getEstimatedEntryCount());
+        assertEquals(definition.getCostPerEntry()/3, 
planner.getPlan().getCostPerEntry(), 0.0001);
+    }
+
+    @Test
+    public void useNumDocsOnFieldForCost() throws Exception {
+        NodeBuilder defn = newLucenePropertyIndexDefinition(builder, "test", 
of("foo", "foo1", "foo2"), "async");
+        long numofDocs = IndexDefinition.DEFAULT_ENTRY_COUNT + 1000;
+
+        IndexDefinition idxDefn = new IndexDefinition(root, 
defn.getNodeState(), "/test");
+        Document doc = new Document();
+        doc.add(new StringField("foo1", "bar1", Field.Store.NO));
+        Directory sampleDirectory = createSampleDirectory(numofDocs, doc);
+        IndexNode node = createIndexNode(idxDefn, sampleDirectory);
+
+        // Query on "foo"
+        FilterImpl filter = createFilter("nt:base");
+        filter.restrictProperty("foo", Operator.EQUAL, 
PropertyValues.newString("bar"));
+        IndexPlanner planner = new IndexPlanner(node, "/test", filter, 
Collections.<OrderEntry>emptyList());
+        QueryIndex.IndexPlan plan = planner.getPlan();
+
+        assertEquals(numofDocs, plan.getEstimatedEntryCount());
+        assertEquals(1.0, plan.getCostPerExecution(), 0);
+        assertEquals(1.0, plan.getCostPerEntry(), 0);
+
+        // Query on "foo1"
+        filter = createFilter("nt:base");
+        filter.restrictProperty("foo1", Operator.EQUAL, 
PropertyValues.newString("bar1"));
+        planner = new IndexPlanner(node, "/test", filter, 
Collections.<OrderEntry>emptyList());
+        plan = planner.getPlan();
+
+        assertEquals(1, plan.getEstimatedEntryCount());
+        assertEquals(1.0, plan.getCostPerExecution(), 0);
+        assertEquals(1.0, plan.getCostPerEntry(), 0);
+
+        // Query on "foo" and "foo1" should use minimum
+        filter = createFilter("nt:base");
+        filter.restrictProperty("foo", Operator.EQUAL, 
PropertyValues.newString("bar"));
+        filter.restrictProperty("foo1", Operator.EQUAL, 
PropertyValues.newString("bar1"));
+        planner = new IndexPlanner(node, "/test", filter, 
Collections.<OrderEntry>emptyList());
+        plan = planner.getPlan();
+
+        assertEquals(1, plan.getEstimatedEntryCount());
+
+        // Query on "foo" and "foo1" and "foo2" should give 0 as foo3 isn't 
there in any document
+        filter = createFilter("nt:base");
+        filter.restrictProperty("foo", Operator.EQUAL, 
PropertyValues.newString("bar"));
+        filter.restrictProperty("foo1", Operator.EQUAL, 
PropertyValues.newString("bar1"));
+        filter.restrictProperty("foo2", Operator.EQUAL, 
PropertyValues.newString("bar2"));
+        planner = new IndexPlanner(node, "/test", filter, 
Collections.<OrderEntry>emptyList());
+        plan = planner.getPlan();
+
+        assertEquals(0, plan.getEstimatedEntryCount());
+    }
+
+    @Test
+    public void weightedPropDefs() throws Exception {
+        String indexPath = "/test";
+        IndexDefinitionBuilder idxBuilder = new 
IndexDefinitionBuilder(child(builder, indexPath));
+        
idxBuilder.indexRule("nt:base").property("foo").propertyIndex().weight(500)
+                .enclosingRule().property("foo1").propertyIndex().weight(20)
+                .enclosingRule().property("foo2").propertyIndex().weight(0)
+                .enclosingRule().property("foo3").propertyIndex()
+        ;
+        NodeState defn = idxBuilder.build();
+
+        List<Document> docs = Lists.newArrayList();
+        Document doc;
+        for (int i = 0; i < 60; i++) {
+            doc = new Document();
+            doc.add(new StringField("foo1", "bar1" + i, Field.Store.NO));
+            docs.add(doc);
+        }
+        doc = new Document();
+        doc.add(new StringField("foo2", "bar2", Field.Store.NO));
+        docs.add(doc);
+        Directory sampleDirectory = createSampleDirectory(1000, docs);
+        IndexDefinition idxDefn = new IndexDefinition(root, defn, indexPath);
+        IndexNode node = createIndexNode(idxDefn, sampleDirectory);
+
+        // Query on "foo"
+        FilterImpl filter = createFilter("nt:base");
+        filter.restrictProperty("foo", Operator.EQUAL, 
PropertyValues.newString("bar"));
+        IndexPlanner planner = new IndexPlanner(node, indexPath, filter, 
Collections.<OrderEntry>emptyList());
+        QueryIndex.IndexPlan plan = planner.getPlan();
+
+        //scale down 1000 by 500 = 2
+        assertEquals(2, plan.getEstimatedEntryCount());
+
+        // Query on "foo1"
+        filter = createFilter("nt:base");
+        filter.restrictProperty("foo1", Operator.EQUAL, 
PropertyValues.newString("bar"));
+        planner = new IndexPlanner(node, indexPath, filter, 
Collections.<OrderEntry>emptyList());
+        plan = planner.getPlan();
+
+        //scale down 60 by 20 = 2
+        assertEquals(3, plan.getEstimatedEntryCount());
+
+        // Query on "foo" and "foo1"
+        filter = createFilter("nt:base");
+        filter.restrictProperty("foo", Operator.EQUAL, 
PropertyValues.newString("bar"));
+        filter.restrictProperty("foo1", Operator.EQUAL, 
PropertyValues.newString("bar"));
+        planner = new IndexPlanner(node, indexPath, filter, 
Collections.<OrderEntry>emptyList());
+        plan = planner.getPlan();
+
+        //min(2, 3)
+        assertEquals(2, plan.getEstimatedEntryCount());
+
+        // Query on "foo1" and "foo2"
+        filter = createFilter("nt:base");
+        filter.restrictProperty("foo1", Operator.EQUAL, 
PropertyValues.newString("bar"));
+        filter.restrictProperty("foo2", Operator.EQUAL, 
PropertyValues.newString("bar"));
+        planner = new IndexPlanner(node, indexPath, filter, 
Collections.<OrderEntry>emptyList());
+        plan = planner.getPlan();
+
+        //don't scale down 1 by 0 (foo1 would estimate 3)
+        assertEquals(1, plan.getEstimatedEntryCount());
+
+        // Query on "foo1" and "foo3"
+        filter = createFilter("nt:base");
+        filter.restrictProperty("foo1", Operator.EQUAL, 
PropertyValues.newString("bar"));
+        filter.restrictProperty("foo3", Operator.EQUAL, 
PropertyValues.newString("bar"));
+        planner = new IndexPlanner(node, indexPath, filter, 
Collections.<OrderEntry>emptyList());
+        plan = planner.getPlan();
+
+        //min(0, 3)
+        assertEquals(0, plan.getEstimatedEntryCount());
+    }
+
+    @Test
+    public void weightedRegexPropDefs() throws Exception {
+        String indexPath = "/test";
+        IndexDefinitionBuilder idxBuilder = new 
IndexDefinitionBuilder(child(builder, indexPath));
+        idxBuilder.indexRule("nt:base").property("foo").propertyIndex()
+                .enclosingRule().property("bar", "bar.*", 
true).propertyIndex().weight(20)
+        ;
+        NodeState defn = idxBuilder.build();
+
+        List<Document> docs = Lists.newArrayList();
+        Document doc;
+        for (int i = 0; i < 60; i++) {
+            doc = new Document();
+            doc.add(new StringField("bar1", "foo1" + i, Field.Store.NO));
+            docs.add(doc);
+        }
+        for (int i = 0; i < 40; i++) {
+            doc = new Document();
+            doc.add(new StringField("bar2", "foo2" + i, Field.Store.NO));
+            docs.add(doc);
+        }
+        Directory sampleDirectory = createSampleDirectory(1000, docs);
+        IndexDefinition idxDefn = new IndexDefinition(root, defn, indexPath);
+        IndexNode node = createIndexNode(idxDefn, sampleDirectory);
+
+        // Query on and "bar1"
+        FilterImpl filter = createFilter("nt:base");
+        filter.restrictProperty("bar1", Operator.EQUAL, 
PropertyValues.newString("foo1"));
+        IndexPlanner planner = new IndexPlanner(node, indexPath, filter, 
Collections.<OrderEntry>emptyList());
+        QueryIndex.IndexPlan plan = planner.getPlan();
+
+        //scale down 60 by 20 = 3
+        assertEquals(3, plan.getEstimatedEntryCount());
+
+        // Query on and "bar1" and "bar2"
+        filter = createFilter("nt:base");
+        filter.restrictProperty("bar1", Operator.EQUAL, 
PropertyValues.newString("foo1"));
+        filter.restrictProperty("bar2", Operator.EQUAL, 
PropertyValues.newString("foo2"));
+        planner = new IndexPlanner(node, indexPath, filter, 
Collections.<OrderEntry>emptyList());
+        plan = planner.getPlan();
+
+        //min(3, 2)
+        assertEquals(2, plan.getEstimatedEntryCount());
+    }
+
+    @Test
+    public void overflowingWeight() throws Exception {
+        String indexPath = "/test";
+        IndexDefinitionBuilder idxBuilder = new 
IndexDefinitionBuilder(child(builder, indexPath));
+        
idxBuilder.indexRule("nt:base").property("foo").propertyIndex().weight(Integer.MAX_VALUE/2)
+                .enclosingRule().property("foo1").propertyIndex()
+        ;
+        NodeState defn = idxBuilder.build();
+
+        List<Document> docs = Lists.newArrayList();
+        Document doc;
+        for (int i = 0; i < 60; i++) {
+            doc = new Document();
+            doc.add(new StringField("foo1", "bar1" + i, Field.Store.NO));
+            docs.add(doc);
+        }
+        Directory sampleDirectory = createSampleDirectory(1000, docs);
+        IndexDefinition idxDefn = new IndexDefinition(root, defn, indexPath);
+        IndexNode node = createIndexNode(idxDefn, sampleDirectory);
+
+        // Query on and "foo"
+        FilterImpl filter = createFilter("nt:base");
+        filter.restrictProperty("foo", Operator.EQUAL, 
PropertyValues.newString("foo1"));
+        IndexPlanner planner = new IndexPlanner(node, indexPath, filter, 
Collections.<OrderEntry>emptyList());
+        QueryIndex.IndexPlan plan = planner.getPlan();
+
+        //scale down 1000 by INT_MAX/2 and ceil ~= 1.
+        assertEquals(1, plan.getEstimatedEntryCount());
+
+        // Query on and "foo" and "foo1"
+        filter = createFilter("nt:base");
+        filter.restrictProperty("foo", Operator.EQUAL, 
PropertyValues.newString("bar"));
+        filter.restrictProperty("foo1", Operator.EQUAL, 
PropertyValues.newString("bar1"));
+        planner = new IndexPlanner(node, indexPath, filter, 
Collections.<OrderEntry>emptyList());
+        plan = planner.getPlan();
+
+        //min(1, 60)
+        assertEquals(1, plan.getEstimatedEntryCount());
+    }
+
+    @Test
+    public void functionPropDef() throws Exception {
+        String indexPath = "/test";
+        IndexDefinitionBuilder idxBuilder = new 
IndexDefinitionBuilder(child(builder, indexPath));
+        idxBuilder.indexRule("nt:base").property("foo").propertyIndex();
+        Tree fooPD = 
idxBuilder.getBuilderTree().getChild("indexRules").getChild("nt:base")
+                .getChild("properties").getChild("foo");
+        fooPD.setProperty(PROP_FUNCTION, "lower([foo])");
+        NodeState defn = idxBuilder.build();
+
+        Document doc = new Document();
+        doc.add(new StringField(convertToPolishNotation("lower([foo])"), 
"bar1", Field.Store.NO));
+        Directory sampleDirectory = createSampleDirectory(2, doc);
+        IndexDefinition idxDefn = new IndexDefinition(root, defn, indexPath);
+        IndexNode node = createIndexNode(idxDefn, sampleDirectory);
+
+        // Query on and "foo"
+        FilterImpl filter = createFilter("nt:base");
+        filter.restrictProperty(convertToPolishNotation("lower([foo])"), 
Operator.EQUAL,
+                PropertyValues.newString("foo1"));
+        IndexPlanner planner = new IndexPlanner(node, indexPath, filter, 
Collections.<OrderEntry>emptyList());
+        QueryIndex.IndexPlan plan = planner.getPlan();
+
+        assertEquals(1, plan.getEstimatedEntryCount());
+    }
+
+    @Test
+    public void fullTextWithPropRestriction() throws Exception{
+        String indexPath = "/test";
+        IndexDefinitionBuilder idxBuilder = new 
IndexDefinitionBuilder(child(builder, indexPath));
+        idxBuilder.indexRule("nt:base").property("foo").nodeScopeIndex()
+                .enclosingRule().property("foo1").propertyIndex()
+                .enclosingRule().property("foo2").analyzed();
+        NodeState defn = idxBuilder.build();
+
+        long numofDocs = IndexDefinition.DEFAULT_ENTRY_COUNT + 1000;
+
+        IndexDefinition idxDefn = new IndexDefinition(root, defn, indexPath);
+        Document doc = new Document();
+        doc.add(new StringField("foo1", "bar1", Field.Store.NO));
+        Directory sampleDirectory = createSampleDirectory(numofDocs, doc);
+        IndexNode node = createIndexNode(idxDefn, sampleDirectory);
+
+        // contains(., 'mountain') AND contains('foo2', 'hill')
+        FilterImpl filter = createFilter("nt:base");
+        filter.setFullTextConstraint(FullTextParser.parse(".", "mountain"));
+        filter.setFullTextConstraint(FullTextParser.parse("foo2", "hill"));
+        IndexPlanner planner = new IndexPlanner(node, indexPath, filter, 
Collections.<OrderEntry>emptyList());
+        QueryIndex.IndexPlan plan = planner.getPlan();
+
+        assertEquals(numofDocs + 1, plan.getEstimatedEntryCount());
+        assertEquals(1.0, plan.getCostPerExecution(), 0);
+        assertEquals(1.0, plan.getCostPerEntry(), 0);
+
+        // contains(., 'mountain') AND [foo1]='bar' AND contains('foo2', 
'hill')
+        filter = createFilter("nt:base");
+        filter.setFullTextConstraint(FullTextParser.parse(".", "mountain"));
+        filter.restrictProperty("foo1", Operator.EQUAL, 
PropertyValues.newString("bar"));
+        filter.setFullTextConstraint(FullTextParser.parse("foo2", "hill"));
+        planner = new IndexPlanner(node, indexPath, filter, 
Collections.<OrderEntry>emptyList());
+        plan = planner.getPlan();
+
+        assertEquals(1, plan.getEstimatedEntryCount());
+        assertEquals(1.0, plan.getCostPerExecution(), 0);
+        assertEquals(1.0, plan.getCostPerEntry(), 0);
+    }
+    //------ END - Cost via doc count per field plan tests
+
+
     private IndexNode createIndexNode(IndexDefinition defn, long numOfDocs) 
throws IOException {
         return new IndexNodeManager("foo", defn, new 
TestReaderFactory(createSampleDirectory(numOfDocs)).createReaders(defn, 
EMPTY_NODE, "foo"), null).acquire();
     }
@@ -1259,6 +1576,10 @@ public class IndexPlannerTest {
         return new IndexNodeManager("foo", defn, new 
TestReaderFactory(createSampleDirectory()).createReaders(defn, EMPTY_NODE, 
"foo"), null).acquire();
     }
 
+    private IndexNode createIndexNode(IndexDefinition defn, Directory 
sampleDirectory) throws IOException {
+        return new IndexNodeManager("foo", defn, new 
TestReaderFactory(sampleDirectory).createReaders(defn, EMPTY_NODE, "foo"), 
null).acquire();
+    }
+
     private FilterImpl createFilter(String nodeTypeName) {
         NodeTypeInfoProvider nodeTypes = new 
NodeStateNodeTypeInfoProvider(root);
         NodeTypeInfo type = nodeTypes.getNodeTypeInfo(nodeTypeName);
@@ -1271,6 +1592,14 @@ public class IndexPlannerTest {
     }
 
     private static Directory createSampleDirectory(long numOfDocs) throws 
IOException {
+        return createSampleDirectory(numOfDocs, Collections.EMPTY_LIST);
+    }
+
+    private static Directory createSampleDirectory(long numOfDocs, @Nonnull 
Document doc) throws IOException {
+        return createSampleDirectory(numOfDocs, 
Collections.singletonList(doc));
+    }
+
+    private static Directory createSampleDirectory(long numOfDocs, 
Iterable<Document> docs) throws IOException {
         Directory dir = new RAMDirectory();
         IndexWriterConfig config = new IndexWriterConfig(VERSION, 
LuceneIndexConstants.ANALYZER);
         IndexWriter writer = new  IndexWriter(dir, config);
@@ -1279,6 +1608,9 @@ public class IndexPlannerTest {
             doc.add(new StringField("foo", "bar" + i, Field.Store.NO));
             writer.addDocument(doc);
         }
+        for (Document doc : docs) {
+            writer.addDocument(doc);
+        }
         writer.close();
         return dir;
     }

Added: 
jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexStatisticsTest.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexStatisticsTest.java?rev=1813955&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexStatisticsTest.java
 (added)
+++ 
jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexStatisticsTest.java
 Wed Nov  1 08:49:52 2017
@@ -0,0 +1,167 @@
+/*
+ * 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 com.google.common.collect.Lists;
+import org.apache.lucene.document.Document;
+import org.apache.lucene.document.Field;
+import org.apache.lucene.document.StringField;
+import org.apache.lucene.index.DirectoryReader;
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.IndexWriter;
+import org.apache.lucene.index.IndexWriterConfig;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.store.Directory;
+import org.apache.lucene.store.RAMDirectory;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.List;
+
+import static 
org.apache.jackrabbit.oak.plugins.index.lucene.LuceneIndexConstants.VERSION;
+
+public class IndexStatisticsTest {
+
+    @Test
+    public void numDocs() throws Exception {
+        Directory d = createSampleDirectory(2);
+        IndexStatistics stats = getStats(d);
+
+        Assert.assertEquals(2, stats.numDocs());
+    }
+
+    @Test
+    public void numDocsWithDelele() throws Exception {
+        Directory d = createSampleDirectory(2);
+        {
+            IndexWriter writer = getWriter(d);
+            writer.deleteDocuments(new Term("foo", "bar1"));
+            writer.close();
+        }
+
+        IndexStatistics stats = getStats(d);
+        Assert.assertEquals(1, stats.numDocs());
+    }
+
+    @Test
+    public void getSimpleFieldDocCnt() throws Exception {
+        Directory d = createSampleDirectory(2);
+        IndexStatistics stats = getStats(d);
+
+        Assert.assertEquals(2, stats.getDocCountFor("foo"));
+    }
+
+    @Test
+    public void getSimpleFieldDocCntWithDelete() throws Exception {
+        Directory d = createSampleDirectory(2);
+        {
+            IndexWriter writer = getWriter(d);
+            writer.deleteDocuments(new Term("foo", "bar1"));
+            writer.close();
+        }
+
+        IndexStatistics stats = getStats(d);
+        Assert.assertEquals("Stats don't need to get accurate result which 
might require reading more",
+                2, stats.getDocCountFor("foo"));
+    }
+
+    @Test
+    public void absentFields() throws Exception {
+        Directory d = createSampleDirectory(1);
+        IndexStatistics stats = getStats(d);
+
+        Assert.assertEquals(1, stats.getDocCountFor("foo"));
+        Assert.assertEquals(0, stats.getDocCountFor("absent"));
+        Assert.assertEquals(-1, stats.getDocCountFor(":someHiddenField"));
+        Assert.assertEquals(-1, 
stats.getDocCountFor(FieldNames.ANALYZED_FIELD_PREFIX + "foo"));
+        Assert.assertEquals(-1, 
stats.getDocCountFor(FieldNames.FULLTEXT_RELATIVE_NODE + "foo"));
+        Assert.assertEquals(-1, stats.getDocCountFor("foo_facet"));
+    }
+
+    @Test
+    public void onlyPropertyFields() throws Exception {
+        Document document = new Document();
+        document.add(new StringField("foo", "manualBar", Field.Store.NO));
+        document.add(new StringField(":someHiddenField", "manualBar", 
Field.Store.NO));
+        document.add(new StringField(FieldNames.ANALYZED_FIELD_PREFIX + "foo", 
"manualBar", Field.Store.NO));
+        document.add(new StringField(FieldNames.FULLTEXT_RELATIVE_NODE + 
"foo", "manualBar", Field.Store.NO));
+        document.add(new StringField("foo_facet", "manualBar", 
Field.Store.NO));
+        Directory d = createSampleDirectory(document);
+        IndexStatistics stats = getStats(d);
+
+        Assert.assertEquals(3, stats.getDocCountFor("foo"));
+        Assert.assertEquals(0, stats.getDocCountFor("absent"));
+        Assert.assertEquals(-1, stats.getDocCountFor(":someHiddenField"));
+        Assert.assertEquals(-1, 
stats.getDocCountFor(FieldNames.ANALYZED_FIELD_PREFIX + "foo"));
+        Assert.assertEquals(-1, 
stats.getDocCountFor(FieldNames.FULLTEXT_RELATIVE_NODE + "foo"));
+        Assert.assertEquals(-1, stats.getDocCountFor("foo_facet"));
+    }
+
+    private static Directory createSampleDirectory(long numOfDocs) throws 
IOException {
+        return createSampleDirectory(numOfDocs, Lists.newArrayList());
+    }
+
+    private static Directory createSampleDirectory(Document moreDoc) throws 
IOException {
+        return createSampleDirectory(2, Collections.singleton(moreDoc));
+    }
+
+    private static Directory createSampleDirectory(long numOfDocs, 
Iterable<Document> moreDocs) throws IOException {
+        List<Document> docs = Lists.newArrayList(moreDocs);
+        for (int i = 0; i < numOfDocs; i++) {
+            Document doc = new Document();
+            doc.add(new StringField("foo", "bar" + i, Field.Store.NO));
+            docs.add(doc);
+        }
+
+        return createSampleDirectory(docs);
+    }
+
+    private static Directory createSampleDirectory(Iterable<Document> docs) 
throws IOException {
+        Directory dir = new RAMDirectory();
+        IndexWriter writer = null;
+        try {
+            writer = getWriter(dir);
+            for (Document doc : docs) {
+                writer.addDocument(doc);
+            }
+            return dir;
+        } finally {
+            if (writer != null) {
+                writer.close();
+            }
+        }
+    }
+
+    private static IndexWriter getWriter(Directory d) throws IOException {
+        IndexWriterConfig config = new IndexWriterConfig(VERSION, 
LuceneIndexConstants.ANALYZER);
+        return new IndexWriter(d, config);
+    }
+
+    private static IndexStatistics getStats(Directory d) throws IOException {
+        IndexReader reader = DirectoryReader.open(d);
+        // no more reads
+        d.close();
+
+        IndexStatistics stats = new IndexStatistics(reader);
+        //close reader... Index stats would read numDocs right away
+        reader.close();
+
+        return stats;
+    }
+}

Propchange: 
jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexStatisticsTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: 
jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/MultiplexingLucenePropertyIndexTest.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/MultiplexingLucenePropertyIndexTest.java?rev=1813955&r1=1813954&r2=1813955&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/MultiplexingLucenePropertyIndexTest.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/MultiplexingLucenePropertyIndexTest.java
 Wed Nov  1 08:49:52 2017
@@ -71,6 +71,9 @@ import org.apache.jackrabbit.oak.spi.sta
 import org.apache.jackrabbit.oak.spi.state.NodeState;
 import org.apache.jackrabbit.oak.spi.state.NodeStateUtils;
 import org.apache.jackrabbit.oak.spi.state.NodeStore;
+import org.apache.lucene.document.Document;
+import org.apache.lucene.document.Field;
+import org.apache.lucene.document.StringField;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.TemporaryFolder;
@@ -136,8 +139,14 @@ public class MultiplexingLucenePropertyI
         LuceneIndexWriterFactory factory = new DefaultIndexWriterFactory(mip, 
directoryFactory, new LuceneIndexWriterConfig());
         LuceneIndexWriter writer = factory.newInstance(defn, builder, true);
 
-        writer.updateDocument("/content/en", newDoc("/content/en"));
-        writer.updateDocument("/libs/config", newDoc("/libs/config"));
+        Document doc = newDoc("/content/en");
+        doc.add(new StringField("foo", "bar", Field.Store.NO));
+        writer.updateDocument("/content/en", doc);
+
+        doc = newDoc("/libs/config");
+        doc.add(new StringField("foo", "baz", Field.Store.NO));
+        writer.updateDocument("/libs/config", doc);
+
         writer.close(0);
 
         //2. Construct the readers


Reply via email to