mirror of https://github.com/apache/lucene.git
SOLR-9519: recurse sub-facets of empty buckets if they can widen domain again
This commit is contained in:
parent
46fad72a9b
commit
cfcf4081fc
|
@ -130,6 +130,10 @@ Bug Fixes
|
|||
|
||||
* SOLR-9005: In files example, add a guard condition to javascript URP script (Alexandre Rafalovitch)
|
||||
|
||||
* SOLR-9519: JSON Facet API: don't stop at an empty facet bucket if any sub-facets still have a chance
|
||||
of matching something due to filter exclusions (which can widen the domain again).
|
||||
(Michael Sun, yonik)
|
||||
|
||||
Other Changes
|
||||
----------------------
|
||||
|
||||
|
|
|
@ -401,24 +401,29 @@ public abstract class FacetProcessor<FacetRequestT extends FacetRequest> {
|
|||
|
||||
void processSubs(SimpleOrderedMap<Object> response, Query filter, DocSet domain) throws IOException {
|
||||
|
||||
// TODO: what if a zero bucket has a sub-facet with an exclusion that would yield results?
|
||||
// should we check for domain-altering exclusions, or even ask the sub-facet for
|
||||
// it's domain and then only skip it if it's 0?
|
||||
|
||||
if (domain == null || domain.size() == 0 && !freq.processEmpty) {
|
||||
return;
|
||||
}
|
||||
boolean emptyDomain = domain == null || domain.size() == 0;
|
||||
|
||||
for (Map.Entry<String,FacetRequest> sub : freq.getSubFacets().entrySet()) {
|
||||
FacetRequest subRequest = sub.getValue();
|
||||
|
||||
// This includes a static check if a sub-facet can possibly produce something from
|
||||
// an empty domain. Should this be changed to a dynamic check as well? That would
|
||||
// probably require actually executing the facet anyway, and dropping it at the
|
||||
// end if it was unproductive.
|
||||
if (emptyDomain && !freq.processEmpty && !subRequest.canProduceFromEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// make a new context for each sub-facet since they can change the domain
|
||||
FacetContext subContext = fcontext.sub(filter, domain);
|
||||
FacetProcessor subProcessor = sub.getValue().createFacetProcessor(subContext);
|
||||
FacetProcessor subProcessor = subRequest.createFacetProcessor(subContext);
|
||||
|
||||
if (fcontext.getDebugInfo() != null) { // if fcontext.debugInfo != null, it means rb.debug() == true
|
||||
FacetDebugInfo fdebug = new FacetDebugInfo();
|
||||
subContext.setDebugInfo(fdebug);
|
||||
fcontext.getDebugInfo().addChild(fdebug);
|
||||
|
||||
fdebug.setReqDescription(sub.getValue().getFacetDescription());
|
||||
fdebug.setReqDescription(subRequest.getFacetDescription());
|
||||
fdebug.setProcessor(subProcessor.getClass().getSimpleName());
|
||||
if (subContext.filter != null) fdebug.setFilter(subContext.filter.toString());
|
||||
|
||||
|
|
|
@ -88,6 +88,16 @@ public abstract class FacetRequest {
|
|||
public boolean toChildren;
|
||||
public String parents; // identifies the parent filter... the full set of parent documents for any block join operation
|
||||
public List<Object> filters; // list of symbolic filters (JSON query format)
|
||||
|
||||
// True if a starting set of documents can be mapped onto a different set of documents not originally in the starting set.
|
||||
public boolean canTransformDomain() {
|
||||
return toParent || toChildren || excludeTags != null;
|
||||
}
|
||||
|
||||
// Can this domain become non-empty if the input domain is empty? This does not check any sub-facets (see canProduceFromEmpty for that)
|
||||
public boolean canBecomeNonEmpty() {
|
||||
return excludeTags != null;
|
||||
}
|
||||
}
|
||||
|
||||
public FacetRequest() {
|
||||
|
@ -119,6 +129,15 @@ public abstract class FacetRequest {
|
|||
return false;
|
||||
}
|
||||
|
||||
/** Returns true if this facet, or any sub-facets can produce results from an empty domain. */
|
||||
public boolean canProduceFromEmpty() {
|
||||
if (domain != null && domain.canBecomeNonEmpty()) return true;
|
||||
for (FacetRequest freq : subFacets.values()) {
|
||||
if (freq.canProduceFromEmpty()) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void addStat(String key, AggValueSource stat) {
|
||||
facetStats.put(key, stat);
|
||||
}
|
||||
|
|
|
@ -998,6 +998,25 @@ public class TestJsonFacets extends SolrTestCaseHS {
|
|||
"}"
|
||||
);
|
||||
|
||||
// test sub-facets of empty buckets with domain filter exclusions (canProduceFromEmpty) (see SOLR-9519)
|
||||
client.testJQ(params(p, "q", "*:*", "fq","{!tag=doc3}id:non-exist", "fq","{!tag=CATA}${cat_s}:A"
|
||||
|
||||
, "json.facet", "{" +
|
||||
"f1:{${terms} type:terms, field:${cat_s}, domain:{excludeTags:doc3} } " +
|
||||
",q1 :{type:query, q:'*:*', facet:{ f1:{${terms} type:terms, field:${cat_s}, domain:{excludeTags:doc3} } } } " + // nested under query
|
||||
",q1a:{type:query, q:'id:4', facet:{ f1:{${terms} type:terms, field:${cat_s}, domain:{excludeTags:doc3} } } } " + // nested under query, make sure id:4 filter still applies
|
||||
",r1 :{type:range, field:${num_d}, start:0, gap:3, end:5, facet:{ f1:{${terms} type:terms, field:${cat_s}, domain:{excludeTags:doc3} } } } " + // nested under range, make sure range constraints still apply
|
||||
",f2:{${terms} type:terms, field:${cat_s}, domain:{filter:'*:*'} } " + // domain filter doesn't widen, so f2 should not appear.
|
||||
"}"
|
||||
)
|
||||
, "facets=={ count:0, " +
|
||||
" f1:{ buckets:[ {val:A, count:2} ] }" +
|
||||
",q1:{ count:0, f1:{buckets:[{val:A, count:2}]} }" +
|
||||
",q1a:{ count:0, f1:{buckets:[{val:A, count:1}]} }" +
|
||||
",r1:{ buckets:[ {val:0.0,count:0,f1:{buckets:[{val:A, count:1}]}}, {val:3.0,count:0,f1:{buckets:[{val:A, count:1}]}} ] }" +
|
||||
"}"
|
||||
);
|
||||
|
||||
// nested query facets on subset (with excludeTags)
|
||||
client.testJQ(params(p, "q", "*:*", "fq","{!tag=abc}id:(2 3)"
|
||||
, "json.facet", "{ processEmpty:true," +
|
||||
|
|
Loading…
Reference in New Issue