You are viewing a plain text version of this content. The canonical link for it is here.
Posted to oak-commits@jackrabbit.apache.org by to...@apache.org on 2018/05/24 13:07:54 UTC

svn commit: r1832166 [1/2] - in /jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search: ./ spi/editor/ spi/query/

Author: tommaso
Date: Thu May 24 13:07:54 2018
New Revision: 1832166

URL: http://svn.apache.org/viewvc?rev=1832166&view=rev
Log:
OAK-7410 - added the first draft of the query part

Added:
    jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/IndexStatistics.java   (with props)
    jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/IndexUpdateListener.java   (with props)
    jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/ReaderRefreshPolicy.java   (with props)
    jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/RefreshOnReadPolicy.java   (with props)
    jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/RefreshOnWritePolicy.java   (with props)
    jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/TimedRefreshPolicy.java   (with props)
    jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/query/
    jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/query/FulltextIndex.java   (with props)
    jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/query/FulltextIndexPlanner.java   (with props)
    jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/query/FulltextIndexTracker.java   (with props)
    jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/query/IndexNodeManager.java   (with props)
Modified:
    jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/BadIndexTracker.java
    jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/FulltextIndexConstants.java
    jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/IndexDefinition.java
    jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/IndexNode.java
    jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/PropertyDefinition.java
    jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/editor/BlobByteSource.java
    jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/editor/DocumentMaker.java
    jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/editor/FulltextDocumentMaker.java
    jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/editor/FulltextIndexEditor.java
    jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/editor/FulltextIndexEditorContext.java
    jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/editor/FulltextIndexWriter.java
    jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/editor/FulltextIndexWriterFactory.java
    jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/editor/TikaParserConfig.java

Modified: jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/BadIndexTracker.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/BadIndexTracker.java?rev=1832166&r1=1832165&r2=1832166&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/BadIndexTracker.java (original)
+++ jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/BadIndexTracker.java Thu May 24 13:07:54 2018
@@ -30,7 +30,7 @@ import com.google.common.collect.Maps;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-class BadIndexTracker {
+public class BadIndexTracker {
     /**
      * Time interval in millis after which a bad index would be accessed again
      * to check if it has been fixed

Modified: jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/FulltextIndexConstants.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/FulltextIndexConstants.java?rev=1832166&r1=1832165&r2=1832166&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/FulltextIndexConstants.java (original)
+++ jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/FulltextIndexConstants.java Thu May 24 13:07:54 2018
@@ -19,7 +19,7 @@ package org.apache.jackrabbit.oak.plugin
 
 public interface FulltextIndexConstants {
 
-    enum IndexingMode {
+  enum IndexingMode {
         SYNC,
         NRT,
         ASYNC;
@@ -49,6 +49,8 @@ public interface FulltextIndexConstants
 
     String PERSISTENCE_OAK = "repository";
 
+  String TEST_MODE = "testMode";
+
     String PERSISTENCE_FILE = "file";
 
     String PERSISTENCE_PATH = "path";

Modified: jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/IndexDefinition.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/IndexDefinition.java?rev=1832166&r1=1832165&r2=1832166&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/IndexDefinition.java (original)
+++ jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/IndexDefinition.java Thu May 24 13:07:54 2018
@@ -179,7 +179,7 @@ public final class IndexDefinition imple
     /**
      * native sort order
      */
-    static final OrderEntry NATIVE_SORT_ORDER = new OrderEntry(JCR_SCORE, Type.UNDEFINED,
+    public static final OrderEntry NATIVE_SORT_ORDER = new OrderEntry(JCR_SCORE, Type.UNDEFINED,
         OrderEntry.Order.DESCENDING);
 
     private final boolean fullTextEnabled;
@@ -260,12 +260,18 @@ public final class IndexDefinition imple
 
     private final boolean syncPropertyIndexes;
 
+    private final boolean testMode;
+
     //~--------------------------------------------------------< Builder >
 
     public static Builder newBuilder(NodeState root, NodeState defn, String indexPath){
         return new Builder(root, defn, indexPath);
     }
 
+    public boolean isTestMode() {
+        return false;
+    }
+
     public static class Builder {
         /**
          * Default unique id used when no existing uid is defined
@@ -343,6 +349,7 @@ public final class IndexDefinition imple
             rulesState = createIndexRules(defn).getNodeState();
         }
 
+        this.testMode = getOptionalValue(defn, FulltextIndexConstants.TEST_MODE, false);
         List<IndexingRule> definedIndexRules = newArrayList();
         this.indexRules = collectIndexRules(rulesState, definedIndexRules);
         this.definedRules = ImmutableList.copyOf(definedIndexRules);
@@ -828,7 +835,7 @@ public final class IndexDefinition imple
         final boolean inherited;
         final int propertyTypes;
         final boolean fulltextEnabled;
-        final boolean propertyIndexEnabled;
+        public final boolean propertyIndexEnabled;
         final boolean nodeFullTextIndexed;
 
         final Aggregate aggregate;

Modified: jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/IndexNode.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/IndexNode.java?rev=1832166&r1=1832165&r2=1832166&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/IndexNode.java (original)
+++ jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/IndexNode.java Thu May 24 13:07:54 2018
@@ -27,5 +27,6 @@ public interface IndexNode {
 
     int getIndexNodeId();
 
-    void refreshReadersOnWriteIfRequired();
+    IndexStatistics getIndexStatistics();
+
 }

Added: jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/IndexStatistics.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/IndexStatistics.java?rev=1832166&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/IndexStatistics.java (added)
+++ jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/IndexStatistics.java Thu May 24 13:07:54 2018
@@ -0,0 +1,28 @@
+/*
+ * 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.search;
+
+/**
+ *
+ */
+public interface IndexStatistics {
+  int numDocs();
+
+  int getDocCountFor(String key);
+}

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

Added: jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/IndexUpdateListener.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/IndexUpdateListener.java?rev=1832166&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/IndexUpdateListener.java (added)
+++ jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/IndexUpdateListener.java Thu May 24 13:07:54 2018
@@ -0,0 +1,25 @@
+/*
+ * 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.search;
+
+interface IndexUpdateListener extends ReaderRefreshPolicy {
+
+    void updated();
+}

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

Modified: jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/PropertyDefinition.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/PropertyDefinition.java?rev=1832166&r1=1832165&r2=1832166&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/PropertyDefinition.java (original)
+++ jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/PropertyDefinition.java Thu May 24 13:07:54 2018
@@ -80,7 +80,7 @@ public class PropertyDefinition {
 
     public final boolean ordered;
 
-    final boolean nullCheckEnabled;
+    public final boolean nullCheckEnabled;
 
     final boolean notNullCheckEnabled;
 
@@ -98,7 +98,7 @@ public class PropertyDefinition {
 
     public final boolean excludeFromAggregate;
 
-    final int weight;
+    public final int weight;
 
     /**
      * Property name excluding the relativePath. For regular expression based definition

Added: jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/ReaderRefreshPolicy.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/ReaderRefreshPolicy.java?rev=1832166&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/ReaderRefreshPolicy.java (added)
+++ jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/ReaderRefreshPolicy.java Thu May 24 13:07:54 2018
@@ -0,0 +1,56 @@
+/*
+ * 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.search;
+
+public interface ReaderRefreshPolicy {
+    ReaderRefreshPolicy NEVER = new ReaderRefreshPolicy() {
+        @Override
+        public void refreshOnReadIfRequired(Runnable refreshCallback) {
+            //Never refresh
+        }
+
+        @Override
+        public void refreshOnWriteIfRequired(Runnable refreshCallback) {
+            //Never refresh
+        }
+    };
+
+    /**
+     * This would be invoked before any query is performed
+     * to provide a chance for IndexNode to refresh the readers
+     *
+     * <p>The index may or may not be updated when this method
+     * is invoked
+     *
+     * @param refreshCallback callback to refresh the readers
+     */
+    void refreshOnReadIfRequired(Runnable refreshCallback);
+
+    /**
+     * This would invoked after some writes have been performed
+     * and as a final step refresh request is being made.
+     *
+     * <p>Any time its invoked it can be assumed that index has been
+     * updated
+     *
+     * @param refreshCallback callback to refresh the readers
+     */
+    void refreshOnWriteIfRequired(Runnable refreshCallback);
+}

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

Added: jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/RefreshOnReadPolicy.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/RefreshOnReadPolicy.java?rev=1832166&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/RefreshOnReadPolicy.java (added)
+++ jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/RefreshOnReadPolicy.java Thu May 24 13:07:54 2018
@@ -0,0 +1,95 @@
+/*
+ * 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.search;
+
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.apache.jackrabbit.oak.stats.Clock;
+
+/**
+ * This policy ensures that any writes that have been done to index are made visible
+ * *before* any read is performed. Its meant as an alternative to {@link RefreshOnWritePolicy}
+ * and for "sync" indexes. For "nrt" indexes {@link TimedRefreshPolicy} should be preferred
+ *
+ * <p>The readers are not refreshed immediately upon write. Instead they would be refreshed if
+ *
+ * <ul>
+ *     <li>Upon write if refreshDelta time has elapsed then readers would be refreshed</li>
+ *     <li>Upon read if index is found to be updated then again readers would be refreshed</li>
+ * </ul>
+ *
+ * <p>This policy can result in some contention if index is being frequently updated and
+ * queried.
+ *
+ * *This is an experimental policy. Currently it causes high contention*
+ */
+public class RefreshOnReadPolicy implements ReaderRefreshPolicy, IndexUpdateListener {
+    private final AtomicBoolean dirty = new AtomicBoolean();
+    private final Object lock = new Object();
+    private final Clock clock;
+    private final long refreshDelta;
+    private volatile long lastRefreshTime;
+
+    public RefreshOnReadPolicy(Clock clock, TimeUnit unit, long refreshDelta) {
+        this.clock = clock;
+        this.refreshDelta = unit.toMillis(refreshDelta);
+    }
+
+    @Override
+    public void refreshOnReadIfRequired(Runnable refreshCallback) {
+        if (dirty.get()){
+            refreshWithLock(refreshCallback, false);
+        }
+    }
+
+    @Override
+    public void refreshOnWriteIfRequired(Runnable refreshCallback) {
+        long currentTime = clock.getTime();
+        if (currentTime - lastRefreshTime > refreshDelta) {
+            //Do not set dirty instead directly refresh
+            refreshWithLock(refreshCallback, true);
+        } else {
+            synchronized (lock){
+                //Needs to be done in a lock otherwise
+                //refreshWithLock would override this
+                dirty.set(true);
+            }
+        }
+    }
+
+    @Override
+    public void updated() {
+        //Detect dirty based on call from refreshOnWriteIfRequired
+        //as that would *always* be called if the index has been updated
+        //And ensures that it gets calls after all changes for that index
+        //for that transaction got committed
+    }
+
+    private void refreshWithLock(Runnable refreshCallback, boolean forceRefresh) {
+        synchronized (lock){
+            if (dirty.get() || forceRefresh) {
+                refreshCallback.run();
+                dirty.set(false);
+                lastRefreshTime = clock.getTime();
+            }
+        }
+    }
+}

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

Added: jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/RefreshOnWritePolicy.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/RefreshOnWritePolicy.java?rev=1832166&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/RefreshOnWritePolicy.java (added)
+++ jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/RefreshOnWritePolicy.java Thu May 24 13:07:54 2018
@@ -0,0 +1,50 @@
+/*
+ * 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.search;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * Policy which performs immediate refresh upon completion of writes
+ */
+public class RefreshOnWritePolicy implements ReaderRefreshPolicy, IndexUpdateListener {
+    private final AtomicBoolean dirty = new AtomicBoolean();
+
+    @Override
+    public void refreshOnReadIfRequired(Runnable refreshCallback) {
+        //As writer itself refreshes the index. No refresh done
+        //on read
+    }
+
+    @Override
+    public void refreshOnWriteIfRequired(Runnable refreshCallback) {
+        //For sync indexing mode we refresh the reader immediately
+        //on the writer thread. So that any read call later sees upto date index
+        if (dirty.get()) {
+            refreshCallback.run();
+            dirty.set(false);
+        }
+    }
+
+    @Override
+    public void updated() {
+        dirty.set(true);
+    }
+}

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

Added: jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/TimedRefreshPolicy.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/TimedRefreshPolicy.java?rev=1832166&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/TimedRefreshPolicy.java (added)
+++ jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/TimedRefreshPolicy.java Thu May 24 13:07:54 2018
@@ -0,0 +1,63 @@
+/*
+ * 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.search;
+
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.apache.jackrabbit.oak.stats.Clock;
+
+public class TimedRefreshPolicy implements ReaderRefreshPolicy, IndexUpdateListener {
+    private final AtomicBoolean dirty = new AtomicBoolean();
+    private final Clock clock;
+    private final long refreshDelta;
+    private volatile long lastRefreshTime;
+
+    public TimedRefreshPolicy(Clock clock, TimeUnit unit, long refreshDelta) {
+        this.clock = clock;
+        this.refreshDelta = unit.toMillis(refreshDelta);
+    }
+
+    @Override
+    public void refreshOnReadIfRequired(Runnable refreshCallback) {
+        refreshIfRequired(refreshCallback);
+    }
+
+    @Override
+    public void refreshOnWriteIfRequired(Runnable refreshCallback) {
+        refreshIfRequired(refreshCallback);
+    }
+
+    @Override
+    public void updated() {
+        dirty.set(true);
+    }
+
+    private void refreshIfRequired(Runnable refreshCallback) {
+        if (dirty.get()){
+            long currentTime = clock.getTime();
+            if (currentTime - lastRefreshTime > refreshDelta
+                    && dirty.compareAndSet(true, false)){
+                lastRefreshTime = currentTime;
+                refreshCallback.run();
+            }
+        }
+    }
+}

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

Modified: jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/editor/BlobByteSource.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/editor/BlobByteSource.java?rev=1832166&r1=1832165&r2=1832166&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/editor/BlobByteSource.java (original)
+++ jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/editor/BlobByteSource.java Thu May 24 13:07:54 2018
@@ -25,6 +25,9 @@ import java.io.InputStream;
 import com.google.common.io.ByteSource;
 import org.apache.jackrabbit.oak.api.Blob;
 
+/**
+ * {@link ByteSource} extension to work with Oak {@link Blob}s
+ */
 public final class BlobByteSource extends ByteSource {
     private final Blob blob;
 

Modified: jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/editor/DocumentMaker.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/editor/DocumentMaker.java?rev=1832166&r1=1832165&r2=1832166&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/editor/DocumentMaker.java (original)
+++ jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/editor/DocumentMaker.java Thu May 24 13:07:54 2018
@@ -25,7 +25,7 @@ import org.apache.jackrabbit.oak.api.Pro
 import org.apache.jackrabbit.oak.spi.state.NodeState;
 
 /**
- * A {@link DocumentMaker} is responsible for creating an instance of a document {@link D} which depends on implementor implementation.
+ * A {@link DocumentMaker} is responsible for creating an instance of a document {@link D} to be indexed.
  * For Apache Lucene that would be a Lucene {@code Document}, for Apache Solr that might be a {@code SolrInputDocument}, etc.
  */
 public interface DocumentMaker<D> {

Modified: jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/editor/FulltextDocumentMaker.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/editor/FulltextDocumentMaker.java?rev=1832166&r1=1832165&r2=1832166&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/editor/FulltextDocumentMaker.java (original)
+++ jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/editor/FulltextDocumentMaker.java Thu May 24 13:07:54 2018
@@ -61,15 +61,15 @@ public abstract class FulltextDocumentMa
     private IndexDefinition.IndexingRule indexingRule;
     private String path;
 
-    abstract D initDoc();
+    protected abstract D initDoc();
 
-    abstract D finalizeDoc(D fields, boolean dirty, boolean facet);
+    protected abstract D finalizeDoc(D fields, boolean dirty, boolean facet);
 
-    abstract StringPropertyState createNodeNamePS();
+    protected abstract StringPropertyState createNodeNamePS();
 
-    abstract boolean isFacetingEnabled();
+    protected abstract boolean isFacetingEnabled();
 
-    abstract boolean isNodeName(String pname);
+    protected abstract boolean isNodeName(String pname);
 
     protected abstract boolean indexTypeOrderedFields(String pname, int tag, PropertyState property, PropertyDefinition pd);
 

Modified: jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/editor/FulltextIndexEditor.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/editor/FulltextIndexEditor.java?rev=1832166&r1=1832165&r2=1832166&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/editor/FulltextIndexEditor.java (original)
+++ jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/editor/FulltextIndexEditor.java Thu May 24 13:07:54 2018
@@ -44,7 +44,7 @@ import org.slf4j.LoggerFactory;
 import static org.apache.jackrabbit.oak.commons.PathUtils.concat;
 
 /**
- * Abstract implementation of an {@link IndexEditor} which supports index time aggregation.
+ * Generic implementation of an {@link IndexEditor} which supports index time aggregation.
  */
 public class FulltextIndexEditor<D> implements IndexEditor, Aggregate.AggregateRoot {
 
@@ -195,7 +195,7 @@ public class FulltextIndexEditor<D> impl
   public Editor childNodeAdded(String name, NodeState after) {
     PathFilter.Result filterResult = getPathFilterResult(name);
     if (filterResult != PathFilter.Result.EXCLUDE) {
-      return new FulltextIndexEditor(this, name, getMatcherState(name, after), filterResult, false);
+      return new FulltextIndexEditor<>(this, name, getMatcherState(name, after), filterResult, false);
     }
     return null;
   }
@@ -205,7 +205,7 @@ public class FulltextIndexEditor<D> impl
       String name, NodeState before, NodeState after) {
     PathFilter.Result filterResult = getPathFilterResult(name);
     if (filterResult != PathFilter.Result.EXCLUDE) {
-      return new FulltextIndexEditor(this, name, getMatcherState(name, after), filterResult, false);
+      return new FulltextIndexEditor<>(this, name, getMatcherState(name, after), filterResult, false);
     }
     return null;
   }
@@ -236,7 +236,7 @@ public class FulltextIndexEditor<D> impl
 
     MatcherState ms = getMatcherState(name, before);
     if (!ms.isEmpty()){
-      return new FulltextIndexEditor(this, name, ms, filterResult, true);
+      return new FulltextIndexEditor<>(this, name, ms, filterResult, true);
     }
     return null; // no need to recurse down the removed subtree
   }

Modified: jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/editor/FulltextIndexEditorContext.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/editor/FulltextIndexEditorContext.java?rev=1832166&r1=1832165&r2=1832166&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/editor/FulltextIndexEditorContext.java (original)
+++ jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/editor/FulltextIndexEditorContext.java Thu May 24 13:07:54 2018
@@ -52,6 +52,7 @@ import static org.apache.jackrabbit.oak.
  *
  */
 public abstract class FulltextIndexEditorContext<D> {
+
   private static final Logger log = LoggerFactory
       .getLogger(FulltextIndexEditorContext.class);
 

Modified: jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/editor/FulltextIndexWriter.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/editor/FulltextIndexWriter.java?rev=1832166&r1=1832165&r2=1832166&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/editor/FulltextIndexWriter.java (original)
+++ jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/editor/FulltextIndexWriter.java Thu May 24 13:07:54 2018
@@ -21,6 +21,12 @@ package org.apache.jackrabbit.oak.plugin
 
 import java.io.IOException;
 
+/**
+ * A {@link FulltextIndexWriter} is responsible for writing / deleting documents of type {@code D} to the index
+ * implementation underlying persistence layer.
+ *
+ * @param <D>
+ */
 public interface FulltextIndexWriter<D> {
 
     /**

Modified: jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/editor/FulltextIndexWriterFactory.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/editor/FulltextIndexWriterFactory.java?rev=1832166&r1=1832165&r2=1832166&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/editor/FulltextIndexWriterFactory.java (original)
+++ jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/editor/FulltextIndexWriterFactory.java Thu May 24 13:07:54 2018
@@ -27,6 +27,13 @@ import org.apache.jackrabbit.oak.spi.sta
  */
 public interface FulltextIndexWriterFactory {
 
+    /**
+     * create a new index writer instance
+     * @param definition the index definition
+     * @param definitionBuilder the node builder associated with the index definition
+     * @param reindex whether or not reindex should be performed
+     * @return an index writer
+     */
     FulltextIndexWriter newInstance(IndexDefinition definition, NodeBuilder definitionBuilder, boolean reindex);
 
 }

Modified: jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/editor/TikaParserConfig.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/editor/TikaParserConfig.java?rev=1832166&r1=1832165&r2=1832166&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/editor/TikaParserConfig.java (original)
+++ jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/editor/TikaParserConfig.java Thu May 24 13:07:54 2018
@@ -36,6 +36,7 @@ import org.w3c.dom.NodeList;
 import org.xml.sax.SAXException;
 
 public class TikaParserConfig {
+
     private static final String EMPTY_PARSER = "org.apache.tika.parser.EmptyParser";
 
     /**

Added: jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/query/FulltextIndex.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/query/FulltextIndex.java?rev=1832166&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/query/FulltextIndex.java (added)
+++ jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/query/FulltextIndex.java Thu May 24 13:07:54 2018
@@ -0,0 +1,483 @@
+/*
+ * 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.search.spi.query;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import javax.jcr.PropertyType;
+
+import com.google.common.collect.Lists;
+import com.google.common.primitives.Chars;
+import org.apache.jackrabbit.oak.api.PropertyValue;
+import org.apache.jackrabbit.oak.api.Result.SizePrecision;
+import org.apache.jackrabbit.oak.api.Type;
+import org.apache.jackrabbit.oak.commons.PathUtils;
+import org.apache.jackrabbit.oak.commons.PerfLogger;
+import org.apache.jackrabbit.oak.commons.json.JsopBuilder;
+import org.apache.jackrabbit.oak.commons.json.JsopWriter;
+import org.apache.jackrabbit.oak.plugins.index.Cursors;
+import org.apache.jackrabbit.oak.plugins.index.Cursors.PathCursor;
+import org.apache.jackrabbit.oak.plugins.index.search.IndexLookup;
+import org.apache.jackrabbit.oak.plugins.index.search.IndexNode;
+import org.apache.jackrabbit.oak.plugins.index.search.PropertyDefinition;
+import org.apache.jackrabbit.oak.plugins.index.search.SizeEstimator;
+import org.apache.jackrabbit.oak.plugins.index.search.spi.query.FulltextIndexPlanner.PlanResult;
+import org.apache.jackrabbit.oak.plugins.memory.PropertyValues;
+import org.apache.jackrabbit.oak.query.facet.FacetResult;
+import org.apache.jackrabbit.oak.spi.query.Cursor;
+import org.apache.jackrabbit.oak.spi.query.Filter;
+import org.apache.jackrabbit.oak.spi.query.Filter.PropertyRestriction;
+import org.apache.jackrabbit.oak.spi.query.IndexRow;
+import org.apache.jackrabbit.oak.spi.query.QueryConstants;
+import org.apache.jackrabbit.oak.spi.query.QueryIndex;
+import org.apache.jackrabbit.oak.spi.query.QueryIndex.AdvanceFulltextQueryIndex;
+import org.apache.jackrabbit.oak.spi.query.QueryLimits;
+import org.apache.jackrabbit.oak.spi.query.fulltext.FullTextExpression;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static com.google.common.base.Preconditions.checkState;
+import static org.apache.jackrabbit.oak.spi.query.QueryIndex.AdvancedQueryIndex;
+import static org.apache.jackrabbit.oak.spi.query.QueryIndex.NativeQueryIndex;
+
+/**
+ * Provides an abstract QueryIndex that does lookups against a fulltext index
+ *
+ * @see QueryIndex
+ *
+ */
+public abstract class FulltextIndex implements AdvancedQueryIndex, QueryIndex, NativeQueryIndex,
+        AdvanceFulltextQueryIndex {
+
+    private final Logger LOG = LoggerFactory
+            .getLogger(getClass());
+    private final PerfLogger PERF_LOGGER =
+            new PerfLogger(LoggerFactory.getLogger(getClass() + ".perf"));
+
+    static final String ATTR_PLAN_RESULT = "oak.fulltext.planResult";
+
+    protected abstract FulltextIndexTracker getIndexTracker();
+
+    protected abstract String getType();
+
+    protected abstract SizeEstimator getSizeEstimator(IndexPlan plan);
+
+    protected abstract String getFulltextRequestString(IndexPlan plan, IndexNode indexNode);
+
+    @Override
+    public List<IndexPlan> getPlans(Filter filter, List<OrderEntry> sortOrder, NodeState rootState) {
+        Collection<String> indexPaths = new IndexLookup(rootState).collectIndexNodePaths(filter, getType());
+        List<IndexPlan> plans = Lists.newArrayListWithCapacity(indexPaths.size());
+        for (String path : indexPaths) {
+            IndexNode indexNode = null;
+            try {
+                indexNode = getIndexTracker().acquireIndexNode(path, getType());
+
+                if (indexNode != null) {
+                    IndexPlan plan = new FulltextIndexPlanner(indexNode, path, filter, sortOrder).getPlan();
+                    if (plan != null) {
+                        plans.add(plan);
+                    }
+                }
+            } catch (Exception e) {
+                LOG.error("Error getting plan for {}", path);
+                LOG.error("Exception:", e);
+            } finally {
+                if (indexNode != null) {
+                    indexNode.release();
+                }
+            }
+        }
+        return plans;
+    }
+
+    @Override
+    public double getCost(Filter filter, NodeState root) {
+        throw new UnsupportedOperationException("Not supported as implementing AdvancedQueryIndex");
+    }
+
+    @Override
+    public String getPlan(Filter filter, NodeState root) {
+        throw new UnsupportedOperationException("Not supported as implementing AdvancedQueryIndex");
+    }
+
+    @Override
+    public String getPlanDescription(IndexPlan plan, NodeState root) {
+        Filter filter = plan.getFilter();
+        IndexNode index = getIndexTracker().acquireIndexNode(getPlanResult(plan).indexPath, getType());
+        checkState(index != null, "The Fulltext of type " + getType() + "  index is not available");
+        try {
+            FullTextExpression ft = filter.getFullTextConstraint();
+            StringBuilder sb = new StringBuilder(getType()).append(":");
+            String path = getPlanResult(plan).indexPath;
+            sb.append(getIndexName(plan))
+                    .append("(")
+                    .append(path)
+                    .append(") ");
+            sb.append(getFulltextRequestString(plan, index));
+            if (plan.getSortOrder() != null && !plan.getSortOrder().isEmpty()) {
+                sb.append(" ordering:").append(plan.getSortOrder());
+            }
+            if (ft != null) {
+                sb.append(" ft:(").append(ft).append(")");
+            }
+            addSyncIndexPlan(plan, sb);
+            return sb.toString();
+        } finally {
+            index.release();
+        }
+    }
+
+    private static void addSyncIndexPlan(IndexPlan plan, StringBuilder sb) {
+        FulltextIndexPlanner.PlanResult pr = getPlanResult(plan);
+        if (pr.hasPropertyIndexResult()) {
+            FulltextIndexPlanner.PropertyIndexResult pres = pr.getPropertyIndexResult();
+            sb.append(" sync:(")
+              .append(pres.propertyName);
+
+            if (!pres.propertyName.equals(pres.pr.propertyName)) {
+               sb.append("[").append(pres.pr.propertyName).append("]");
+            }
+
+            sb.append(" ").append(pres.pr);
+            sb.append(")");
+        }
+
+        if (pr.evaluateSyncNodeTypeRestriction()) {
+            sb.append(" sync:(nodeType");
+            sb.append(" primaryTypes : ").append(plan.getFilter().getPrimaryTypes());
+            sb.append(" mixinTypes : ").append(plan.getFilter().getMixinTypes());
+            sb.append(")");
+        }
+    }
+
+    @Override
+    public Cursor query(final Filter filter, final NodeState root) {
+        throw new UnsupportedOperationException("Not supported as implementing AdvancedQueryIndex");
+    }
+
+    private static boolean shouldInclude(String docPath, IndexPlan plan) {
+        String path = getPathRestriction(plan);
+
+        boolean include = true;
+
+        Filter filter = plan.getFilter();
+        switch (filter.getPathRestriction()) {
+            case EXACT:
+                include = path.equals(docPath);
+                break;
+            case DIRECT_CHILDREN:
+                include = PathUtils.getParentPath(docPath).equals(path);
+                break;
+            case ALL_CHILDREN:
+                include = PathUtils.isAncestor(path, docPath);
+                break;
+        }
+
+        return include;
+    }
+
+    @Override
+    public NodeAggregator getNodeAggregator() {
+        return null;
+    }
+
+    /**
+     * In a fulltext term for jcr:contains(foo, 'bar') 'foo'
+     * is the property name. While in jcr:contains(foo/*, 'bar')
+     * 'foo' is node name
+     *
+     * @return true if the term is related to node
+     */
+    public static boolean isNodePath(String fulltextTermPath) {
+        return fulltextTermPath.endsWith("/*");
+    }
+
+    protected IndexNode acquireIndexNode(IndexPlan plan) {
+        return getIndexTracker().acquireIndexNode(getPlanResult(plan).indexPath, getType());
+    }
+
+    protected static String getIndexName(IndexPlan plan) {
+        return PathUtils.getName(getPlanResult(plan).indexPath);
+    }
+
+    protected static int determinePropertyType(PropertyDefinition defn, PropertyRestriction pr) {
+        int typeFromRestriction = pr.propertyType;
+        if (typeFromRestriction == PropertyType.UNDEFINED) {
+            //If no explicit type defined then determine the type from restriction
+            //value
+            if (pr.first != null && pr.first.getType() != Type.UNDEFINED) {
+                typeFromRestriction = pr.first.getType().tag();
+            } else if (pr.last != null && pr.last.getType() != Type.UNDEFINED) {
+                typeFromRestriction = pr.last.getType().tag();
+            } else if (pr.list != null && !pr.list.isEmpty()) {
+                typeFromRestriction = pr.list.get(0).getType().tag();
+            }
+        }
+        return getPropertyType(defn, pr.propertyName, typeFromRestriction);
+    }
+
+    private static int getPropertyType(PropertyDefinition defn, String name, int defaultVal) {
+        if (defn.isTypeDefined()) {
+            return defn.getType();
+        }
+        return defaultVal;
+    }
+
+    protected static PlanResult getPlanResult(IndexPlan plan) {
+        return (PlanResult) plan.getAttribute(ATTR_PLAN_RESULT);
+    }
+
+    /**
+     * Following chars are used as operators in Lucene Query and should be escaped
+     */
+    private static final char[] QUERY_OPERATORS = {':' , '/', '!', '&', '|', '='};
+
+    /**
+     * Following logic is taken from org.apache.jackrabbit.core.query.lucene.JackrabbitQueryParser#parse(java.lang.String)
+     */
+    static String rewriteQueryText(String textsearch) {
+        // replace escaped ' with just '
+        StringBuilder rewritten = new StringBuilder();
+        // most query parsers recognize 'AND' and 'NOT' as
+        // keywords.
+        textsearch = textsearch.replaceAll("AND", "and");
+        textsearch = textsearch.replaceAll("NOT", "not");
+        boolean escaped = false;
+        for (int i = 0; i < textsearch.length(); i++) {
+            char c = textsearch.charAt(i);
+            if (c == '\\') {
+                if (escaped) {
+                    rewritten.append("\\\\");
+                    escaped = false;
+                } else {
+                    escaped = true;
+                }
+            } else if (c == '\'') {
+                if (escaped) {
+                    escaped = false;
+                }
+                rewritten.append(c);
+            } else if (Chars.contains(QUERY_OPERATORS, c)) {
+                rewritten.append('\\').append(c);
+            } else {
+                if (escaped) {
+                    rewritten.append('\\');
+                    escaped = false;
+                }
+                rewritten.append(c);
+            }
+        }
+        return rewritten.toString();
+    }
+
+    protected static String getPathRestriction(IndexPlan plan) {
+        Filter f = plan.getFilter();
+        String pathPrefix = plan.getPathPrefix();
+        if (pathPrefix.isEmpty()) {
+            return f.getPath();
+        }
+        String relativePath = PathUtils.relativize(pathPrefix, f.getPath());
+        return "/" + relativePath;
+    }
+
+    static class FulltextResultRow {
+        final String path;
+        final double score;
+        final String suggestion;
+        final boolean isVirutal;
+        final Map<String, String> excerpts;
+        final String explanation;
+        final List<FacetResult.Facet> facets;
+
+        FulltextResultRow(String path, double score, Map<String, String> excerpts, List<FacetResult.Facet> facets, String explanation) {
+            this.explanation = explanation;
+            this.excerpts = excerpts;
+            this.facets = facets;
+            this.isVirutal = false;
+            this.path = path;
+            this.score = score;
+            this.suggestion = null;
+        }
+
+        FulltextResultRow(String suggestion, long weight) {
+            this.isVirutal = true;
+            this.path = "/";
+            this.score = weight;
+            this.suggestion = suggestion;
+            this.excerpts = null;
+            this.facets = null;
+            this.explanation = null;
+        }
+
+        FulltextResultRow(String suggestion) {
+            this(suggestion, 1);
+        }
+
+        @Override
+        public String toString() {
+            return String.format("%s (%1.2f)", path, score);
+        }
+    }
+
+    /**
+     * A cursor over Fulltext results. The result includes the path,
+     * and the jcr:score pseudo-property as returned by Lucene.
+     */
+    static class FulltextPathCursor implements Cursor {
+
+        private final Logger log = LoggerFactory.getLogger(getClass());
+        private static final int TRAVERSING_WARNING = Integer.getInteger("oak.traversing.warning", 10000);
+
+        private final Cursor pathCursor;
+        private final String pathPrefix;
+        FulltextResultRow currentRow;
+        private final SizeEstimator sizeEstimator;
+        private long estimatedSize;
+        private int numberOfFacets;
+
+        FulltextPathCursor(final Iterator<FulltextResultRow> it, final IndexPlan plan, QueryLimits settings, SizeEstimator sizeEstimator) {
+            pathPrefix = plan.getPathPrefix();
+            this.sizeEstimator = sizeEstimator;
+            Iterator<String> pathIterator = new Iterator<String>() {
+
+                private int readCount;
+
+                @Override
+                public boolean hasNext() {
+                    return it.hasNext();
+                }
+
+                @Override
+                public String next() {
+                    currentRow = it.next();
+                    readCount++;
+                    if (readCount % TRAVERSING_WARNING == 0) {
+                        Cursors.checkReadLimit(readCount, settings);
+                        log.warn("Index-Traversed {} nodes with filter {}", readCount, plan.getFilter());
+                    }
+                    return currentRow.path;
+                }
+
+                @Override
+                public void remove() {
+                    it.remove();
+                }
+
+            };
+
+            PlanResult planResult = getPlanResult(plan);
+            pathCursor = new PathCursor(pathIterator, planResult.isUniquePathsRequired(), settings);
+            numberOfFacets = planResult.indexDefinition.getNumberOfTopFacets();
+        }
+
+
+        @Override
+        public boolean hasNext() {
+            return pathCursor.hasNext();
+        }
+
+        @Override
+        public void remove() {
+            pathCursor.remove();
+        }
+
+        @Override
+        public IndexRow next() {
+            final IndexRow pathRow = pathCursor.next();
+            return new IndexRow() {
+
+                @Override
+                public boolean isVirtualRow() {
+                    return currentRow.isVirutal;
+                }
+
+                @Override
+                public String getPath() {
+                    String sub = pathRow.getPath();
+                    if (isVirtualRow()) {
+                        return sub;
+                    } else if (!"".equals(pathPrefix) && PathUtils.denotesRoot(sub)) {
+                        return pathPrefix;
+                    } else if (PathUtils.isAbsolute(sub)) {
+                        return pathPrefix + sub;
+                    } else {
+                        return PathUtils.concat(pathPrefix, sub);
+                    }
+                }
+
+                @Override
+                public PropertyValue getValue(String columnName) {
+                    // overlay the score
+                    if (QueryConstants.JCR_SCORE.equals(columnName)) {
+                        return PropertyValues.newDouble(currentRow.score);
+                    }
+                    if (QueryConstants.REP_SPELLCHECK.equals(columnName) || QueryConstants.REP_SUGGEST.equals(columnName)) {
+                        return PropertyValues.newString(currentRow.suggestion);
+                    }
+                    if (QueryConstants.OAK_SCORE_EXPLANATION.equals(columnName)) {
+                        return PropertyValues.newString(currentRow.explanation);
+                    }
+                    if (columnName.startsWith(QueryConstants.REP_EXCERPT)) {
+                        String excerpt = currentRow.excerpts.get(columnName);
+                        if (excerpt != null) {
+                            return PropertyValues.newString(excerpt);
+                        }
+                    }
+                    if (columnName.startsWith(QueryConstants.REP_FACET)) {
+                        List<FacetResult.Facet> facets = currentRow.facets;
+                        try {
+                            if (facets != null) {
+                                JsopWriter writer = new JsopBuilder();
+                                writer.object();
+                                for (FacetResult.Facet f : facets) {
+                                    writer.key(f.getLabel()).value(f.getCount());
+                                }
+                                writer.endObject();
+                                return PropertyValues.newString(writer.toString());
+                            } else {
+                                return null;
+                            }
+                        } catch (Exception e) {
+                            throw new RuntimeException(e);
+                        }
+                    }
+                    return pathRow.getValue(columnName);
+                }
+
+            };
+        }
+
+        @Override
+        public long getSize(SizePrecision precision, long max) {
+            if (estimatedSize != 0) {
+                return estimatedSize;
+            }
+            return estimatedSize = sizeEstimator.getSize();
+        }
+    }
+
+    static String parseFacetField(String columnName) {
+        return columnName.substring(QueryConstants.REP_FACET.length() + 1, columnName.length() - 1);
+    }
+}

Propchange: jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/query/FulltextIndex.java
------------------------------------------------------------------------------
    svn:eol-style = native