You are viewing a plain text version of this content. The canonical link for it is here.
Posted to oak-commits@jackrabbit.apache.org by to...@apache.org on 2018/04/12 11:46:18 UTC
svn commit: r1828972 [2/5] - in /jackrabbit/oak/trunk/oak-search: ./
src/main/java/org/apache/jackrabbit/oak/plugins/index/search/
src/main/java/org/apache/jackrabbit/oak/plugins/index/search/util/
src/test/java/org/apache/jackrabbit/oak/plugins/index/...
Added: 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=1828972&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/IndexDefinition.java (added)
+++ jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/IndexDefinition.java Thu Apr 12 11:46:17 2018
@@ -0,0 +1,1716 @@
+/*
+ * 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.InputStream;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Pattern;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import javax.jcr.PropertyType;
+import javax.jcr.RepositoryException;
+import javax.jcr.nodetype.NodeTypeIterator;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Sets;
+import com.google.common.primitives.Ints;
+import org.apache.jackrabbit.JcrConstants;
+import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.api.Tree;
+import org.apache.jackrabbit.oak.api.Type;
+import org.apache.jackrabbit.oak.commons.PathUtils;
+import org.apache.jackrabbit.oak.namepath.NamePathMapper;
+import org.apache.jackrabbit.oak.plugins.index.IndexConstants;
+import org.apache.jackrabbit.oak.plugins.index.search.util.ConfigUtil;
+import org.apache.jackrabbit.oak.plugins.index.search.util.FunctionIndexProcessor;
+import org.apache.jackrabbit.oak.plugins.memory.PropertyStates;
+import org.apache.jackrabbit.oak.plugins.nodetype.ReadOnlyNodeTypeManager;
+import org.apache.jackrabbit.oak.plugins.tree.TreeUtil;
+import org.apache.jackrabbit.oak.plugins.tree.factories.TreeFactory;
+import org.apache.jackrabbit.oak.spi.filter.PathFilter;
+import org.apache.jackrabbit.oak.spi.query.QueryIndex.OrderEntry;
+import org.apache.jackrabbit.oak.spi.state.ChildNodeEntry;
+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.spi.state.ReadOnlyBuilder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.collect.Lists.newArrayList;
+import static com.google.common.collect.Lists.newArrayListWithCapacity;
+import static com.google.common.collect.Maps.newHashMap;
+import static com.google.common.collect.Sets.newHashSet;
+import static org.apache.jackrabbit.JcrConstants.JCR_SCORE;
+import static org.apache.jackrabbit.JcrConstants.JCR_SYSTEM;
+import static org.apache.jackrabbit.JcrConstants.NT_BASE;
+import static org.apache.jackrabbit.oak.api.Type.NAMES;
+import static org.apache.jackrabbit.oak.commons.PathUtils.getParentPath;
+import static org.apache.jackrabbit.oak.plugins.index.search.FulltextIndexConstants.COMPAT_MODE;
+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;
+import static org.apache.jackrabbit.oak.plugins.index.search.FulltextIndexConstants.EVALUATE_PATH_RESTRICTION;
+import static org.apache.jackrabbit.oak.plugins.index.search.FulltextIndexConstants.EXCLUDE_PROPERTY_NAMES;
+import static org.apache.jackrabbit.oak.plugins.index.search.FulltextIndexConstants.EXPERIMENTAL_STORAGE;
+import static org.apache.jackrabbit.oak.plugins.index.search.FulltextIndexConstants.FACETS;
+import static org.apache.jackrabbit.oak.plugins.index.search.FulltextIndexConstants.FIELD_BOOST;
+import static org.apache.jackrabbit.oak.plugins.index.search.FulltextIndexConstants.FULL_TEXT_ENABLED;
+import static org.apache.jackrabbit.oak.plugins.index.search.FulltextIndexConstants.INCLUDE_PROPERTY_NAMES;
+import static org.apache.jackrabbit.oak.plugins.index.search.FulltextIndexConstants.INCLUDE_PROPERTY_TYPES;
+import static org.apache.jackrabbit.oak.plugins.index.search.FulltextIndexConstants.ORDERED_PROP_NAMES;
+import static org.apache.jackrabbit.oak.plugins.index.search.FulltextIndexConstants.PROP_FACETS_TOP_CHILDREN;
+import static org.apache.jackrabbit.oak.plugins.index.search.FulltextIndexConstants.PROP_NODE;
+import static org.apache.jackrabbit.oak.plugins.index.search.FulltextIndexConstants.TIKA;
+import static org.apache.jackrabbit.oak.plugins.index.search.FulltextIndexConstants.TIKA_CONFIG;
+import static org.apache.jackrabbit.oak.plugins.index.search.FulltextIndexConstants.TIKA_MAPPED_TYPE;
+import static org.apache.jackrabbit.oak.plugins.index.search.FulltextIndexConstants.TIKA_MIME_TYPES;
+import static org.apache.jackrabbit.oak.plugins.index.search.PropertyDefinition.DEFAULT_BOOST;
+import static org.apache.jackrabbit.oak.plugins.index.search.util.ConfigUtil.getOptionalValue;
+import static org.apache.jackrabbit.oak.plugins.memory.EmptyNodeState.EMPTY_NODE;
+import static org.apache.jackrabbit.oak.spi.nodetype.NodeTypeConstants.JCR_NODE_TYPES;
+import static org.apache.jackrabbit.oak.spi.nodetype.NodeTypeConstants.NODE_TYPES_PATH;
+
+public final class IndexDefinition implements Aggregate.AggregateMapper {
+ /**
+ * Name of the internal property that contains the child order defined in
+ * org.apache.jackrabbit.oak.plugins.tree.TreeConstants
+ */
+ private static final String OAK_CHILD_ORDER = ":childOrder";
+
+ private static final Logger log = LoggerFactory.getLogger(IndexDefinition.class);
+
+ private static boolean disableStoredIndexDefinition;
+
+ /**
+ * Blob size to use by default. To avoid issues in OAK-2105 the size should not
+ * be power of 2.
+ */
+ public static final int DEFAULT_BLOB_SIZE = 1024 * 1024 - 1024;
+
+ /**
+ * Default entry count to keep estimated entry count low.
+ */
+ static final long DEFAULT_ENTRY_COUNT = 1000;
+
+ /**
+ * Default value for property {@link #maxFieldLength}.
+ */
+ public static final int DEFAULT_MAX_FIELD_LENGTH = 10000;
+
+ static final int DEFAULT_MAX_EXTRACT_LENGTH = -10;
+
+ /**
+ * System managed hidden property to record the current index version
+ */
+ static final String INDEX_VERSION = ":version";
+
+ /**
+ * Hidden node under index definition which is used to store the index definition
+ * nodestate as it was at time of reindexing
+ */
+ public static final String INDEX_DEFINITION_NODE = ":index-definition";
+
+ /**
+ * Hidden node under index definition which is used to store meta info
+ */
+ public static final String STATUS_NODE = ":status";
+
+ /**
+ * Property on status node which refers to the date when the index was lastUpdated
+ * 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";
+
+ /**
+ * Meta property which provides the unique id
+ */
+ public static final String PROP_UID = "uid";
+
+ private static String TYPES_ALLOW_ALL_NAME = "all";
+
+ static final int TYPES_ALLOW_NONE = PropertyType.UNDEFINED;
+
+ static final int TYPES_ALLOW_ALL = -1;
+
+ /**
+ * Default suggesterUpdateFrequencyMinutes
+ */
+ static final int DEFAULT_SUGGESTER_UPDATE_FREQUENCY_MINUTES = 10;
+
+ /**
+ * Default no. of facets retrieved
+ */
+ static final int DEFAULT_FACET_COUNT = 10;
+
+ /**
+ * native sort order
+ */
+ static final OrderEntry NATIVE_SORT_ORDER = new OrderEntry(JCR_SCORE, Type.UNDEFINED,
+ OrderEntry.Order.DESCENDING);
+
+ private final boolean fullTextEnabled;
+
+ private final NodeState definition;
+
+ private final NodeState root;
+
+ private final String funcName;
+
+ private final int blobSize;
+
+ /**
+ * Defines the maximum estimated entry count configured.
+ * Defaults to {#DEFAULT_ENTRY_COUNT}
+ */
+ private final long entryCount;
+
+ private final boolean entryCountDefined;
+
+ private final double costPerEntry;
+
+ private final double costPerExecution;
+
+ /**
+ * The {@link IndexingRule}s inside this configuration. Keys being the NodeType names
+ */
+ private final Map<String, List<IndexingRule>> indexRules;
+
+ private final List<IndexingRule> definedRules;
+
+ private final String indexName;
+
+ private final boolean evaluatePathRestrictions;
+
+ private final Map<String, Aggregate> aggregates;
+
+ private final String scorerProviderName;
+
+ private final boolean hasCustomTikaConfig;
+
+ private final Map<String, String> customTikaMimeTypeMappings;
+
+ private final int maxFieldLength;
+
+ private final int maxExtractLength;
+
+ private final int suggesterUpdateFrequencyMinutes;
+
+ private final long reindexCount;
+
+ private final PathFilter pathFilter;
+
+ @Nullable
+ private final String[] queryPaths;
+
+ private final boolean suggestAnalyzed;
+
+ private final int numberOfTopFacets;
+
+ private final boolean suggestEnabled;
+
+ private final boolean spellcheckEnabled;
+
+ private final String indexPath;
+
+ private final boolean syncIndexMode;
+
+ private final boolean nodeTypeIndex;
+
+ @Nullable
+ private final String uid;
+
+ @Nullable
+ private final String[] indexTags;
+
+ private final boolean syncPropertyIndexes;
+
+ //~--------------------------------------------------------< Builder >
+
+ public static Builder newBuilder(NodeState root, NodeState defn, String indexPath){
+ return new Builder(root, defn, indexPath);
+ }
+
+ public static class Builder {
+ /**
+ * Default unique id used when no existing uid is defined
+ * and index is not populated
+ */
+ private static final String DEFAULT_UID = "0";
+ private final NodeState root;
+ private final NodeState defn;
+ private final String indexPath;
+ private String uid;
+ private boolean reindexMode;
+
+ public Builder(NodeState root, NodeState defn, String indexPath) {
+ this.root = checkNotNull(root);
+ this.defn = checkNotNull(defn);
+ this.indexPath = checkNotNull(indexPath);
+ }
+
+ public Builder uid(String uid){
+ this.uid = uid;
+ return this;
+ }
+
+ public Builder reindex(){
+ this.reindexMode = true;
+ return this;
+ }
+
+ public IndexDefinition build(){
+ if (uid == null){
+ uid = determineUniqueId(defn);
+ if (uid == null && !IndexDefinition.hasPersistedIndex(defn)){
+ uid = DEFAULT_UID;
+ }
+ }
+
+ NodeState indexDefnStateToUse = defn;
+ if (!reindexMode){
+ indexDefnStateToUse = getIndexDefinitionState(defn);
+ }
+ return new IndexDefinition(root, indexDefnStateToUse, uid, indexPath);
+ }
+ }
+
+ public IndexDefinition(NodeState root, NodeState defn, String indexPath) {
+ this(root, getIndexDefinitionState(defn), determineUniqueId(defn), indexPath);
+ }
+
+ private IndexDefinition(NodeState root, NodeState defn, String uid, String indexPath) {
+ this.root = root;
+ this.uid = uid;
+ this.definition = defn;
+ this.indexPath = checkNotNull(indexPath);
+ this.indexName = indexPath;
+ this.indexTags = getOptionalStrings(defn, IndexConstants.INDEX_TAGS);
+ this.nodeTypeIndex = getOptionalValue(defn, FulltextIndexConstants.PROP_INDEX_NODE_TYPE, false);
+
+ this.blobSize = getOptionalValue(defn, BLOB_SIZE, DEFAULT_BLOB_SIZE);
+
+ this.aggregates = nodeTypeIndex ? Collections.emptyMap() : collectAggregates(defn);
+
+ NodeState rulesState = defn.getChildNode(FulltextIndexConstants.INDEX_RULES);
+ if (!rulesState.exists()){
+ rulesState = createIndexRules(defn).getNodeState();
+ }
+
+ List<IndexingRule> definedIndexRules = newArrayList();
+ this.indexRules = collectIndexRules(rulesState, definedIndexRules);
+ this.definedRules = ImmutableList.copyOf(definedIndexRules);
+
+ this.fullTextEnabled = hasFulltextEnabledIndexRule(definedIndexRules);
+ this.evaluatePathRestrictions = getOptionalValue(defn, EVALUATE_PATH_RESTRICTION, false);
+
+ String functionName = getOptionalValue(defn, FulltextIndexConstants.FUNC_NAME, null);
+ if (fullTextEnabled && functionName == null){
+ functionName = "lucene";
+ }
+ this.funcName = functionName != null ? "native*" + functionName : null;
+
+ if (defn.hasProperty(ENTRY_COUNT_PROPERTY_NAME)) {
+ this.entryCountDefined = true;
+ this.entryCount = defn.getProperty(ENTRY_COUNT_PROPERTY_NAME).getValue(Type.LONG);
+ } else {
+ this.entryCountDefined = false;
+ this.entryCount = DEFAULT_ENTRY_COUNT;
+ }
+
+ this.maxFieldLength = getOptionalValue(defn, FulltextIndexConstants.MAX_FIELD_LENGTH, DEFAULT_MAX_FIELD_LENGTH);
+ this.costPerEntry = getOptionalValue(defn, FulltextIndexConstants.COST_PER_ENTRY, 1.0);
+ this.costPerExecution = getOptionalValue(defn, FulltextIndexConstants.COST_PER_EXECUTION, 1.0);
+ this.hasCustomTikaConfig = getTikaConfigNode().exists();
+ this.customTikaMimeTypeMappings = buildMimeTypeMap(definition.getChildNode(TIKA).getChildNode(TIKA_MIME_TYPES));
+ this.maxExtractLength = determineMaxExtractLength();
+ this.suggesterUpdateFrequencyMinutes = evaluateSuggesterUpdateFrequencyMinutes(defn,
+ DEFAULT_SUGGESTER_UPDATE_FREQUENCY_MINUTES);
+ this.scorerProviderName = getOptionalValue(defn, FulltextIndexConstants.PROP_SCORER_PROVIDER, null);
+ this.reindexCount = getOptionalValue(defn, REINDEX_COUNT, 0);
+ this.pathFilter = PathFilter.from(new ReadOnlyBuilder(defn));
+ this.queryPaths = getOptionalStrings(defn, IndexConstants.QUERY_PATHS);
+ this.suggestAnalyzed = evaluateSuggestAnalyzed(defn, false);
+
+ if (defn.hasChildNode(FACETS)) {
+ NodeState facetsConfig = defn.getChildNode(FACETS);
+ this.numberOfTopFacets = getOptionalValue(facetsConfig, PROP_FACETS_TOP_CHILDREN, DEFAULT_FACET_COUNT);
+ } else {
+ this.numberOfTopFacets = DEFAULT_FACET_COUNT;
+ }
+
+ this.suggestEnabled = evaluateSuggestionEnabled();
+ this.spellcheckEnabled = evaluateSpellcheckEnabled();
+ this.syncIndexMode = supportsSyncIndexing(defn);
+ this.syncPropertyIndexes = definedRules.stream().anyMatch(ir -> !ir.syncProps.isEmpty());
+ }
+
+ public NodeState getDefinitionNodeState() {
+ return definition;
+ }
+
+ public boolean isFullTextEnabled() {
+ return fullTextEnabled;
+ }
+
+ public String getFunctionName(){
+ return funcName;
+ }
+
+ public boolean hasFunctionDefined(){
+ return funcName != null;
+ }
+
+ /**
+ * Size in bytes for the blobs created while storing the index content
+ * @return size in bytes
+ */
+ public int getBlobSize() {
+ return blobSize;
+ }
+
+ public long getReindexCount(){
+ return reindexCount;
+ }
+
+ public long getEntryCount() {
+ return entryCount;
+ }
+
+ private int evaluateSuggesterUpdateFrequencyMinutes(NodeState defn, int defaultValue) {
+ NodeState suggestionConfig = defn.getChildNode(FulltextIndexConstants.SUGGESTION_CONFIG);
+
+ if (!suggestionConfig.exists()) {
+ //handle backward compatibility
+ return getOptionalValue(defn, FulltextIndexConstants.SUGGEST_UPDATE_FREQUENCY_MINUTES, defaultValue);
+ }
+
+ return getOptionalValue(suggestionConfig, FulltextIndexConstants.SUGGEST_UPDATE_FREQUENCY_MINUTES, defaultValue);
+ }
+
+ public int getSuggesterUpdateFrequencyMinutes() {
+ return suggesterUpdateFrequencyMinutes;
+ }
+
+ public boolean isEntryCountDefined() {
+ return entryCountDefined;
+ }
+
+ public double getCostPerEntry() {
+ return costPerEntry;
+ }
+
+ public double getCostPerExecution() {
+ return costPerExecution;
+ }
+
+ public long getFulltextEntryCount(long numOfDocs){
+ if (isEntryCountDefined()){
+ return Math.min(getEntryCount(), numOfDocs);
+ }
+ return numOfDocs;
+ }
+
+ public boolean isOfOldFormat(){
+ return !hasIndexingRules(definition);
+ }
+
+ public boolean evaluatePathRestrictions() {
+ return evaluatePathRestrictions;
+ }
+
+ public boolean hasCustomTikaConfig(){
+ return hasCustomTikaConfig;
+ }
+
+ public InputStream getTikaConfig(){
+ return ConfigUtil.getBlob(getTikaConfigNode(), TIKA_CONFIG).getNewStream();
+ }
+
+ public String getTikaMappedMimeType(String type) {
+ return customTikaMimeTypeMappings.getOrDefault(type, type);
+ }
+
+ public String getIndexName() {
+ return indexName;
+ }
+
+ public String[] getIndexTags() {
+ return indexTags;
+ }
+
+ public int getMaxExtractLength() {
+ return maxExtractLength;
+ }
+
+ public String getScorerProviderName() {
+ return scorerProviderName;
+ }
+
+ public PathFilter getPathFilter() {
+ return pathFilter;
+ }
+
+ @Nullable
+ public String[] getQueryPaths() {
+ return queryPaths;
+ }
+
+ @CheckForNull
+ public String getUniqueId() {
+ return uid;
+ }
+
+ public boolean isSyncIndexingEnabled() {
+ return syncIndexMode;
+ }
+
+ public boolean hasSyncPropertyDefinitions() {
+ return syncPropertyIndexes;
+ }
+
+ public boolean isPureNodeTypeIndex() {
+ return nodeTypeIndex;
+ }
+
+ /**
+ * Check if the index definition is fresh of some index has happened
+ *
+ * @param definition nodestate for Index Definition
+ * @return true if index has some indexed content
+ */
+ public static boolean hasPersistedIndex(NodeState definition){
+ for (String rm : definition.getChildNodeNames()) {
+ if (NodeStateUtils.isHidden(rm)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static boolean isDisableStoredIndexDefinition() {
+ return disableStoredIndexDefinition;
+ }
+
+ public static void setDisableStoredIndexDefinition(boolean disableStoredIndexDefinitionDefault) {
+ IndexDefinition.disableStoredIndexDefinition = disableStoredIndexDefinitionDefault;
+ }
+
+ public Set<String> getRelativeNodeNames(){
+ //Can be computed lazily as required only for oak-run indexing for now
+ Set<String> names = new HashSet<>();
+ for (IndexingRule r : definedRules) {
+ for (Aggregate.Include i : r.aggregate.getIncludes()) {
+ for (int d = 0; d < i.maxDepth(); d++) {
+ if (!i.isPattern(d)) {
+ names.add(i.getElementNameIfNotAPattern(d));
+ }
+ }
+ }
+ }
+ return names;
+ }
+
+ public boolean indexesRelativeNodes(){
+ for (IndexingRule r : definedRules) {
+ if (!r.aggregate.getIncludes().isEmpty()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return "Fulltext Index : " + indexName;
+ }
+
+
+ //~---------------------------------------------------< Aggregates >
+
+ @CheckForNull
+ public Aggregate getAggregate(String nodeType){
+ Aggregate agg = aggregates.get(nodeType);
+ return agg != null ? agg : null;
+ }
+
+ private Map<String, Aggregate> collectAggregates(NodeState defn) {
+ Map<String, Aggregate> aggregateMap = newHashMap();
+
+ for (ChildNodeEntry cne : defn.getChildNode(FulltextIndexConstants.AGGREGATES).getChildNodeEntries()) {
+ String nodeType = cne.getName();
+ int recursionLimit = getOptionalValue(cne.getNodeState(), FulltextIndexConstants.AGG_RECURSIVE_LIMIT,
+ Aggregate.RECURSIVE_AGGREGATION_LIMIT_DEFAULT);
+
+ List<Aggregate.Include> includes = newArrayList();
+ for (ChildNodeEntry include : cne.getNodeState().getChildNodeEntries()) {
+ NodeState is = include.getNodeState();
+ String primaryType = is.getString(FulltextIndexConstants.AGG_PRIMARY_TYPE);
+ String path = is.getString(FulltextIndexConstants.AGG_PATH);
+ boolean relativeNode = getOptionalValue(is, FulltextIndexConstants.AGG_RELATIVE_NODE, false);
+ if (path == null) {
+ log.warn("Aggregate pattern in {} does not have required property [{}]. {} aggregate rule would " +
+ "be ignored", this, FulltextIndexConstants.AGG_PATH, include.getName());
+ continue;
+ }
+ includes.add(new Aggregate.NodeInclude(this, primaryType, path, relativeNode));
+ }
+ aggregateMap.put(nodeType, new Aggregate(nodeType, includes, recursionLimit));
+ }
+
+ return ImmutableMap.copyOf(aggregateMap);
+ }
+
+ //~---------------------------------------------------< IndexRule >
+
+ public boolean hasMatchingNodeTypeReg(NodeState root){
+ return this.root.getChildNode(JCR_SYSTEM).getChildNode(JCR_NODE_TYPES)
+ .equals(root.getChildNode(JCR_SYSTEM).getChildNode(JCR_NODE_TYPES));
+ }
+
+
+ public List<IndexingRule> getDefinedRules() {
+ return definedRules;
+ }
+
+ @CheckForNull
+ public IndexingRule getApplicableIndexingRule(String primaryNodeType) {
+ //This method would be invoked for every node. So be as
+ //conservative as possible in object creation
+ List<IndexingRule> rules = null;
+ List<IndexingRule> r = indexRules.get(primaryNodeType);
+ if (r != null) {
+ rules = new ArrayList<IndexingRule>();
+ rules.addAll(r);
+ }
+
+ if (rules != null) {
+ for (IndexingRule rule : rules) {
+ if (rule.appliesTo(primaryNodeType)) {
+ return rule;
+ }
+ }
+ }
+
+ // no applicable rule
+ return null;
+ }
+
+ /**
+ * Returns the first indexing rule that applies to the given node
+ * <code>state</code>.
+ *
+ * @param state a node state.
+ * @return the indexing rule or <code>null</code> if none applies.
+ */
+ @CheckForNull
+ public IndexingRule getApplicableIndexingRule(NodeState state) {
+ //This method would be invoked for every node. So be as
+ //conservative as possible in object creation
+ List<IndexingRule> rules = null;
+ List<IndexingRule> r = indexRules.get(getPrimaryTypeName(state));
+ if (r != null) {
+ rules = new ArrayList<IndexingRule>();
+ rules.addAll(r);
+ }
+
+ for (String name : getMixinTypeNames(state)) {
+ r = indexRules.get(name);
+ if (r != null) {
+ if (rules == null) {
+ rules = new ArrayList<IndexingRule>();
+ }
+ rules.addAll(r);
+ }
+ }
+
+ if (rules != null) {
+ for (IndexingRule rule : rules) {
+ if (rule.appliesTo(state)) {
+ return rule;
+ }
+ }
+ }
+
+ // no applicable rule
+ return null;
+ }
+
+ private Map<String, List<IndexingRule>> collectIndexRules(NodeState indexRules,
+ List<IndexingRule> definedIndexRules){
+ //TODO if a rule is defined for nt:base then this map would have entry for each
+ //registered nodeType in the system
+
+ if (!indexRules.exists()) {
+ return Collections.emptyMap();
+ }
+
+ if (!hasOrderableChildren(indexRules)){
+ log.warn("IndexRule node does not have orderable children in [{}]", IndexDefinition.this);
+ }
+
+ Map<String, List<IndexingRule>> nt2rules = newHashMap();
+ ReadOnlyNodeTypeManager ntReg = createNodeTypeManager(TreeFactory.createReadOnlyTree(root));
+
+ //Use Tree API to read ordered child nodes
+ Tree ruleTree = TreeFactory.createReadOnlyTree(indexRules);
+ final List<String> allNames = getAllNodeTypes(ntReg);
+ for (Tree ruleEntry : ruleTree.getChildren()) {
+ IndexingRule rule = new IndexingRule(ruleEntry.getName(), indexRules.getChildNode(ruleEntry.getName()));
+ definedIndexRules.add(rule);
+
+ // register under node type and all its sub types
+ log.trace("Found rule '{}' for NodeType '{}'", rule, rule.getNodeTypeName());
+
+ List<String> ntNames = allNames;
+ if (!rule.inherited){
+ //Trim the list to rule's nodeType so that inheritance check
+ //is not performed for other nodeTypes
+ ntNames = Collections.singletonList(rule.getNodeTypeName());
+ }
+
+ for (String ntName : ntNames) {
+ if (ntReg.isNodeType(ntName, rule.getNodeTypeName())) {
+ List<IndexingRule> perNtConfig = nt2rules.get(ntName);
+ if (perNtConfig == null) {
+ perNtConfig = new ArrayList<IndexingRule>();
+ nt2rules.put(ntName, perNtConfig);
+ }
+ log.trace("Registering rule '{}' for name '{}'", rule, ntName);
+ perNtConfig.add(new IndexingRule(rule, ntName));
+ }
+ }
+ }
+
+ for (Map.Entry<String, List<IndexingRule>> e : nt2rules.entrySet()){
+ e.setValue(ImmutableList.copyOf(e.getValue()));
+ }
+
+ return ImmutableMap.copyOf(nt2rules);
+ }
+
+ private boolean areAllTypesIndexed() {
+ IndexingRule ntBaseRule = getApplicableIndexingRule(NT_BASE);
+ return ntBaseRule != null;
+ }
+
+ private boolean evaluateSuggestionEnabled() {
+ for (IndexingRule indexingRule : definedRules) {
+ for (PropertyDefinition propertyDefinition : indexingRule.propConfigs.values()) {
+ if (propertyDefinition.useInSuggest) {
+ return true;
+ }
+ }
+ for (NamePattern np : indexingRule.namePatterns) {
+ if (np.getConfig().useInSuggest) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ public boolean isSuggestEnabled() {
+ return suggestEnabled;
+ }
+
+ private boolean evaluateSpellcheckEnabled() {
+ for (IndexingRule indexingRule : definedRules) {
+ for (PropertyDefinition propertyDefinition : indexingRule.propConfigs.values()) {
+ if (propertyDefinition.useInSpellcheck) {
+ return true;
+ }
+ }
+ for (NamePattern np : indexingRule.namePatterns) {
+ if (np.getConfig().useInSpellcheck) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ public boolean isSpellcheckEnabled() {
+ return spellcheckEnabled;
+ }
+
+ public String getIndexPath() {
+ return indexPath;
+ }
+
+ private boolean evaluateSuggestAnalyzed(NodeState defn, boolean defaultValue) {
+ NodeState suggestionConfig = defn.getChildNode(FulltextIndexConstants.SUGGESTION_CONFIG);
+
+ if (!suggestionConfig.exists()) {
+ //handle backward compatibility
+ return getOptionalValue(defn, FulltextIndexConstants.SUGGEST_ANALYZED, defaultValue);
+ }
+
+ return getOptionalValue(suggestionConfig, FulltextIndexConstants.SUGGEST_ANALYZED, defaultValue);
+ }
+
+ public boolean isSuggestAnalyzed() {
+ return suggestAnalyzed;
+ }
+
+ public int getNumberOfTopFacets() {
+ return numberOfTopFacets;
+ }
+
+ public class IndexingRule {
+ private final String baseNodeType;
+ private final String nodeTypeName;
+ /**
+ * Case insensitive map of lower cased propertyName to propertyConfigs
+ */
+ private final Map<String, PropertyDefinition> propConfigs;
+ private final List<NamePattern> namePatterns;
+ private final List<PropertyDefinition> nullCheckEnabledProperties;
+ private final List<PropertyDefinition> functionRestrictions;
+ private final List<PropertyDefinition> notNullCheckEnabledProperties;
+ private final List<PropertyDefinition> nodeScopeAnalyzedProps;
+ private final List<PropertyDefinition> syncProps;
+ private final boolean indexesAllNodesOfMatchingType;
+ private final boolean nodeNameIndexed;
+
+ final float boost;
+ final boolean inherited;
+ final int propertyTypes;
+ final boolean fulltextEnabled;
+ final boolean propertyIndexEnabled;
+ final boolean nodeFullTextIndexed;
+
+ final Aggregate aggregate;
+ final Aggregate propAggregate;
+
+
+ IndexingRule(String nodeTypeName, NodeState config) {
+ this.nodeTypeName = nodeTypeName;
+ this.baseNodeType = nodeTypeName;
+ this.boost = getOptionalValue(config, FIELD_BOOST, DEFAULT_BOOST);
+ this.inherited = getOptionalValue(config, FulltextIndexConstants.RULE_INHERITED, true);
+ this.propertyTypes = getSupportedTypes(config, INCLUDE_PROPERTY_TYPES, TYPES_ALLOW_ALL);
+
+ List<NamePattern> namePatterns = newArrayList();
+ List<PropertyDefinition> nonExistentProperties = newArrayList();
+ List<PropertyDefinition> functionRestrictions = newArrayList();
+ List<PropertyDefinition> existentProperties = newArrayList();
+ List<PropertyDefinition> nodeScopeAnalyzedProps = newArrayList();
+ List<PropertyDefinition> syncProps = newArrayList();
+ List<Aggregate.Include> propIncludes = newArrayList();
+ this.propConfigs = collectPropConfigs(config, namePatterns, propIncludes, nonExistentProperties,
+ existentProperties, nodeScopeAnalyzedProps, functionRestrictions, syncProps);
+ this.propAggregate = new Aggregate(nodeTypeName, propIncludes);
+ this.aggregate = combine(propAggregate, nodeTypeName);
+
+ this.namePatterns = ImmutableList.copyOf(namePatterns);
+ this.nodeScopeAnalyzedProps = ImmutableList.copyOf(nodeScopeAnalyzedProps);
+ this.nullCheckEnabledProperties = ImmutableList.copyOf(nonExistentProperties);
+ this.functionRestrictions = ImmutableList.copyOf(functionRestrictions);
+ this.notNullCheckEnabledProperties = ImmutableList.copyOf(existentProperties);
+ this.fulltextEnabled = aggregate.hasNodeAggregates() || hasAnyFullTextEnabledProperty();
+ this.nodeFullTextIndexed = aggregate.hasNodeAggregates() || anyNodeScopeIndexedProperty();
+ this.propertyIndexEnabled = hasAnyPropertyIndexConfigured();
+ this.indexesAllNodesOfMatchingType = areAlMatchingNodeByTypeIndexed();
+ this.nodeNameIndexed = evaluateNodeNameIndexed(config);
+ this.syncProps = ImmutableList.copyOf(syncProps);
+ validateRuleDefinition();
+ }
+
+ /**
+ * Creates a new indexing rule base on an existing one, but for a
+ * different node type name.
+ *
+ * @param original the existing rule.
+ * @param nodeTypeName the node type name for the rule.
+ */
+ IndexingRule(IndexingRule original, String nodeTypeName) {
+ this.nodeTypeName = nodeTypeName;
+ this.baseNodeType = original.getNodeTypeName();
+ this.propConfigs = original.propConfigs;
+ this.namePatterns = original.namePatterns;
+ this.boost = original.boost;
+ this.inherited = original.inherited;
+ this.propertyTypes = original.propertyTypes;
+ this.propertyIndexEnabled = original.propertyIndexEnabled;
+ this.propAggregate = original.propAggregate;
+ this.nullCheckEnabledProperties = original.nullCheckEnabledProperties;
+ this.notNullCheckEnabledProperties = original.notNullCheckEnabledProperties;
+ this.functionRestrictions = original.functionRestrictions;
+ this.nodeScopeAnalyzedProps = original.nodeScopeAnalyzedProps;
+ this.aggregate = combine(propAggregate, nodeTypeName);
+ this.fulltextEnabled = aggregate.hasNodeAggregates() || original.fulltextEnabled;
+ this.nodeFullTextIndexed = aggregate.hasNodeAggregates() || original.nodeFullTextIndexed;
+ this.indexesAllNodesOfMatchingType = areAlMatchingNodeByTypeIndexed();
+ this.nodeNameIndexed = original.nodeNameIndexed;
+ this.syncProps = original.syncProps;
+ }
+
+ /**
+ * Returns <code>true</code> if the property with the given name is
+ * indexed according to this rule.
+ *
+ * @param propertyName the name of a property.
+ * @return <code>true</code> if the property is indexed;
+ * <code>false</code> otherwise.
+ */
+ public boolean isIndexed(String propertyName) {
+ return getConfig(propertyName) != null;
+ }
+
+
+ /**
+ * Returns the name of the node type where this rule applies to.
+ *
+ * @return name of the node type.
+ */
+ public String getNodeTypeName() {
+ return nodeTypeName;
+ }
+
+ /**
+ * @return name of the base node type.
+ */
+ public String getBaseNodeType() {
+ return baseNodeType;
+ }
+
+ public List<PropertyDefinition> getNullCheckEnabledProperties() {
+ return nullCheckEnabledProperties;
+ }
+
+ public List<PropertyDefinition> getFunctionRestrictions() {
+ return functionRestrictions;
+ }
+
+ public List<PropertyDefinition> getNotNullCheckEnabledProperties() {
+ return notNullCheckEnabledProperties;
+ }
+
+ public List<PropertyDefinition> getNodeScopeAnalyzedProps() {
+ return nodeScopeAnalyzedProps;
+ }
+
+ @Override
+ public String toString() {
+ String str = "IndexRule: "+ nodeTypeName;
+ if (!baseNodeType.equals(nodeTypeName)){
+ str += "(" + baseNodeType + ")";
+ }
+ return str;
+ }
+
+ public boolean isAggregated(String nodePath) {
+ return aggregate.hasRelativeNodeInclude(nodePath);
+ }
+
+ /**
+ * Returns <code>true</code> if this rule applies to the given node
+ * <code>state</code>.
+ *
+ * @param state the state to check.
+ * @return <code>true</code> the rule applies to the given node;
+ * <code>false</code> otherwise.
+ */
+ public boolean appliesTo(NodeState state) {
+ for (String mixinName : getMixinTypeNames(state)){
+ if (nodeTypeName.equals(mixinName)){
+ return true;
+ }
+ }
+
+ if (!nodeTypeName.equals(getPrimaryTypeName(state))) {
+ return false;
+ }
+ //TODO Add support for condition
+ //return condition == null || condition.evaluate(state);
+ return true;
+ }
+
+ public boolean appliesTo(String nodeTypeName) {
+ if (!this.nodeTypeName.equals(nodeTypeName)) {
+ return false;
+ }
+
+ //TODO Once condition support is done return false
+ //return condition == null || condition.evaluate(state);
+ return true;
+
+ }
+
+ public boolean isNodeNameIndexed() {
+ return nodeNameIndexed;
+ }
+
+ /**
+ * Returns true if the rule has any property definition which enables
+ * evaluation of fulltext related clauses
+ */
+ public boolean isFulltextEnabled() {
+ return fulltextEnabled;
+ }
+
+ /**
+ * Returns true if a fulltext field for the node would be created
+ * for any node matching the current rule.
+ */
+ public boolean isNodeFullTextIndexed() {
+ return nodeFullTextIndexed;
+ }
+
+ /**
+ * @param propertyName name of a property.
+ * @return the property configuration or <code>null</code> if this
+ * indexing rule does not contain a configuration for the given
+ * property.
+ */
+ @CheckForNull
+ public PropertyDefinition getConfig(String propertyName) {
+ PropertyDefinition config = propConfigs.get(propertyName.toLowerCase(Locale.ENGLISH));
+ if (config != null) {
+ return config;
+ } else if (namePatterns.size() > 0) {
+ // check patterns
+ for (NamePattern np : namePatterns) {
+ if (np.matches(propertyName)) {
+ return np.getConfig();
+ }
+ }
+ }
+ return null;
+ }
+
+ public boolean includePropertyType(int type){
+ return IndexDefinition.includePropertyType(propertyTypes, type);
+ }
+
+ public Aggregate getAggregate() {
+ return aggregate;
+ }
+
+ /**
+ * Flag to determine weather current index rule definition allows indexing of all
+ * node of type as covered by the current rule. For example if the rule only indexes
+ * certain property 'foo' for node type 'app:Asset' then index would only have
+ * entries for those assets where foo is defined. Such an index cannot claim that
+ * it has entries for all assets.
+
+ * @return true in case all matching node types are covered by this rule
+ */
+ public boolean indexesAllNodesOfMatchingType() {
+ return indexesAllNodesOfMatchingType;
+ }
+
+ public boolean isBasedOnNtBase(){
+ return JcrConstants.NT_BASE.equals(baseNodeType);
+ }
+
+ private Map<String, PropertyDefinition> collectPropConfigs(NodeState config,
+ List<NamePattern> patterns,
+ List<Aggregate.Include> propAggregate,
+ List<PropertyDefinition> nonExistentProperties,
+ List<PropertyDefinition> existentProperties,
+ List<PropertyDefinition> nodeScopeAnalyzedProps,
+ List<PropertyDefinition> functionRestrictions,
+ List<PropertyDefinition> syncProps) {
+ Map<String, PropertyDefinition> propDefns = newHashMap();
+ NodeState propNode = config.getChildNode(FulltextIndexConstants.PROP_NODE);
+
+ if (propNode.exists() && !hasOrderableChildren(propNode)){
+ log.warn("Properties node for [{}] does not have orderable " +
+ "children in [{}]", this, IndexDefinition.this);
+ }
+
+ //In case of a pure nodetype index we just index primaryType and mixins
+ //and ignore any other property definition
+ if (nodeTypeIndex) {
+ boolean sync = getOptionalValue(config, FulltextIndexConstants.PROP_SYNC, false);
+ PropertyDefinition pdpt = createNodeTypeDefinition(this, JcrConstants.JCR_PRIMARYTYPE, sync);
+ PropertyDefinition pdmixin = createNodeTypeDefinition(this, JcrConstants.JCR_MIXINTYPES, sync);
+
+ propDefns.put(pdpt.name.toLowerCase(Locale.ENGLISH), pdpt);
+ propDefns.put(pdmixin.name.toLowerCase(Locale.ENGLISH), pdmixin);
+
+ if (sync) {
+ syncProps.add(pdpt);
+ syncProps.add(pdmixin);
+ }
+
+ if (propNode.getChildNodeCount(1) > 0) {
+ log.warn("Index at [{}] has {} enabled and cannot support other property " +
+ "definitions", indexPath, FulltextIndexConstants.PROP_INDEX_NODE_TYPE);
+ }
+
+ return ImmutableMap.copyOf(propDefns);
+ }
+
+ //Include all immediate child nodes to 'properties' node by default
+ Tree propTree = TreeFactory.createReadOnlyTree(propNode);
+ for (Tree prop : propTree.getChildren()) {
+ String propName = prop.getName();
+ NodeState propDefnNode = propNode.getChildNode(propName);
+ if (propDefnNode.exists() && !propDefns.containsKey(propName)) {
+ PropertyDefinition pd = new PropertyDefinition(this, propName, propDefnNode);
+ if (pd.function != null) {
+ functionRestrictions.add(pd);
+ String[] properties = FunctionIndexProcessor.getProperties(pd.functionCode);
+ for (String p : properties) {
+ if (PathUtils.getDepth(p) > 1) {
+ PropertyDefinition pd2 = new PropertyDefinition(this, p, propDefnNode);
+ propAggregate.add(new Aggregate.PropertyInclude(pd2));
+ }
+ }
+ // a function index has no other options
+ continue;
+ }
+ if(pd.isRegexp){
+ patterns.add(new NamePattern(pd.name, pd));
+ } else {
+ propDefns.put(pd.name.toLowerCase(Locale.ENGLISH), pd);
+ }
+
+ if (pd.relative){
+ propAggregate.add(new Aggregate.PropertyInclude(pd));
+ }
+
+ if (pd.nullCheckEnabled){
+ nonExistentProperties.add(pd);
+ }
+
+ if (pd.notNullCheckEnabled){
+ existentProperties.add(pd);
+ }
+
+ //Include props with name, boosted and nodeScopeIndex
+ if (pd.nodeScopeIndex
+ && pd.analyzed
+ && !pd.isRegexp){
+ nodeScopeAnalyzedProps.add(pd);
+ }
+
+ if (pd.sync) {
+ syncProps.add(pd);
+ }
+ }
+ }
+ ensureNodeTypeIndexingIsConsistent(propDefns, syncProps);
+ return ImmutableMap.copyOf(propDefns);
+ }
+
+ /**
+ * If jcr:primaryType is indexed but jcr:mixinTypes is not indexed
+ * then ensure that jcr:mixinTypes is also indexed
+ */
+ private void ensureNodeTypeIndexingIsConsistent(Map<String, PropertyDefinition> propDefns,
+ List<PropertyDefinition> syncProps) {
+ PropertyDefinition pd_pr = propDefns.get(JcrConstants.JCR_PRIMARYTYPE.toLowerCase(Locale.ENGLISH));
+ PropertyDefinition pd_mixin = propDefns.get(JcrConstants.JCR_MIXINTYPES.toLowerCase(Locale.ENGLISH));
+
+ if (pd_pr != null && pd_pr.propertyIndex && pd_mixin == null) {
+ pd_mixin = createNodeTypeDefinition(this, JcrConstants.JCR_MIXINTYPES, pd_pr.sync);
+ syncProps.add(pd_mixin);
+ propDefns.put(JcrConstants.JCR_MIXINTYPES.toLowerCase(Locale.ENGLISH), pd_mixin);
+ }
+ }
+
+ private boolean hasAnyFullTextEnabledProperty() {
+ for (PropertyDefinition pd : propConfigs.values()){
+ if (pd.fulltextEnabled()){
+ return true;
+ }
+ }
+
+ for (NamePattern np : namePatterns){
+ if (np.getConfig().fulltextEnabled()){
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean hasAnyPropertyIndexConfigured() {
+ for (PropertyDefinition pd : propConfigs.values()){
+ if (pd.propertyIndex){
+ return true;
+ }
+ }
+
+ for (NamePattern np : namePatterns){
+ if (np.getConfig().propertyIndex){
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean anyNodeScopeIndexedProperty(){
+ //Check if there is any nodeScope indexed property as
+ //for such case a node would always be indexed
+ for (PropertyDefinition pd : propConfigs.values()){
+ if (pd.nodeScopeIndex){
+ return true;
+ }
+ }
+
+ for (NamePattern np : namePatterns){
+ if (np.getConfig().nodeScopeIndex){
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private boolean areAlMatchingNodeByTypeIndexed(){
+ if (nodeTypeIndex) {
+ return true;
+ }
+
+ if (nodeFullTextIndexed){
+ return true;
+ }
+
+ //If there is nullCheckEnabled property which is not relative then
+ //all nodes would be indexed. relativeProperty with nullCheckEnabled might
+ //not ensure that (OAK-1085)
+ for (PropertyDefinition pd : nullCheckEnabledProperties){
+ if (!pd.relative) {
+ return true;
+ }
+ }
+
+ //jcr:primaryType is present on all node. So if such a property
+ //is indexed then it would mean all nodes covered by this index rule
+ //are indexed
+ if (getConfig(JcrConstants.JCR_PRIMARYTYPE) != null){
+ return true;
+ }
+
+ return false;
+ }
+
+ private boolean evaluateNodeNameIndexed(NodeState config) {
+ //check global config first
+ if (getOptionalValue(config, FulltextIndexConstants.INDEX_NODE_NAME, false)) {
+ return true;
+ }
+
+ //iterate over property definitions
+ for (PropertyDefinition pd : propConfigs.values()){
+ if (FulltextIndexConstants.PROPDEF_PROP_NODE_NAME.equals(pd.name)){
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private Aggregate combine(Aggregate propAggregate, String nodeTypeName){
+ Aggregate nodeTypeAgg = IndexDefinition.this.getAggregate(nodeTypeName);
+ List<Aggregate.Include> includes = newArrayList();
+ includes.addAll(propAggregate.getIncludes());
+ if (nodeTypeAgg != null){
+ includes.addAll(nodeTypeAgg.getIncludes());
+ }
+ return new Aggregate(nodeTypeName, includes);
+ }
+
+ private void validateRuleDefinition() {
+ if (!nullCheckEnabledProperties.isEmpty() && isBasedOnNtBase()){
+ throw new IllegalStateException("nt:base based rule cannot have a " +
+ "PropertyDefinition with nullCheckEnabled");
+ }
+ }
+ }
+
+ /**
+ * A property name pattern.
+ */
+ private static final class NamePattern {
+ private final String parentPath;
+ /**
+ * The pattern to match.
+ */
+ private final Pattern pattern;
+
+ /**
+ * The associated configuration.
+ */
+ private final PropertyDefinition config;
+
+ /**
+ * Creates a new name pattern.
+ *
+ * @param pattern the pattern as defined by the property definition
+ * @param config the associated configuration.
+ */
+ private NamePattern(String pattern,
+ PropertyDefinition config){
+
+ //Special handling for all props regex as its already being used
+ //and use of '/' in regex would confuse the parent path calculation
+ //logic
+ if (FulltextIndexConstants.REGEX_ALL_PROPS.equals(pattern)){
+ this.parentPath = "";
+ this.pattern = Pattern.compile(pattern);
+ } else {
+ this.parentPath = getParentPath(pattern);
+ this.pattern = Pattern.compile(PathUtils.getName(pattern));
+ }
+ this.config = config;
+ }
+
+ /**
+ * @param propertyPath property name to match
+ * @return <code>true</code> if <code>property name</code> matches this name
+ * pattern; <code>false</code> otherwise.
+ */
+ boolean matches(String propertyPath) {
+ String parentPath = getParentPath(propertyPath);
+ String propertyName = PathUtils.getName(propertyPath);
+ if (!this.parentPath.equals(parentPath)) {
+ return false;
+ }
+ return pattern.matcher(propertyName).matches();
+ }
+
+ PropertyDefinition getConfig() {
+ return config;
+ }
+ }
+
+ //~---------------------------------------------< compatibility >
+
+ public static NodeBuilder updateDefinition(NodeBuilder indexDefn){
+ return updateDefinition(indexDefn, "unknown");
+ }
+
+ public static NodeBuilder updateDefinition(NodeBuilder indexDefn, String indexPath){
+ NodeState defn = indexDefn.getBaseState();
+ if (!hasIndexingRules(defn)){
+ NodeState rulesState = createIndexRules(defn).getNodeState();
+ indexDefn.setChildNode(FulltextIndexConstants.INDEX_RULES, rulesState);
+ indexDefn.setProperty(INDEX_VERSION, determineIndexFormatVersion(defn).getVersion());
+
+ indexDefn.removeProperty(DECLARING_NODE_TYPES);
+ indexDefn.removeProperty(INCLUDE_PROPERTY_NAMES);
+ indexDefn.removeProperty(EXCLUDE_PROPERTY_NAMES);
+ indexDefn.removeProperty(ORDERED_PROP_NAMES);
+ indexDefn.removeProperty(FULL_TEXT_ENABLED);
+ indexDefn.child(PROP_NODE).remove();
+ log.info("Updated index definition for {}", indexPath);
+ }
+ return indexDefn;
+ }
+
+ /**
+ * Constructs IndexingRule based on earlier format of index configuration
+ */
+ private static NodeBuilder createIndexRules(NodeState defn){
+ NodeBuilder builder = EMPTY_NODE.builder();
+ Set<String> declaringNodeTypes = getMultiProperty(defn, DECLARING_NODE_TYPES);
+ Set<String> includes = getMultiProperty(defn, INCLUDE_PROPERTY_NAMES);
+ Set<String> excludes = toLowerCase(getMultiProperty(defn, EXCLUDE_PROPERTY_NAMES));
+ Set<String> orderedProps = getMultiProperty(defn, ORDERED_PROP_NAMES);
+ boolean fullTextEnabled = getOptionalValue(defn, FULL_TEXT_ENABLED, true);
+ boolean storageEnabled = getOptionalValue(defn, EXPERIMENTAL_STORAGE, true);
+ NodeState propNodeState = defn.getChildNode(FulltextIndexConstants.PROP_NODE);
+
+ //If no explicit nodeType defined then all config applies for nt:base
+ if (declaringNodeTypes.isEmpty()){
+ declaringNodeTypes = Collections.singleton(NT_BASE);
+ }
+
+ Set<String> propNamesSet = Sets.newHashSet();
+ propNamesSet.addAll(includes);
+ propNamesSet.addAll(excludes);
+ propNamesSet.addAll(orderedProps);
+
+ //Also include all immediate leaf propNode names
+ for (ChildNodeEntry cne : propNodeState.getChildNodeEntries()){
+ if (!propNamesSet.contains(cne.getName())
+ && Iterables.isEmpty(cne.getNodeState().getChildNodeNames())){
+ propNamesSet.add(cne.getName());
+ }
+ }
+
+ List<String> propNames = new ArrayList<String>(propNamesSet);
+
+ final String includeAllProp = FulltextIndexConstants.REGEX_ALL_PROPS;
+ if (fullTextEnabled
+ && includes.isEmpty()){
+ //Add the regEx for including all properties at the end
+ //for fulltext index and when no explicit includes are defined
+ propNames.add(includeAllProp);
+ }
+
+ for (String typeName : declaringNodeTypes){
+ NodeBuilder rule = builder.child(typeName);
+ markAsNtUnstructured(rule);
+ List<String> propNodeNames = newArrayListWithCapacity(propNamesSet.size());
+ NodeBuilder propNodes = rule.child(PROP_NODE);
+ int i = 0;
+ for (String propName : propNames){
+ String propNodeName = propName;
+
+ //For proper propName use the propName as childNode name
+ if(PropertyDefinition.isRelativeProperty(propName)
+ || propName.equals(includeAllProp)){
+ propNodeName = "prop" + i++;
+ }
+ propNodeNames.add(propNodeName);
+
+ NodeBuilder prop = propNodes.child(propNodeName);
+ markAsNtUnstructured(prop);
+ prop.setProperty(FulltextIndexConstants.PROP_NAME, propName);
+
+ if (excludes.contains(propName)){
+ prop.setProperty(FulltextIndexConstants.PROP_INDEX, false);
+ } else if (fullTextEnabled){
+ prop.setProperty(FulltextIndexConstants.PROP_ANALYZED, true);
+ prop.setProperty(FulltextIndexConstants.PROP_NODE_SCOPE_INDEX, true);
+ prop.setProperty(FulltextIndexConstants.PROP_USE_IN_EXCERPT, storageEnabled);
+ prop.setProperty(FulltextIndexConstants.PROP_PROPERTY_INDEX, false);
+ } else {
+ prop.setProperty(FulltextIndexConstants.PROP_PROPERTY_INDEX, true);
+
+ if (orderedProps.contains(propName)){
+ prop.setProperty(FulltextIndexConstants.PROP_ORDERED, true);
+ }
+ }
+
+ if (propName.equals(includeAllProp)){
+ prop.setProperty(FulltextIndexConstants.PROP_IS_REGEX, true);
+ } else {
+ //Copy over the property configuration
+ NodeState propDefNode = getPropDefnNode(defn, propName);
+ if (propDefNode != null){
+ for (PropertyState ps : propDefNode.getProperties()){
+ prop.setProperty(ps);
+ }
+ }
+ }
+ }
+
+ //If no propertyType defined then default to UNKNOWN such that none
+ //of the properties get indexed
+ PropertyState supportedTypes = defn.getProperty(INCLUDE_PROPERTY_TYPES);
+ if (supportedTypes == null){
+ supportedTypes = PropertyStates.createProperty(INCLUDE_PROPERTY_TYPES, TYPES_ALLOW_ALL_NAME);
+ }
+ rule.setProperty(supportedTypes);
+
+ if (!NT_BASE.equals(typeName)) {
+ rule.setProperty(FulltextIndexConstants.RULE_INHERITED, false);
+ }
+
+ propNodes.setProperty(OAK_CHILD_ORDER, propNodeNames ,NAMES);
+ markAsNtUnstructured(propNodes);
+ }
+
+ markAsNtUnstructured(builder);
+ builder.setProperty(OAK_CHILD_ORDER, declaringNodeTypes ,NAMES);
+ return builder;
+ }
+
+ private static NodeState getPropDefnNode(NodeState defn, String propName){
+ NodeState propNode = defn.getChildNode(FulltextIndexConstants.PROP_NODE);
+ NodeState propDefNode;
+ if (PropertyDefinition.isRelativeProperty(propName)) {
+ NodeState result = propNode;
+ for (String name : PathUtils.elements(propName)) {
+ result = result.getChildNode(name);
+ }
+ propDefNode = result;
+ } else {
+ propDefNode = propNode.getChildNode(propName);
+ }
+ return propDefNode.exists() ? propDefNode : null;
+ }
+
+ //~---------------------------------------------< utility >
+
+ private int determineMaxExtractLength() {
+ int length = getOptionalValue(definition.getChildNode(TIKA), FulltextIndexConstants.TIKA_MAX_EXTRACT_LENGTH,
+ DEFAULT_MAX_EXTRACT_LENGTH);
+ if (length < 0){
+ return - length * maxFieldLength;
+ }
+ return length;
+ }
+
+ private NodeState getTikaConfigNode() {
+ return definition.getChildNode(TIKA).getChildNode(TIKA_CONFIG);
+ }
+
+ private static Set<String> getMultiProperty(NodeState definition, String propName){
+ PropertyState pse = definition.getProperty(propName);
+ return pse != null ? ImmutableSet.copyOf(pse.getValue(Type.STRINGS)) : Collections.<String>emptySet();
+ }
+
+ private static Set<String> toLowerCase(Set<String> values){
+ Set<String> result = newHashSet();
+ for(String val : values){
+ result.add(val.toLowerCase());
+ }
+ return ImmutableSet.copyOf(result);
+ }
+
+ private static List<String> getAllNodeTypes(ReadOnlyNodeTypeManager ntReg) {
+ try {
+ List<String> typeNames = newArrayList();
+ NodeTypeIterator ntItr = ntReg.getAllNodeTypes();
+ while (ntItr.hasNext()){
+ typeNames.add(ntItr.nextNodeType().getName());
+ }
+ return typeNames;
+ } catch (RepositoryException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private static ReadOnlyNodeTypeManager createNodeTypeManager(final Tree root) {
+ return new ReadOnlyNodeTypeManager() {
+ @Override
+ protected Tree getTypes() {
+ return TreeUtil.getTree(root,NODE_TYPES_PATH);
+ }
+
+ @Nonnull
+ @Override
+ protected NamePathMapper getNamePathMapper() {
+ return NamePathMapper.DEFAULT;
+ }
+ };
+ }
+
+ private static String getPrimaryTypeName(NodeState state) {
+ String primaryType = state.getName(JcrConstants.JCR_PRIMARYTYPE);
+
+ //To ensure compatibility with previous Tree based usage look based on string also
+ if (primaryType == null) {
+ primaryType = state.getString(JcrConstants.JCR_PRIMARYTYPE);
+ }
+ //In case not a proper JCR assume nt:base TODO return null and ignore indexing such nodes
+ //at all
+ return primaryType != null ? primaryType : "nt:base";
+ }
+
+ private static Iterable<String> getMixinTypeNames(NodeState state) {
+ PropertyState property = state.getProperty(JcrConstants.JCR_MIXINTYPES);
+ return property != null ? property.getValue(Type.NAMES) : Collections.<String>emptyList();
+ }
+
+ private static boolean hasOrderableChildren(NodeState state){
+ return state.hasProperty(OAK_CHILD_ORDER);
+ }
+
+ static int getSupportedTypes(NodeState defn, String typePropertyName, int defaultVal) {
+ PropertyState pst = defn.getProperty(typePropertyName);
+ if (pst != null) {
+ int types = 0;
+ for (String inc : pst.getValue(Type.STRINGS)) {
+ if (TYPES_ALLOW_ALL_NAME.equals(inc)){
+ return TYPES_ALLOW_ALL;
+ }
+
+ try {
+ types |= 1 << PropertyType.valueFromName(inc);
+ } catch (IllegalArgumentException e) {
+ log.warn("Unknown property type: " + inc);
+ }
+ }
+ return types;
+ }
+ return defaultVal;
+ }
+
+ static boolean includePropertyType(int includedPropertyTypes, int type){
+ if(includedPropertyTypes == TYPES_ALLOW_ALL){
+ return true;
+ }
+
+ if (includedPropertyTypes == TYPES_ALLOW_NONE){
+ return false;
+ }
+
+ return (includedPropertyTypes & (1 << type)) != 0;
+ }
+
+ private static boolean hasFulltextEnabledIndexRule(List<IndexingRule> rules) {
+ for (IndexingRule rule : rules){
+ if (rule.isFulltextEnabled()){
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private static void markAsNtUnstructured(NodeBuilder nb){
+ nb.setProperty(JcrConstants.JCR_PRIMARYTYPE, JcrConstants.NT_UNSTRUCTURED, Type.NAME);
+ }
+
+ private static IndexFormatVersion determineIndexFormatVersion(NodeState defn) {
+ //Compat mode version if specified has highest priority
+ if (defn.hasProperty(COMPAT_MODE)){
+ return versionFrom(defn.getProperty(COMPAT_MODE));
+ }
+
+ if (defn.hasProperty(INDEX_VERSION)){
+ return versionFrom(defn.getProperty(INDEX_VERSION));
+ }
+
+ //No existing index data i.e. reindex or fresh index
+ if (!defn.getChildNode(INDEX_DATA_CHILD_NAME).exists()){
+ return determineVersionForFreshIndex(defn);
+ }
+
+ boolean fullTextEnabled = getOptionalValue(defn, FULL_TEXT_ENABLED, true);
+
+ //A fulltext index with old indexing format confirms to V1. However
+ //a propertyIndex with old indexing format confirms to V2
+ return fullTextEnabled ? IndexFormatVersion.V1 : IndexFormatVersion.V2;
+ }
+
+ static IndexFormatVersion determineVersionForFreshIndex(NodeState defn){
+ return determineVersionForFreshIndex(defn.getProperty(FULL_TEXT_ENABLED),
+ defn.getProperty(COMPAT_MODE), defn.getProperty(INDEX_VERSION));
+ }
+
+ static IndexFormatVersion determineVersionForFreshIndex(NodeBuilder defnb){
+ return determineVersionForFreshIndex(defnb.getProperty(FULL_TEXT_ENABLED),
+ defnb.getProperty(COMPAT_MODE), defnb.getProperty(INDEX_VERSION));
+ }
+
+ private static IndexFormatVersion determineVersionForFreshIndex(PropertyState fulltext,
+ PropertyState compat,
+ PropertyState version){
+ if (compat != null){
+ return versionFrom(compat);
+ }
+
+ IndexFormatVersion defaultToUse = IndexFormatVersion.getDefault();
+ IndexFormatVersion existing = version != null ? versionFrom(version) : null;
+
+ //As per OAK-2290 current might be less than current used version. So
+ //set to current only if it is greater than existing
+
+ //Per setting use default configured
+ IndexFormatVersion result = defaultToUse;
+
+ //If default configured is lesser than existing then prefer existing
+ if (existing != null){
+ result = IndexFormatVersion.max(result,existing);
+ }
+
+ //Check if fulltext is false which indicates its a property index and
+ //hence confirm to V2 or above
+ if (fulltext != null && !fulltext.getValue(Type.BOOLEAN)){
+ return IndexFormatVersion.max(result,IndexFormatVersion.V2);
+ }
+
+ return result;
+ }
+
+ private static String[] getOptionalStrings(NodeState defn, String propertyName) {
+ PropertyState ps = defn.getProperty(propertyName);
+ if (ps != null) {
+ return Iterables.toArray(ps.getValue(Type.STRINGS), String.class);
+ }
+ return null;
+ }
+
+ private static IndexFormatVersion versionFrom(PropertyState ps){
+ return IndexFormatVersion.getVersion(Ints.checkedCast(ps.getValue(Type.LONG)));
+ }
+
+ private static boolean hasIndexingRules(NodeState defn) {
+ return defn.getChildNode(FulltextIndexConstants.INDEX_RULES).exists();
+ }
+
+ @CheckForNull
+ private static String determineUniqueId(NodeState defn) {
+ return defn.getChildNode(STATUS_NODE).getString(PROP_UID);
+ }
+
+ private static boolean supportsSyncIndexing(NodeState defn) {
+ return supportsIndexingMode(new ReadOnlyBuilder(defn), INDEXING_MODE_SYNC);
+ }
+
+ private static boolean supportsIndexingMode(NodeBuilder defn, String mode) {
+ PropertyState async = defn.getProperty(IndexConstants.ASYNC_PROPERTY_NAME);
+ if (async == null){
+ return false;
+ }
+ return Iterables.contains(async.getValue(Type.STRINGS), mode);
+ }
+
+ private static NodeState getIndexDefinitionState(NodeState defn) {
+ if (isDisableStoredIndexDefinition()){
+ return defn;
+ }
+ NodeState storedState = defn.getChildNode(INDEX_DEFINITION_NODE);
+ return storedState.exists() ? storedState : defn;
+ }
+
+ private static Map<String, String> buildMimeTypeMap(NodeState node) {
+ ImmutableMap.Builder<String, String> map = ImmutableMap.builder();
+ for (ChildNodeEntry child : node.getChildNodeEntries()) {
+ for (ChildNodeEntry subChild : child.getNodeState().getChildNodeEntries()) {
+ StringBuilder typeBuilder = new StringBuilder(child.getName())
+ .append('/')
+ .append(subChild.getName());
+ PropertyState property = subChild.getNodeState().getProperty(TIKA_MAPPED_TYPE);
+ if (property != null) {
+ map.put(typeBuilder.toString(), property.getValue(Type.STRING));
+ }
+ }
+ }
+ return map.build();
+ }
+
+ private static PropertyDefinition createNodeTypeDefinition(IndexingRule rule, String name, boolean sync) {
+ NodeBuilder builder = EMPTY_NODE.builder();
+ //A nodetype index just required propertyIndex and sync flags to be set
+ builder.setProperty(FulltextIndexConstants.PROP_PROPERTY_INDEX, true);
+ if (sync) {
+ builder.setProperty(FulltextIndexConstants.PROP_SYNC, sync);
+ }
+ builder.setProperty(FulltextIndexConstants.PROP_NAME, name);
+ return new PropertyDefinition(rule, name, builder.getNodeState());
+ }
+
+}
Propchange: jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/IndexDefinition.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/IndexFormatVersion.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/IndexFormatVersion.java?rev=1828972&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/IndexFormatVersion.java (added)
+++ jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/IndexFormatVersion.java Thu Apr 12 11:46:17 2018
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.jackrabbit.oak.plugins.index.search;
+
+public enum IndexFormatVersion {
+ /**
+ * Index confirming to Oak version upto 1.0.8
+ */
+ V1(1),
+ /**
+ * Index confirming to Oak version upto 1.0.9
+ */
+ V2(2);
+
+ private final int version;
+
+ IndexFormatVersion(int version) {
+ this.version = version;
+ }
+
+ /**
+ * Returns <code>true</code> if this version is at least as high as the
+ * given <code>version</code>.
+ *
+ * @param version the other version to compare.
+ * @return <code>true</code> if this version is at least as high as the
+ * provided; <code>false</code> otherwise.
+ */
+ public boolean isAtLeast(IndexFormatVersion version) {
+ return this.version >= version.getVersion();
+ }
+
+ public int getVersion() {
+ return version;
+ }
+
+ public static IndexFormatVersion getVersion(int version) {
+ switch(version){
+ case 1 : return V1;
+ case 2 : return V2;
+ default : throw new IllegalArgumentException("Unknown version : " + version);
+ }
+ }
+
+ public static IndexFormatVersion getDefault(){
+ return V2;
+ }
+
+ public static IndexFormatVersion max(IndexFormatVersion o1, IndexFormatVersion o2){
+ return o1.version > o2.version ? o1 : o2;
+ }
+
+}
Propchange: jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/IndexFormatVersion.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/IndexLookup.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/IndexLookup.java?rev=1828972&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/IndexLookup.java (added)
+++ jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/IndexLookup.java Thu Apr 12 11:46:17 2018
@@ -0,0 +1,81 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.jackrabbit.oak.plugins.index.search;
+
+import java.util.Collection;
+import java.util.Set;
+
+import com.google.common.collect.Sets;
+import org.apache.jackrabbit.oak.commons.PathUtils;
+import org.apache.jackrabbit.oak.spi.query.Filter;
+import org.apache.jackrabbit.oak.spi.state.ChildNodeEntry;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+
+import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.INDEX_DEFINITIONS_NAME;
+import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.TYPE_PROPERTY_NAME;
+
+public class IndexLookup {
+ private final NodeState root;
+
+ public IndexLookup(NodeState root) {
+ this.root = root;
+ }
+
+ public Collection<String> collectIndexNodePaths(Filter filter, String type){
+ return collectIndexNodePaths(filter, type, true);
+ }
+
+ private Collection<String> collectIndexNodePaths(Filter filter, String type, boolean recurse){
+ Set<String> paths = Sets.newHashSet();
+
+ collectIndexNodePaths(root, type, "/", paths);
+
+ if (recurse) {
+ StringBuilder sb = new StringBuilder();
+ NodeState nodeState = root;
+ for (String element : PathUtils.elements(filter.getPath())) {
+ nodeState = nodeState.getChildNode(element);
+ collectIndexNodePaths(nodeState, type,
+ sb.append("/").append(element).toString(),
+ paths);
+ }
+ }
+
+ return paths;
+ }
+
+ public static void collectIndexNodePaths(NodeState nodeState, String type, String parentPath, Collection<String> paths) {
+ NodeState state = nodeState.getChildNode(INDEX_DEFINITIONS_NAME);
+ for (ChildNodeEntry entry : state.getChildNodeEntries()) {
+ if (isIndexOfType(entry.getNodeState(), type)) {
+ paths.add(createIndexNodePath(parentPath, entry.getName()));
+ }
+ }
+ }
+
+ private static boolean isIndexOfType(NodeState nodeState, String type) {
+ return type.equals(nodeState.getString(TYPE_PROPERTY_NAME));
+ }
+
+
+ private static String createIndexNodePath(String parentPath, String name){
+ return PathUtils.concat(parentPath, INDEX_DEFINITIONS_NAME, name);
+ }
+}
Propchange: jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/IndexLookup.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/IndexNode.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/IndexNode.java?rev=1828972&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/IndexNode.java (added)
+++ jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/IndexNode.java Thu Apr 12 11:46:17 2018
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.jackrabbit.oak.plugins.index.search;
+
+public interface IndexNode {
+
+ void release();
+
+ IndexDefinition getDefinition();
+
+ int getIndexNodeId();
+
+ void refreshReadersOnWriteIfRequired();
+}
Propchange: jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/IndexNode.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: 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=1828972&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/NodeStateCloner.java (added)
+++ jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/NodeStateCloner.java Thu Apr 12 11:46:17 2018
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.jackrabbit.oak.plugins.index.search;
+
+import org.apache.jackrabbit.oak.spi.state.ApplyDiff;
+import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+import org.apache.jackrabbit.oak.spi.state.NodeStateUtils;
+
+import static org.apache.jackrabbit.oak.plugins.memory.EmptyNodeState.EMPTY_NODE;
+
+class NodeStateCloner {
+
+ public static NodeState cloneVisibleState(NodeState state){
+ NodeBuilder builder = EMPTY_NODE.builder();
+ new ApplyVisibleDiff(builder).apply(state);
+ return builder.getNodeState();
+ }
+
+ private static class ApplyVisibleDiff extends ApplyDiff {
+ public ApplyVisibleDiff(NodeBuilder builder) {
+ super(builder);
+ }
+
+ @Override
+ public boolean childNodeAdded(String name, NodeState after) {
+ if (NodeStateUtils.isHidden(name)){
+ return true;
+ }
+ return after.compareAgainstBaseState(
+ EMPTY_NODE, new ApplyVisibleDiff(builder.child(name)));
+ }
+ }
+}
Propchange: jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/NodeStateCloner.java
------------------------------------------------------------------------------
svn:eol-style = native