Author: tommaso
Date: Mon Jul 27 21:16:08 2015
New Revision: 1692955

URL: http://svn.apache.org/r1692955
Log:
OAK-3147 - configurable jcr:content descendants collapsing

Modified:
    
jackrabbit/oak/trunk/oak-solr-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/solr/configuration/DefaultSolrConfiguration.java
    
jackrabbit/oak/trunk/oak-solr-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/solr/configuration/OakSolrConfiguration.java
    
jackrabbit/oak/trunk/oak-solr-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/solr/configuration/OakSolrConfigurationDefaults.java
    
jackrabbit/oak/trunk/oak-solr-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/solr/configuration/nodestate/OakSolrNodeStateConfiguration.java
    
jackrabbit/oak/trunk/oak-solr-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/solr/index/SolrIndexEditor.java
    
jackrabbit/oak/trunk/oak-solr-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/solr/osgi/OakSolrConfigurationProviderService.java
    
jackrabbit/oak/trunk/oak-solr-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/solr/query/FilterQueryParser.java
    
jackrabbit/oak/trunk/oak-solr-core/src/main/resources/solr/oak/conf/schema.xml
    
jackrabbit/oak/trunk/oak-solr-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/solr/query/FilterQueryParserTest.java
    
jackrabbit/oak/trunk/oak-solr-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/solr/query/SolrIndexQueryTestIT.java
    
jackrabbit/oak/trunk/oak-solr-core/src/test/resources/solr/oak/conf/schema.xml

Modified: 
jackrabbit/oak/trunk/oak-solr-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/solr/configuration/DefaultSolrConfiguration.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-solr-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/solr/configuration/DefaultSolrConfiguration.java?rev=1692955&r1=1692954&r2=1692955&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-solr-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/solr/configuration/DefaultSolrConfiguration.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-solr-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/solr/configuration/DefaultSolrConfiguration.java
 Mon Jul 27 21:16:08 2015
@@ -121,4 +121,15 @@ public class DefaultSolrConfiguration im
         return usedProperties;
     }
 
+    @Override
+    public boolean collapseJcrContentNodes() {
+        return OakSolrConfigurationDefaults.COLLAPSE_JCR_CONTENT_NODES;
+    }
+
+    @Nonnull
+    @Override
+    public String getCollapsedPathField() {
+        return OakSolrConfigurationDefaults.COLLAPSED_PATH_FIELD;
+    }
+
 }

Modified: 
jackrabbit/oak/trunk/oak-solr-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/solr/configuration/OakSolrConfiguration.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-solr-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/solr/configuration/OakSolrConfiguration.java?rev=1692955&r1=1692954&r2=1692955&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-solr-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/solr/configuration/OakSolrConfiguration.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-solr-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/solr/configuration/OakSolrConfiguration.java
 Mon Jul 27 21:16:08 2015
@@ -132,6 +132,25 @@ public interface OakSolrConfiguration {
     Collection<String> getUsedProperties();
 
     /**
+     * Make all nodes / documents matching a query that are descendants of a 
'jcr:content' node collapse into such a
+     * node. That will result in resultsets being tipically much more compact 
in cases where most / all of the matching
+     * nodes match such a hierarchy.
+     *
+     * @return {@code true} if only the 'jcr:content' node should be returned 
for all its the matching descendants,
+     * {@code false} otherwise
+     */
+    boolean collapseJcrContentNodes();
+
+    /**
+     * Provide the name of the field to be used for indexing the collapsed 
path of each node when {@link #collapseJcrContentNodes()}
+     * is set to {@code true}.
+     *
+     * @return the name of the Solr field to be used for indexing and 
searching on collapsed paths.
+     */
+    @Nonnull
+    String getCollapsedPathField();
+
+    /**
      * Enum for describing Solr commit policy used in a certain instance
      */
     enum CommitPolicy {

Modified: 
jackrabbit/oak/trunk/oak-solr-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/solr/configuration/OakSolrConfigurationDefaults.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-solr-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/solr/configuration/OakSolrConfigurationDefaults.java?rev=1692955&r1=1692954&r2=1692955&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-solr-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/solr/configuration/OakSolrConfigurationDefaults.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-solr-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/solr/configuration/OakSolrConfigurationDefaults.java
 Mon Jul 27 21:16:08 2015
@@ -38,5 +38,6 @@ public class OakSolrConfigurationDefault
             Arrays.asList("rep:members", "rep:authorizableId", "jcr:uuid", 
"rep:principalName", "rep:password"));
     public static final String TYPE_MAPPINGS = "";
     public static final String PROPERTY_MAPPINGS = "";
-
+    public static final boolean COLLAPSE_JCR_CONTENT_NODES = false;
+    public static final String COLLAPSED_PATH_FIELD = "path_collapsed";
 }

Modified: 
jackrabbit/oak/trunk/oak-solr-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/solr/configuration/nodestate/OakSolrNodeStateConfiguration.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-solr-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/solr/configuration/nodestate/OakSolrNodeStateConfiguration.java?rev=1692955&r1=1692954&r2=1692955&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-solr-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/solr/configuration/nodestate/OakSolrNodeStateConfiguration.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-solr-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/solr/configuration/nodestate/OakSolrNodeStateConfiguration.java
 Mon Jul 27 21:16:08 2015
@@ -179,6 +179,17 @@ public class OakSolrNodeStateConfigurati
         return usedProperties;
     }
 
+    @Override
+    public boolean collapseJcrContentNodes() {
+        return getBooleanValueFor(Properties.COLLAPSE_JCR_CONTENT_NODES, 
OakSolrConfigurationDefaults.COLLAPSE_JCR_CONTENT_NODES);
+    }
+
+    @Nonnull
+    @Override
+    public String getCollapsedPathField() {
+        return getStringValueFor(Properties.COLLAPSED_PATH_FIELD, 
OakSolrConfigurationDefaults.COLLAPSED_PATH_FIELD);
+    }
+
     private boolean getBooleanValueFor(String propertyName, boolean 
defaultValue) {
         boolean value = defaultValue;
         PropertyState property = definition.getProperty(propertyName);
@@ -228,6 +239,7 @@ public class OakSolrNodeStateConfigurati
     public final class Properties {
         // --> oak solr config properties <--
         public static final String PATH_FIELD = "pathField";
+        public static final String COLLAPSED_PATH_FIELD = "pathField";
         public static final String PARENT_FIELD = "parentField";
         public static final String CHILDREN_FIELD = "childrenField";
         public static final String DESCENDANTS_FIELD = "descendantsField";
@@ -241,6 +253,6 @@ public class OakSolrNodeStateConfigurati
         public static final String TYPE_MAPPINGS = "typeMappings";
         public static final String PROPERTY_MAPPINGS = "propertyMappings";
         public static final String USED_PROPERTIES = "usedProperties";
-
+        public static final String COLLAPSE_JCR_CONTENT_NODES = 
"collapseJcrContentNodes";
     }
 }

Modified: 
jackrabbit/oak/trunk/oak-solr-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/solr/index/SolrIndexEditor.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-solr-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/solr/index/SolrIndexEditor.java?rev=1692955&r1=1692954&r2=1692955&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-solr-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/solr/index/SolrIndexEditor.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-solr-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/solr/index/SolrIndexEditor.java
 Mon Jul 27 21:16:08 2015
@@ -210,6 +210,18 @@ class SolrIndexEditor implements IndexEd
         SolrInputDocument inputDocument = new SolrInputDocument();
         String path = getPath();
         inputDocument.addField(configuration.getPathField(), path);
+
+        if (configuration.collapseJcrContentNodes()) {
+            int jcrContentIndex = path.lastIndexOf(JcrConstants.JCR_CONTENT);
+            if (jcrContentIndex >= 0) {
+                int index = jcrContentIndex + 
JcrConstants.JCR_CONTENT.length();
+                String collapsedPath = path.substring(0, index);
+                inputDocument.addField(configuration.getCollapsedPathField(), 
collapsedPath);
+            } else {
+                inputDocument.addField(configuration.getCollapsedPathField(), 
path);
+            }
+        }
+
         for (PropertyState property : state.getProperties()) {
             if ((configuration.getUsedProperties().size() > 0 && 
configuration.getUsedProperties().contains(property.getName()))
                     || 
!configuration.getIgnoredProperties().contains(property.getName())) {

Modified: 
jackrabbit/oak/trunk/oak-solr-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/solr/osgi/OakSolrConfigurationProviderService.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-solr-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/solr/osgi/OakSolrConfigurationProviderService.java?rev=1692955&r1=1692954&r2=1692955&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-solr-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/solr/osgi/OakSolrConfigurationProviderService.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-solr-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/solr/osgi/OakSolrConfigurationProviderService.java
 Mon Jul 27 21:16:08 2015
@@ -59,6 +59,9 @@ public class OakSolrConfigurationProvide
     @Property(value = OakSolrConfigurationDefaults.CATCHALL_FIELD, label = 
"catch all field")
     private static final String CATCH_ALL_FIELD = "catch.all.field";
 
+    @Property(value = OakSolrConfigurationDefaults.COLLAPSED_PATH_FIELD, label 
= "field for collapsing jcr:content paths")
+    private static final String COLLAPSED_PATH_FIELD = "collapsed.path.field";
+
     @Property(options = {
             @PropertyOption(name = "HARD",
                     value = "Hard commit"
@@ -73,7 +76,6 @@ public class OakSolrConfigurationProvide
     )
     private static final String COMMIT_POLICY = "commit.policy";
 
-
     @Property(intValue = OakSolrConfigurationDefaults.ROWS, label = "rows")
     private static final String ROWS = "rows";
 
@@ -103,10 +105,14 @@ public class OakSolrConfigurationProvide
             "mappings from JCR property names to Solr fields")
     private static final String PROPERTY_MAPPINGS = "property.mappings";
 
+    @Property(boolValue = 
OakSolrConfigurationDefaults.COLLAPSE_JCR_CONTENT_NODES, label = "collapse 
jcr:content nodes")
+    private static final String COLLAPSE_JCR_CONTENT_NODES = 
"collapse.jcrcontent.nodes";
+
     private String pathChildrenFieldName;
     private String pathParentFieldName;
     private String pathDescendantsFieldName;
     private String pathExactFieldName;
+    private String collapsedPathField;
     private String catchAllField;
     private OakSolrConfiguration.CommitPolicy commitPolicy;
     private int rows;
@@ -117,6 +123,7 @@ public class OakSolrConfigurationProvide
     private String[] usedProperties;
     private String[] typeMappings;
     private String[] propertyMappings;
+    private boolean collapseJcrContentNodes;
 
     private OakSolrConfiguration oakSolrConfiguration;
 
@@ -125,6 +132,7 @@ public class OakSolrConfigurationProvide
         pathChildrenFieldName = 
String.valueOf(componentContext.getProperties().get(PATH_CHILDREN_FIELD));
         pathParentFieldName = 
String.valueOf(componentContext.getProperties().get(PATH_PARENT_FIELD));
         pathExactFieldName = 
String.valueOf(componentContext.getProperties().get(PATH_EXACT_FIELD));
+        collapsedPathField= 
String.valueOf(componentContext.getProperties().get(COLLAPSED_PATH_FIELD));
         pathDescendantsFieldName = 
String.valueOf(componentContext.getProperties().get(PATH_DESCENDANTS_FIELD));
         catchAllField = 
String.valueOf(componentContext.getProperties().get(CATCH_ALL_FIELD));
         rows = 
Integer.parseInt(String.valueOf(componentContext.getProperties().get(ROWS)));
@@ -136,6 +144,7 @@ public class OakSolrConfigurationProvide
         ignoredProperties = 
PropertiesUtil.toStringArray(componentContext.getProperties().get(IGNORED_PROPERTIES));
         usedProperties = 
PropertiesUtil.toStringArray(componentContext.getProperties().get(USED_PROPERTIES));
         propertyMappings = 
PropertiesUtil.toStringArray(componentContext.getProperties().get(PROPERTY_MAPPINGS));
+        collapseJcrContentNodes = 
Boolean.valueOf(String.valueOf(componentContext.getProperties().get(COLLAPSE_JCR_CONTENT_NODES)));
     }
 
     @Deactivate
@@ -262,6 +271,17 @@ public class OakSolrConfigurationProvide
                         return Collections.emptyList();
                     }
                 }
+
+                @Override
+                public boolean collapseJcrContentNodes() {
+                    return collapseJcrContentNodes;
+                }
+
+                @Nonnull
+                @Override
+                public String getCollapsedPathField() {
+                    return collapsedPathField;
+                }
             };
         }
         return oakSolrConfiguration;

Modified: 
jackrabbit/oak/trunk/oak-solr-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/solr/query/FilterQueryParser.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-solr-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/solr/query/FilterQueryParser.java?rev=1692955&r1=1692954&r2=1692955&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-solr-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/solr/query/FilterQueryParser.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-solr-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/solr/query/FilterQueryParser.java
 Mon Jul 27 21:16:08 2015
@@ -234,6 +234,10 @@ class FilterQueryParser {
             }
         }
 
+        if (configuration.collapseJcrContentNodes()) {
+            solrQuery.addFilterQuery("{!collapse field=" + 
configuration.getCollapsedPathField() + "}");
+        }
+
         if (queryBuilder.length() == 0) {
             queryBuilder.append("*:*");
         }

Modified: 
jackrabbit/oak/trunk/oak-solr-core/src/main/resources/solr/oak/conf/schema.xml
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-solr-core/src/main/resources/solr/oak/conf/schema.xml?rev=1692955&r1=1692954&r2=1692955&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-solr-core/src/main/resources/solr/oak/conf/schema.xml 
(original)
+++ 
jackrabbit/oak/trunk/oak-solr-core/src/main/resources/solr/oak/conf/schema.xml 
Mon Jul 27 21:16:08 2015
@@ -118,6 +118,7 @@
         <field name=":indexed" type="tdate" indexed="true" stored="false" 
default="NOW" docValues="true"/>
         <field name=":suggest-weight" type="tint" indexed="false" 
stored="false" default="1" docValues="true"/>
         <field name=":suggest" type="string" indexed="true" stored="true" 
multiValued="true" />
+        <field name="path_collapsed" type="string" indexed="true" 
stored="false"/>
         <field name="_version_" type="long" indexed="true" stored="true"/>
 
         <!-- sorting dynamic fields -->

Modified: 
jackrabbit/oak/trunk/oak-solr-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/solr/query/FilterQueryParserTest.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-solr-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/solr/query/FilterQueryParserTest.java?rev=1692955&r1=1692954&r2=1692955&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-solr-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/solr/query/FilterQueryParserTest.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-solr-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/solr/query/FilterQueryParserTest.java
 Mon Jul 27 21:16:08 2015
@@ -22,8 +22,11 @@ import org.apache.jackrabbit.oak.spi.que
 import org.apache.solr.client.solrj.SolrQuery;
 import org.junit.Test;
 
+import java.util.Arrays;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
@@ -57,7 +60,8 @@ public class FilterQueryParserTest {
         when(filter.getPath()).thenReturn("/");
         SolrQuery solrQuery = FilterQueryParser.getQuery(filter, null, 
configuration);
         assertNotNull(solrQuery);
-        
assertEquals(configuration.getFieldForPathRestriction(pathRestriction)+":\\/", 
solrQuery.get("fq"));
+        String[] filterQueries = solrQuery.getFilterQueries();
+        
assertTrue(Arrays.asList(filterQueries).contains(configuration.getFieldForPathRestriction(pathRestriction)
 + ":\\/"));
         assertEquals("*:*", solrQuery.get("q"));
     }
 

Modified: 
jackrabbit/oak/trunk/oak-solr-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/solr/query/SolrIndexQueryTestIT.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-solr-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/solr/query/SolrIndexQueryTestIT.java?rev=1692955&r1=1692954&r2=1692955&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-solr-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/solr/query/SolrIndexQueryTestIT.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-solr-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/solr/query/SolrIndexQueryTestIT.java
 Mon Jul 27 21:16:08 2015
@@ -296,20 +296,27 @@ public class SolrIndexQueryTestIT extend
     public void testRepSimilarXPathQuery() throws Exception {
         String query = "//element(*, nt:base)[rep:similar(., '/test/a')]";
         Tree test = root.getTree("/").addChild("test");
-        test.addChild("a").setProperty("text", "Hello World Hello World");
-        test.addChild("b").setProperty("text", "Hello World");
-        test.addChild("c").setProperty("text", "World");
-        test.addChild("d").setProperty("text", "Hello");
-        test.addChild("e").setProperty("text", "World");
-        test.addChild("f").setProperty("text", "Hello");
-        test.addChild("g").setProperty("text", "World");
-        test.addChild("h").setProperty("text", "Hello");
+        test.addChild("a").setProperty("text", "the quick brown fox jumped 
over the lazy white dog");
+        test.addChild("b").setProperty("text", "I am a dog");
+        test.addChild("c").setProperty("text", "dogs don't hurt");
+        test.addChild("d").setProperty("text", "white men can't jump");
+        test.addChild("e").setProperty("text", "the fox is brown");
+        test.addChild("f").setProperty("text", "a quickest dog jumped over the 
quick white dog");
+        test.addChild("g").setProperty("text", "hello world");
+        test.addChild("h").setProperty("text", "over the lazy top");
         root.commit();
         Iterator<String> result = executeQuery(query, "xpath").iterator();
         assertTrue(result.hasNext());
         assertEquals("/test/b", result.next());
         assertTrue(result.hasNext());
-        assertEquals("/test/c", result.next());
+        assertEquals("/test/d", result.next());
+        assertTrue(result.hasNext());
+        assertEquals("/test/e", result.next());
+        assertTrue(result.hasNext());
+        assertEquals("/test/f", result.next());
+        assertTrue(result.hasNext());
+        assertEquals("/test/h", result.next());
+        assertFalse(result.hasNext());
     }
 
     @Test

Modified: 
jackrabbit/oak/trunk/oak-solr-core/src/test/resources/solr/oak/conf/schema.xml
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-solr-core/src/test/resources/solr/oak/conf/schema.xml?rev=1692955&r1=1692954&r2=1692955&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-solr-core/src/test/resources/solr/oak/conf/schema.xml 
(original)
+++ 
jackrabbit/oak/trunk/oak-solr-core/src/test/resources/solr/oak/conf/schema.xml 
Mon Jul 27 21:16:08 2015
@@ -126,11 +126,12 @@
         <field name="path_des" type="descendent_path" indexed="true" 
stored="false"/>
         <field name="ignored" type="ignored" />
         <field name="catch_all" type="text_general" indexed="true" 
stored="false" multiValued="true" termVectors="true"/> <!-- term vectors used 
for rep:similar -->
-        <field name="_version_" type="long" indexed="true" stored="true"/>
         <field name=":path" type="string" indexed="true" stored="false"/>
         <field name=":indexed" type="tdate" indexed="true" stored="false" 
default="NOW" docValues="true"/>
         <field name=":suggest-weight" type="tint" indexed="false" 
stored="false" default="1"  docValues="true"/>
         <field name=":suggest" type="string" indexed="true" stored="true" 
multiValued="true" />
+        <field name="path_collapsed" type="string" indexed="true" 
stored="true"/>
+        <field name="_version_" type="long" indexed="true" stored="true"/>
 
         <!-- sorting dynamic fields -->
         <dynamicField name="*_double_sort" type="tdouble" indexed="false" 
stored="false" multiValued="false" docValues="true"/>


Reply via email to