Added:
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=1831979&view=auto
==============================================================================
---
jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/editor/FulltextIndexEditor.java
(added)
+++
jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/editor/FulltextIndexEditor.java
Mon May 21 17:06:10 2018
@@ -0,0 +1,401 @@
+/*
+ * 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.Collections;
+import java.util.List;
+import java.util.Set;
+
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import org.apache.jackrabbit.oak.api.CommitFailedException;
+import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.commons.PathUtils;
+import org.apache.jackrabbit.oak.plugins.index.IndexEditor;
+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.PropertyUpdateCallback;
+import org.apache.jackrabbit.oak.plugins.memory.EmptyNodeState;
+import org.apache.jackrabbit.oak.spi.commit.Editor;
+import org.apache.jackrabbit.oak.spi.filter.PathFilter;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static org.apache.jackrabbit.oak.commons.PathUtils.concat;
+
+/**
+ * Abstract implementation of an {@link IndexEditor} which supports index time
aggregation.
+ */
+public class FulltextIndexEditor<D> implements IndexEditor,
Aggregate.AggregateRoot {
+
+ private static final Logger log =
+ LoggerFactory.getLogger(FulltextIndexEditor.class);
+
+ public static final String TEXT_EXTRACTION_ERROR = "TextExtractionError";
+
+ private final FulltextIndexEditorContext<D> context;
+
+ /** Name of this node, or {@code null} for the root node. */
+ private final String name;
+
+ /** Parent editor or {@code null} if this is the root editor. */
+ private final FulltextIndexEditor parent;
+
+ /** Path of this editor, built lazily in {@link #getPath()}. */
+ private String path;
+
+ private boolean propertiesChanged = false;
+
+ private List<PropertyState> propertiesModified = Lists.newArrayList();
+
+ /**
+ * Flag indicating if the current tree being traversed has a deleted parent.
+ */
+ private final boolean isDeleted;
+
+ private IndexDefinition.IndexingRule indexingRule;
+
+ private List<Aggregate.Matcher> currentMatchers = Collections.emptyList();
+
+ private final MatcherState matcherState;
+
+ private final PathFilter.Result pathFilterResult;
+
+ FulltextIndexEditor(FulltextIndexEditorContext<D> context) throws
CommitFailedException {
+ this.parent = null;
+ this.name = null;
+ this.path = "/";
+ this.context = context;
+ this.isDeleted = false;
+ this.matcherState = MatcherState.NONE;
+ this.pathFilterResult =
context.getDefinition().getPathFilter().filter(PathUtils.ROOT_PATH);
+ }
+
+ private FulltextIndexEditor(FulltextIndexEditor<D> parent, String name,
+ MatcherState matcherState,
+ PathFilter.Result pathFilterResult,
+ boolean isDeleted) {
+ this.parent = parent;
+ this.name = name;
+ this.path = null;
+ this.context = parent.context;
+ this.isDeleted = isDeleted;
+ this.matcherState = matcherState;
+ this.pathFilterResult = pathFilterResult;
+ }
+
+ public String getPath() {
+ if (path == null) { // => parent != null
+ path = concat(parent.getPath(), name);
+ }
+ return path;
+ }
+
+ @Override
+ public void enter(NodeState before, NodeState after) {
+ if (EmptyNodeState.MISSING_NODE == before && parent == null){
+ context.enableReindexMode();
+ }
+
+ //Only check for indexing if the result is include.
+ //In case like TRAVERSE nothing needs to be indexed for those
+ //path
+ if (pathFilterResult == PathFilter.Result.INCLUDE) {
+ //For traversal in deleted sub tree before state has to be used
+ NodeState current = after.exists() ? after : before;
+ indexingRule = getDefinition().getApplicableIndexingRule(current);
+
+ if (indexingRule != null) {
+ currentMatchers = indexingRule.getAggregate().createMatchers(this);
+ }
+ }
+ }
+
+ @Override
+ public void leave(NodeState before, NodeState after)
+ throws CommitFailedException {
+ if (propertiesChanged || !before.exists()) {
+ String path = getPath();
+ if (addOrUpdate(path, after, before.exists())) {
+ long indexed = context.incIndexedNodes();
+ if (indexed % 1000 == 0) {
+ log.debug("[{}] => Indexed {} nodes...", getIndexName(), indexed);
+ }
+ }
+ }
+
+ for (Aggregate.Matcher m : matcherState.affectedMatchers){
+ m.markRootDirty();
+ }
+
+ if (parent == null) {
+ PropertyUpdateCallback callback = context.getPropertyUpdateCallback();
+ if (callback != null) {
+ callback.done();
+ }
+
+ try {
+ context.closeWriter();
+ } catch (IOException e) {
+ CommitFailedException ce = new CommitFailedException("Fulltext", 4,
+ "Failed to close the Fulltext index " +
context.getIndexingContext().getIndexPath(), e);
+ context.getIndexingContext().indexUpdateFailed(ce);
+ throw ce;
+ }
+ if (context.getIndexedNodes() > 0) {
+ log.debug("[{}] => Indexed {} nodes, done.", getIndexName(),
context.getIndexedNodes());
+ }
+ }
+ }
+
+ @Override
+ public void propertyAdded(PropertyState after) {
+ markPropertyChanged(after.getName());
+ checkAggregates(after.getName());
+ propertyUpdated(null, after);
+ }
+
+ @Override
+ public void propertyChanged(PropertyState before, PropertyState after) {
+ markPropertyChanged(before.getName());
+ propertiesModified.add(before);
+ checkAggregates(before.getName());
+ propertyUpdated(before, after);
+ }
+
+ @Override
+ public void propertyDeleted(PropertyState before) {
+ markPropertyChanged(before.getName());
+ propertiesModified.add(before);
+ checkAggregates(before.getName());
+ propertyUpdated(before, null);
+ }
+
+ @Override
+ 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 null;
+ }
+
+ @Override
+ public Editor childNodeChanged(
+ 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 null;
+ }
+
+ @Override
+ public Editor childNodeDeleted(String name, NodeState before)
+ throws CommitFailedException {
+ PathFilter.Result filterResult = getPathFilterResult(name);
+ if (filterResult == PathFilter.Result.EXCLUDE) {
+ return null;
+ }
+
+ if (!isDeleted) {
+ // tree deletion is handled on the parent node
+ String path = concat(getPath(), name);
+ try {
+ FulltextIndexWriter writer = context.getWriter();
+ // Remove all index entries in the removed subtree
+ writer.deleteDocuments(path);
+ this.context.indexUpdate();
+ } catch (IOException e) {
+ CommitFailedException ce = new CommitFailedException("Fulltext", 5,
"Failed to remove the index entries of"
+ + " the removed subtree " + path + "for index " +
context.getIndexingContext().getIndexPath(), e);
+ context.getIndexingContext().indexUpdateFailed(ce);
+ throw ce;
+ }
+ }
+
+ MatcherState ms = getMatcherState(name, before);
+ if (!ms.isEmpty()){
+ return new FulltextIndexEditor(this, name, ms, filterResult, true);
+ }
+ return null; // no need to recurse down the removed subtree
+ }
+
+ FulltextIndexEditorContext<D> getContext() {
+ return context;
+ }
+
+ private boolean addOrUpdate(String path, NodeState state, boolean isUpdate)
+ throws CommitFailedException {
+ try {
+ D d = makeDocument(path, state, isUpdate);
+ if (d != null) {
+ if (log.isTraceEnabled()) {
+ log.trace("[{}] Indexed document for {} is {}", getIndexName(),
path, d);
+ }
+ context.indexUpdate();
+ context.getWriter().updateDocument(path, d);
+ return true;
+ }
+ } catch (IOException e) {
+ CommitFailedException ce = new CommitFailedException("Fulltext", 3,
+ "Failed to index the node " + path, e);
+ context.getIndexingContext().indexUpdateFailed(ce);
+ throw ce;
+ } catch (IllegalArgumentException ie) {
+ log.warn("Failed to index the node [{}]", path, ie);
+ }
+ return false;
+ }
+
+ private D makeDocument(String path, NodeState state, boolean isUpdate)
throws IOException {
+ if (!isIndexable()) {
+ return null;
+ }
+ return context.newDocumentMaker(indexingRule, path).makeDocument(state,
isUpdate, propertiesModified);
+ }
+
+
+ //~-------------------------------------------------------< Aggregate >
+
+ @Override
+ public void markDirty() {
+ propertiesChanged = true;
+ }
+
+ private MatcherState getMatcherState(String name, NodeState after) {
+ List<Aggregate.Matcher> matched = Lists.newArrayList();
+ List<Aggregate.Matcher> inherited = Lists.newArrayList();
+ for (Aggregate.Matcher m : Iterables.concat(matcherState.inherited,
currentMatchers)) {
+ Aggregate.Matcher result = m.match(name, after);
+ if (result.getStatus() == Aggregate.Matcher.Status.MATCH_FOUND){
+ matched.add(result);
+ }
+
+ if (result.getStatus() != Aggregate.Matcher.Status.FAIL){
+ inherited.addAll(result.nextSet());
+ }
+ }
+
+ if (!matched.isEmpty() || !inherited.isEmpty()) {
+ return new MatcherState(matched, inherited);
+ }
+ return MatcherState.NONE;
+ }
+
+
+ /**
+ * Determines which all matchers are affected by this property change
+ *
+ * @param name modified property name
+ */
+ private void checkAggregates(String name) {
+ for (Aggregate.Matcher m : matcherState.matched) {
+ if (!matcherState.affectedMatchers.contains(m)
+ && m.aggregatesProperty(name)) {
+ matcherState.affectedMatchers.add(m);
+ }
+ }
+ }
+
+ static class MatcherState {
+ final static MatcherState NONE = new
MatcherState(Collections.<Aggregate.Matcher>emptyList(),
+ Collections.<Aggregate.Matcher>emptyList());
+
+ final List<Aggregate.Matcher> matched;
+ final List<Aggregate.Matcher> inherited;
+ final Set<Aggregate.Matcher> affectedMatchers;
+
+ public MatcherState(List<Aggregate.Matcher> matched,
+ List<Aggregate.Matcher> inherited){
+ this.matched = matched;
+ this.inherited = inherited;
+
+ //Affected matches would only be used when there are
+ //some matched matchers
+ if (matched.isEmpty()){
+ affectedMatchers = Collections.emptySet();
+ } else {
+ affectedMatchers = Sets.newIdentityHashSet();
+ }
+ }
+
+ public boolean isEmpty() {
+ return matched.isEmpty() && inherited.isEmpty();
+ }
+ }
+
+ private void markPropertyChanged(String name) {
+ if (isIndexable()
+ && !propertiesChanged
+ && indexingRule.isIndexed(name)) {
+ propertiesChanged = true;
+ }
+ }
+
+ private void propertyUpdated(PropertyState before, PropertyState after) {
+ PropertyUpdateCallback callback = context.getPropertyUpdateCallback();
+
+ //Avoid further work if no callback is present
+ if (callback == null) {
+ return;
+ }
+
+ String propertyName = before != null ? before.getName() : after.getName();
+
+ if (isIndexable()) {
+ PropertyDefinition pd = indexingRule.getConfig(propertyName);
+ if (pd != null) {
+ callback.propertyUpdated(getPath(), propertyName, pd, before, after);
+ }
+ }
+
+ for (Aggregate.Matcher m : matcherState.matched) {
+ if (m.aggregatesProperty(propertyName)) {
+ Aggregate.Include i = m.getCurrentInclude();
+ if (i instanceof Aggregate.PropertyInclude) {
+ PropertyDefinition pd = ((Aggregate.PropertyInclude)
i).getPropertyDefinition();
+ String propertyRelativePath = PathUtils.concat(m.getMatchedPath(),
propertyName);
+
+ callback.propertyUpdated(m.getRootPath(), propertyRelativePath, pd,
before, after);
+ }
+ }
+ }
+ }
+
+ private IndexDefinition getDefinition() {
+ return context.getDefinition();
+ }
+
+ private boolean isIndexable(){
+ return indexingRule != null;
+ }
+
+ private PathFilter.Result getPathFilterResult(String childNodeName) {
+ return context.getDefinition().getPathFilter().filter(concat(getPath(),
childNodeName));
+ }
+
+ private String getIndexName() {
+ return context.getDefinition().getIndexName();
+ }
+}
Propchange:
jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/editor/FulltextIndexEditor.java
------------------------------------------------------------------------------
svn:eol-style = native
Added:
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=1831979&view=auto
==============================================================================
---
jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/editor/FulltextIndexEditorContext.java
(added)
+++
jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/editor/FulltextIndexEditorContext.java
Mon May 21 17:06:10 2018
@@ -0,0 +1,264 @@
+/*
+ * 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.Calendar;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+
+import org.apache.jackrabbit.oak.api.CommitFailedException;
+import org.apache.jackrabbit.oak.api.Type;
+import org.apache.jackrabbit.oak.commons.PerfLogger;
+import org.apache.jackrabbit.oak.plugins.index.IndexConstants;
+import org.apache.jackrabbit.oak.plugins.index.IndexUpdateCallback;
+import org.apache.jackrabbit.oak.plugins.index.IndexingContext;
+import org.apache.jackrabbit.oak.plugins.index.search.ExtractedTextCache;
+import org.apache.jackrabbit.oak.plugins.index.search.IndexDefinition;
+import org.apache.jackrabbit.oak.plugins.index.search.NodeStateCloner;
+import org.apache.jackrabbit.oak.plugins.index.search.PropertyUpdateCallback;
+import org.apache.jackrabbit.oak.plugins.index.search.ReindexOperations;
+import org.apache.jackrabbit.oak.spi.commit.CommitInfo;
+import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+import org.apache.jackrabbit.oak.spi.state.NodeStateUtils;
+import org.apache.jackrabbit.oak.stats.Clock;
+import org.apache.jackrabbit.util.ISO8601;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static
org.apache.jackrabbit.oak.plugins.index.search.FulltextIndexConstants.PROP_REFRESH_DEFN;
+import static
org.apache.jackrabbit.oak.plugins.index.search.IndexDefinition.INDEX_DEFINITION_NODE;
+
+/**
+ *
+ */
+public abstract class FulltextIndexEditorContext<D> {
+ private static final Logger log = LoggerFactory
+ .getLogger(FulltextIndexEditorContext.class);
+
+ private static final PerfLogger PERF_LOGGER =
+ new
PerfLogger(LoggerFactory.getLogger(FulltextIndexEditorContext.class.getName() +
".perf"));
+
+ private IndexDefinition definition;
+
+ private final NodeBuilder definitionBuilder;
+
+ private final FulltextIndexWriterFactory indexWriterFactory;
+
+ private FulltextIndexWriter writer = null;
+
+ private long indexedNodes;
+
+ private final IndexUpdateCallback updateCallback;
+
+ private boolean reindex;
+
+ private final ExtractedTextCache extractedTextCache;
+
+ private final NodeState root;
+
+ private final IndexingContext indexingContext;
+
+ private final boolean asyncIndexing;
+
+ //Intentionally static, so that it can be set without passing around clock
objects
+ //Set for testing ONLY
+ private static Clock clock = Clock.SIMPLE;
+
+ private final boolean indexDefnRewritten;
+
+ private FulltextBinaryTextExtractor textExtractor;
+
+ private PropertyUpdateCallback propertyUpdateCallback;
+
+ FulltextIndexEditorContext(NodeState root, NodeBuilder definition,
+ @Nullable IndexDefinition indexDefinition,
+ IndexUpdateCallback updateCallback,
+ FulltextIndexWriterFactory indexWriterFactory,
+ ExtractedTextCache extractedTextCache,
+ IndexingContext indexingContext, boolean
asyncIndexing) {
+ this.root = root;
+ this.indexingContext = checkNotNull(indexingContext);
+ this.definitionBuilder = definition;
+ this.indexWriterFactory = indexWriterFactory;
+ this.definition = indexDefinition != null ? indexDefinition :
+ createIndexDefinition(root, definition, indexingContext,
asyncIndexing);
+ this.indexedNodes = 0;
+ this.updateCallback = updateCallback;
+ this.extractedTextCache = extractedTextCache;
+ this.asyncIndexing = asyncIndexing;
+ if (this.definition.isOfOldFormat()){
+ indexDefnRewritten = true;
+ IndexDefinition.updateDefinition(definition,
indexingContext.getIndexPath());
+ } else {
+ indexDefnRewritten = false;
+ }
+ }
+
+
+ abstract DocumentMaker<D> newDocumentMaker(IndexDefinition.IndexingRule
rule, String path);
+
+ abstract FulltextBinaryTextExtractor
createBinaryTextExtractor(ExtractedTextCache extractedTextCache,
IndexDefinition definition, boolean reindex);
+
+ FulltextIndexWriter getWriter() throws IOException {
+ if (writer == null) {
+ //Lazy initialization so as to ensure that definition is based
+ //on latest NodeBuilder state specially in case of reindexing
+ writer = indexWriterFactory.newInstance(definition, definitionBuilder,
reindex);
+ }
+ return writer;
+ }
+
+ public IndexingContext getIndexingContext() {
+ return indexingContext;
+ }
+
+ @CheckForNull
+ public PropertyUpdateCallback getPropertyUpdateCallback() {
+ return propertyUpdateCallback;
+ }
+
+ void setPropertyUpdateCallback(PropertyUpdateCallback
propertyUpdateCallback) {
+ this.propertyUpdateCallback = propertyUpdateCallback;
+ }
+
+ /**
+ * close writer if it's not null
+ */
+ void closeWriter() throws IOException {
+ Calendar currentTime = getCalendar();
+ final long start = PERF_LOGGER.start();
+ boolean indexUpdated = getWriter().close(currentTime.getTimeInMillis());
+
+ if (indexUpdated) {
+ PERF_LOGGER.end(start, -1, "Closed writer for directory {}", definition);
+ //OAK-2029 Record the last updated status so
+ //as to make IndexTracker detect changes when index
+ //is stored in file system
+ NodeBuilder status =
definitionBuilder.child(IndexDefinition.STATUS_NODE);
+ status.setProperty(IndexDefinition.STATUS_LAST_UPDATED,
getUpdatedTime(currentTime), Type.DATE);
+ status.setProperty("indexedNodes", indexedNodes);
+
+ PERF_LOGGER.end(start, -1, "Overall Closed IndexWriter for directory
{}", definition);
+
+ if (textExtractor != null){
+ textExtractor.done(reindex);
+ }
+ }
+ }
+
+ private String getUpdatedTime(Calendar currentTime) {
+ CommitInfo info = getIndexingContext().getCommitInfo();
+ String checkpointTime = (String)
info.getInfo().get(IndexConstants.CHECKPOINT_CREATION_TIME);
+ if (checkpointTime != null) {
+ return checkpointTime;
+ }
+ return ISO8601.format(currentTime);
+ }
+
+ /** Only set for testing */
+ static void setClock(Clock c) {
+ checkNotNull(c);
+ clock = c;
+ }
+
+ static private Calendar getCalendar() {
+ Calendar ret = Calendar.getInstance();
+ ret.setTime(clock.getDate());
+ return ret;
+ }
+
+ public void enableReindexMode(){
+ reindex = true;
+ ReindexOperations reindexOps = new ReindexOperations(root,
definitionBuilder, definition.getIndexPath());
+ definition = reindexOps.apply(indexDefnRewritten);
+ }
+
+ public long incIndexedNodes() {
+ indexedNodes++;
+ return indexedNodes;
+ }
+
+ boolean isAsyncIndexing() {
+ return asyncIndexing;
+ }
+
+ public long getIndexedNodes() {
+ return indexedNodes;
+ }
+
+ void indexUpdate() throws CommitFailedException {
+ updateCallback.indexUpdate();
+ }
+
+ public IndexDefinition getDefinition() {
+ return definition;
+ }
+
+ private FulltextBinaryTextExtractor getTextExtractor(){
+ if (textExtractor == null && isAsyncIndexing()){
+ //Create lazily to ensure that if its reindex case then update
definition is picked
+ textExtractor = createBinaryTextExtractor(extractedTextCache,
definition, reindex);
+ }
+ return textExtractor;
+ }
+
+ public boolean isReindex() {
+ return reindex;
+ }
+
+ public static String configureUniqueId(NodeBuilder definition) {
+ NodeBuilder status = definition.child(IndexDefinition.STATUS_NODE);
+ String uid = status.getString(IndexDefinition.PROP_UID);
+ if (uid == null) {
+ try {
+ uid = String.valueOf(Clock.SIMPLE.getTimeIncreasing());
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ uid = String.valueOf(Clock.SIMPLE.getTime());
+ }
+ status.setProperty(IndexDefinition.PROP_UID, uid);
+ }
+ return uid;
+ }
+
+ private static IndexDefinition createIndexDefinition(NodeState root,
NodeBuilder definition, IndexingContext
+ indexingContext, boolean asyncIndexing) {
+ NodeState defnState = definition.getBaseState();
+ if (asyncIndexing && !IndexDefinition.isDisableStoredIndexDefinition()){
+ if (definition.getBoolean(PROP_REFRESH_DEFN)){
+ definition.removeProperty(PROP_REFRESH_DEFN);
+ NodeState clonedState = NodeStateCloner.cloneVisibleState(defnState);
+ definition.setChildNode(INDEX_DEFINITION_NODE, clonedState);
+ log.info("Refreshed the index definition for [{}]",
indexingContext.getIndexPath());
+ if (log.isDebugEnabled()){
+ log.debug("Updated index definition is {}",
NodeStateUtils.toString(clonedState));
+ }
+ } else if (!definition.hasChildNode(INDEX_DEFINITION_NODE)){
+ definition.setChildNode(INDEX_DEFINITION_NODE,
NodeStateCloner.cloneVisibleState(defnState));
+ log.info("Stored the cloned index definition for [{}]. Changes in
index definition would now only be " +
+ "effective post reindexing", indexingContext.getIndexPath());
+ }
+ }
+ return new IndexDefinition(root, defnState,indexingContext.getIndexPath());
+ }
+}
Propchange:
jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/editor/FulltextIndexEditorContext.java
------------------------------------------------------------------------------
svn:eol-style = native
Added:
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=1831979&view=auto
==============================================================================
---
jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/editor/FulltextIndexWriter.java
(added)
+++
jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/editor/FulltextIndexWriter.java
Mon May 21 17:06:10 2018
@@ -0,0 +1,48 @@
+/*
+ * 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;
+
+public interface FulltextIndexWriter<D> {
+
+ /**
+ * Updates the document having given path
+ *
+ * @param path path of the NodeState which the Document represents
+ * @param doc updated document
+ */
+ void updateDocument(String path, D doc) throws IOException;
+
+ /**
+ * Deletes documents which are same or child of given path
+ *
+ * @param path path whose children need to be deleted
+ */
+ void deleteDocuments(String path) throws IOException;
+
+ /**
+ * Closes the underlying resources.
+ *
+ * @param timestamp timestamp to be used for recording at status in
NodeBuilder
+ * @return true if index was updated or any write happened.
+ */
+ boolean close(long timestamp) throws IOException;
+}
Propchange:
jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/editor/FulltextIndexWriter.java
------------------------------------------------------------------------------
svn:eol-style = native
Added:
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=1831979&view=auto
==============================================================================
---
jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/editor/FulltextIndexWriterFactory.java
(added)
+++
jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/editor/FulltextIndexWriterFactory.java
Mon May 21 17:06:10 2018
@@ -0,0 +1,32 @@
+/*
+ * 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 org.apache.jackrabbit.oak.plugins.index.search.IndexDefinition;
+import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
+
+/**
+ * Factory class for {@link FulltextIndexWriter}s
+ */
+public interface FulltextIndexWriterFactory {
+
+ FulltextIndexWriter newInstance(IndexDefinition definition, NodeBuilder
definitionBuilder, boolean reindex);
+
+}
Propchange:
jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/editor/FulltextIndexWriterFactory.java
------------------------------------------------------------------------------
svn:eol-style = native
Added:
jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/editor/TextExtractionStats.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/editor/TextExtractionStats.java?rev=1831979&view=auto
==============================================================================
---
jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/editor/TextExtractionStats.java
(added)
+++
jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/editor/TextExtractionStats.java
Mon May 21 17:06:10 2018
@@ -0,0 +1,84 @@
+/*
+ * 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.util.concurrent.TimeUnit;
+
+import org.apache.jackrabbit.oak.plugins.index.search.ExtractedTextCache;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static org.apache.jackrabbit.oak.commons.IOUtils.humanReadableByteCount;
+
+class TextExtractionStats {
+ private static final Logger log =
LoggerFactory.getLogger(TextExtractionStats.class);
+ /**
+ * Log stats only if time spent is more than 1 min
+ */
+ private static final long LOGGING_THRESHOLD = TimeUnit.MINUTES.toMillis(1);
+ private int count;
+ private long totalBytesRead;
+ private long totalTime;
+ private long totalTextLength;
+
+ public void addStats(long timeInMillis, long bytesRead, int textLength) {
+ count++;
+ totalBytesRead += bytesRead;
+ totalTime += timeInMillis;
+ totalTextLength += textLength;
+ }
+
+ public void log(boolean reindex) {
+ if (log.isDebugEnabled()) {
+ log.debug("Text extraction stats {}", this);
+ } else if (anyParsingDone() && (reindex || isTakingLotsOfTime())) {
+ log.info("Text extraction stats {}", this);
+ }
+ }
+
+ public void collectStats(ExtractedTextCache cache){
+ cache.addStats(count, totalTime, totalBytesRead, totalTextLength);
+ }
+
+ private boolean isTakingLotsOfTime() {
+ return totalTime > LOGGING_THRESHOLD;
+ }
+
+ private boolean anyParsingDone() {
+ return count > 0;
+ }
+
+ @Override
+ public String toString() {
+ return String.format(" %d (Time Taken %s, Bytes Read %s, Extracted
text size %s)",
+ count,
+ timeInWords(totalTime),
+ humanReadableByteCount(totalBytesRead),
+ humanReadableByteCount(totalTextLength));
+ }
+
+ private static String timeInWords(long millis) {
+ return String.format("%d min, %d sec",
+ TimeUnit.MILLISECONDS.toMinutes(millis),
+ TimeUnit.MILLISECONDS.toSeconds(millis) -
+
TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(millis))
+ );
+ }
+}
Propchange:
jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/editor/TextExtractionStats.java
------------------------------------------------------------------------------
svn:eol-style = native
Added:
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=1831979&view=auto
==============================================================================
---
jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/editor/TikaParserConfig.java
(added)
+++
jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/editor/TikaParserConfig.java
Mon May 21 17:06:10 2018
@@ -0,0 +1,96 @@
+/*
+ * 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.util.HashSet;
+import java.util.Set;
+
+import javax.xml.parsers.DocumentBuilder;
+
+import com.google.common.base.Strings;
+import org.apache.tika.exception.TikaException;
+import org.apache.tika.mime.MediaType;
+import org.apache.tika.parser.ParseContext;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.SAXException;
+
+public class TikaParserConfig {
+ private static final String EMPTY_PARSER =
"org.apache.tika.parser.EmptyParser";
+
+ /**
+ * Determines the set of MediaType which have been configured with an
EmptyParser.
+ *
+ * @param configStream stream for tika config
+ * @return set of MediaTypes which are not indexed
+ */
+ public static Set<MediaType> getNonIndexedMediaTypes(InputStream
configStream) throws
+ TikaException, IOException, SAXException {
+ Set<MediaType> result = new HashSet<>();
+ Element element =
getBuilder().parse(configStream).getDocumentElement();
+ NodeList nodes = element.getElementsByTagName("parsers");
+ if (nodes.getLength() == 1) {
+ Node parentNode = nodes.item(0);
+ NodeList parsersNodes = parentNode.getChildNodes();
+ for (int i = 0; i < parsersNodes.getLength(); i++) {
+ Node node = parsersNodes.item(i);
+ if (node instanceof Element) {
+ String className = ((Element) node).getAttribute("class");
+ if (EMPTY_PARSER.equals(className)) {
+ NodeList mimes = ((Element)
node).getElementsByTagName("mime");
+ parseMimeTypes(result, mimes);
+ }
+ }
+ }
+ }
+ return result;
+ }
+
+
+ private static void parseMimeTypes(Set<MediaType> result, NodeList mimes) {
+ /*
+ <parser class="org.apache.tika.parser.EmptyParser">
+ <mime>application/x-archive</mime>
+ <mime>application/x-bzip</mime>
+ <mime>application/x-bzip2</mime>
+ </parser>
+ */
+ for (int j = 0; j < mimes.getLength(); j++) {
+ Node mime = mimes.item(j);
+ if (mime instanceof Element) {
+ String mimeValue = mime.getTextContent();
+ mimeValue = Strings.emptyToNull(mimeValue);
+ if (mimeValue != null) {
+ MediaType mediaType = MediaType.parse(mimeValue.trim());
+ if (mediaType != null) {
+ result.add(mediaType);
+ }
+ }
+ }
+ }
+ }
+
+ private static DocumentBuilder getBuilder() throws TikaException {
+ return new ParseContext().getDocumentBuilder();
+ }
+}
Propchange:
jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/editor/TikaParserConfig.java
------------------------------------------------------------------------------
svn:eol-style = native
Modified:
jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/util/IndexHelper.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/util/IndexHelper.java?rev=1831979&r1=1831978&r2=1831979&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/util/IndexHelper.java
(original)
+++
jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/util/IndexHelper.java
Mon May 21 17:06:10 2018
@@ -151,7 +151,7 @@ public class IndexHelper {
@Nonnull NodeBuilder index, @Nonnull String name, String type,
@Nonnull Set<String> includes,
@Nonnull String async) {
- checkArgument(!includes.isEmpty(), "Lucene property index " +
+ checkArgument(!includes.isEmpty(), "Fulltext property index " +
"requires explicit list of property names to be indexed");
index = index.child(name);
Added:
jackrabbit/oak/trunk/oak-search/src/main/resources/org/apache/jackrabbit/oak/plugins/index/search/tika-config.xml
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-search/src/main/resources/org/apache/jackrabbit/oak/plugins/index/search/tika-config.xml?rev=1831979&view=auto
==============================================================================
---
jackrabbit/oak/trunk/oak-search/src/main/resources/org/apache/jackrabbit/oak/plugins/index/search/tika-config.xml
(added)
+++
jackrabbit/oak/trunk/oak-search/src/main/resources/org/apache/jackrabbit/oak/plugins/index/search/tika-config.xml
Mon May 21 17:06:10 2018
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+ ~ 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.
+ -->
+
+<properties>
+ <detectors>
+ <detector class="org.apache.tika.detect.TypeDetector"/>
+ </detectors>
+ <parsers>
+ <parser class="org.apache.tika.parser.DefaultParser"/>
+ <parser class="org.apache.tika.parser.EmptyParser">
+ <!-- Disable package extraction as it's too resource-intensive -->
+ <mime>application/x-archive</mime>
+ <mime>application/x-bzip</mime>
+ <mime>application/x-bzip2</mime>
+ <mime>application/x-cpio</mime>
+ <mime>application/x-gtar</mime>
+ <mime>application/x-gzip</mime>
+ <mime>application/x-tar</mime>
+ <mime>application/zip</mime>
+ <!-- Disable image extraction as there's no text to be found -->
+ <mime>image/bmp</mime>
+ <mime>image/gif</mime>
+ <mime>image/jpeg</mime>
+ <mime>image/png</mime>
+ <mime>image/tiff</mime>
+ <mime>image/vnd.wap.wbmp</mime>
+ <mime>image/x-icon</mime>
+ <mime>image/x-psd</mime>
+ <mime>image/x-xcf</mime>
+ </parser>
+ </parsers>
+ <service-loader initializableProblemHandler="ignore" dynamic="true"/>
+</properties>
Propchange:
jackrabbit/oak/trunk/oak-search/src/main/resources/org/apache/jackrabbit/oak/plugins/index/search/tika-config.xml
------------------------------------------------------------------------------
svn:eol-style = native
Modified:
jackrabbit/oak/trunk/oak-search/src/test/java/org/apache/jackrabbit/oak/plugins/index/search/TestUtil.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-search/src/test/java/org/apache/jackrabbit/oak/plugins/index/search/TestUtil.java?rev=1831979&r1=1831978&r2=1831979&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-search/src/test/java/org/apache/jackrabbit/oak/plugins/index/search/TestUtil.java
(original)
+++
jackrabbit/oak/trunk/oak-search/src/test/java/org/apache/jackrabbit/oak/plugins/index/search/TestUtil.java
Mon May 21 17:06:10 2018
@@ -248,7 +248,6 @@ public class TestUtil {
private static PropertyState createAsyncProperty(IndexingMode
indexingMode) {
switch(indexingMode) {
- case NRT :
case SYNC :
return createAsyncProperty(indexingMode.asyncValueName());
case ASYNC: