OAK-4922 - Implement number of facets retrieved in query configurable for LucenePropertyIndex
git-svn-id: https://svn.apache.org/repos/asf/jackrabbit/oak/branches/1.4@1781917 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/oak-doc/src/site/markdown/query/lucene.md b/oak-doc/src/site/markdown/query/lucene.md
index 07f55c7..ee856df 100644
--- a/oak-doc/src/site/markdown/query/lucene.md
+++ b/oak-doc/src/site/markdown/query/lucene.md
@@ -1131,18 +1131,27 @@
index definition.
By default ACL checks are always performed on facets by the Lucene property index however this can be avoided by setting
the property _secure_ to _false_ in the _facets_ configuration node.
+`@since Oak 1.5.15` The no. of facets to be retrieved is configurable via the _topChildren_ property, which defaults to 10.
+
```
+/oak:index/lucene-with-unsecure-facets
+ - jcr:primaryType = "oak:QueryIndexDefinition"
+ - compatVersion = 2
+ - type = "lucene"
+ - async = "async"
+ + facets
+ - topChildren = 100
+ - secure = false
+ + indexRules
+ - jcr:primaryType = "nt:unstructured"
+ nt:base
+ properties
- jcr:primaryType = "nt:unstructured"
+ jcr:title
- facets = true
- propertyIndex = true
- + facets
- - secure = false
```
-
#### Score Explanation
`@since Oak 1.3.12`
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 78aadcc..29a6bc4 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
@@ -133,11 +133,16 @@
static final int TYPES_ALLOW_ALL = -1;
/**
- * Deafult suggesterUpdateFrequencyMinutes
+ * 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,
@@ -215,6 +220,8 @@
private final boolean secureFacets;
+ private final int numberOfTopFacets;
+
private final boolean suggestEnabled;
private final boolean spellcheckEnabled;
@@ -286,7 +293,16 @@
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, DEFAULT_FACET_COUNT);
+ } else {
+ this.secureFacets = true;
+ this.numberOfTopFacets = DEFAULT_FACET_COUNT;
+ }
+
this.suggestEnabled = evaluateSuggestionEnabled();
this.spellcheckEnabled = evaluateSpellcheckEnabled();
}
@@ -686,6 +702,10 @@
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 ccca3f2..532b423 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
@@ -326,6 +326,13 @@
String PROP_SECURE_FACETS = "secure";
/**
+ * Optional (index definition) property indicating max number of facets that will be retrieved
+ * in query
+ * Default is {@link IndexDefinition#DEFAULT_FACET_COUNT}
+ */
+ 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 c2de1cf..f2e7e00 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
@@ -1528,6 +1528,7 @@
LuceneResultRow currentRow;
private final SizeEstimator sizeEstimator;
private long estimatedSize;
+ private int numberOfFacets;
LucenePathCursor(final Iterator<LuceneResultRow> it, final IndexPlan plan, QueryEngineSettings settings, SizeEstimator sizeEstimator) {
pathPrefix = plan.getPathPrefix();
@@ -1551,7 +1552,10 @@
}
};
- pathCursor = new PathCursor(pathIterator, getPlanResult(plan).isUniquePathsRequired(), settings);
+
+ PlanResult planResult = getPlanResult(plan);
+ pathCursor = new PathCursor(pathIterator, planResult.isUniquePathsRequired(), settings);
+ numberOfFacets = planResult.indexDefinition.getNumberOfTopFacets();
}
@@ -1607,7 +1611,7 @@
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/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/util/FacetHelper.java b/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/util/FacetHelper.java
index 7f12dd6..8e42798 100644
--- a/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/util/FacetHelper.java
+++ b/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/util/FacetHelper.java
@@ -47,7 +47,7 @@
private static final Logger LOGGER = LoggerFactory.getLogger(FacetHelper.class);
/**
- * IndexPaln Attribute name which refers to the name of the fields that should used for facets.
+ * IndexPaln Attribute name which refers to the name of the fields that should be used for facets.
*/
public static final String ATTR_FACET_FIELDS = "oak.facet.fields";
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 6de018d..fc4b73c 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 @@
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 @@
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 @@
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
@@ -59,6 +69,13 @@
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();
}
@@ -301,4 +318,147 @@
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<FacetResult.Facet> 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<FacetResult.Facet> 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<FacetResult.Facet> facets = facetResult.getFacets(pn);
+ assertNotNull(facets);
+ assertEquals(7, facets.size());
+ }
}
\ No newline at end of file