Index: lucene/facet/src/test/org/apache/lucene/facet/search/TestDrillSideways.java =================================================================== --- lucene/facet/src/test/org/apache/lucene/facet/search/TestDrillSideways.java (revision 1465450) +++ lucene/facet/src/test/org/apache/lucene/facet/search/TestDrillSideways.java (working copy) @@ -232,6 +232,20 @@ // published once: assertEquals("Author: Lisa=2 Frank=1 Susan=1 Bob=1", toString(r.facetResults.get(1))); + // LUCENE-4915: test drilling down on a dimension but + // NOT facet counting it: + ddq = new DrillDownQuery(fsp.indexingParams, new MatchAllDocsQuery()); + ddq.add(new CategoryPath("Author", "Lisa"), + new CategoryPath("Author", "Tom")); + fsp = new FacetSearchParams( + new CountFacetRequest(new CategoryPath("Publish Date"), 10)); + r = new DrillSideways(searcher, taxoReader).search(null, ddq, 10, fsp); + assertEquals(2, r.hits.totalHits); + assertEquals(1, r.facetResults.size()); + // Publish Date is only drill-down, and Lisa published + // one in 2012 and one in 2010: + assertEquals("Publish Date: 2012=1 2010=1", toString(r.facetResults.get(0))); + // Test main query gets null scorer: fsp = new FacetSearchParams( new CountFacetRequest(new CategoryPath("Publish Date"), 10), @@ -594,19 +608,36 @@ int numIters = atLeast(10); for(int iter=0;iter requests = new ArrayList(); - for(int i=0;i(); - for(int i=0;i newRequests = new ArrayList(); + for(FacetRequest oldRequest : requests) { + newRequests.add(new CountFacetRequest(oldRequest.categoryPath, topN)); } - fsp = new FacetSearchParams(requests); + fsp = new FacetSearchParams(newRequests); actual = ds.search(ddq, filter, null, numDocs, sort, true, true, fsp); - verifyEquals(dimValues, s, expected, actual, scores, topN, doUseDV); + verifyEquals(newRequests, dimValues, s, expected, actual, scores, topN, doUseDV); // Make sure drill down doesn't change score: TopDocs ddqHits = s.search(ddq, filter, numDocs); @@ -877,7 +908,8 @@ return topNIDs; } - private SimpleFacetResult slowDrillSidewaysSearch(IndexSearcher s, List docs, String contentToken, String[][] drillDowns, + private SimpleFacetResult slowDrillSidewaysSearch(IndexSearcher s, List requests, List docs, + String contentToken, String[][] drillDowns, String[][] dimValues, Filter onlyEven) throws Exception { int numDims = dimValues.length; @@ -953,7 +985,8 @@ SimpleFacetResult res = new SimpleFacetResult(); res.hits = hits; res.counts = new int[numDims][]; - for(int dim=0;dim requests, String[][] dimValues, IndexSearcher s, SimpleFacetResult expected, DrillSidewaysResult actual, Map scores, int topN, boolean isSortedSetDV) throws Exception { if (VERBOSE) { System.out.println(" verify totHits=" + expected.hits.size()); @@ -981,9 +1014,28 @@ assertEquals(scores.get(expected.hits.get(i).id), actual.hits.scoreDocs[i].score, 0.0f); } - assertEquals(expected.counts.length, actual.facetResults.size()); + int numExpected = 0; for(int dim=0;dim subResults = fr.getFacetResultNode().subResults; if (VERBOSE) { System.out.println(" dim" + dim); @@ -991,7 +1043,7 @@ } Map actualValues = new HashMap(); - int idx = 0; + idx = 0; for(FacetResultNode childNode : subResults) { actualValues.put(childNode.label.components[1], (int) childNode.value); if (VERBOSE) { Index: lucene/facet/src/java/org/apache/lucene/facet/search/DrillDownQuery.java =================================================================== --- lucene/facet/src/java/org/apache/lucene/facet/search/DrillDownQuery.java (revision 1465450) +++ lucene/facet/src/java/org/apache/lucene/facet/search/DrillDownQuery.java (working copy) @@ -19,7 +19,9 @@ import java.io.IOException; import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; +import java.util.regex.Pattern; import org.apache.lucene.facet.params.CategoryListParams; import org.apache.lucene.facet.params.FacetIndexingParams; @@ -61,7 +63,7 @@ private final BooleanQuery query; private final Map drillDownDims = new LinkedHashMap(); - private final FacetIndexingParams fip; + final FacetIndexingParams fip; /** Used by clone() */ DrillDownQuery(FacetIndexingParams fip, BooleanQuery query, Map drillDownDims) { @@ -87,6 +89,32 @@ fip = other.fip; } + /** Used by DrillSideways */ + DrillDownQuery(FacetIndexingParams fip, Query baseQuery, List clauses) { + this.fip = fip; + this.query = new BooleanQuery(true); + if (baseQuery != null) { + query.add(baseQuery, Occur.MUST); + } + for(Query clause : clauses) { + query.add(clause, Occur.MUST); + drillDownDims.put(getDim(clause), drillDownDims.size()); + } + } + + String getDim(Query clause) { + assert clause instanceof ConstantScoreQuery; + clause = ((ConstantScoreQuery) clause).getQuery(); + assert clause instanceof TermQuery || clause instanceof BooleanQuery; + String term; + if (clause instanceof TermQuery) { + term = ((TermQuery) clause).getTerm().text(); + } else { + term = ((TermQuery) ((BooleanQuery) clause).getClauses()[0].getQuery()).getTerm().text(); + } + return term.split(Pattern.quote(Character.toString(fip.getFacetDelimChar())), 2)[0]; + } + /** * Creates a new {@link DrillDownQuery} without a base query, * to perform a pure browsing query (equivalent to using Index: lucene/facet/src/java/org/apache/lucene/facet/search/DrillSideways.java =================================================================== --- lucene/facet/src/java/org/apache/lucene/facet/search/DrillSideways.java (revision 1465450) +++ lucene/facet/src/java/org/apache/lucene/facet/search/DrillSideways.java (working copy) @@ -19,8 +19,10 @@ import java.io.IOException; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; import org.apache.lucene.facet.params.FacetSearchParams; import org.apache.lucene.facet.taxonomy.TaxonomyReader; @@ -33,6 +35,7 @@ import org.apache.lucene.search.Filter; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.MatchAllDocsQuery; +import org.apache.lucene.search.MultiCollector; import org.apache.lucene.search.Query; import org.apache.lucene.search.ScoreDoc; import org.apache.lucene.search.Sort; @@ -71,6 +74,64 @@ this.taxoReader = taxoReader; } + /** Moves any drill-downs that don't have a corresponding + * facet request into the baseQuery. This is unusual, + * yet allowed, because typically the added drill-downs are because + * the user has clicked on previously presented facets, + * and those same facets would be computed this time + * around. */ + private static DrillDownQuery moveDrillDownOnlyClauses(DrillDownQuery in, FacetSearchParams fsp) { + Set facetDims = new HashSet(); + for(FacetRequest fr : fsp.facetRequests) { + if (fr.categoryPath.length == 0) { + throw new IllegalArgumentException("all FacetRequests must have CategoryPath with length > 0"); + } + facetDims.add(fr.categoryPath.components[0]); + } + + BooleanClause[] clauses = in.getBooleanQuery().getClauses(); + Map drillDownDims = in.getDims(); + + int startClause; + if (clauses.length == drillDownDims.size()) { + startClause = 0; + } else { + assert clauses.length == 1+drillDownDims.size(); + startClause = 1; + } + + // Break out drill-down clauses that have no + // corresponding facet request and move them inside the + // baseQuery: + List nonFacetClauses = new ArrayList(); + List facetClauses = new ArrayList(); + for(int i=startClause;i drillDownDims = query.getDims(); if (drillDownDims.isEmpty()) { - throw new IllegalArgumentException("there must be at least one drill-down"); + // Just do ordinary search: + FacetsCollector c = FacetsCollector.create(getDrillDownAccumulator(fsp)); + searcher.search(query, MultiCollector.wrap(hitCollector, c)); + return new DrillSidewaysResult(c.getFacetResults(), null); } BooleanQuery ddq = query.getBooleanQuery(); BooleanClause[] clauses = ddq.getClauses(); - for(FacetRequest fr : fsp.facetRequests) { - if (fr.categoryPath.length == 0) { - throw new IllegalArgumentException("all FacetRequests must have CategoryPath with length > 0"); - } - } - Query baseQuery; int startClause; if (clauses.length == drillDownDims.size()) {