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 ca...@apache.org on 2018/10/08 17:09:14 UTC
svn commit: r1843175 - in /jackrabbit/oak/trunk/oak-lucene/src:
main/java/org/apache/jackrabbit/oak/plugins/index/lucene/util/FilteredSortedSetDocValuesFacetCounts.java
test/java/org/apache/jackrabbit/oak/jcr/query/FacetTest.java
Author: catholicon
Date: Mon Oct 8 17:09:13 2018
New Revision: 1843175
URL: http://svn.apache.org/viewvc?rev=1843175&view=rev
Log:
OAK-7808: Incorrect facet counts when some results are inaccessible due to ACLs
Modified:
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/util/FilteredSortedSetDocValuesFacetCounts.java
jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/jcr/query/FacetTest.java
Modified: jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/util/FilteredSortedSetDocValuesFacetCounts.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/util/FilteredSortedSetDocValuesFacetCounts.java?rev=1843175&r1=1843174&r2=1843175&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/util/FilteredSortedSetDocValuesFacetCounts.java (original)
+++ jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/util/FilteredSortedSetDocValuesFacetCounts.java Mon Oct 8 17:09:13 2018
@@ -19,9 +19,9 @@
package org.apache.jackrabbit.oak.plugins.index.lucene.util;
import java.io.IOException;
-import java.util.HashMap;
import java.util.Map;
+import com.google.common.collect.Maps;
import org.apache.jackrabbit.oak.plugins.index.search.FieldNames;
import org.apache.jackrabbit.oak.spi.query.Filter;
import org.apache.lucene.document.Document;
@@ -34,9 +34,10 @@ import org.apache.lucene.facet.sortedset
import org.apache.lucene.facet.sortedset.SortedSetDocValuesReaderState;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.SortedSetDocValues;
+import org.apache.lucene.index.TermsEnum;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TopDocs;
-import org.apache.lucene.util.BytesRef;
+import org.jetbrains.annotations.NotNull;
/**
* ACL filtered version of {@link SortedSetDocValuesFacetCounts}
@@ -65,9 +66,10 @@ class FilteredSortedSetDocValuesFacetCou
}
LabelAndValue[] labelAndValues = topChildren.labelValues;
+ InaccessibleFacetCountManager inaccessibleFacetCountManager = new InaccessibleFacetCountManager();
for (ScoreDoc scoreDoc : docs.scoreDocs) {
- labelAndValues = filterFacet(scoreDoc.doc, dim, labelAndValues);
+ labelAndValues = filterFacet(scoreDoc.doc, dim, labelAndValues, inaccessibleFacetCountManager);
}
int childCount = labelAndValues.length;
@@ -79,45 +81,82 @@ class FilteredSortedSetDocValuesFacetCou
return new FacetResult(dim, path, value, labelAndValues, childCount);
}
- private LabelAndValue[] filterFacet(int docId, String dimension, LabelAndValue[] labelAndValues) throws IOException {
- boolean filterd = false;
- Map<String, Long> newValues = new HashMap<String, Long>();
-
+ private LabelAndValue[] filterFacet(int docId, String dimension, LabelAndValue[] labelAndValues,
+ InaccessibleFacetCountManager inaccessibleFacetCountManager) throws IOException {
Document document = reader.document(docId);
- SortedSetDocValues docValues = state.getDocValues();
- docValues.setDocument(docId);
// filter using doc values (avoiding requiring stored values)
if (!filter.isAccessible(document.getField(FieldNames.PATH).stringValue() + "/" + dimension)) {
- filterd = true;
- for (LabelAndValue lv : labelAndValues) {
- long existingCount = lv.value.longValue();
-
- BytesRef key = new BytesRef(FacetsConfig.pathToString(dimension, new String[]{lv.label}));
- long l = docValues.lookupTerm(key);
- if (l >= 0) {
- if (existingCount > 0) {
- newValues.put(lv.label, existingCount - 1);
- } else {
- if (newValues.containsKey(lv.label)) {
- newValues.remove(lv.label);
- }
- }
+
+ SortedSetDocValues docValues = state.getDocValues();
+ docValues.setDocument(docId);
+ TermsEnum termsEnum = docValues.termsEnum();
+
+ long ord = docValues.nextOrd();
+
+ while (ord != SortedSetDocValues.NO_MORE_ORDS) {
+ termsEnum.seekExact(ord);
+ String facetDVTerm = termsEnum.term().utf8ToString();
+ String [] facetDVDimPaths = FacetsConfig.stringToPath(facetDVTerm);
+
+ for (int i = 1; i < facetDVDimPaths.length; i++) {
+ inaccessibleFacetCountManager.markInaccessbile(facetDVDimPaths[i]);
}
+
+ ord = docValues.nextOrd();
}
}
- LabelAndValue[] filteredLVs;
- if (filterd) {
- filteredLVs = new LabelAndValue[newValues.size()];
- int i = 0;
- for (Map.Entry<String, Long> entry : newValues.entrySet()) {
- filteredLVs[i] = new LabelAndValue(entry.getKey(), entry.getValue());
- i++;
+
+ return inaccessibleFacetCountManager.updateLabelAndValue(labelAndValues);
+ }
+
+ static class InaccessibleFacetCountManager {
+ Map<String, Long> inaccessbileCounts = Maps.newHashMap();
+
+ void markInaccessbile(@NotNull String label) {
+ Long count = inaccessbileCounts.get(label);
+ if (count == null) {
+ count = 1L;
+ } else {
+ count--;
}
- } else {
- filteredLVs = labelAndValues;
+ inaccessbileCounts.put(label, count);
}
- return filteredLVs;
+ LabelAndValue[] updateLabelAndValue(LabelAndValue[] currentLabels) {
+ int numZeros = 0;
+
+ LabelAndValue[] newValues;
+
+ for (int i = 0; i < currentLabels.length; i++) {
+ LabelAndValue lv = currentLabels[i];
+ Long inaccessibleCount = inaccessbileCounts.get(lv.label);
+
+ if (inaccessibleCount != null) {
+ long newValue = lv.value.longValue() - inaccessibleCount;
+
+ if (newValue <= 0) {
+ newValue = 0;
+ numZeros++;
+ }
+
+ currentLabels[i] = new LabelAndValue(lv.label, newValue);
+ }
+ }
+
+ if (numZeros > 0) {
+ newValues = new LabelAndValue[currentLabels.length - numZeros];
+ int i = 0;
+ for (LabelAndValue lv : currentLabels) {
+ if (lv.value.longValue() > 0) {
+ newValues[i++] = lv;
+ }
+ }
+ } else {
+ newValues = currentLabels;
+ }
+
+ return newValues;
+ }
}
}
\ No newline at end of file
Modified: jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/jcr/query/FacetTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/jcr/query/FacetTest.java?rev=1843175&r1=1843174&r2=1843175&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/jcr/query/FacetTest.java (original)
+++ jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/jcr/query/FacetTest.java Mon Oct 8 17:09:13 2018
@@ -26,10 +26,13 @@ import javax.jcr.query.Query;
import javax.jcr.query.QueryManager;
import javax.jcr.query.QueryResult;
import javax.jcr.query.RowIterator;
+import javax.jcr.security.Privilege;
import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+import org.apache.jackrabbit.commons.jackrabbit.authorization.AccessControlUtils;
import org.apache.jackrabbit.core.query.AbstractQueryTest;
-import org.apache.jackrabbit.oak.plugins.index.lucene.LuceneIndexConstants;
import org.apache.jackrabbit.oak.plugins.index.search.FulltextIndexConstants;
import org.apache.jackrabbit.oak.query.facet.FacetResult;
import org.junit.After;
@@ -633,6 +636,110 @@ public class FacetTest extends AbstractQ
assertFalse(rows.hasNext());
}
+ public void testNoFacetsIfNoAccess() throws Exception {
+ deny(testRootNode.addNode("test1")).setProperty("jcr:title", "test1");
+ deny(testRootNode.addNode("test2")).addNode("child").setProperty("jcr:title", "test2");
+ deny(testRootNode.addNode("test3").addNode("child")).setProperty("jcr:title", "test3");
+ superuser.save();
+
+ Session anonUser = getHelper().getReadOnlySession();
+ QueryManager queryManager = anonUser.getWorkspace().getQueryManager();
+ Query q = queryManager.createQuery("//*[@jcr:title]/(rep:facet(jcr:title))", Query.XPATH);
+ QueryResult result = q.execute();
+ FacetResult facetResult = new FacetResult(result);
+
+ assertNotNull("facetResult is null", facetResult);
+ assertTrue(facetResult.getDimensions().isEmpty());
+ }
+
+ public void testOnlyAllowedFacetLabelsShowUp() throws Exception {
+ deny(testRootNode.addNode("test1")).setProperty("jcr:title", "test1");
+ deny(testRootNode.addNode("test2")).addNode("child").setProperty("jcr:title", "test2");
+ testRootNode.addNode("test3").addNode("child").setProperty("jcr:title", "test3");
+ superuser.save();
+
+ Session anonUser = getHelper().getReadOnlySession();
+ QueryManager queryManager = anonUser.getWorkspace().getQueryManager();
+ Query q = queryManager.createQuery("//*[@jcr:title]/(rep:facet(jcr:title))", Query.XPATH);
+ QueryResult result = q.execute();
+ FacetResult facetResult = new FacetResult(result);
+
+ assertNotNull("facetResult is null", facetResult);
+ assertEquals("Unexpected number of dimension", 1, facetResult.getFacets("jcr:title").size());
+ FacetResult.Facet facet = facetResult.getFacets("jcr:title").get(0);
+ assertEquals("Unexpected facet label", "test3", facet.getLabel());
+ assertEquals("Unexpected facet count", 1, facet.getCount());
+ }
+
+ public void testInaccessibleFacetCounts() throws Exception {
+ deny(testRootNode.addNode("test1")).setProperty("jcr:title", "test");
+ deny(testRootNode.addNode("test2")).addNode("child").setProperty("jcr:title", "test");
+ testRootNode.addNode("test3").addNode("child").setProperty("jcr:title", "test");
+ testRootNode.addNode("test4").addNode("child").setProperty("jcr:title", "another-test");
+ superuser.save();
+
+ Session anonUser = getHelper().getReadOnlySession();
+ QueryManager queryManager = anonUser.getWorkspace().getQueryManager();
+ Query q = queryManager.createQuery("//*[@jcr:title]/(rep:facet(jcr:title))", Query.XPATH);
+ QueryResult result = q.execute();
+ FacetResult facetResult = new FacetResult(result);
+
+ assertNotNull("facetResult is null", facetResult);
+ assertEquals("Unexpected number of labels", 2, facetResult.getFacets("jcr:title").size());
+ Map<String, Integer> facets = facetResult.getFacets("jcr:title")
+ .stream().collect(Collectors.toMap(FacetResult.Facet::getLabel, FacetResult.Facet::getCount));
+ assertEquals("Unexpected facet count for jcr:title", 1, (int)facets.get("test"));
+ assertEquals("Unexpected facet count for jcr:title", 1, (int)facets.get("another-test"));
+ }
+
+ public void testAllowedSubNodeFacet() throws Exception {
+ allow(
+ deny(testRootNode.addNode("parent")).addNode("child")
+ ).setProperty("jcr:title", "test");
+ superuser.save();
+
+ Session anonUser = getHelper().getReadOnlySession();
+ QueryManager queryManager = anonUser.getWorkspace().getQueryManager();
+ Query q = queryManager.createQuery("//*[@jcr:title]/(rep:facet(jcr:title))", Query.XPATH);
+ QueryResult result = q.execute();
+ FacetResult facetResult = new FacetResult(result);
+
+ assertNotNull("facetResult is null", facetResult);
+ assertEquals("Unexpected number of labels", 1, facetResult.getFacets("jcr:title").size());
+ FacetResult.Facet facet = facetResult.getFacets("jcr:title").get(0);
+ assertEquals("Unexpected facet label", "test", facet.getLabel());
+ assertEquals("Unexpected facet count", 1, facet.getCount());
+ }
+
+ public void testAcRelativeFacetsAccessControl() throws Exception {
+ deny(testRootNode.addNode("test1")).addNode("jc").setProperty("text", "test_1");
+ deny(testRootNode.addNode("test2").addNode("jc")).setProperty("text", "test_2");
+ testRootNode.addNode("test3").addNode("jc").setProperty("text", "test_3");
+ superuser.save();
+
+ Session anonUser = getHelper().getReadOnlySession();
+ QueryManager queryManager = anonUser.getWorkspace().getQueryManager();
+ Query q = queryManager.createQuery("//*[jcr:contains(jc/@text, 'test')]/(rep:facet(jc/text))", Query.XPATH);
+ QueryResult result = q.execute();
+ FacetResult facetResult = new FacetResult(result);
+
+ assertNotNull("facetResult is null", facetResult);
+ assertEquals("Unexpected number of dimension", 1, facetResult.getFacets("jc/text").size());
+ FacetResult.Facet facet = facetResult.getFacets("jc/text").get(0);
+ assertEquals("Unexpected facet label", "test_3", facet.getLabel());
+ assertEquals("Unexpected facet count", 1, facet.getCount());
+ }
+
+ public Node deny(Node node) throws RepositoryException {
+ AccessControlUtils.deny(node, "anonymous", Privilege.JCR_ALL);
+ return node;
+ }
+
+ public Node allow(Node node) throws RepositoryException {
+ AccessControlUtils.allow(node, "anonymous", Privilege.JCR_READ);
+ return node;
+ }
+
private void markIndexForReindex() throws RepositoryException {
superuser.getNode("/oak:index/luceneGlobal").setProperty(REINDEX_PROPERTY_NAME, true);
}