You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by kr...@apache.org on 2016/11/10 21:15:54 UTC
[24/36] lucene-solr:jira/solr-8593: SOLR-9519: recurse sub-facets of
empty buckets if they can widen domain again
SOLR-9519: recurse sub-facets of empty buckets if they can widen domain again
Project: http://git-wip-us.apache.org/repos/asf/lucene-solr/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucene-solr/commit/cfcf4081
Tree: http://git-wip-us.apache.org/repos/asf/lucene-solr/tree/cfcf4081
Diff: http://git-wip-us.apache.org/repos/asf/lucene-solr/diff/cfcf4081
Branch: refs/heads/jira/solr-8593
Commit: cfcf4081fcf04cf2e1d6293a05a2005f0a99942c
Parents: 46fad72
Author: yonik <yo...@apache.org>
Authored: Tue Nov 8 12:10:34 2016 -0500
Committer: yonik <yo...@apache.org>
Committed: Tue Nov 8 12:10:53 2016 -0500
----------------------------------------------------------------------
solr/CHANGES.txt | 4 ++++
.../solr/search/facet/FacetProcessor.java | 23 ++++++++++++--------
.../apache/solr/search/facet/FacetRequest.java | 19 ++++++++++++++++
.../solr/search/facet/TestJsonFacets.java | 19 ++++++++++++++++
4 files changed, 56 insertions(+), 9 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/cfcf4081/solr/CHANGES.txt
----------------------------------------------------------------------
diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt
index 5c0ff06..77897ef 100644
--- a/solr/CHANGES.txt
+++ b/solr/CHANGES.txt
@@ -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
----------------------
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/cfcf4081/solr/core/src/java/org/apache/solr/search/facet/FacetProcessor.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/search/facet/FacetProcessor.java b/solr/core/src/java/org/apache/solr/search/facet/FacetProcessor.java
index 3a26e5b..84b11a0 100644
--- a/solr/core/src/java/org/apache/solr/search/facet/FacetProcessor.java
+++ b/solr/core/src/java/org/apache/solr/search/facet/FacetProcessor.java
@@ -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());
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/cfcf4081/solr/core/src/java/org/apache/solr/search/facet/FacetRequest.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/search/facet/FacetRequest.java b/solr/core/src/java/org/apache/solr/search/facet/FacetRequest.java
index 273466c..9f68380 100644
--- a/solr/core/src/java/org/apache/solr/search/facet/FacetRequest.java
+++ b/solr/core/src/java/org/apache/solr/search/facet/FacetRequest.java
@@ -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);
}
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/cfcf4081/solr/core/src/test/org/apache/solr/search/facet/TestJsonFacets.java
----------------------------------------------------------------------
diff --git a/solr/core/src/test/org/apache/solr/search/facet/TestJsonFacets.java b/solr/core/src/test/org/apache/solr/search/facet/TestJsonFacets.java
index b08e940..32f9dfa 100644
--- a/solr/core/src/test/org/apache/solr/search/facet/TestJsonFacets.java
+++ b/solr/core/src/test/org/apache/solr/search/facet/TestJsonFacets.java
@@ -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," +