diff --git a/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexDefinition.java b/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexDefinition.java index efba33b..1d1b232 100644 --- a/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexDefinition.java +++ b/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexDefinition.java @@ -226,6 +226,7 @@ public final class IndexDefinition implements Aggregate.AggregateMapper { private final boolean suggestAnalyzed; private final boolean secureFacets; + private final int numberOfTopFacets; private final boolean suggestEnabled; @@ -305,7 +306,16 @@ public final class IndexDefinition implements Aggregate.AggregateMapper { this.queryPaths = getQueryPaths(defn); this.saveDirListing = getOptionalValue(defn, LuceneIndexConstants.SAVE_DIR_LISTING, true); this.suggestAnalyzed = evaluateSuggestAnalyzed(defn, false); - this.secureFacets = defn.hasChildNode(FACETS) && getOptionalValue(defn.getChildNode(FACETS), PROP_SECURE_FACETS, true); + + if (defn.hasChildNode(FACETS)) { + NodeState facetsConfig = defn.getChildNode(FACETS); + this.secureFacets = getOptionalValue(facetsConfig, PROP_SECURE_FACETS, true); + this.numberOfTopFacets = getOptionalValue(facetsConfig, PROP_FACETS_TOP_CHILDREN, 10); + } else { + this.secureFacets = true; + this.numberOfTopFacets = 10; + } + this.suggestEnabled = evaluateSuggestionEnabled(); this.spellcheckEnabled = evaluateSpellcheckEnabled(); this.nrtIndexMode = supportsNRTIndexing(defn); @@ -722,6 +732,10 @@ public final class IndexDefinition implements Aggregate.AggregateMapper { return secureFacets; } + public int getNumberOfTopFacets() { + return numberOfTopFacets; + } + public class IndexingRule { private final String baseNodeType; private final String nodeTypeName; diff --git a/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexConstants.java b/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexConstants.java index a70d083..bf10e15 100644 --- a/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexConstants.java +++ b/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexConstants.java @@ -340,6 +340,13 @@ public interface LuceneIndexConstants { String PROP_SECURE_FACETS = "secure"; /** + * Optional (index definition) property indicating max number of facets that will be retrieved + * in query + * Default is 10 + */ + String PROP_FACETS_TOP_CHILDREN = "topChildren"; + + /** * Optional (property definition) property indicating whether facets should be created * for this property */ diff --git a/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LucenePropertyIndex.java b/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LucenePropertyIndex.java index aebfdf6..35ef935 100644 --- a/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LucenePropertyIndex.java +++ b/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LucenePropertyIndex.java @@ -1559,6 +1559,7 @@ public class LucenePropertyIndex implements AdvancedQueryIndex, QueryIndex, Nati LuceneResultRow currentRow; private final SizeEstimator sizeEstimator; private long estimatedSize; + private int numberOfFacets; LucenePathCursor(final Iterator it, final IndexPlan plan, QueryEngineSettings settings, SizeEstimator sizeEstimator) { pathPrefix = plan.getPathPrefix(); @@ -1582,7 +1583,10 @@ public class LucenePropertyIndex implements AdvancedQueryIndex, QueryIndex, Nati } }; - pathCursor = new PathCursor(pathIterator, getPlanResult(plan).isUniquePathsRequired(), settings); + + PlanResult planResult = getPlanResult(plan); + pathCursor = new PathCursor(pathIterator, planResult.isUniquePathsRequired(), settings); + numberOfFacets = planResult.indexDefinition.getNumberOfTopFacets(); } @@ -1640,7 +1644,7 @@ public class LucenePropertyIndex implements AdvancedQueryIndex, QueryIndex, Nati Facets facets = currentRow.facets; try { if (facets != null) { - FacetResult topChildren = facets.getTopChildren(10, facetFieldName); + FacetResult topChildren = facets.getTopChildren(numberOfFacets, facetFieldName); if (topChildren != null) { JsopWriter writer = new JsopBuilder(); writer.object(); diff --git a/oak-lucene/src/test/java/org/apache/jackrabbit/oak/jcr/query/FacetTest.java b/oak-lucene/src/test/java/org/apache/jackrabbit/oak/jcr/query/FacetTest.java index ea04baa..793ba9a 100644 --- a/oak-lucene/src/test/java/org/apache/jackrabbit/oak/jcr/query/FacetTest.java +++ b/oak-lucene/src/test/java/org/apache/jackrabbit/oak/jcr/query/FacetTest.java @@ -20,6 +20,7 @@ package org.apache.jackrabbit.oak.jcr.query; import javax.jcr.Node; import javax.jcr.NodeIterator; +import javax.jcr.RepositoryException; import javax.jcr.Session; import javax.jcr.query.Query; import javax.jcr.query.QueryManager; @@ -38,6 +39,8 @@ import org.junit.Before; public class FacetTest extends AbstractQueryTest { public static final String FACET_CONFING_PROP_PATH = "/oak:index/luceneGlobal/indexRules/nt:base/properties/allProps/facets"; + public static final String FACET_CONFING_NODE_PATH = "/oak:index/luceneGlobal/facets"; + public static final String INDEX_CONFING_NODE_PATH = "/oak:index/luceneGlobal"; @Before protected void setUp() throws Exception { @@ -48,6 +51,13 @@ public class FacetTest extends AbstractQueryTest { superuser.save(); superuser.refresh(true); } + + if (!superuser.nodeExists(FACET_CONFING_NODE_PATH)) { + Node node = superuser.getNode(INDEX_CONFING_NODE_PATH); + node.addNode(LuceneIndexConstants.FACETS); + superuser.save(); + superuser.refresh(true); + } } @After @@ -57,6 +67,13 @@ public class FacetTest extends AbstractQueryTest { superuser.save(); superuser.refresh(true); } + + if (superuser.nodeExists(FACET_CONFING_NODE_PATH)) { + superuser.getNode(FACET_CONFING_NODE_PATH).remove(); + superuser.save(); + superuser.refresh(true); + } + super.tearDown(); } @@ -299,4 +316,147 @@ public class FacetTest extends AbstractQueryTest { assertFalse(nodes.hasNext()); } + public void testFacetRetrievalDefaultNumberOfFacets() throws RepositoryException { + Session session = superuser; + Node n1 = testRootNode.addNode("node1"); + String pn = "jcr:title"; + n1.setProperty(pn, "hello 1"); + Node n2 = testRootNode.addNode("node2"); + n2.setProperty(pn, "hallo 2"); + Node n3 = testRootNode.addNode("node3"); + n3.setProperty(pn, "hallo 3"); + Node n4 = testRootNode.addNode("node4"); + n4.setProperty(pn, "hallo 4"); + Node n5 = testRootNode.addNode("node5"); + n5.setProperty(pn, "hallo 5"); + Node n6 = testRootNode.addNode("node6"); + n6.setProperty(pn, "hallo 6"); + Node n7 = testRootNode.addNode("node7"); + n7.setProperty(pn, "hallo 7"); + Node n8 = testRootNode.addNode("node8"); + n8.setProperty(pn, "hallo 8"); + Node n9 = testRootNode.addNode("node9"); + n9.setProperty(pn, "hallo 9"); + Node n10 = testRootNode.addNode("node10"); + n10.setProperty(pn, "hallo 10"); + Node n11 = testRootNode.addNode("node11"); + n11.setProperty(pn, "hallo 11"); + Node n12 = testRootNode.addNode("node12"); + n12.setProperty(pn, "hallo 12"); + session.save(); + + QueryManager qm = session.getWorkspace().getQueryManager(); + String sql2 = "select [jcr:path], [rep:facet(" + pn + ")] from [nt:base] " + + "where contains([" + pn + "], 'hallo') order by [jcr:path]"; + Query q = qm.createQuery(sql2, Query.JCR_SQL2); + QueryResult result = q.execute(); + FacetResult facetResult = new FacetResult(result); + assertNotNull(facetResult); + assertNotNull(facetResult.getDimensions()); + assertEquals(1, facetResult.getDimensions().size()); + assertTrue(facetResult.getDimensions().contains(pn)); + List facets = facetResult.getFacets(pn); + assertNotNull(facets); + assertEquals(10, facets.size()); + } + + public void testFacetRetrievalNumberOfFacetsConfiguredHigherThanDefault() throws RepositoryException { + + Node facetsConfig = superuser.getNode(FACET_CONFING_NODE_PATH); + facetsConfig.setProperty(LuceneIndexConstants.PROP_FACETS_TOP_CHILDREN, 11); + superuser.save(); + superuser.refresh(true); + + Session session = superuser; + Node n1 = testRootNode.addNode("node1"); + String pn = "jcr:title"; + n1.setProperty(pn, "hello 1"); + Node n2 = testRootNode.addNode("node2"); + n2.setProperty(pn, "hallo 2"); + Node n3 = testRootNode.addNode("node3"); + n3.setProperty(pn, "hallo 3"); + Node n4 = testRootNode.addNode("node4"); + n4.setProperty(pn, "hallo 4"); + Node n5 = testRootNode.addNode("node5"); + n5.setProperty(pn, "hallo 5"); + Node n6 = testRootNode.addNode("node6"); + n6.setProperty(pn, "hallo 6"); + Node n7 = testRootNode.addNode("node7"); + n7.setProperty(pn, "hallo 7"); + Node n8 = testRootNode.addNode("node8"); + n8.setProperty(pn, "hallo 8"); + Node n9 = testRootNode.addNode("node9"); + n9.setProperty(pn, "hallo 9"); + Node n10 = testRootNode.addNode("node10"); + n10.setProperty(pn, "hallo 10"); + Node n11 = testRootNode.addNode("node11"); + n11.setProperty(pn, "hallo 11"); + Node n12 = testRootNode.addNode("node12"); + n12.setProperty(pn, "hallo 12"); + session.save(); + + QueryManager qm = session.getWorkspace().getQueryManager(); + String sql2 = "select [jcr:path], [rep:facet(" + pn + ")] from [nt:base] " + + "where contains([" + pn + "], 'hallo') order by [jcr:path]"; + Query q = qm.createQuery(sql2, Query.JCR_SQL2); + QueryResult result = q.execute(); + FacetResult facetResult = new FacetResult(result); + assertNotNull(facetResult); + assertNotNull(facetResult.getDimensions()); + assertEquals(1, facetResult.getDimensions().size()); + assertTrue(facetResult.getDimensions().contains(pn)); + List facets = facetResult.getFacets(pn); + assertNotNull(facets); + assertEquals(11, facets.size()); + } + + public void testFacetRetrievalNumberOfFacetsConfiguredLowerThanDefault() throws RepositoryException { + + Node facetsConfig = superuser.getNode(FACET_CONFING_NODE_PATH); + facetsConfig.setProperty(LuceneIndexConstants.PROP_FACETS_TOP_CHILDREN, 7); + superuser.save(); + superuser.refresh(true); + + Session session = superuser; + Node n1 = testRootNode.addNode("node1"); + String pn = "jcr:title"; + n1.setProperty(pn, "hello 1"); + Node n2 = testRootNode.addNode("node2"); + n2.setProperty(pn, "hallo 2"); + Node n3 = testRootNode.addNode("node3"); + n3.setProperty(pn, "hallo 3"); + Node n4 = testRootNode.addNode("node4"); + n4.setProperty(pn, "hallo 4"); + Node n5 = testRootNode.addNode("node5"); + n5.setProperty(pn, "hallo 5"); + Node n6 = testRootNode.addNode("node6"); + n6.setProperty(pn, "hallo 6"); + Node n7 = testRootNode.addNode("node7"); + n7.setProperty(pn, "hallo 7"); + Node n8 = testRootNode.addNode("node8"); + n8.setProperty(pn, "hallo 8"); + Node n9 = testRootNode.addNode("node9"); + n9.setProperty(pn, "hallo 9"); + Node n10 = testRootNode.addNode("node10"); + n10.setProperty(pn, "hallo 10"); + Node n11 = testRootNode.addNode("node11"); + n11.setProperty(pn, "hallo 11"); + Node n12 = testRootNode.addNode("node12"); + n12.setProperty(pn, "hallo 12"); + session.save(); + + QueryManager qm = session.getWorkspace().getQueryManager(); + String sql2 = "select [jcr:path], [rep:facet(" + pn + ")] from [nt:base] " + + "where contains([" + pn + "], 'hallo') order by [jcr:path]"; + Query q = qm.createQuery(sql2, Query.JCR_SQL2); + QueryResult result = q.execute(); + FacetResult facetResult = new FacetResult(result); + assertNotNull(facetResult); + assertNotNull(facetResult.getDimensions()); + assertEquals(1, facetResult.getDimensions().size()); + assertTrue(facetResult.getDimensions().contains(pn)); + List facets = facetResult.getFacets(pn); + assertNotNull(facets); + assertEquals(7, facets.size()); + } } \ No newline at end of file