mirror of https://github.com/apache/lucene.git
SOLR-7452: refinement of missing buckets and partial facets through missing buckets
This commit is contained in:
parent
e80643e5a7
commit
cc623403bd
|
@ -18,8 +18,11 @@
|
||||||
package org.apache.solr.search.facet;
|
package org.apache.solr.search.facet;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import org.apache.solr.common.util.SimpleOrderedMap;
|
import org.apache.solr.common.util.SimpleOrderedMap;
|
||||||
|
@ -167,7 +170,29 @@ public class FacetFieldMerger extends FacetRequestSortedMerger<FacetField> {
|
||||||
// basically , only do at the top-level facet?
|
// basically , only do at the top-level facet?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
Map<String, Object> getRefinementSpecial(Context mcontext, Map<String, Object> refinement, Collection<String> tagsWithPartial) {
|
||||||
|
if (!tagsWithPartial.isEmpty()) {
|
||||||
|
// Since special buckets missing and allBuckets themselves will always be included, we only need to worry about subfacets being partial.
|
||||||
|
if (freq.missing) {
|
||||||
|
refinement = getRefinementSpecial(mcontext, refinement, tagsWithPartial, missingBucket, "missing");
|
||||||
|
}
|
||||||
|
if (freq.allBuckets) {
|
||||||
|
refinement = getRefinementSpecial(mcontext, refinement, tagsWithPartial, allBuckets, "allBuckets");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return refinement;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, Object> getRefinementSpecial(Context mcontext, Map<String, Object> refinement, Collection<String> tagsWithPartial, FacetBucket bucket, String label) {
|
||||||
|
// boolean prev = mcontext.setBucketWasMissing(true); // the special buckets should have the same "missing" status as this facet, so no need to set it again
|
||||||
|
Map<String, Object> bucketRefinement = bucket.getRefinement(mcontext, tagsWithPartial);
|
||||||
|
if (bucketRefinement != null) {
|
||||||
|
refinement = refinement == null ? new HashMap<>(2) : refinement;
|
||||||
|
refinement.put(label, bucketRefinement);
|
||||||
|
}
|
||||||
|
return refinement;
|
||||||
|
}
|
||||||
|
|
||||||
private static class FacetNumBucketsMerger extends FacetMerger {
|
private static class FacetNumBucketsMerger extends FacetMerger {
|
||||||
long sumBuckets;
|
long sumBuckets;
|
||||||
|
|
|
@ -36,6 +36,8 @@ import org.apache.solr.schema.FieldType;
|
||||||
import org.apache.solr.schema.SchemaField;
|
import org.apache.solr.schema.SchemaField;
|
||||||
import org.apache.solr.search.DocSet;
|
import org.apache.solr.search.DocSet;
|
||||||
|
|
||||||
|
import static org.apache.solr.search.facet.FacetContext.SKIP_FACET;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Facet processing based on field values. (not range nor by query)
|
* Facet processing based on field values. (not range nor by query)
|
||||||
* @see FacetField
|
* @see FacetField
|
||||||
|
@ -528,6 +530,9 @@ abstract class FacetFieldProcessor extends FacetProcessor<FacetField> {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected SimpleOrderedMap<Object> refineFacets() throws IOException {
|
protected SimpleOrderedMap<Object> refineFacets() throws IOException {
|
||||||
|
boolean skipThisFacet = (fcontext.flags & SKIP_FACET) != 0;
|
||||||
|
|
||||||
|
|
||||||
List leaves = asList(fcontext.facetInfo.get("_l")); // We have not seen this bucket: do full faceting for this bucket, including all sub-facets
|
List leaves = asList(fcontext.facetInfo.get("_l")); // We have not seen this bucket: do full faceting for this bucket, including all sub-facets
|
||||||
List<List> skip = asList(fcontext.facetInfo.get("_s")); // We have seen this bucket, so skip stats on it, and skip sub-facets except for the specified sub-facets that should calculate specified buckets.
|
List<List> skip = asList(fcontext.facetInfo.get("_s")); // We have seen this bucket, so skip stats on it, and skip sub-facets except for the specified sub-facets that should calculate specified buckets.
|
||||||
List<List> partial = asList(fcontext.facetInfo.get("_p")); // We have not seen this bucket, do full faceting for this bucket, and most sub-facets... but some sub-facets are partial and should only visit specified buckets.
|
List<List> partial = asList(fcontext.facetInfo.get("_p")); // We have not seen this bucket, do full faceting for this bucket, and most sub-facets... but some sub-facets are partial and should only visit specified buckets.
|
||||||
|
@ -563,6 +568,15 @@ abstract class FacetFieldProcessor extends FacetProcessor<FacetField> {
|
||||||
bucketList.add( refineBucket(bucketVal, false, facetInfo ) );
|
bucketList.add( refineBucket(bucketVal, false, facetInfo ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (freq.missing) {
|
||||||
|
Map<String,Object> bucketFacetInfo = (Map<String,Object>)fcontext.facetInfo.get("missing");
|
||||||
|
|
||||||
|
if (bucketFacetInfo != null || !skipThisFacet) {
|
||||||
|
SimpleOrderedMap<Object> missingBucket = new SimpleOrderedMap<>();
|
||||||
|
fillBucket(missingBucket, getFieldMissingQuery(fcontext.searcher, freq.field), null, skipThisFacet, bucketFacetInfo);
|
||||||
|
res.add("missing", missingBucket);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// If there are just a couple of leaves, and if the domain is large, then
|
// If there are just a couple of leaves, and if the domain is large, then
|
||||||
// going by term is likely the most efficient?
|
// going by term is likely the most efficient?
|
||||||
|
|
|
@ -240,6 +240,14 @@ abstract class FacetRequestSortedMerger<FacetRequestT extends FacetRequestSorted
|
||||||
if (skipBuckets != null) refinement.put("_s", skipBuckets);
|
if (skipBuckets != null) refinement.put("_s", skipBuckets);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
refinement = getRefinementSpecial(mcontext, refinement, tagsWithPartial);
|
||||||
|
|
||||||
|
return refinement;
|
||||||
|
}
|
||||||
|
|
||||||
|
// utility method for subclasses to override to finish calculating faceting (special buckets in field facets)... this feels hacky and we
|
||||||
|
// should find a better way.
|
||||||
|
Map<String,Object> getRefinementSpecial(Context mcontext, Map<String,Object> refinement, Collection<String> tagsWithPartial) {
|
||||||
return refinement;
|
return refinement;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -219,6 +219,17 @@ public class TestJsonFacetRefinement extends SolrTestCaseHS {
|
||||||
"}"
|
"}"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// test partial _p under a missing bucket
|
||||||
|
doTestRefine("{top:{type:terms, field:Afield, refine:true, limit:1, missing:true, facet:{x : {type:terms, field:X, limit:1, refine:true} } } }",
|
||||||
|
"{top: {buckets:[], missing:{count:12, x:{buckets:[{val:x2, count:4},{val:x3, count:2}]} } } }",
|
||||||
|
"{top: {buckets:[], missing:{count:10, x:{buckets:[{val:x1, count:5},{val:x4, count:3}]} } } }",
|
||||||
|
"=={top: {" +
|
||||||
|
"missing:{x:{_l:[x1]}}" +
|
||||||
|
" } " +
|
||||||
|
"}"
|
||||||
|
, null
|
||||||
|
);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -266,6 +277,17 @@ public class TestJsonFacetRefinement extends SolrTestCaseHS {
|
||||||
);
|
);
|
||||||
****/
|
****/
|
||||||
|
|
||||||
|
// test refining under the special "missing" bucket of a field facet
|
||||||
|
client.testJQ(params(p, "q", "*:*",
|
||||||
|
"json.facet", "{" +
|
||||||
|
"f:{type:terms, field:missing_s, limit:1, overrequest:0, missing:true, refine:true, facet:{ cat:{type:terms, field:${cat_s}, limit:1, overrequest:0, refine:true } }}" +
|
||||||
|
"}"
|
||||||
|
)
|
||||||
|
, "facets=={ count:8" +
|
||||||
|
", f:{ buckets:[], missing:{count:8, cat:{buckets:[{val:A,count:4}]} } }" + // just like the previous response, just nested under a field facet
|
||||||
|
"}"
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
client.testJQ(params(p, "q", "*:*",
|
client.testJQ(params(p, "q", "*:*",
|
||||||
"json.facet", "{" +
|
"json.facet", "{" +
|
||||||
|
@ -317,7 +339,7 @@ public class TestJsonFacetRefinement extends SolrTestCaseHS {
|
||||||
"}"
|
"}"
|
||||||
);
|
);
|
||||||
|
|
||||||
// test missing buckets (field facet within field facet)
|
// test partial buckets (field facet within field facet)
|
||||||
client.testJQ(params(p, "q", "*:*",
|
client.testJQ(params(p, "q", "*:*",
|
||||||
"json.facet", "{" +
|
"json.facet", "{" +
|
||||||
"ab:{type:terms, field:${cat_s}, limit:1, overrequest:0, refine:true, facet:{ xy:{type:terms, field:${xy_s}, limit:1, overrequest:0, refine:true } }}" +
|
"ab:{type:terms, field:${cat_s}, limit:1, overrequest:0, refine:true, facet:{ xy:{type:terms, field:${xy_s}, limit:1, overrequest:0, refine:true } }}" +
|
||||||
|
@ -345,6 +367,8 @@ public class TestJsonFacetRefinement extends SolrTestCaseHS {
|
||||||
"}"
|
"}"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue