Author: tommaso
Date: Mon May 21 17:06:10 2018
New Revision: 1831979
URL: http://svn.apache.org/viewvc?rev=1831979&view=rev
Log:
OAK-7410 - first draft of abstract editor
Added:
jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/ExtractedTextCache.java
(with props)
jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/ReindexOperations.java
(with props)
jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/TextExtractionStatsMBean.java
(with props)
jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/
jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/editor/
jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/editor/BlobByteSource.java
(with props)
jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/editor/DocumentMaker.java
(with props)
jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/editor/FulltextBinaryTextExtractor.java
(with props)
jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/editor/FulltextDocumentMaker.java
(with props)
jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/editor/FulltextIndexEditor.java
(with props)
jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/editor/FulltextIndexEditorContext.java
(with props)
jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/editor/FulltextIndexWriter.java
(with props)
jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/editor/FulltextIndexWriterFactory.java
(with props)
jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/editor/TextExtractionStats.java
(with props)
jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/editor/TikaParserConfig.java
(with props)
jackrabbit/oak/trunk/oak-search/src/main/resources/
jackrabbit/oak/trunk/oak-search/src/main/resources/org/
jackrabbit/oak/trunk/oak-search/src/main/resources/org/apache/
jackrabbit/oak/trunk/oak-search/src/main/resources/org/apache/jackrabbit/
jackrabbit/oak/trunk/oak-search/src/main/resources/org/apache/jackrabbit/oak/
jackrabbit/oak/trunk/oak-search/src/main/resources/org/apache/jackrabbit/oak/plugins/
jackrabbit/oak/trunk/oak-search/src/main/resources/org/apache/jackrabbit/oak/plugins/index/
jackrabbit/oak/trunk/oak-search/src/main/resources/org/apache/jackrabbit/oak/plugins/index/search/
jackrabbit/oak/trunk/oak-search/src/main/resources/org/apache/jackrabbit/oak/plugins/index/search/tika-config.xml
(with props)
Modified:
jackrabbit/oak/trunk/oak-search/pom.xml
jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/Aggregate.java
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/NodeStateCloner.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/util/IndexHelper.java
jackrabbit/oak/trunk/oak-search/src/test/java/org/apache/jackrabbit/oak/plugins/index/search/TestUtil.java
Modified: jackrabbit/oak/trunk/oak-search/pom.xml
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-search/pom.xml?rev=1831979&r1=1831978&r2=1831979&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-search/pom.xml (original)
+++ jackrabbit/oak/trunk/oak-search/pom.xml Mon May 21 17:06:10 2018
@@ -112,6 +112,11 @@
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
+ <dependency>
+ <groupId>org.apache.tika</groupId>
+ <artifactId>tika-core</artifactId>
+ <version>${tika.version}</version>
+ </dependency>
<!-- Logging -->
<dependency>
Modified:
jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/Aggregate.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/Aggregate.java?rev=1831979&r1=1831978&r2=1831979&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/Aggregate.java
(original)
+++
jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/Aggregate.java
Mon May 21 17:06:10 2018
@@ -47,7 +47,7 @@ import static com.google.common.collect.
import static org.apache.jackrabbit.oak.commons.PathUtils.elements;
import static org.apache.jackrabbit.oak.commons.PathUtils.getParentPath;
-class Aggregate {
+public class Aggregate {
public static final String MATCH_ALL = "*";
@@ -425,9 +425,9 @@ class Aggregate {
}
public static class NodeIncludeResult {
- final NodeState nodeState;
- final String nodePath;
- final String rootIncludePath;
+ public final NodeState nodeState;
+ public final String nodePath;
+ public final String rootIncludePath;
public NodeIncludeResult(String nodePath, NodeState nodeState) {
this(nodePath, null, nodeState);
@@ -453,9 +453,9 @@ class Aggregate {
}
public static class PropertyIncludeResult {
- final PropertyState propertyState;
- final PropertyDefinition pd;
- final String propertyPath;
+ public final PropertyState propertyState;
+ public final PropertyDefinition pd;
+ public final String propertyPath;
final String nodePath;
public PropertyIncludeResult(PropertyState propertyState,
PropertyDefinition pd,
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=1831979&r1=1831978&r2=1831979&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
Mon May 21 17:06:10 2018
@@ -81,10 +81,10 @@ class BadIndexTracker {
BadIndexInfo badIndex = badPersistedIndexes.get(path);
if (badIndex == null) {
badPersistedIndexes.put(path, new BadIndexInfo(path, e, true));
- log.error("Could not open the Lucene index at [{}]", path, e);
+ log.error("Could not open the Fulltext index at [{}]", path, e);
} else {
badIndex.failedAccess(e);
- log.error("Could not open the Lucene index at [{}] . {}",
+ log.error("Could not open the Fulltext index at [{}] . {}",
path, badIndex.getStats(), e);
}
}
@@ -97,10 +97,10 @@ class BadIndexTracker {
BadIndexInfo badIndex = badIndexesForRead.get(path);
if (badIndex == null) {
badIndexesForRead.put(path, new BadIndexInfo(path, e, false));
- log.error("Could not access the Lucene index at [{}]", path, e);
+ log.error("Could not access the Fulltext index at [{}]", path, e);
} else {
badIndex.failedAccess(e);
- log.error("Could not access the Lucene index at [{}] . {}",
+ log.error("Could not access the Fulltext index at [{}] . {}",
path, badIndex.getStats(), e);
}
}
Added:
jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/ExtractedTextCache.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/ExtractedTextCache.java?rev=1831979&view=auto
==============================================================================
---
jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/ExtractedTextCache.java
(added)
+++
jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/ExtractedTextCache.java
Mon May 21 17:06:10 2018
@@ -0,0 +1,408 @@
+/*
+ * 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.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Map.Entry;
+import java.util.Properties;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nonnull;
+
+import com.google.common.cache.Cache;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.Weigher;
+import org.apache.jackrabbit.oak.api.Blob;
+import org.apache.jackrabbit.oak.cache.CacheStats;
+import org.apache.jackrabbit.oak.commons.IOUtils;
+import org.apache.jackrabbit.oak.plugins.index.fulltext.ExtractedText;
+import
org.apache.jackrabbit.oak.plugins.index.fulltext.ExtractedText.ExtractionResult;
+import
org.apache.jackrabbit.oak.plugins.index.fulltext.PreExtractedTextProvider;
+import
org.apache.jackrabbit.oak.plugins.index.search.spi.editor.FulltextIndexEditor;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static org.apache.jackrabbit.oak.commons.PathUtils.concat;
+
+public class ExtractedTextCache {
+ private static final boolean CACHE_ONLY_SUCCESS =
+ Boolean.getBoolean("oak.extracted.cacheOnlySuccess");
+ private static final int EXTRACTION_TIMEOUT_SECONDS =
+ Integer.getInteger("oak.extraction.timeoutSeconds", 60);
+ private static final int EXTRACTION_MAX_THREADS =
+ Integer.getInteger("oak.extraction.maxThreads", 10);
+ private static final boolean EXTRACT_IN_CALLER_THREAD =
+ Boolean.getBoolean("oak.extraction.inCallerThread");
+ private static final boolean EXTRACT_FORGET_TIMEOUT =
+ Boolean.getBoolean("oak.extraction.forgetTimeout");
+
+ private static final String TIMEOUT_MAP =
"textExtractionTimeout.properties";
+ private static final String EMPTY_STRING = "";
+ private static final Logger log =
LoggerFactory.getLogger(ExtractedTextCache.class);
+ private volatile PreExtractedTextProvider extractedTextProvider;
+ private int textExtractionCount;
+ private long totalBytesRead;
+ private long totalTextSize;
+ private long totalTime;
+ private int preFetchedCount;
+ private final Cache<String, String> cache;
+ private final ConcurrentHashMap<String, String> timeoutMap;
+ private final File indexDir;
+ private final CacheStats cacheStats;
+ private final boolean alwaysUsePreExtractedCache;
+ private volatile ExecutorService executorService;
+ private volatile int timeoutCount;
+ private long extractionTimeoutMillis = EXTRACTION_TIMEOUT_SECONDS * 1000;
+
+ public ExtractedTextCache(long maxWeight, long expiryTimeInSecs){
+ this(maxWeight, expiryTimeInSecs, false, null);
+ }
+
+ public ExtractedTextCache(long maxWeight, long expiryTimeInSecs, boolean
alwaysUsePreExtractedCache,
+ File indexDir) {
+ if (maxWeight > 0) {
+ cache = CacheBuilder.newBuilder()
+ .weigher(EmpiricalWeigher.INSTANCE)
+ .maximumWeight(maxWeight)
+ .expireAfterAccess(expiryTimeInSecs, TimeUnit.SECONDS)
+ .recordStats()
+ .build();
+ cacheStats = new CacheStats(cache, "ExtractedTextCache",
+ EmpiricalWeigher.INSTANCE, maxWeight);
+ } else {
+ cache = null;
+ cacheStats = null;
+ }
+ this.alwaysUsePreExtractedCache = alwaysUsePreExtractedCache;
+ this.timeoutMap = new ConcurrentHashMap<String, String>();
+ this.indexDir = indexDir;
+ loadTimeoutMap();
+ }
+
+ /**
+ * Get the pre extracted text for given blob
+ * @return null if no pre extracted text entry found. Otherwise returns
the pre extracted
+ * text
+ */
+ @CheckForNull
+ public String get(String nodePath, String propertyName, Blob blob, boolean
reindexMode){
+ String result = null;
+ //Consult the PreExtractedTextProvider only in reindex mode and not in
+ //incremental indexing mode. As that would only contain older entries
+ //That also avoid loading on various state (See DataStoreTextWriter)
+ String propertyPath = concat(nodePath, propertyName);
+ log.trace("Looking for extracted text for [{}] with blobId [{}]",
propertyPath, blob.getContentIdentity());
+ if ((reindexMode || alwaysUsePreExtractedCache) &&
extractedTextProvider != null){
+ try {
+ ExtractedText text =
extractedTextProvider.getText(propertyPath, blob);
+ if (text != null) {
+ preFetchedCount++;
+ result = getText(text);
+ }
+ } catch (IOException e) {
+ log.warn("Error occurred while fetching pre extracted text for
{}", propertyPath, e);
+ }
+ }
+ String id = blob.getContentIdentity();
+ if (cache != null && id != null && result == null) {
+ result = cache.getIfPresent(id);
+ }
+ if (result == null && id != null) {
+ result = timeoutMap.get(id);
+ }
+ return result;
+ }
+
+ public void put(@Nonnull Blob blob, @Nonnull ExtractedText extractedText) {
+ String id = blob.getContentIdentity();
+ if (cache != null && id != null) {
+ if (extractedText.getExtractionResult() == ExtractionResult.SUCCESS
+ || !CACHE_ONLY_SUCCESS) {
+ cache.put(id, getText(extractedText));
+ }
+ }
+ }
+
+ public void putTimeout(@Nonnull Blob blob, @Nonnull ExtractedText
extractedText) {
+ if (EXTRACT_FORGET_TIMEOUT) {
+ return;
+ }
+ String id = blob.getContentIdentity();
+ timeoutMap.put(id, getText(extractedText));
+ storeTimeoutMap();
+ }
+
+ private static String getText(ExtractedText text) {
+ switch (text.getExtractionResult()) {
+ case SUCCESS:
+ return text.getExtractedText().toString();
+ case ERROR:
+ return FulltextIndexEditor.TEXT_EXTRACTION_ERROR;
+ case EMPTY:
+ return EMPTY_STRING;
+ }
+ throw new IllegalArgumentException();
+ }
+
+ public void addStats(int count, long timeInMillis, long bytesRead, long
textLength){
+ this.textExtractionCount += count;
+ this.totalTime += timeInMillis;
+ this.totalBytesRead += bytesRead;
+ this.totalTextSize += textLength;
+ }
+
+ public TextExtractionStatsMBean getStatsMBean() {
+ return new TextExtractionStatsMBean() {
+ @Override
+ public boolean isPreExtractedTextProviderConfigured() {
+ return extractedTextProvider != null;
+ }
+
+ @Override
+ public int getTextExtractionCount() {
+ return textExtractionCount;
+ }
+
+ @Override
+ public long getTotalTime() {
+ return totalTime;
+ }
+
+ @Override
+ public int getPreFetchedCount() {
+ return preFetchedCount;
+ }
+
+ @Override
+ public String getExtractedTextSize() {
+ return IOUtils.humanReadableByteCount(totalTextSize);
+ }
+
+ @Override
+ public String getBytesRead() {
+ return IOUtils.humanReadableByteCount(totalBytesRead);
+ }
+
+ @Override
+ public boolean isAlwaysUsePreExtractedCache() {
+ return alwaysUsePreExtractedCache;
+ }
+
+ @Override
+ public int getTimeoutCount() {
+ return timeoutCount;
+ }
+ };
+ }
+
+ @CheckForNull
+ public CacheStats getCacheStats() {
+ return cacheStats;
+ }
+
+ public void setExtractedTextProvider(PreExtractedTextProvider
extractedTextProvider) {
+ this.extractedTextProvider = extractedTextProvider;
+ }
+
+ public PreExtractedTextProvider getExtractedTextProvider() {
+ return extractedTextProvider;
+ }
+
+ void resetCache(){
+ if (cache != null){
+ cache.invalidateAll();
+ }
+ }
+
+ boolean isAlwaysUsePreExtractedCache() {
+ return alwaysUsePreExtractedCache;
+ }
+
+ //Taken from DocumentNodeStore and cache packages as they are private
+ private static class EmpiricalWeigher implements Weigher<String, String> {
+ public static final EmpiricalWeigher INSTANCE = new EmpiricalWeigher();
+
+ private EmpiricalWeigher() {
+ }
+
+ private static long getMemory(@Nonnull String s) {
+ return 16 // shallow size
+ + 40 + (long)s.length() * 2; // value
+ }
+
+ @Override
+ public int weigh(String key, String value) {
+ long size = 168; // overhead for each cache entry
+ size += getMemory(key); // key
+ size += getMemory(value); // value
+ if (size > Integer.MAX_VALUE) {
+ log.debug("Calculated weight larger than Integer.MAX_VALUE:
{}.", size);
+ size = Integer.MAX_VALUE;
+ }
+ return (int) size;
+ }
+ }
+
+ public void close() {
+ resetCache();
+ // don't clean the persistent map on purpose, so we don't re-try
+ // after restarting the service or so
+ closeExecutorService();
+ }
+
+ public void process(String name, Callable<Void> callable) throws
InterruptedException, Throwable {
+ Callable<Void> callable2 = new Callable<Void>() {
+ @Override
+ public Void call() throws Exception {
+ Thread t = Thread.currentThread();
+ String oldThreadName = t.getName();
+ t.setName(oldThreadName + ": " + name);
+ try {
+ return callable.call();
+ } finally {
+ Thread.currentThread().setName(oldThreadName);
+ }
+ }
+ };
+ try {
+ if (EXTRACT_IN_CALLER_THREAD) {
+ callable2.call();
+ } else {
+ Future<Void> future = getExecutor().submit(callable2);
+ future.get(extractionTimeoutMillis, TimeUnit.MILLISECONDS);
+ }
+ } catch (TimeoutException e) {
+ timeoutCount++;
+ throw e;
+ } catch (InterruptedException e) {
+ throw e;
+ } catch (ExecutionException e) {
+ throw e.getCause();
+ }
+ }
+
+ public void setExtractionTimeoutMillis(int extractionTimeoutMillis) {
+ this.extractionTimeoutMillis = extractionTimeoutMillis;
+ }
+
+ private ExecutorService getExecutor() {
+ if (executorService == null) {
+ createExecutor();
+ }
+ return executorService;
+ }
+
+ private synchronized void createExecutor() {
+ if (executorService != null) {
+ return;
+ }
+ log.debug("ExtractedTextCache createExecutor " + this);
+ ThreadPoolExecutor executor = new ThreadPoolExecutor(1,
EXTRACTION_MAX_THREADS,
+ 60L, TimeUnit.SECONDS,
+ new LinkedBlockingQueue<Runnable>(), new ThreadFactory() {
+ private final AtomicInteger counter = new AtomicInteger();
+ private final Thread.UncaughtExceptionHandler handler = new
Thread.UncaughtExceptionHandler() {
+ @Override
+ public void uncaughtException(Thread t, Throwable e) {
+ log.warn("Error occurred in asynchronous processing ", e);
+ }
+ };
+ @Override
+ public Thread newThread(@Nonnull Runnable r) {
+ Thread thread = new Thread(r, createName());
+ thread.setDaemon(true);
+ thread.setPriority(Thread.MIN_PRIORITY);
+ thread.setUncaughtExceptionHandler(handler);
+ return thread;
+ }
+
+ private String createName() {
+ int index = counter.getAndIncrement();
+ return "oak binary text extractor" + (index == 0 ? "" : " " +
index);
+ }
+ });
+ executor.setKeepAliveTime(1, TimeUnit.MINUTES);
+ executor.allowCoreThreadTimeOut(true);
+ executorService = executor;
+ }
+
+ private synchronized void closeExecutorService() {
+ if (executorService != null) {
+ log.debug("ExtractedTextCache closeExecutorService " + this);
+ executorService.shutdown();
+ try {
+ executorService.awaitTermination(1, TimeUnit.MINUTES);
+ } catch (InterruptedException e) {
+ log.warn("Interrupted", e);
+ }
+ executorService = null;
+ }
+ }
+
+ private synchronized void loadTimeoutMap() {
+ if (indexDir == null || !indexDir.exists()) {
+ return;
+ }
+ File file = new File(indexDir, TIMEOUT_MAP);
+ if (!file.exists()) {
+ return;
+ }
+ try (FileInputStream in = new FileInputStream(file)) {
+ Properties prop = new Properties();
+ prop.load(in);
+ for(Entry<Object, Object> e : prop.entrySet()) {
+ timeoutMap.put(e.getKey().toString(), e.getValue().toString());
+ }
+ } catch (Exception e) {
+ log.warn("Could not load timeout map {} from {}",
+ TIMEOUT_MAP, indexDir, e);
+ }
+ }
+
+ private synchronized void storeTimeoutMap() {
+ if (indexDir == null || !indexDir.exists()) {
+ return;
+ }
+ File file = new File(indexDir, TIMEOUT_MAP);
+ try (FileOutputStream out = new FileOutputStream(file)) {
+ Properties prop = new Properties();
+ prop.putAll(timeoutMap);
+ prop.store(out, "Text extraction timed out for the following
binaries, and will not be retried");
+ } catch (Exception e) {
+ log.warn("Could not store timeout map {} from {}",
+ TIMEOUT_MAP, indexDir, e);
+ }
+ }
+
+}
Propchange:
jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/ExtractedTextCache.java
------------------------------------------------------------------------------
svn:eol-style = native
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=1831979&r1=1831978&r2=1831979&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
Mon May 21 17:06:10 2018
@@ -282,7 +282,7 @@ public interface FulltextIndexConstants
String SUGGEST_ANALYZED = "suggestAnalyzed";
/**
- * Integer property indicating that LuceneIndex should be
+ * Integer property indicating that the index should be
* used in compat mode to specific version
*/
String COMPAT_MODE = "compatVersion";
@@ -311,7 +311,7 @@ public interface FulltextIndexConstants
String PROP_FUNCTION = "function";
/**
- * Boolean property which signal LuceneIndexEditor to refresh the stored
index definition
+ * Boolean property which signal FulltextIndexEditor to refresh the stored
index definition
*/
String PROP_REFRESH_DEFN = "refresh";
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=1831979&r1=1831978&r2=1831979&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
Mon May 21 17:06:10 2018
@@ -79,7 +79,6 @@ import static org.apache.jackrabbit.oak.
import static
org.apache.jackrabbit.oak.plugins.index.search.FulltextIndexConstants.INDEX_DATA_CHILD_NAME;
import static
org.apache.jackrabbit.oak.plugins.index.IndexConstants.DECLARING_NODE_TYPES;
import static
org.apache.jackrabbit.oak.plugins.index.IndexConstants.ENTRY_COUNT_PROPERTY_NAME;
-import static
org.apache.jackrabbit.oak.plugins.index.IndexConstants.INDEXING_MODE_NRT;
import static
org.apache.jackrabbit.oak.plugins.index.IndexConstants.INDEXING_MODE_SYNC;
import static
org.apache.jackrabbit.oak.plugins.index.IndexConstants.REINDEX_COUNT;
import static
org.apache.jackrabbit.oak.plugins.index.search.FulltextIndexConstants.BLOB_SIZE;
@@ -154,7 +153,7 @@ public final class IndexDefinition imple
* This may not be the same time as when index was closed but the time of
checkpoint
* upto which index is upto date (OAK-6194)
*/
- static final String STATUS_LAST_UPDATED = "lastUpdated";
+ public static final String STATUS_LAST_UPDATED = "lastUpdated";
/**
* Meta property which provides the unique id
@@ -189,6 +188,8 @@ public final class IndexDefinition imple
private final NodeState root;
+ private final IndexFormatVersion version;
+
private final String funcName;
private final int blobSize;
@@ -276,6 +277,7 @@ public final class IndexDefinition imple
private final String indexPath;
private String uid;
private boolean reindexMode;
+ private IndexFormatVersion version;
public Builder(NodeState root, NodeState defn, String indexPath) {
this.root = checkNotNull(root);
@@ -288,12 +290,21 @@ public final class IndexDefinition imple
return this;
}
+ public Builder version(IndexFormatVersion version){
+ this.version = version;
+ return this;
+ }
+
+
public Builder reindex(){
this.reindexMode = true;
return this;
}
public IndexDefinition build(){
+ if (version == null){
+ version = determineIndexFormatVersion(defn);
+ }
if (uid == null){
uid = determineUniqueId(defn);
if (uid == null && !IndexDefinition.hasPersistedIndex(defn)){
@@ -305,16 +316,17 @@ public final class IndexDefinition imple
if (!reindexMode){
indexDefnStateToUse = getIndexDefinitionState(defn);
}
- return new IndexDefinition(root, indexDefnStateToUse, uid,
indexPath);
+ return new IndexDefinition(root, indexDefnStateToUse, version,
uid, indexPath);
}
}
public IndexDefinition(NodeState root, NodeState defn, String indexPath) {
- this(root, getIndexDefinitionState(defn), determineUniqueId(defn),
indexPath);
+ this(root, getIndexDefinitionState(defn),
determineIndexFormatVersion(defn), determineUniqueId(defn), indexPath);
}
- private IndexDefinition(NodeState root, NodeState defn, String uid, String
indexPath) {
+ private IndexDefinition(NodeState root, NodeState defn, IndexFormatVersion
version, String uid, String indexPath) {
this.root = root;
+ this.version = checkNotNull(version);
this.uid = uid;
this.definition = defn;
this.indexPath = checkNotNull(indexPath);
@@ -340,7 +352,7 @@ public final class IndexDefinition imple
String functionName = getOptionalValue(defn,
FulltextIndexConstants.FUNC_NAME, null);
if (fullTextEnabled && functionName == null){
- functionName = "lucene";
+ functionName = "fulltext";
}
this.funcName = functionName != null ? "native*" + functionName : null;
@@ -445,6 +457,11 @@ public final class IndexDefinition imple
return numOfDocs;
}
+ public IndexFormatVersion getVersion() {
+ return version;
+ }
+
+
public boolean isOfOldFormat(){
return !hasIndexingRules(definition);
}
Modified:
jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/NodeStateCloner.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/NodeStateCloner.java?rev=1831979&r1=1831978&r2=1831979&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/NodeStateCloner.java
(original)
+++
jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/NodeStateCloner.java
Mon May 21 17:06:10 2018
@@ -26,7 +26,7 @@ import org.apache.jackrabbit.oak.spi.sta
import static
org.apache.jackrabbit.oak.plugins.memory.EmptyNodeState.EMPTY_NODE;
-class NodeStateCloner {
+public class NodeStateCloner {
public static NodeState cloneVisibleState(NodeState state){
NodeBuilder builder = EMPTY_NODE.builder();
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=1831979&r1=1831978&r2=1831979&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
Mon May 21 17:06:10 2018
@@ -68,17 +68,17 @@ public class PropertyDefinition {
final boolean isRegexp;
- final boolean index;
+ public final boolean index;
final boolean stored;
- final boolean nodeScopeIndex;
+ public final boolean nodeScopeIndex;
- final boolean propertyIndex;
+ public final boolean propertyIndex;
- final boolean analyzed;
+ public final boolean analyzed;
- final boolean ordered;
+ public final boolean ordered;
final boolean nullCheckEnabled;
@@ -86,17 +86,17 @@ public class PropertyDefinition {
final int includedPropertyTypes;
- final boolean relative;
+ public final boolean relative;
- final boolean useInSuggest;
+ public final boolean useInSuggest;
- final boolean useInSpellcheck;
+ public final boolean useInSpellcheck;
- final boolean facet;
+ public final boolean facet;
- final String[] ancestors;
+ public final String[] ancestors;
- final boolean excludeFromAggregate;
+ public final boolean excludeFromAggregate;
final int weight;
@@ -105,17 +105,17 @@ public class PropertyDefinition {
* its set to null
*/
@CheckForNull
- final String nonRelativeName;
+ public final String nonRelativeName;
/**
* For function-based indexes: the function name, in Polish notation.
*/
- final String function;
+ public final String function;
/**
* For function-based indexes: the function code, as tokens.
*/
- final String[] functionCode;
+ public final String[] functionCode;
public final ValuePattern valuePattern;
Added:
jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/ReindexOperations.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/ReindexOperations.java?rev=1831979&view=auto
==============================================================================
---
jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/ReindexOperations.java
(added)
+++
jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/ReindexOperations.java
Mon May 21 17:06:10 2018
@@ -0,0 +1,60 @@
+/*
+ * 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 org.apache.jackrabbit.oak.spi.state.NodeBuilder;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+
+import static
org.apache.jackrabbit.oak.plugins.index.search.IndexDefinition.INDEX_DEFINITION_NODE;
+import static
org.apache.jackrabbit.oak.plugins.index.search.spi.editor.FulltextIndexEditorContext.configureUniqueId;
+
+public class ReindexOperations {
+ private final NodeState root;
+ private final NodeBuilder definitionBuilder;
+ private final String indexPath;
+
+ public ReindexOperations(NodeState root, NodeBuilder definitionBuilder,
String indexPath) {
+ this.root = root;
+ this.definitionBuilder = definitionBuilder;
+ this.indexPath = indexPath;
+ }
+
+ public IndexDefinition apply(boolean useStateFromBuilder) {
+ IndexFormatVersion version =
IndexDefinition.determineVersionForFreshIndex(definitionBuilder);
+ definitionBuilder.setProperty(IndexDefinition.INDEX_VERSION,
version.getVersion());
+
+ //Avoid obtaining the latest NodeState from builder as that would
force purge of current transient state
+ //as index definition does not get modified as part of IndexUpdate run
in most case we rely on base state
+ //For case where index definition is rewritten there we get fresh state
+ NodeState defnState = useStateFromBuilder ?
definitionBuilder.getNodeState() : definitionBuilder.getBaseState();
+ if (!IndexDefinition.isDisableStoredIndexDefinition()) {
+ definitionBuilder.setChildNode(INDEX_DEFINITION_NODE,
NodeStateCloner.cloneVisibleState(defnState));
+ }
+ String uid = configureUniqueId(definitionBuilder);
+
+ //Refresh the index definition based on update builder state
+ return IndexDefinition
+ .newBuilder(root, defnState, indexPath)
+ .version(version)
+ .uid(uid)
+ .reindex()
+ .build();
+ }
+}
Propchange:
jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/ReindexOperations.java
------------------------------------------------------------------------------
svn:eol-style = native
Added:
jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/TextExtractionStatsMBean.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/TextExtractionStatsMBean.java?rev=1831979&view=auto
==============================================================================
---
jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/TextExtractionStatsMBean.java
(added)
+++
jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/TextExtractionStatsMBean.java
Mon May 21 17:06:10 2018
@@ -0,0 +1,43 @@
+/*
+ * 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 org.osgi.annotation.versioning.ProviderType;
+
+@ProviderType
+public interface TextExtractionStatsMBean {
+ String TYPE = "TextExtractionStats";
+
+ boolean isPreExtractedTextProviderConfigured();
+
+ boolean isAlwaysUsePreExtractedCache();
+
+ int getTextExtractionCount();
+
+ long getTotalTime();
+
+ int getPreFetchedCount();
+
+ String getExtractedTextSize();
+
+ String getBytesRead();
+
+ int getTimeoutCount();
+}
Propchange:
jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/TextExtractionStatsMBean.java
------------------------------------------------------------------------------
svn:eol-style = native
Added:
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=1831979&view=auto
==============================================================================
---
jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/editor/BlobByteSource.java
(added)
+++
jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/editor/BlobByteSource.java
Mon May 21 17:06:10 2018
@@ -0,0 +1,49 @@
+/*
+ * 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.editor;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import com.google.common.io.ByteSource;
+import org.apache.jackrabbit.oak.api.Blob;
+
+public final class BlobByteSource extends ByteSource {
+ private final Blob blob;
+
+ public BlobByteSource(Blob blob) {
+ this.blob = blob;
+ }
+
+ @Override
+ public InputStream openStream() throws IOException {
+ return blob.getNewStream();
+ }
+
+ @Override
+ public long size() throws IOException {
+ return blob.length();
+ }
+
+ @Override
+ public boolean isEmpty() throws IOException {
+ return blob.length() == 0;
+ }
+}
Propchange:
jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/editor/BlobByteSource.java
------------------------------------------------------------------------------
svn:eol-style = native
Added:
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=1831979&view=auto
==============================================================================
---
jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/editor/DocumentMaker.java
(added)
+++
jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/editor/DocumentMaker.java
Mon May 21 17:06:10 2018
@@ -0,0 +1,43 @@
+/*
+ * 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.editor;
+
+import java.io.IOException;
+import java.util.List;
+
+import org.apache.jackrabbit.oak.api.PropertyState;
+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.
+ * For Apache Lucene that would be a Lucene {@code Document}, for Apache Solr
that might be a {@code SolrInputDocument}, etc.
+ */
+public interface DocumentMaker<D> {
+
+ /**
+ * create a document from the current state and list of modified properties
+ * @param state the node state
+ * @param isUpdate whether it is an update or not
+ * @param propertiesModified the list of modified properties
+ * @return a document to be indexed
+ * @throws IOException whether node state read operations or document
creation fail
+ */
+ D makeDocument(NodeState state, boolean isUpdate, List<PropertyState>
propertiesModified) throws IOException;
+
+}
Propchange:
jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/editor/DocumentMaker.java
------------------------------------------------------------------------------
svn:eol-style = native
Added:
jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/editor/FulltextBinaryTextExtractor.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/editor/FulltextBinaryTextExtractor.java?rev=1831979&view=auto
==============================================================================
---
jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/editor/FulltextBinaryTextExtractor.java
(added)
+++
jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/editor/FulltextBinaryTextExtractor.java
Mon May 21 17:06:10 2018
@@ -0,0 +1,350 @@
+/*
+ * 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.editor;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.Callable;
+import java.util.concurrent.TimeoutException;
+
+import javax.annotation.Nullable;
+
+import com.google.common.io.CountingInputStream;
+import org.apache.commons.io.IOUtils;
+import org.apache.jackrabbit.JcrConstants;
+import org.apache.jackrabbit.oak.api.Blob;
+import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.api.Type;
+import org.apache.jackrabbit.oak.commons.io.LazyInputStream;
+import org.apache.jackrabbit.oak.plugins.index.fulltext.ExtractedText;
+import org.apache.jackrabbit.oak.plugins.index.search.ExtractedTextCache;
+import org.apache.jackrabbit.oak.plugins.index.search.IndexDefinition;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+import org.apache.tika.config.TikaConfig;
+import org.apache.tika.exception.TikaException;
+import org.apache.tika.metadata.Metadata;
+import org.apache.tika.mime.MediaType;
+import org.apache.tika.parser.AutoDetectParser;
+import org.apache.tika.parser.ParseContext;
+import org.apache.tika.parser.Parser;
+import org.apache.tika.sax.WriteOutContentHandler;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.xml.sax.SAXException;
+
+import static org.apache.jackrabbit.JcrConstants.JCR_DATA;
+import static
org.apache.jackrabbit.oak.plugins.index.search.spi.editor.FulltextIndexEditor.TEXT_EXTRACTION_ERROR;
+
+/**
+ *
+ */
+public class FulltextBinaryTextExtractor {
+
+ public static final String NO_NODEPATH_KEY = "*";
+
+ private static final Logger log =
LoggerFactory.getLogger(FulltextBinaryTextExtractor.class);
+ private static final Parser defaultParser = createDefaultParser();
+ private static final long SMALL_BINARY =
Long.getLong("oak.search.smallBinary", 16 * 1024);
+ private final TextExtractionStats textExtractionStats = new
TextExtractionStats();
+ private final ExtractedTextCache extractedTextCache;
+ private final IndexDefinition definition;
+ private final boolean reindex;
+ private Parser parser;
+ private TikaConfigHolder tikaConfig;
+ /**
+ * The media types supported by the parser used.
+ */
+ private Set<MediaType> supportedMediaTypes;
+ private Set<MediaType> nonIndexedMediaType;
+
+ public FulltextBinaryTextExtractor(ExtractedTextCache extractedTextCache,
IndexDefinition definition, boolean reindex) {
+ this.extractedTextCache = extractedTextCache;
+ this.definition = definition;
+ this.reindex = reindex;
+ }
+
+ public void done(boolean reindex){
+ textExtractionStats.log(reindex);
+ textExtractionStats.collectStats(extractedTextCache);
+ }
+
+ public Map<String, String> newBinary(
+ PropertyState property, NodeState state, String nodePath, String path) {
+ Map<String,String> fields = new HashMap<>();
+ Metadata metadata = new Metadata();
+
+ //jcr:mimeType is mandatory for a binary to be indexed
+ String type = state.getString(JcrConstants.JCR_MIMETYPE);
+ type = definition.getTikaMappedMimeType(type);
+
+ if (type == null || !isSupportedMediaType(type)) {
+ log.trace(
+ "[{}] Ignoring binary content for node {} due to unsupported (or
null) jcr:mimeType [{}]",
+ getIndexName(), path, type);
+ return fields;
+ }
+
+ metadata.set(Metadata.CONTENT_TYPE, type);
+ if (JCR_DATA.equals(property.getName())) {
+ String encoding = state.getString(JcrConstants.JCR_ENCODING);
+ if (encoding != null) { // not mandatory
+ metadata.set(Metadata.CONTENT_ENCODING, encoding);
+ }
+ }
+
+ for (Blob v : property.getValue(Type.BINARIES)) {
+ String value = parseStringValue(v, metadata, path, property.getName());
+ if (value == null){
+ continue;
+ }
+
+ if (nodePath != null){
+ fields.put(nodePath, value);
+ } else {
+ fields.put(NO_NODEPATH_KEY, value);
+ }
+ }
+ return fields;
+ }
+
+ private String parseStringValue(Blob v, Metadata metadata, String path,
String propertyName) {
+ String text = extractedTextCache.get(path, propertyName, v, reindex);
+ if (text == null){
+ text = parseStringValue0(v, metadata, path);
+ }
+ return text;
+ }
+
+ private String parseStringValue0(Blob v, Metadata metadata, String path) {
+ WriteOutContentHandler handler = new
WriteOutContentHandler(definition.getMaxExtractLength());
+ long start = System.currentTimeMillis();
+ long bytesRead = 0;
+ long length = v.length();
+ if (log.isDebugEnabled()) {
+ log.debug("Extracting {}, {} bytes, id {}", path, length,
v.getContentIdentity());
+ }
+ try {
+ CountingInputStream stream = new CountingInputStream(new
LazyInputStream(new BlobByteSource(v)));
+ try {
+ if (length > SMALL_BINARY) {
+ String name = "Extracting " + path + ", " + length + " bytes";
+ extractedTextCache.process(name, new Callable<Void>() {
+ @Override
+ public Void call() throws Exception {
+ getParser().parse(stream, handler, metadata, new ParseContext());
+ return null;
+ }
+ });
+ } else {
+ getParser().parse(stream, handler, metadata, new ParseContext());
+ }
+ } finally {
+ bytesRead = stream.getCount();
+ stream.close();
+ }
+ } catch (LinkageError e) {
+ // Capture errors caused by extraction libraries
+ // not being present. This is equivalent to disabling
+ // selected media types in configuration, so we can simply
+ // ignore these errors.
+ log.debug(
+ "[{}] Failed to extract text from a binary property: {}."
+ + " This often happens when some media types are disabled by
configuration."
+ + " The stack trace is included to flag some 'unintended'
failures",
+ getIndexName(), path, e);
+ extractedTextCache.put(v, ExtractedText.ERROR);
+ return TEXT_EXTRACTION_ERROR;
+ } catch (TimeoutException t) {
+ log.warn(
+ "[{}] Failed to extract text from a binary property due to timeout:
{}.",
+ getIndexName(), path);
+ extractedTextCache.put(v, ExtractedText.ERROR);
+ extractedTextCache.putTimeout(v, ExtractedText.ERROR);
+ return TEXT_EXTRACTION_ERROR;
+ } catch (Throwable t) {
+ // Capture and report any other full text extraction problems.
+ // The special STOP exception is used for normal termination.
+ if (!handler.isWriteLimitReached(t)) {
+ log.debug(
+ "[{}] Failed to extract text from a binary property: {}."
+ + " This is a fairly common case, and nothing to"
+ + " worry about. The stack trace is included to"
+ + " help improve the text extraction feature.",
+ getIndexName(), path, t);
+ extractedTextCache.put(v, ExtractedText.ERROR);
+ return TEXT_EXTRACTION_ERROR;
+ } else {
+ log.debug("Extracted text size exceeded configured limit({})",
definition.getMaxExtractLength());
+ }
+ }
+ String result = handler.toString();
+ if (bytesRead > 0) {
+ long time = System.currentTimeMillis() - start;
+ int len = result.length();
+ recordTextExtractionStats(time, bytesRead, len);
+ if (log.isDebugEnabled()) {
+ log.debug("Extracting {} took {} ms, {} bytes read, {} text size",
+ path, time, bytesRead, len);
+ }
+ }
+ extractedTextCache.put(v, new
ExtractedText(ExtractedText.ExtractionResult.SUCCESS, result));
+ return result;
+ }
+
+ private void recordTextExtractionStats(long timeInMillis, long bytesRead,
int textLength) {
+ textExtractionStats.addStats(timeInMillis, bytesRead, textLength);
+ }
+
+ private String getIndexName() {
+ return definition.getIndexName();
+ }
+
+ //~-------------------------------------------< Tika >
+
+ public TikaConfig getTikaConfig(){
+ if (tikaConfig == null) {
+ tikaConfig = initializeTikaConfig(definition);
+ }
+ return tikaConfig.config;
+ }
+
+ private Parser getParser() {
+ if (parser == null){
+ parser = initializeTikaParser(definition);
+ }
+ return parser;
+ }
+
+ private boolean isSupportedMediaType(String type) {
+ if (supportedMediaTypes == null) {
+ supportedMediaTypes = getParser().getSupportedTypes(new ParseContext());
+ nonIndexedMediaType = getNonIndexedMediaTypes();
+ }
+ MediaType mediaType = MediaType.parse(type);
+ return supportedMediaTypes.contains(mediaType) &&
!nonIndexedMediaType.contains(mediaType);
+ }
+
+ private Set<MediaType> getNonIndexedMediaTypes() {
+ InputStream configStream = null;
+ String configSource = null;
+ try {
+ if (definition.hasCustomTikaConfig()) {
+ configSource = String.format("Custom config at %s",
definition.getIndexPath());
+ configStream = definition.getTikaConfig();
+ } else {
+ URL configUrl =
FulltextIndexEditorContext.class.getResource("tika-config.xml");
+ configSource = "Default : tika-config.xml";
+ if (configUrl != null) {
+ configStream = configUrl.openStream();
+ }
+ }
+
+ if (configStream != null) {
+ return TikaParserConfig.getNonIndexedMediaTypes(configStream);
+ }
+ } catch (TikaException | IOException | SAXException e) {
+ log.warn("Tika configuration not available : " + configSource, e);
+ } finally {
+ IOUtils.closeQuietly(configStream);
+ }
+ return Collections.emptySet();
+ }
+
+
+ private static TikaConfigHolder initializeTikaConfig(@Nullable
IndexDefinition definition) {
+ ClassLoader current = Thread.currentThread().getContextClassLoader();
+ InputStream configStream = null;
+ String configSource = null;
+
+ try {
+
Thread.currentThread().setContextClassLoader(FulltextIndexEditorContext.class.getClassLoader());
+ if (definition != null && definition.hasCustomTikaConfig()) {
+ log.debug("[{}] Using custom tika config", definition.getIndexName());
+ configSource = "Custom config at " + definition.getIndexPath();
+ configStream = definition.getTikaConfig();
+ } else {
+ URL configUrl =
FulltextIndexEditorContext.class.getResource("tika-config.xml");
+ if (configUrl != null) {
+ configSource = configUrl.toString();
+ configStream = configUrl.openStream();
+ }
+ }
+
+ if (configStream != null) {
+ return new TikaConfigHolder(new TikaConfig(configStream),
configSource);
+ }
+ } catch (TikaException | IOException | SAXException e) {
+ log.warn("Tika configuration not available : " + configSource, e);
+ } finally {
+ IOUtils.closeQuietly(configStream);
+ Thread.currentThread().setContextClassLoader(current);
+ }
+ return new TikaConfigHolder(TikaConfig.getDefaultConfig(), "Default
Config");
+ }
+
+ private Parser initializeTikaParser(IndexDefinition definition) {
+ ClassLoader current = Thread.currentThread().getContextClassLoader();
+ try {
+ if (definition.hasCustomTikaConfig()) {
+
Thread.currentThread().setContextClassLoader(FulltextIndexEditorContext.class.getClassLoader());
+ return new AutoDetectParser(getTikaConfig());
+ }
+ } finally {
+ Thread.currentThread().setContextClassLoader(current);
+ }
+ return defaultParser;
+ }
+
+ private static AutoDetectParser createDefaultParser() {
+ ClassLoader current = Thread.currentThread().getContextClassLoader();
+ TikaConfigHolder configHolder = null;
+ try {
+ configHolder = initializeTikaConfig(null);
+
Thread.currentThread().setContextClassLoader(FulltextIndexEditorContext.class.getClassLoader());
+ log.info("Loaded default Tika Config from classpath {}", configHolder);
+ return new AutoDetectParser(configHolder.config);
+ } catch (Exception e) {
+ log.warn("Tika configuration not available : " + configHolder, e);
+ } finally {
+ Thread.currentThread().setContextClassLoader(current);
+ }
+ return new AutoDetectParser();
+ }
+
+ private static final class TikaConfigHolder{
+ final TikaConfig config;
+ final String sourceInfo;
+
+ public TikaConfigHolder(TikaConfig config, String sourceInfo) {
+ this.config = config;
+ this.sourceInfo = sourceInfo;
+ }
+
+ @Override
+ public String toString() {
+ return sourceInfo;
+ }
+ }
+
+}
Propchange:
jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/editor/FulltextBinaryTextExtractor.java
------------------------------------------------------------------------------
svn:eol-style = native
Added:
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=1831979&view=auto
==============================================================================
---
jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/editor/FulltextDocumentMaker.java
(added)
+++
jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/editor/FulltextDocumentMaker.java
Mon May 21 17:06:10 2018
@@ -0,0 +1,558 @@
+/*
+ * 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.editor;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import javax.annotation.CheckForNull;
+import javax.jcr.PropertyType;
+
+import com.google.common.collect.Iterables;
+import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.api.Type;
+import org.apache.jackrabbit.oak.commons.PathUtils;
+
+import org.apache.jackrabbit.oak.plugins.index.search.Aggregate;
+import org.apache.jackrabbit.oak.plugins.index.search.IndexDefinition;
+import org.apache.jackrabbit.oak.plugins.index.search.PropertyDefinition;
+import
org.apache.jackrabbit.oak.plugins.index.search.util.FunctionIndexProcessor;
+import org.apache.jackrabbit.oak.plugins.memory.StringPropertyState;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static org.apache.jackrabbit.oak.commons.PathUtils.getName;
+
+import static
org.apache.jackrabbit.oak.plugins.index.search.util.ConfigUtil.getPrimaryTypeName;
+
+/**
+ * Abstract implementation of a {@link DocumentMaker}.
+ *
+ * @param <D> the type of documents to be indexed specific to subclasses
implementations
+ */
+public abstract class FulltextDocumentMaker<D> implements DocumentMaker<D> {
+
+ private final Logger log = LoggerFactory.getLogger(getClass());
+
+ private FulltextBinaryTextExtractor textExtractor;
+ private IndexDefinition definition;
+ private IndexDefinition.IndexingRule indexingRule;
+ private String path;
+
+ abstract D initDoc();
+
+ abstract D finalizeDoc(D fields, boolean dirty, boolean facet);
+
+ abstract StringPropertyState createNodeNamePS();
+
+ abstract boolean isFacetingEnabled();
+
+ abstract boolean isNodeName(String pname);
+
+ protected abstract boolean indexTypeOrderedFields(String pname, int tag,
PropertyState property, PropertyDefinition pd);
+
+ protected abstract boolean addBinary(D doc, Map<String, String> binaryMap);
+
+ protected abstract boolean indexFacetProperty(D doc, int tag,
PropertyState property, String pname);
+
+ protected abstract boolean indexAnalyzedProperty(D doc, String pname,
String value, PropertyDefinition pd);
+
+ protected abstract boolean indexSuggestValue(D doc, String value);
+
+ protected abstract boolean indexSpellcheckValue(D doc, String value);
+
+ protected abstract boolean indexFulltextValue(D doc, String value);
+
+ protected abstract boolean indexTypedProperty(D doc, PropertyState
property, String pname, PropertyDefinition pd);
+
+ protected abstract boolean indexNotNullProperty(D doc, PropertyDefinition
pd);
+
+ protected abstract boolean indexNullProperty(D doc, PropertyDefinition pd);
+
+ protected abstract boolean indexAggregateValue(D doc,
Aggregate.NodeIncludeResult result, String value, PropertyDefinition pd);
+
+ protected abstract boolean indexNodeName(D doc, String value);
+
+ @CheckForNull
+ public D makeDocument(NodeState state) throws IOException {
+ return makeDocument(state, false,
Collections.<PropertyState>emptyList());
+ }
+
+ @CheckForNull
+ public D makeDocument(NodeState state, boolean isUpdate,
List<PropertyState> propertiesModified) throws IOException {
+ boolean facet = false;
+
+ D document = initDoc();
+ boolean dirty = false;
+
+ //We 'intentionally' are indexing node names only on root state as we
don't support indexing relative or
+ //regex for node name indexing
+ PropertyState nodenamePS = createNodeNamePS();
+ for (PropertyState property : Iterables.concat(state.getProperties(),
Collections.singleton(nodenamePS))) {
+ String pname = property.getName();
+
+ if (!isVisible(pname) && !isNodeName(pname)) {
+ continue;
+ }
+
+ PropertyDefinition pd = indexingRule.getConfig(pname);
+
+ if (pd == null || !pd.index){
+ continue;
+ }
+
+ if (pd.ordered) {
+ dirty |= addTypedOrderedFields(document, property, pname, pd);
+ }
+
+ dirty |= indexProperty(path, document, state, property, pname, pd);
+
+ facet |= pd.facet;
+ }
+
+ boolean[] dirties = indexAggregates(path, document, state);
+ dirty |= dirties[0]; // any (aggregate) indexing happened
+ facet |= dirties[1]; // facet indexing during (index-time) aggregation
+ dirty |= indexNullCheckEnabledProps(path, document, state);
+ dirty |= indexFunctionRestrictions(path, document, state);
+ dirty |= indexNotNullCheckEnabledProps(path, document, state);
+
+ dirty |= augmentCustomFields(path, document, state);
+
+ // Check if a node having a single property was modified/deleted
+ if (!dirty) {
+ dirty = indexIfSinglePropertyRemoved(propertiesModified);
+ }
+
+ if (isUpdate && !dirty) {
+ // updated the state but had no relevant changes
+ return null;
+ }
+
+ String name = getName(path);
+ if (indexingRule.isNodeNameIndexed()){
+ addNodeNameField(document, name);
+ dirty = true;
+ }
+
+ //For property index no use making an empty document if
+ //none of the properties are indexed
+ if(!indexingRule.indexesAllNodesOfMatchingType() && !dirty){
+ return null;
+ }
+
+ return finalizeDoc(document, dirty, facet);
+ }
+
+
+ private boolean indexFacets(D doc, PropertyState property, String pname,
PropertyDefinition pd) {
+ int tag = property.getType().tag();
+ int idxDefinedTag = pd.getType();
+ // Try converting type to the defined type in the index definition
+ if (tag != idxDefinedTag) {
+ log.debug("[{}] Facet property defined with type {} differs from
property {} with type {} in "
+ + "path {}",
+ getIndexName(),
+ Type.fromTag(idxDefinedTag, false), property.toString(),
+ Type.fromTag(tag, false), path);
+ tag = idxDefinedTag;
+ }
+ return indexFacetProperty(doc, tag, property, pname);
+ }
+
+ private boolean indexProperty(String path,
+ D doc,
+ NodeState state,
+ PropertyState property,
+ String pname,
+ PropertyDefinition pd) {
+ boolean includeTypeForFullText =
indexingRule.includePropertyType(property.getType().tag());
+
+ boolean dirty = false;
+ if (Type.BINARY.tag() == property.getType().tag()
+ && includeTypeForFullText) {
+ Map<String, String> binaryMap = newBinary(property, state, null,
path + "@" + pname);
+ addBinary(doc, binaryMap);
+ dirty = true;
+ } else {
+ if (pd.propertyIndex &&
pd.includePropertyType(property.getType().tag())) {
+ dirty |= addTypedFields(doc, property, pname, pd);
+ }
+
+ if (pd.fulltextEnabled() && includeTypeForFullText) {
+ for (String value : property.getValue(Type.STRINGS)) {
+
+ if (!includePropertyValue(value, pd)){
+ continue;
+ }
+
+ if (pd.analyzed &&
pd.includePropertyType(property.getType().tag())) {
+ indexAnalyzedProperty(doc, pname, value, pd);
+ }
+
+ if (pd.useInSuggest) {
+ indexSuggestValue(doc, value);
+ }
+
+ if (pd.useInSpellcheck) {
+ indexSpellcheckValue(doc, value);
+ }
+
+ if (pd.nodeScopeIndex) {
+ indexFulltextValue(doc, value);
+ }
+ dirty = true;
+ }
+ }
+ if (pd.facet && isFacetingEnabled()) {
+ dirty |= indexFacets(doc, property, pname, pd);
+ }
+
+ }
+
+ return dirty;
+ }
+
+ private boolean addTypedFields(D doc, PropertyState property, String
pname, PropertyDefinition pd) {
+ return indexTypedProperty(doc, property, pname, pd);
+ }
+
+ private boolean addTypedOrderedFields(D doc,
+ PropertyState property,
+ String pname,
+ PropertyDefinition pd) {
+ // Ignore and warn if property multi-valued as not supported
+ if (property.getType().isArray()) {
+ log.warn(
+ "[{}] Ignoring ordered property {} of type {} for path {}
as multivalued ordered property not supported",
+ getIndexName(), pname,
+ Type.fromTag(property.getType().tag(), true), path);
+ return false;
+ }
+
+ int tag = property.getType().tag();
+ int idxDefinedTag = pd.getType();
+ // Try converting type to the defined type in the index definition
+ if (tag != idxDefinedTag) {
+ log.debug(
+ "[{}] Ordered property defined with type {} differs from
property {} with type {} in "
+ + "path {}",
+ getIndexName(),
+ Type.fromTag(idxDefinedTag, false), property.toString(),
+ Type.fromTag(tag, false), path);
+ tag = idxDefinedTag;
+ }
+ return indexTypeOrderedFields(pname, tag, property, pd);
+ }
+
+ protected boolean includePropertyValue(PropertyState property, int i,
PropertyDefinition pd) {
+ if (property.getType().tag() == PropertyType.BINARY){
+ return true;
+ }
+
+ if (pd.valuePattern.matchesAll()) {
+ return true;
+ }
+
+ return includePropertyValue(property.getValue(Type.STRING, i), pd);
+ }
+
+ protected boolean includePropertyValue(String value, PropertyDefinition
pd){
+ return pd.valuePattern.matches(value);
+ }
+
+ private static boolean isVisible(String name) {
+ return name.charAt(0) != ':';
+ }
+
+ private Map<String,String> newBinary(
+ PropertyState property, NodeState state, String nodePath, String
path) {
+ if (textExtractor == null){
+ //Skip text extraction for sync indexing
+ return Collections.emptyMap();
+ }
+
+ return textExtractor.newBinary(property, state, nodePath, path);
+ }
+
+ private boolean augmentCustomFields(final String path, final D doc,
+ final NodeState document) {
+ boolean dirty = false;
+
+ // TODO : extract more generic SPI for augmentor factory
+
+// if (augmentorFactory != null) {
+// Iterable<Field> augmentedFields = augmentorFactory
+// .getIndexFieldProvider(indexingRule.getNodeTypeName())
+// .getAugmentedFields(path, document,
definition.getDefinitionNodeState());
+//
+// for (Field field : augmentedFields) {
+// fields.add(field);
+// dirty = true;
+// }
+// }
+
+ return dirty;
+ }
+
+ //~-------------------------------------------------------< NullCheck
Support >
+
+ private boolean indexNotNullCheckEnabledProps(String path, D doc,
NodeState state) {
+ boolean fieldAdded = false;
+ for (PropertyDefinition pd :
indexingRule.getNotNullCheckEnabledProperties()) {
+ if (isPropertyNotNull(state, pd)) {
+ fieldAdded = indexNotNullProperty(doc, pd);
+ }
+ }
+ return fieldAdded;
+ }
+
+
+ private boolean indexNullCheckEnabledProps(String path, D doc, NodeState
state) {
+ boolean fieldAdded = false;
+ for (PropertyDefinition pd :
indexingRule.getNullCheckEnabledProperties()) {
+ if (isPropertyNull(state, pd)) {
+ fieldAdded = indexNullProperty(doc, pd);
+ }
+ }
+ return fieldAdded;
+ }
+
+ private boolean indexFunctionRestrictions(String path, D fields, NodeState
state) {
+ boolean fieldAdded = false;
+ for (PropertyDefinition pd : indexingRule.getFunctionRestrictions()) {
+ PropertyState functionValue = calculateValue(path, state,
pd.functionCode);
+ if (functionValue != null) {
+ if (pd.ordered) {
+ addTypedOrderedFields(fields, functionValue, pd.function,
pd);
+ }
+ addTypedFields(fields, functionValue, pd.function, pd);
+ fieldAdded = true;
+ }
+ }
+ return fieldAdded;
+ }
+
+ private PropertyState calculateValue(String path, NodeState state,
String[] functionCode) {
+ try {
+ return FunctionIndexProcessor.tryCalculateValue(path, state,
functionCode);
+ } catch (RuntimeException e) {
+ log.error("Failed to calculate function value for {} at {}",
+ Arrays.toString(functionCode), path, e);
+ throw e;
+ }
+ }
+
+ private boolean indexIfSinglePropertyRemoved(List<PropertyState>
propertiesModified) {
+ boolean dirty = false;
+ for (PropertyState ps : propertiesModified) {
+ PropertyDefinition pd = indexingRule.getConfig(ps.getName());
+ if (pd != null
+ && pd.index
+ && (pd.includePropertyType(ps.getType().tag())
+ ||
indexingRule.includePropertyType(ps.getType().tag()))) {
+ dirty = true;
+ break;
+ }
+ }
+ return dirty;
+ }
+
+ /**
+ * Determine if the property as defined by PropertyDefinition exists or
not.
+ *
+ * <p>For relative property if the intermediate nodes do not exist then
property is
+ * <bold>not</bold> considered to be null</p>
+ *
+ * @return true if the property does not exist
+ */
+ private boolean isPropertyNull(NodeState state, PropertyDefinition pd){
+ NodeState propertyNode = getPropertyNode(state, pd);
+ if (!propertyNode.exists()){
+ return false;
+ }
+ return !propertyNode.hasProperty(pd.nonRelativeName);
+ }
+
+ /**
+ * Determine if the property as defined by PropertyDefinition exists or
not.
+ *
+ * <p>For relative property if the intermediate nodes do not exist then
property is
+ * considered to be null</p>
+ *
+ * @return true if the property exists
+ */
+ private boolean isPropertyNotNull(NodeState state, PropertyDefinition pd){
+ NodeState propertyNode = getPropertyNode(state, pd);
+ if (!propertyNode.exists()){
+ return false;
+ }
+ return propertyNode.hasProperty(pd.nonRelativeName);
+ }
+
+ private static NodeState getPropertyNode(NodeState nodeState,
PropertyDefinition pd) {
+ if (!pd.relative){
+ return nodeState;
+ }
+ NodeState node = nodeState;
+ for (String name : pd.ancestors) {
+ node = node.getChildNode(name);
+ }
+ return node;
+ }
+
+ /**
+ * index aggregates on a certain path
+ * @param path the path of the node
+ * @param fields the list of fields
+ * @param state the node state
+ * @return an array of booleans whose first element is {@code true} if any
indexing has happened
+ * and the second element is {@code true} if facets on any (aggregate)
property have been indexed
+ */
+ private boolean[] indexAggregates(final String path, final D fields,
+ final NodeState state) {
+ final AtomicBoolean dirtyFlag = new AtomicBoolean();
+ final AtomicBoolean facetFlag = new AtomicBoolean();
+ indexingRule.getAggregate().collectAggregates(state, new
Aggregate.ResultCollector() {
+ @Override
+ public void onResult(Aggregate.NodeIncludeResult result) {
+ boolean dirty = indexAggregatedNode(path, fields, result);
+ if (dirty) {
+ dirtyFlag.set(true);
+ }
+ }
+
+ @Override
+ public void onResult(Aggregate.PropertyIncludeResult result) {
+ boolean dirty = false;
+ if (result.pd.ordered) {
+ dirty |= addTypedOrderedFields(fields,
result.propertyState,
+ result.propertyPath, result.pd);
+ }
+ dirty |= indexProperty(path, fields, state,
result.propertyState,
+ result.propertyPath, result.pd);
+
+ if (result.pd.facet) {
+ facetFlag.set(true);
+ }
+ if (dirty) {
+ dirtyFlag.set(true);
+ }
+ }
+ });
+ return new boolean[]{dirtyFlag.get(), facetFlag.get()};
+ }
+ /**
+ * Create the fulltext field from the aggregated nodes. If result is for
aggregate for a relative node
+ * include then
+ * @param path current node path
+ * @param doc document
+ * @param result aggregate result
+ * @return true if a field was created for passed node result
+ */
+ private boolean indexAggregatedNode(String path, D doc,
Aggregate.NodeIncludeResult result) {
+ //rule for node being aggregated might be null if such nodes
+ //are not indexed on there own. In such cases we rely in current
+ //rule for some checks
+ IndexDefinition.IndexingRule ruleAggNode = definition
+
.getApplicableIndexingRule(getPrimaryTypeName(result.nodeState));
+ boolean dirty = false;
+
+ for (PropertyState property : result.nodeState.getProperties()){
+ String pname = property.getName();
+ String propertyPath = PathUtils.concat(result.nodePath, pname);
+
+ if (!isVisible(pname)) {
+ continue;
+ }
+
+ //Check if type is indexed
+ int type = property.getType().tag();
+ if (ruleAggNode != null ) {
+ if (!ruleAggNode.includePropertyType(type)) {
+ continue;
+ }
+ } else if (!indexingRule.includePropertyType(type)){
+ continue;
+ }
+
+ //Check if any explicit property defn is defined via relative path
+ // and is marked to exclude this property from being indexed. We
exclude
+ //it from aggregation if
+ // 1. Its not to be indexed i.e. index=false
+ // 2. Its explicitly excluded from aggregation i.e.
excludeFromAggregation=true
+ PropertyDefinition pdForRootNode =
indexingRule.getConfig(propertyPath);
+ if (pdForRootNode != null && (!pdForRootNode.index ||
pdForRootNode.excludeFromAggregate)) {
+ continue;
+ }
+
+ if (Type.BINARY == property.getType()) {
+ String aggreagtedNodePath = PathUtils.concat(path,
result.nodePath);
+ //Here the fulltext is being created for aggregate root hence
nodePath passed
+ //should be null
+ String nodePath = result.isRelativeNode() ?
result.rootIncludePath : null;
+ Map<String, String> stringStringMap = newBinary(property,
result.nodeState, nodePath, aggreagtedNodePath + "@" + pname);
+ addBinary(doc, stringStringMap);
+ dirty = true;
+ } else {
+ PropertyDefinition pd = null;
+ if (ruleAggNode != null){
+ pd = ruleAggNode.getConfig(pname);
+ }
+
+ if (pd != null && !pd.nodeScopeIndex){
+ continue;
+ }
+
+ for (String value : property.getValue(Type.STRINGS)) {
+ dirty = indexAggregateValue(doc, result, value, pd);
+ }
+ }
+ }
+ return dirty;
+ }
+
+ private String getIndexName() {
+ return definition.getIndexName();
+ }
+
+ /**
+ * Extracts the local name of the current node ignoring any namespace
prefix
+ *
+ * @param name node name
+ */
+ private void addNodeNameField(D doc, String name) {
+ //TODO Need to check if it covers all cases
+ int colon = name.indexOf(':');
+ String value = colon < 0 ? name : name.substring(colon + 1);
+
+ //For now just add a single term. Later we can look into using
different analyzer
+ //to analyze the node name and add multiple terms. Like add multiple
terms for a
+ //cameCase file name to allow faster like search
+ indexNodeName(doc, value);
+ }
+
+
+}
Propchange:
jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/editor/FulltextDocumentMaker.java
------------------------------------------------------------------------------
svn:eol-style = native