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 ch...@apache.org on 2014/11/12 14:13:52 UTC

svn commit: r1638781 - in /jackrabbit/oak/trunk/oak-lucene/src: main/java/org/apache/jackrabbit/oak/plugins/index/lucene/ test/java/org/apache/jackrabbit/oak/plugins/index/lucene/

Author: chetanm
Date: Wed Nov 12 13:13:52 2014
New Revision: 1638781

URL: http://svn.apache.org/r1638781
Log:
OAK-2261 - Enable support for NodeType based indexing rules (WIP)

Add support for IndexRule configuration parsing

Modified:
    jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexDefinition.java
    jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexConstants.java
    jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/PropertyDefinition.java
    jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexDefinitionTest.java

Modified: jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexDefinition.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexDefinition.java?rev=1638781&r1=1638780&r2=1638781&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexDefinition.java (original)
+++ jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexDefinition.java Wed Nov 12 13:13:52 2014
@@ -19,25 +19,39 @@
 
 package org.apache.jackrabbit.oak.plugins.index.lucene;
 
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.regex.Pattern;
 
 import javax.annotation.CheckForNull;
 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 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.core.ImmutableRoot;
+import org.apache.jackrabbit.oak.namepath.NamePathMapper;
 import org.apache.jackrabbit.oak.plugins.index.lucene.util.LuceneIndexHelper;
+import org.apache.jackrabbit.oak.plugins.nodetype.ReadOnlyNodeTypeManager;
+import org.apache.jackrabbit.oak.spi.state.ChildNodeEntry;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
+import org.apache.jackrabbit.oak.util.TreeUtil;
 import org.apache.lucene.codecs.Codec;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import static com.google.common.collect.Lists.newArrayList;
 import static com.google.common.collect.Maps.newHashMap;
 import static com.google.common.collect.Sets.newHashSet;
 import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.DECLARING_NODE_TYPES;
@@ -46,10 +60,12 @@ import static org.apache.jackrabbit.oak.
 import static org.apache.jackrabbit.oak.plugins.index.lucene.LuceneIndexConstants.BLOB_SIZE;
 import static org.apache.jackrabbit.oak.plugins.index.lucene.LuceneIndexConstants.EXCLUDE_PROPERTY_NAMES;
 import static org.apache.jackrabbit.oak.plugins.index.lucene.LuceneIndexConstants.EXPERIMENTAL_STORAGE;
+import static org.apache.jackrabbit.oak.plugins.index.lucene.LuceneIndexConstants.FIELD_BOOST;
 import static org.apache.jackrabbit.oak.plugins.index.lucene.LuceneIndexConstants.FULL_TEXT_ENABLED;
 import static org.apache.jackrabbit.oak.plugins.index.lucene.LuceneIndexConstants.INCLUDE_PROPERTY_NAMES;
 import static org.apache.jackrabbit.oak.plugins.index.lucene.LuceneIndexConstants.INCLUDE_PROPERTY_TYPES;
 import static org.apache.jackrabbit.oak.plugins.index.lucene.LuceneIndexConstants.ORDERED_PROP_NAMES;
+import static org.apache.jackrabbit.oak.plugins.index.lucene.PropertyDefinition.DEFAULT_BOOST;
 import static org.apache.jackrabbit.oak.plugins.index.lucene.util.ConfigUtil.getOptionalValue;
 
 class IndexDefinition {
@@ -104,6 +120,11 @@ class IndexDefinition {
      */
     private final long entryCount;
 
+    /**
+     * The {@link IndexingRule}s inside this configuration. Keys being the NodeType names
+     */
+    private final Map<String, List<IndexingRule>> indexRules;
+
     public IndexDefinition(NodeState root, NodeState defn) {
         this.root = root;
         this.definition = defn;
@@ -135,6 +156,9 @@ class IndexDefinition {
         //TODO Flag out invalid propertyNames like one which are absolute
         this.relativeProps = collectRelativeProps(Iterables.concat(includes, orderedProps));
         this.propDefns = collectPropertyDefns(defn);
+
+        this.indexRules = collectIndexRules(defn.getChildNode(LuceneIndexConstants.INDEX_RULES));
+
         this.relativePropNames = collectRelPropertyNames(this.relativeProps.values());
         this.relativePropsMaxLevels = getRelPropertyMaxLevels(this.relativeProps.values());
 
@@ -335,6 +359,238 @@ class IndexDefinition {
         return codec;
     }
 
+    //~---------------------------------------------------< IndexRule >
+
+    /**
+     * 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.
+     */
+    public IndexingRule getApplicableIndexingRule(Tree state) {
+        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){
+        //TODO if a rule is defined for nt:base then this map would have entry for each
+        //registered nodeType in the system
+
+        //TODO Add check that children are orderable
+
+        Map<String, List<IndexingRule>> nt2rules = newHashMap();
+        ReadOnlyNodeTypeManager ntReg = ReadOnlyNodeTypeManager.getInstance(new ImmutableRoot(root),
+                NamePathMapper.DEFAULT);
+
+        for (ChildNodeEntry ruleEntry : indexRules.getChildNodeEntries()) {
+            IndexingRule rule = new IndexingRule(ruleEntry.getName(), ruleEntry.getNodeState());
+            // register under node type and all its sub types
+            log.debug("Found rule '{}' for NodeType '{}'", rule, rule.getNodeTypeName());
+            NodeTypeIterator ntItr = getAllNodeTypes(ntReg);
+            while (ntItr.hasNext()) {
+                String ntName = ntItr.nextNodeType().getName();
+
+                if (ntReg.isNodeType(ntName, rule.getNodeTypeName())) {
+                    List<IndexingRule> perNtConfig = nt2rules.get(ntName);
+                    if (perNtConfig == null) {
+                        perNtConfig = new ArrayList<IndexingRule>();
+                        nt2rules.put(ntName, perNtConfig);
+                    }
+                    log.debug("Registering it for name '{}'", 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);
+    }
+
+    public class IndexingRule {
+        private final String nodeTypeName;
+        private final Map<String, PropertyDefinition> propConfigs;
+        private final List<NamePattern> namePatterns;
+        final float boost;
+
+        IndexingRule(String nodeTypeName, NodeState config) {
+            this.nodeTypeName = nodeTypeName;
+            this.boost = getOptionalValue(config, FIELD_BOOST, DEFAULT_BOOST);
+            this.propConfigs = collectPropConfigs(config);
+            this.namePatterns = collectNamePatterns(propConfigs.values());
+        }
+
+        /**
+         * 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.propConfigs = original.propConfigs;
+            this.namePatterns = original.namePatterns;
+            this.boost = original.boost;
+        }
+
+        /**
+         * 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;
+        }
+
+        /**
+         * 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.
+         */
+        private boolean appliesTo(Tree state) {
+            if (!nodeTypeName.equals(getPrimaryTypeName(state))) {
+                return false;
+            }
+            //TODO Add support for condition
+            //return condition == null || condition.evaluate(state);
+            return true;
+        }
+
+        /**
+         * @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);
+            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;
+        }
+
+        private Map<String, PropertyDefinition> collectPropConfigs(NodeState config) {
+            Map<String, PropertyDefinition> propDefns = newHashMap();
+            NodeState propNode = config.getChildNode(LuceneIndexConstants.PROP_NODE);
+            //Include all immediate child nodes to 'properties' node by default
+            for (String propName : propNode.getChildNodeNames()) {
+                NodeState propDefnNode = propNode.getChildNode(propName);
+                if (propDefnNode.exists() && !propDefns.containsKey(propName)) {
+                    PropertyDefinition pd = new PropertyDefinition(IndexDefinition.this, propName, propDefnNode);
+                    propDefns.put(pd.name, pd);
+                }
+            }
+            return ImmutableMap.copyOf(propDefns);
+        }
+
+        private List<NamePattern> collectNamePatterns(Collection<PropertyDefinition> propConfigs) {
+            List<NamePattern> patterns = newArrayList();
+            for (PropertyDefinition pd : propConfigs) {
+                if (pd.isRegexp) {
+                    patterns.add(new NamePattern(pd.name, pd));
+                }
+            }
+            return ImmutableList.copyOf(patterns);
+        }
+    }
+
+    /**
+     * A property name pattern.
+     */
+    private static final class NamePattern {
+        /**
+         * 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){
+
+            this.pattern = Pattern.compile(pattern);
+            this.config = config;
+        }
+
+        /**
+         * @param path 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) {
+            return pattern.matcher(propertyPath).matches();
+        }
+
+        PropertyDefinition getConfig() {
+            return config;
+        }
+    }
+
+    //~---------------------------------------------< utility >
+
     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();
@@ -347,4 +603,23 @@ class IndexDefinition {
         }
         return ImmutableSet.copyOf(result);
     }
+
+    private static NodeTypeIterator getAllNodeTypes(ReadOnlyNodeTypeManager ntReg) {
+        try {
+            return ntReg.getAllNodeTypes();
+        } catch (RepositoryException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private static String getPrimaryTypeName(Tree state) {
+        String primaryType = TreeUtil.getPrimaryTypeName(state);
+        //In case not a proper JCR assume nt:base
+        return primaryType != null ? primaryType : "nt:base";
+    }
+
+    private static Iterable<String> getMixinTypeNames(Tree tree) {
+        PropertyState property = tree.getProperty(JcrConstants.JCR_MIMETYPE);
+        return property != null ? property.getValue(Type.NAMES) : Collections.<String>emptyList();
+    }
 }

Modified: jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexConstants.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexConstants.java?rev=1638781&r1=1638780&r2=1638781&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexConstants.java (original)
+++ jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexConstants.java Wed Nov 12 13:13:52 2014
@@ -101,9 +101,17 @@ public interface LuceneIndexConstants {
      */
     String PROP_NODE = "properties";
 
+    String INDEX_RULES = "indexRules";
+
     /**
      * Field boost factor
      */
     String FIELD_BOOST = "boost";
 
+    /**
+     * Property name defined explicitly. Mostly used in case of relative property names
+     */
+    String PROP_NAME = "name";
+
+    String PROP_IS_REGEX = "isRegexp";
 }

Modified: jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/PropertyDefinition.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/PropertyDefinition.java?rev=1638781&r1=1638780&r2=1638781&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/PropertyDefinition.java (original)
+++ jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/PropertyDefinition.java Wed Nov 12 13:13:52 2014
@@ -21,11 +21,14 @@ package org.apache.jackrabbit.oak.plugin
 
 import javax.jcr.PropertyType;
 
+import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.api.Type;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import static org.apache.jackrabbit.oak.plugins.index.lucene.LuceneIndexConstants.FIELD_BOOST;
+import static org.apache.jackrabbit.oak.plugins.index.lucene.LuceneIndexConstants.PROP_IS_REGEX;
 import static org.apache.jackrabbit.oak.plugins.index.lucene.util.ConfigUtil.getOptionalValue;
 
 class PropertyDefinition {
@@ -33,10 +36,16 @@ class PropertyDefinition {
     /**
      * The default boost: 1.0f.
      */
-    private static final float DEFAULT_BOOST = 1.0f;
+    static final float DEFAULT_BOOST = 1.0f;
 
-    private final String name;
     private final NodeState definition;
+    /**
+     * Property name. By default derived from the NodeState name which has the
+     * property definition. However in case property name is a pattern, relative
+     * property etc then it should be defined via 'name' property in NodeState.
+     * In such case NodeState name can be set to anything
+     */
+    final String name;
 
     final int propertyType;
     /**
@@ -44,8 +53,11 @@ class PropertyDefinition {
      */
     final float boost;
 
+    final boolean isRegexp;
+
     public PropertyDefinition(IndexDefinition idxDefn, String name, NodeState defn) {
-        this.name = name;
+        this.isRegexp = getOptionalValue(defn, PROP_IS_REGEX, false);
+        this.name = getName(defn, name);
         this.definition = defn;
         this.boost = getOptionalValue(defn, FIELD_BOOST, DEFAULT_BOOST);
         this.propertyType = getPropertyType(idxDefn, name, defn);
@@ -55,6 +67,13 @@ class PropertyDefinition {
         return propertyType;
     }
 
+    //~---------------------------------------------< internal >
+
+    private static String getName(NodeState definition, String defaultName){
+        PropertyState ps = definition.getProperty(LuceneIndexConstants.PROP_NAME);
+        return ps == null ? defaultName : ps.getValue(Type.STRING);
+    }
+
     private static int getPropertyType(IndexDefinition idxDefn, String name, NodeState defn) {
         int type = PropertyType.UNDEFINED;
         if (defn.hasProperty(LuceneIndexConstants.PROP_TYPE)) {

Modified: jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexDefinitionTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexDefinitionTest.java?rev=1638781&r1=1638780&r2=1638781&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexDefinitionTest.java (original)
+++ jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexDefinitionTest.java Wed Nov 12 13:13:52 2014
@@ -22,17 +22,25 @@ package org.apache.jackrabbit.oak.plugin
 import javax.jcr.PropertyType;
 
 import com.google.common.collect.Iterables;
+import org.apache.jackrabbit.JcrConstants;
+import org.apache.jackrabbit.oak.api.Tree;
+import org.apache.jackrabbit.oak.commons.PathUtils;
+import org.apache.jackrabbit.oak.plugins.tree.ImmutableTree;
 import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
 import org.apache.lucene.codecs.Codec;
+import org.apache.jackrabbit.oak.plugins.index.lucene.IndexDefinition.IndexingRule;
 import org.junit.Test;
 
+import static com.google.common.base.Preconditions.checkNotNull;
 import static com.google.common.collect.ImmutableSet.of;
 import static javax.jcr.PropertyType.TYPENAME_LONG;
 import static org.apache.jackrabbit.oak.api.Type.STRINGS;
 import static org.apache.jackrabbit.oak.plugins.index.lucene.LuceneIndexConstants.INCLUDE_PROPERTY_NAMES;
 import static org.apache.jackrabbit.oak.plugins.index.lucene.LuceneIndexConstants.INCLUDE_PROPERTY_TYPES;
+import static org.apache.jackrabbit.oak.plugins.index.lucene.LuceneIndexConstants.INDEX_RULES;
 import static org.apache.jackrabbit.oak.plugins.index.lucene.LuceneIndexConstants.PROP_NODE;
+import static org.apache.jackrabbit.oak.plugins.memory.EmptyNodeState.EMPTY_NODE;
 import static org.apache.jackrabbit.oak.plugins.memory.PropertyStates.createProperty;
 import static org.apache.jackrabbit.oak.plugins.nodetype.write.InitialContent.INITIAL_CONTENT;
 import static org.junit.Assert.assertEquals;
@@ -144,4 +152,83 @@ public class IndexDefinitionTest {
         assertEquals(PropertyType.DATE, defn.getPropDefn("foo1/bar").getPropertyType());
         assertEquals(PropertyType.LONG, defn.getPropDefn("foo2/bar2/baz").getPropertyType());
     }
+
+    @Test
+    public void indexRuleSanity() throws Exception{
+        NodeBuilder rules = builder.child(INDEX_RULES);
+        rules.child("nt:folder").setProperty(LuceneIndexConstants.FIELD_BOOST, 2.0);
+        child(rules, "nt:folder/properties/prop1")
+                .setProperty(LuceneIndexConstants.FIELD_BOOST, 3.0)
+                .setProperty(LuceneIndexConstants.PROP_TYPE, PropertyType.TYPENAME_BOOLEAN);
+
+        IndexDefinition defn = new IndexDefinition(root, builder.getNodeState());
+
+        assertNull(defn.getApplicableIndexingRule(newTree(newNode("nt:base"))));
+
+        IndexingRule rule1 = defn.getApplicableIndexingRule(newTree(newNode("nt:folder")));
+        assertNotNull(rule1);
+        assertEquals(2.0f, rule1.boost, 0);
+
+        assertTrue(rule1.isIndexed("prop1"));
+        assertFalse(rule1.isIndexed("prop2"));
+
+        PropertyDefinition pd = rule1.getConfig("prop1");
+        assertEquals(3.0f, pd.boost, 0);
+        assertEquals(PropertyType.BOOLEAN, pd.propertyType);
+    }
+
+    @Test
+    public void indexRuleInheritance() throws Exception{
+        NodeBuilder rules = builder.child(INDEX_RULES);
+        rules.child("nt:hierarchyNode").setProperty(LuceneIndexConstants.FIELD_BOOST, 2.0);
+
+        IndexDefinition defn = new IndexDefinition(root, builder.getNodeState());
+
+        assertNull(defn.getApplicableIndexingRule(newTree(newNode("nt:base"))));
+        assertNotNull(defn.getApplicableIndexingRule(newTree(newNode("nt:hierarchyNode"))));
+        assertNotNull(defn.getApplicableIndexingRule(newTree(newNode("nt:folder"))));
+
+        //TODO Inheritance and mixin
+    }
+
+    @Test
+    public void indexRuleWithPropertyRegEx() throws Exception{
+        NodeBuilder rules = builder.child(INDEX_RULES);
+        rules.child("nt:folder");
+        child(rules, "nt:folder/properties/prop1")
+                .setProperty(LuceneIndexConstants.FIELD_BOOST, 3.0);
+        child(rules, "nt:folder/properties/prop2")
+                .setProperty(LuceneIndexConstants.PROP_NAME, "foo.*")
+                .setProperty(LuceneIndexConstants.PROP_IS_REGEX, true)
+                .setProperty(LuceneIndexConstants.FIELD_BOOST, 4.0);
+
+        IndexDefinition defn = new IndexDefinition(root, builder.getNodeState());
+
+        IndexingRule rule1 = defn.getApplicableIndexingRule(newTree(newNode("nt:folder")));
+        assertNotNull(rule1);
+
+        assertTrue(rule1.isIndexed("prop1"));
+        assertFalse(rule1.isIndexed("prop2"));
+        assertTrue(rule1.isIndexed("fooProp"));
+
+        PropertyDefinition pd = rule1.getConfig("fooProp2");
+        assertEquals(4.0f, pd.boost, 0);
+    }
+
+    private static Tree newTree(NodeBuilder nb){
+        return new ImmutableTree(nb.getNodeState());
+    }
+
+    private static NodeBuilder newNode(String typeName){
+        NodeBuilder builder = EMPTY_NODE.builder();
+        builder.setProperty(JcrConstants.JCR_PRIMARYTYPE, typeName);
+        return builder;
+    }
+
+    private static NodeBuilder child(NodeBuilder nb, String path) {
+        for (String name : PathUtils.elements(checkNotNull(path))) {
+            nb = nb.child(name);
+        }
+        return nb;
+    }
 }