You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by mi...@apache.org on 2014/01/28 18:07:20 UTC
svn commit: r1562127 - in /lucene/dev/branches/lucene5376/lucene:
core/src/java/org/apache/lucene/search/
demo/src/java/org/apache/lucene/demo/facet/
facet/src/java/org/apache/lucene/facet/
facet/src/java/org/apache/lucene/facet/range/ facet/src/test/o...
Author: mikemccand
Date: Tue Jan 28 17:07:19 2014
New Revision: 1562127
URL: http://svn.apache.org/r1562127
Log:
LUCENE-5418: fix drill-down & drill-sideways to use Bits interface when applying a Filter
Added:
lucene/dev/branches/lucene5376/lucene/facet/src/java/org/apache/lucene/facet/range/SlowBitsDocIdSetIterator.java (with props)
Modified:
lucene/dev/branches/lucene5376/lucene/core/src/java/org/apache/lucene/search/DocIdSet.java
lucene/dev/branches/lucene5376/lucene/core/src/java/org/apache/lucene/search/FilteredQuery.java
lucene/dev/branches/lucene5376/lucene/demo/src/java/org/apache/lucene/demo/facet/DistanceFacetsExample.java
lucene/dev/branches/lucene5376/lucene/facet/src/java/org/apache/lucene/facet/DrillDownQuery.java
lucene/dev/branches/lucene5376/lucene/facet/src/java/org/apache/lucene/facet/DrillSideways.java
lucene/dev/branches/lucene5376/lucene/facet/src/java/org/apache/lucene/facet/DrillSidewaysQuery.java
lucene/dev/branches/lucene5376/lucene/facet/src/java/org/apache/lucene/facet/DrillSidewaysScorer.java
lucene/dev/branches/lucene5376/lucene/facet/src/java/org/apache/lucene/facet/range/DoubleRange.java
lucene/dev/branches/lucene5376/lucene/facet/src/java/org/apache/lucene/facet/range/LongRange.java
lucene/dev/branches/lucene5376/lucene/facet/src/java/org/apache/lucene/facet/range/Range.java
lucene/dev/branches/lucene5376/lucene/facet/src/test/org/apache/lucene/facet/range/TestRangeFacetCounts.java
Modified: lucene/dev/branches/lucene5376/lucene/core/src/java/org/apache/lucene/search/DocIdSet.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene5376/lucene/core/src/java/org/apache/lucene/search/DocIdSet.java?rev=1562127&r1=1562126&r2=1562127&view=diff
==============================================================================
--- lucene/dev/branches/lucene5376/lucene/core/src/java/org/apache/lucene/search/DocIdSet.java (original)
+++ lucene/dev/branches/lucene5376/lucene/core/src/java/org/apache/lucene/search/DocIdSet.java Tue Jan 28 17:07:19 2014
@@ -31,6 +31,13 @@ public abstract class DocIdSet {
* are no docs that match. */
public abstract DocIdSetIterator iterator() throws IOException;
+ // TODO: somehow this class should express the cost of
+ // iteration vs the cost of random access Bits; for
+ // expensive Filters (e.g. distance < 1 km) we should use
+ // bits() after all other Query/Filters have matched, but
+ // this is the opposite of what bits() is for now
+ // (down-low filtering)
+
/** Optionally provides a {@link Bits} interface for random access
* to matching documents.
* @return {@code null}, if this {@code DocIdSet} does not support random access.
Modified: lucene/dev/branches/lucene5376/lucene/core/src/java/org/apache/lucene/search/FilteredQuery.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene5376/lucene/core/src/java/org/apache/lucene/search/FilteredQuery.java?rev=1562127&r1=1562126&r2=1562127&view=diff
==============================================================================
--- lucene/dev/branches/lucene5376/lucene/core/src/java/org/apache/lucene/search/FilteredQuery.java (original)
+++ lucene/dev/branches/lucene5376/lucene/core/src/java/org/apache/lucene/search/FilteredQuery.java Tue Jan 28 17:07:19 2014
@@ -50,7 +50,7 @@ public class FilteredQuery extends Query
* @param query Query to be filtered, cannot be <code>null</code>.
* @param filter Filter to apply to query results, cannot be <code>null</code>.
*/
- public FilteredQuery (Query query, Filter filter) {
+ public FilteredQuery(Query query, Filter filter) {
this(query, filter, RANDOM_ACCESS_FILTER_STRATEGY);
}
@@ -63,7 +63,7 @@ public class FilteredQuery extends Query
*
* @see FilterStrategy
*/
- public FilteredQuery (Query query, Filter filter, FilterStrategy strategy) {
+ public FilteredQuery(Query query, Filter filter, FilterStrategy strategy) {
if (query == null || filter == null)
throw new IllegalArgumentException("Query and filter cannot be null.");
if (strategy == null)
@@ -118,7 +118,9 @@ public class FilteredQuery extends Query
// return this query
@Override
- public Query getQuery() { return FilteredQuery.this; }
+ public Query getQuery() {
+ return FilteredQuery.this;
+ }
// return a filtering scorer
@Override
@@ -130,8 +132,8 @@ public class FilteredQuery extends Query
// this means the filter does not accept any documents.
return null;
}
+
return strategy.filteredScorer(context, scoreDocsInOrder, topScorer, weight, filterDocIdSet);
-
}
};
}
@@ -183,14 +185,12 @@ public class FilteredQuery extends Query
@Override
public int advance(int target) throws IOException {
-
int doc = scorer.advance(target);
if (doc != Scorer.NO_MORE_DOCS && !filterbits.get(doc)) {
return scorerDoc = nextDoc();
} else {
return scorerDoc = doc;
}
-
}
@Override
@@ -303,7 +303,9 @@ public class FilteredQuery extends Query
}
@Override
- public final int freq() throws IOException { return scorer.freq(); }
+ public final int freq() throws IOException {
+ return scorer.freq();
+ }
@Override
public final Collection<ChildScorer> getChildren() {
@@ -527,7 +529,7 @@ public class FilteredQuery extends Query
final Bits filterAcceptDocs = docIdSet.bits();
// force if RA is requested
- final boolean useRandomAccess = (filterAcceptDocs != null && (useRandomAccess(filterAcceptDocs, firstFilterDoc)));
+ final boolean useRandomAccess = filterAcceptDocs != null && useRandomAccess(filterAcceptDocs, firstFilterDoc);
if (useRandomAccess) {
// if we are using random access, we return the inner scorer, just with other acceptDocs
return weight.scorer(context, scoreDocsInOrder, topScorer, filterAcceptDocs);
Modified: lucene/dev/branches/lucene5376/lucene/demo/src/java/org/apache/lucene/demo/facet/DistanceFacetsExample.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene5376/lucene/demo/src/java/org/apache/lucene/demo/facet/DistanceFacetsExample.java?rev=1562127&r1=1562126&r2=1562127&view=diff
==============================================================================
--- lucene/dev/branches/lucene5376/lucene/demo/src/java/org/apache/lucene/demo/facet/DistanceFacetsExample.java (original)
+++ lucene/dev/branches/lucene5376/lucene/demo/src/java/org/apache/lucene/demo/facet/DistanceFacetsExample.java Tue Jan 28 17:07:19 2014
@@ -29,16 +29,18 @@ import org.apache.lucene.expressions.Exp
import org.apache.lucene.expressions.SimpleBindings;
import org.apache.lucene.expressions.js.JavascriptCompiler;
import org.apache.lucene.facet.DrillDownQuery;
+import org.apache.lucene.facet.DrillSideways;
import org.apache.lucene.facet.FacetResult;
import org.apache.lucene.facet.Facets;
import org.apache.lucene.facet.FacetsCollector;
+import org.apache.lucene.facet.FacetsConfig;
import org.apache.lucene.facet.range.DoubleRange;
import org.apache.lucene.facet.range.DoubleRangeFacetCounts;
+import org.apache.lucene.facet.taxonomy.TaxonomyReader;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.queries.function.ValueSource;
-import org.apache.lucene.search.ConstantScoreQuery;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.MatchAllDocsQuery;
import org.apache.lucene.search.SortField;
@@ -59,6 +61,7 @@ public class DistanceFacetsExample imple
private final Directory indexDir = new RAMDirectory();
private IndexSearcher searcher;
+ private final FacetsConfig config = new FacetsConfig();
/** Empty constructor */
public DistanceFacetsExample() {}
@@ -127,10 +130,16 @@ public class DistanceFacetsExample imple
// Passing no baseQuery means we drill down on all
// documents ("browse only"):
DrillDownQuery q = new DrillDownQuery(null);
-
- q.add("field", new ConstantScoreQuery(range.getFilter(getDistanceValueSource())));
-
- return searcher.search(q, 10);
+ final ValueSource vs = getDistanceValueSource();
+ q.add("field", range.getFilter(vs));
+ DrillSideways ds = new DrillSideways(searcher, config, (TaxonomyReader) null) {
+ @Override
+ protected Facets buildFacetsResult(FacetsCollector drillDowns, FacetsCollector[] drillSideways, String[] drillSidewaysDims) throws IOException {
+ assert drillSideways.length == 1;
+ return new DoubleRangeFacetCounts("field", vs, drillSideways[0], ONE_KM, TWO_KM, FIVE_KM, TEN_KM);
+ }
+ };
+ return ds.search(q, 10).hits;
}
@Override
Modified: lucene/dev/branches/lucene5376/lucene/facet/src/java/org/apache/lucene/facet/DrillDownQuery.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene5376/lucene/facet/src/java/org/apache/lucene/facet/DrillDownQuery.java?rev=1562127&r1=1562126&r2=1562127&view=diff
==============================================================================
--- lucene/dev/branches/lucene5376/lucene/facet/src/java/org/apache/lucene/facet/DrillDownQuery.java (original)
+++ lucene/dev/branches/lucene5376/lucene/facet/src/java/org/apache/lucene/facet/DrillDownQuery.java Tue Jan 28 17:07:19 2014
@@ -18,22 +18,20 @@ package org.apache.lucene.facet;
*/
import java.io.IOException;
+import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
-import org.apache.lucene.facet.range.DoubleRangeFacetCounts;
-import org.apache.lucene.facet.range.LongRangeFacetCounts;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.Term;
-import org.apache.lucene.search.BooleanClause;
import org.apache.lucene.search.BooleanClause.Occur;
+import org.apache.lucene.search.BooleanClause;
import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.ConstantScoreQuery;
import org.apache.lucene.search.Filter;
import org.apache.lucene.search.FilteredQuery;
import org.apache.lucene.search.MatchAllDocsQuery;
-import org.apache.lucene.search.NumericRangeQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.TermQuery;
@@ -86,7 +84,7 @@ public final class DrillDownQuery extend
/** Used by DrillSideways */
DrillDownQuery(FacetsConfig config, Query baseQuery, List<Query> clauses, Map<String,Integer> drillDownDims) {
- this.query = new BooleanQuery(true);
+ query = new BooleanQuery(true);
if (baseQuery != null) {
query.add(baseQuery, Occur.MUST);
}
@@ -155,11 +153,12 @@ public final class DrillDownQuery extend
/** Expert: add a custom drill-down subQuery. Use this
* when you have a separate way to drill-down on the
- * dimension than the indexed facet ordinals (for
- * example, use a {@link NumericRangeQuery} to drill down
- * after {@link LongRangeFacetCounts} or {@link DoubleRangeFacetCounts}. */
+ * dimension than the indexed facet ordinals. */
public void add(String dim, Query subQuery) {
+ if (drillDownDims.containsKey(dim)) {
+ throw new IllegalArgumentException("dimension \"" + dim + "\" already has a drill-down");
+ }
// TODO: we should use FilteredQuery?
// So scores of the drill-down query don't have an
@@ -172,6 +171,40 @@ public final class DrillDownQuery extend
drillDownDims.put(dim, drillDownDims.size());
}
+ /** Expert: add a custom drill-down Filter, e.g. when
+ * drilling down after range faceting. */
+ public void add(String dim, Filter subFilter) {
+
+ if (drillDownDims.containsKey(dim)) {
+ throw new IllegalArgumentException("dimension \"" + dim + "\" already has a drill-down");
+ }
+
+ // TODO: we should use FilteredQuery?
+
+ // So scores of the drill-down query don't have an
+ // effect:
+ final ConstantScoreQuery drillDownQuery = new ConstantScoreQuery(subFilter);
+ drillDownQuery.setBoost(0.0f);
+
+ query.add(drillDownQuery, Occur.MUST);
+
+ drillDownDims.put(dim, drillDownDims.size());
+ }
+
+ static Filter getFilter(Query query) {
+ if (query instanceof ConstantScoreQuery) {
+ ConstantScoreQuery csq = (ConstantScoreQuery) query;
+ Filter filter = csq.getFilter();
+ if (filter != null) {
+ return filter;
+ } else {
+ return getFilter(csq.getQuery());
+ }
+ } else {
+ return null;
+ }
+ }
+
@Override
public DrillDownQuery clone() {
return new DrillDownQuery(config, query, drillDownDims);
@@ -199,7 +232,63 @@ public final class DrillDownQuery extend
if (query.clauses().size() == 0) {
return new MatchAllDocsQuery();
}
- return query;
+
+ List<Filter> filters = new ArrayList<Filter>();
+ List<Query> queries = new ArrayList<Query>();
+ List<BooleanClause> clauses = query.clauses();
+ Query baseQuery;
+ int startIndex;
+ if (drillDownDims.size() == query.clauses().size()) {
+ baseQuery = new MatchAllDocsQuery();
+ startIndex = 0;
+ } else {
+ baseQuery = clauses.get(0).getQuery();
+ startIndex = 1;
+ }
+
+ for(int i=startIndex;i<clauses.size();i++) {
+ BooleanClause clause = clauses.get(i);
+ Query queryClause = clause.getQuery();
+ Filter filter = getFilter(queryClause);
+ if (filter != null) {
+ filters.add(filter);
+ } else {
+ queries.add(queryClause);
+ }
+ }
+
+ if (filters.isEmpty()) {
+ return query;
+ } else {
+ // Wrap all filters using FilteredQuery
+
+ // TODO: this is hackish; we need to do it because
+ // BooleanQuery can't be trusted to handle the
+ // "expensive filter" case. Really, each Filter should
+ // know its cost and we should take that more
+ // carefully into account when picking the right
+ // strategy/optimization:
+ Query wrapped;
+ if (queries.isEmpty()) {
+ wrapped = baseQuery;
+ } else {
+ // disable coord
+ BooleanQuery wrappedBQ = new BooleanQuery(true);
+ if ((baseQuery instanceof MatchAllDocsQuery) == false) {
+ wrappedBQ.add(baseQuery, BooleanClause.Occur.MUST);
+ }
+ for(Query q : queries) {
+ wrappedBQ.add(q, BooleanClause.Occur.MUST);
+ }
+ wrapped = wrappedBQ;
+ }
+
+ for(Filter filter : filters) {
+ wrapped = new FilteredQuery(wrapped, filter, FilteredQuery.QUERY_FIRST_FILTER_STRATEGY);
+ }
+
+ return wrapped;
+ }
}
@Override
Modified: lucene/dev/branches/lucene5376/lucene/facet/src/java/org/apache/lucene/facet/DrillSideways.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene5376/lucene/facet/src/java/org/apache/lucene/facet/DrillSideways.java?rev=1562127&r1=1562126&r2=1562127&view=diff
==============================================================================
--- lucene/dev/branches/lucene5376/lucene/facet/src/java/org/apache/lucene/facet/DrillSideways.java (original)
+++ lucene/dev/branches/lucene5376/lucene/facet/src/java/org/apache/lucene/facet/DrillSideways.java Tue Jan 28 17:07:19 2014
@@ -182,8 +182,8 @@ public class DrillSideways {
* drill down and sideways counts.
*/
public DrillSidewaysResult search(DrillDownQuery query,
- Filter filter, FieldDoc after, int topN, Sort sort, boolean doDocScores,
- boolean doMaxScore) throws IOException {
+ Filter filter, FieldDoc after, int topN, Sort sort, boolean doDocScores,
+ boolean doMaxScore) throws IOException {
if (filter != null) {
query = new DrillDownQuery(config, filter, query);
}
Modified: lucene/dev/branches/lucene5376/lucene/facet/src/java/org/apache/lucene/facet/DrillSidewaysQuery.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene5376/lucene/facet/src/java/org/apache/lucene/facet/DrillSidewaysQuery.java?rev=1562127&r1=1562126&r2=1562127&view=diff
==============================================================================
--- lucene/dev/branches/lucene5376/lucene/facet/src/java/org/apache/lucene/facet/DrillSidewaysQuery.java (original)
+++ lucene/dev/branches/lucene5376/lucene/facet/src/java/org/apache/lucene/facet/DrillSidewaysQuery.java Tue Jan 28 17:07:19 2014
@@ -22,8 +22,10 @@ import java.util.Arrays;
import org.apache.lucene.index.AtomicReaderContext;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.search.Collector;
+import org.apache.lucene.search.DocIdSet;
import org.apache.lucene.search.DocIdSetIterator;
import org.apache.lucene.search.Explanation;
+import org.apache.lucene.search.Filter;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.Scorer;
@@ -73,9 +75,17 @@ class DrillSidewaysQuery extends Query {
@Override
public Weight createWeight(IndexSearcher searcher) throws IOException {
final Weight baseWeight = baseQuery.createWeight(searcher);
- final Weight[] drillDownWeights = new Weight[drillDownQueries.length];
+ final Object[] drillDowns = new Object[drillDownQueries.length];
for(int dim=0;dim<drillDownQueries.length;dim++) {
- drillDownWeights[dim] = searcher.rewrite(drillDownQueries[dim]).createWeight(searcher);
+ Query query = drillDownQueries[dim];
+ Filter filter = DrillDownQuery.getFilter(query);
+ if (filter != null) {
+ drillDowns[dim] = filter;
+ } else {
+ // TODO: would be nice if we could say "we will do no
+ // scoring" here....
+ drillDowns[dim] = searcher.rewrite(query).createWeight(searcher);
+ }
}
return new Weight() {
@@ -110,30 +120,69 @@ class DrillSidewaysQuery extends Query {
public Scorer scorer(AtomicReaderContext context, boolean scoreDocsInOrder,
boolean topScorer, Bits acceptDocs) throws IOException {
- DrillSidewaysScorer.DocsAndCost[] dims = new DrillSidewaysScorer.DocsAndCost[drillDownWeights.length];
+ // TODO: it could be better if we take acceptDocs
+ // into account instead of baseScorer?
+ Scorer baseScorer = baseWeight.scorer(context, scoreDocsInOrder, false, acceptDocs);
+
+ DrillSidewaysScorer.DocsAndCost[] dims = new DrillSidewaysScorer.DocsAndCost[drillDowns.length];
int nullCount = 0;
for(int dim=0;dim<dims.length;dim++) {
dims[dim] = new DrillSidewaysScorer.DocsAndCost();
dims[dim].sidewaysCollector = drillSidewaysCollectors[dim];
- DocIdSetIterator disi = drillDownWeights[dim].scorer(context, true, false, null);
- if (disi == null) {
- nullCount++;
- continue;
+ if (drillDowns[dim] instanceof Filter) {
+ // Pass null for acceptDocs because we already
+ // passed it to baseScorer and baseScorer is
+ // MUST'd here
+ DocIdSet dis = ((Filter) drillDowns[dim]).getDocIdSet(context, null);
+
+ Bits bits = dis.bits();
+ if (bits != null) {
+ // TODO: this logic is too naive: the
+ // existence of bits() in DIS today means
+ // either "I'm a cheap FixedBitSet so apply me down
+ // low as you decode the postings" or "I'm so
+ // horribly expensive so apply me after all
+ // other Query/Filter clauses pass"
+
+ // Filter supports random access; use that to
+ // prevent .advance() on costly filters:
+ dims[dim].bits = bits;
+
+ // TODO: Filter needs to express its expected
+ // cost somehow, before pulling the iterator;
+ // we should use that here to set the order to
+ // check the filters:
+
+ } else {
+ DocIdSetIterator disi = dis.iterator();
+ if (disi == null) {
+ nullCount++;
+ continue;
+ }
+ dims[dim].disi = disi;
+ }
+ } else {
+ DocIdSetIterator disi = ((Weight) drillDowns[dim]).scorer(context, true, false, null);
+ if (disi == null) {
+ nullCount++;
+ continue;
+ }
+ dims[dim].disi = disi;
}
- dims[dim].disi = disi;
}
- if (nullCount > 1 || (nullCount == 1 && dims.length == 1)) {
+ // If more than one dim has no matches, then there
+ // are no hits nor drill-sideways counts. Or, if we
+ // have only one dim and that dim has no matches,
+ // same thing.
+ //if (nullCount > 1 || (nullCount == 1 && dims.length == 1)) {
+ if (nullCount > 1) {
return null;
}
// Sort drill-downs by most restrictive first:
Arrays.sort(dims);
- // TODO: it could be better if we take acceptDocs
- // into account instead of baseScorer?
- Scorer baseScorer = baseWeight.scorer(context, scoreDocsInOrder, false, acceptDocs);
-
if (baseScorer == null) {
return null;
}
Modified: lucene/dev/branches/lucene5376/lucene/facet/src/java/org/apache/lucene/facet/DrillSidewaysScorer.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene5376/lucene/facet/src/java/org/apache/lucene/facet/DrillSidewaysScorer.java?rev=1562127&r1=1562126&r2=1562127&view=diff
==============================================================================
--- lucene/dev/branches/lucene5376/lucene/facet/src/java/org/apache/lucene/facet/DrillSidewaysScorer.java (original)
+++ lucene/dev/branches/lucene5376/lucene/facet/src/java/org/apache/lucene/facet/DrillSidewaysScorer.java Tue Jan 28 17:07:19 2014
@@ -26,6 +26,7 @@ import org.apache.lucene.search.Collecto
import org.apache.lucene.search.DocIdSetIterator;
import org.apache.lucene.search.Scorer;
import org.apache.lucene.search.Weight;
+import org.apache.lucene.util.Bits;
import org.apache.lucene.util.FixedBitSet;
class DrillSidewaysScorer extends Scorer {
@@ -82,20 +83,39 @@ class DrillSidewaysScorer extends Scorer
// Position all scorers to their first matching doc:
baseScorer.nextDoc();
+ int numBits = 0;
for (DocsAndCost dim : dims) {
- dim.disi.nextDoc();
+ if (dim.disi != null) {
+ dim.disi.nextDoc();
+ } else if (dim.bits != null) {
+ numBits++;
+ }
}
final int numDims = dims.length;
- DocIdSetIterator[] disis = new DocIdSetIterator[numDims];
- Collector[] sidewaysCollectors = new Collector[numDims];
+ Bits[] bits = new Bits[numBits];
+ Collector[] bitsSidewaysCollectors = new Collector[numBits];
+
+ DocIdSetIterator[] disis = new DocIdSetIterator[numDims-numBits];
+ Collector[] sidewaysCollectors = new Collector[numDims-numBits];
long drillDownCost = 0;
+ int disiUpto = 0;
+ int bitsUpto = 0;
for (int dim=0;dim<numDims;dim++) {
DocIdSetIterator disi = dims[dim].disi;
- disis[dim] = disi;
- sidewaysCollectors[dim] = dims[dim].sidewaysCollector;
- drillDownCost += disi.cost();
+ if (dims[dim].bits == null) {
+ disis[disiUpto] = disi;
+ sidewaysCollectors[disiUpto] = dims[dim].sidewaysCollector;
+ disiUpto++;
+ if (disi != null) {
+ drillDownCost += disi.cost();
+ }
+ } else {
+ bits[bitsUpto] = dims[dim].bits;
+ bitsSidewaysCollectors[bitsUpto] = dims[dim].sidewaysCollector;
+ bitsUpto++;
+ }
}
long baseQueryCost = baseScorer.cost();
@@ -110,10 +130,10 @@ class DrillSidewaysScorer extends Scorer
}
*/
- if (scoreSubDocsAtOnce || baseQueryCost < drillDownCost/10) {
- //System.out.println("baseAdvance");
- doBaseAdvanceScoring(collector, disis, sidewaysCollectors);
- } else if (numDims > 1 && dims[1].disi.cost() < baseQueryCost/10) {
+ if (bitsUpto > 0 || scoreSubDocsAtOnce || baseQueryCost < drillDownCost/10) {
+ //System.out.println("queryFirst");
+ doQueryFirstScoring(collector, disis, sidewaysCollectors, bits, bitsSidewaysCollectors);
+ } else if (numDims > 1 && (dims[1].disi == null || dims[1].disi.cost() < baseQueryCost/10)) {
//System.out.println("drillDownAdvance");
doDrillDownAdvanceScoring(collector, disis, sidewaysCollectors);
} else {
@@ -127,32 +147,55 @@ class DrillSidewaysScorer extends Scorer
* (i.e., like BooleanScorer2, not BooleanScorer). In
* this case we just .next() on base and .advance() on
* the dim filters. */
- private void doBaseAdvanceScoring(Collector collector, DocIdSetIterator[] disis, Collector[] sidewaysCollectors) throws IOException {
+ private void doQueryFirstScoring(Collector collector, DocIdSetIterator[] disis, Collector[] sidewaysCollectors,
+ Bits[] bits, Collector[] bitsSidewaysCollectors) throws IOException {
//if (DEBUG) {
- // System.out.println(" doBaseAdvanceScoring");
+ // System.out.println(" doQueryFirstScoring");
//}
int docID = baseScorer.docID();
- final int numDims = dims.length;
-
nextDoc: while (docID != NO_MORE_DOCS) {
- int failedDim = -1;
- for (int dim=0;dim<numDims;dim++) {
+ Collector failedCollector = null;
+ for (int i=0;i<disis.length;i++) {
// TODO: should we sort this 2nd dimension of
// docsEnums from most frequent to least?
- DocIdSetIterator disi = disis[dim];
- if (disi.docID() < docID) {
- disi.advance(docID);
+ DocIdSetIterator disi = disis[i];
+ if (disi != null) {
+ if (disi.docID() < docID) {
+ disi.advance(docID);
+ }
+ if (disi.docID() > docID) {
+ if (failedCollector != null) {
+ // More than one dim fails on this document, so
+ // it's neither a hit nor a near-miss; move to
+ // next doc:
+ docID = baseScorer.nextDoc();
+ continue nextDoc;
+ } else {
+ failedCollector = sidewaysCollectors[i];
+ }
+ }
}
- if (disi.docID() > docID) {
- if (failedDim != -1) {
+ }
+
+ // TODO: for the "non-costly Bits" we really should
+ // have passed them down as acceptDocs, but
+ // unfortunately we cannot distinguish today betwen
+ // "bits() is so costly that you should apply it last"
+ // from "bits() is so cheap that you should apply it
+ // everywhere down low"
+
+ // Fold in Filter Bits last, since they may be costly:
+ for(int i=0;i<bits.length;i++) {
+ if (bits[i].get(docID) == false) {
+ if (failedCollector != null) {
// More than one dim fails on this document, so
// it's neither a hit nor a near-miss; move to
// next doc:
docID = baseScorer.nextDoc();
continue nextDoc;
} else {
- failedDim = dim;
+ failedCollector = bitsSidewaysCollectors[i];
}
}
}
@@ -163,12 +206,12 @@ class DrillSidewaysScorer extends Scorer
// daat here:
collectScore = baseScorer.score();
- if (failedDim == -1) {
+ if (failedCollector == null) {
// Hit passed all filters, so it's "real":
- collectHit(collector, sidewaysCollectors);
+ collectHit(collector, sidewaysCollectors, bitsSidewaysCollectors);
} else {
// Hit missed exactly one filter:
- collectNearMiss(sidewaysCollectors, failedDim);
+ collectNearMiss(failedCollector);
}
docID = baseScorer.nextDoc();
@@ -207,60 +250,64 @@ class DrillSidewaysScorer extends Scorer
// System.out.println(" dim0");
//}
DocIdSetIterator disi = disis[0];
- int docID = disi.docID();
- while (docID < nextChunkStart) {
- int slot = docID & MASK;
+ if (disi != null) {
+ int docID = disi.docID();
+ while (docID < nextChunkStart) {
+ int slot = docID & MASK;
- if (docIDs[slot] != docID) {
- seen.set(slot);
- // Mark slot as valid:
- //if (DEBUG) {
- // System.out.println(" set docID=" + docID + " id=" + context.reader().document(docID).get("id"));
- //}
- docIDs[slot] = docID;
- missingDims[slot] = 1;
- counts[slot] = 1;
- }
+ if (docIDs[slot] != docID) {
+ seen.set(slot);
+ // Mark slot as valid:
+ //if (DEBUG) {
+ // System.out.println(" set docID=" + docID + " id=" + context.reader().document(docID).get("id"));
+ //}
+ docIDs[slot] = docID;
+ missingDims[slot] = 1;
+ counts[slot] = 1;
+ }
- docID = disi.nextDoc();
+ docID = disi.nextDoc();
+ }
}
-
+
// Second dim:
//if (DEBUG) {
// System.out.println(" dim1");
//}
disi = disis[1];
- docID = disi.docID();
- while (docID < nextChunkStart) {
- int slot = docID & MASK;
+ if (disi != null) {
+ int docID = disi.docID();
+ while (docID < nextChunkStart) {
+ int slot = docID & MASK;
- if (docIDs[slot] != docID) {
- // Mark slot as valid:
- seen.set(slot);
- //if (DEBUG) {
- // System.out.println(" set docID=" + docID + " missingDim=0 id=" + context.reader().document(docID).get("id"));
- //}
- docIDs[slot] = docID;
- missingDims[slot] = 0;
- counts[slot] = 1;
- } else {
- // TODO: single-valued dims will always be true
- // below; we could somehow specialize
- if (missingDims[slot] >= 1) {
- missingDims[slot] = 2;
- counts[slot] = 2;
+ if (docIDs[slot] != docID) {
+ // Mark slot as valid:
+ seen.set(slot);
//if (DEBUG) {
- // System.out.println(" set docID=" + docID + " missingDim=2 id=" + context.reader().document(docID).get("id"));
+ // System.out.println(" set docID=" + docID + " missingDim=0 id=" + context.reader().document(docID).get("id"));
//}
- } else {
+ docIDs[slot] = docID;
+ missingDims[slot] = 0;
counts[slot] = 1;
- //if (DEBUG) {
- // System.out.println(" set docID=" + docID + " missingDim=" + missingDims[slot] + " id=" + context.reader().document(docID).get("id"));
- //}
+ } else {
+ // TODO: single-valued dims will always be true
+ // below; we could somehow specialize
+ if (missingDims[slot] >= 1) {
+ missingDims[slot] = 2;
+ counts[slot] = 2;
+ //if (DEBUG) {
+ // System.out.println(" set docID=" + docID + " missingDim=2 id=" + context.reader().document(docID).get("id"));
+ //}
+ } else {
+ counts[slot] = 1;
+ //if (DEBUG) {
+ // System.out.println(" set docID=" + docID + " missingDim=" + missingDims[slot] + " id=" + context.reader().document(docID).get("id"));
+ //}
+ }
}
- }
- docID = disi.nextDoc();
+ docID = disi.nextDoc();
+ }
}
// After this we can "upgrade" to conjunction, because
@@ -318,28 +365,30 @@ class DrillSidewaysScorer extends Scorer
// System.out.println(" dim=" + dim + " [" + dims[dim].dim + "]");
//}
disi = disis[dim];
- docID = disi.docID();
- while (docID < nextChunkStart) {
- int slot = docID & MASK;
- if (docIDs[slot] == docID && counts[slot] >= dim) {
- // TODO: single-valued dims will always be true
- // below; we could somehow specialize
- if (missingDims[slot] >= dim) {
- //if (DEBUG) {
- // System.out.println(" set docID=" + docID + " count=" + (dim+2));
- //}
- missingDims[slot] = dim+1;
- counts[slot] = dim+2;
- } else {
- //if (DEBUG) {
- // System.out.println(" set docID=" + docID + " missing count=" + (dim+1));
- //}
- counts[slot] = dim+1;
+ if (disi != null) {
+ int docID = disi.docID();
+ while (docID < nextChunkStart) {
+ int slot = docID & MASK;
+ if (docIDs[slot] == docID && counts[slot] >= dim) {
+ // TODO: single-valued dims will always be true
+ // below; we could somehow specialize
+ if (missingDims[slot] >= dim) {
+ //if (DEBUG) {
+ // System.out.println(" set docID=" + docID + " count=" + (dim+2));
+ //}
+ missingDims[slot] = dim+1;
+ counts[slot] = dim+2;
+ } else {
+ //if (DEBUG) {
+ // System.out.println(" set docID=" + docID + " missing count=" + (dim+1));
+ //}
+ counts[slot] = dim+1;
+ }
}
- }
- // TODO: sometimes use advance?
- docID = disi.nextDoc();
+ // TODO: sometimes use advance?
+ docID = disi.nextDoc();
+ }
}
}
@@ -357,7 +406,7 @@ class DrillSidewaysScorer extends Scorer
if (counts[slot] == 1+numDims) {
collectHit(collector, sidewaysCollectors);
} else if (counts[slot] == numDims) {
- collectNearMiss(sidewaysCollectors, missingDims[slot]);
+ collectNearMiss(sidewaysCollectors[missingDims[slot]]);
}
}
@@ -432,58 +481,61 @@ class DrillSidewaysScorer extends Scorer
// System.out.println(" dim=0 [" + dims[0].dim + "]");
//}
DocIdSetIterator disi = disis[0];
- docID = disi.docID();
- //if (DEBUG) {
- // System.out.println(" start docID=" + docID);
- //}
- while (docID < nextChunkStart) {
- int slot = docID & MASK;
- if (docIDs[slot] == docID) {
- //if (DEBUG) {
- // System.out.println(" set docID=" + docID + " count=2");
- //}
- missingDims[slot] = 1;
- counts[slot] = 2;
+ if (disi != null) {
+ docID = disi.docID();
+ //if (DEBUG) {
+ // System.out.println(" start docID=" + docID);
+ //}
+ while (docID < nextChunkStart) {
+ int slot = docID & MASK;
+ if (docIDs[slot] == docID) {
+ //if (DEBUG) {
+ // System.out.println(" set docID=" + docID + " count=2");
+ //}
+ missingDims[slot] = 1;
+ counts[slot] = 2;
+ }
+ docID = disi.nextDoc();
}
- docID = disi.nextDoc();
}
for (int dim=1;dim<numDims;dim++) {
//if (DEBUG) {
// System.out.println(" dim=" + dim + " [" + dims[dim].dim + "]");
//}
+
disi = disis[dim];
- docID = disi.docID();
- //if (DEBUG) {
- // System.out.println(" start docID=" + docID);
- //}
- while (docID < nextChunkStart) {
- int slot = docID & MASK;
- if (docIDs[slot] == docID && counts[slot] >= dim) {
- // This doc is still in the running...
- // TODO: single-valued dims will always be true
- // below; we could somehow specialize
- if (missingDims[slot] >= dim) {
- //if (DEBUG) {
- // System.out.println(" set docID=" + docID + " count=" + (dim+2));
- //}
- missingDims[slot] = dim+1;
- counts[slot] = dim+2;
- } else {
- //if (DEBUG) {
- // System.out.println(" set docID=" + docID + " missing count=" + (dim+1));
- //}
- counts[slot] = dim+1;
+ if (disi != null) {
+ docID = disi.docID();
+ //if (DEBUG) {
+ // System.out.println(" start docID=" + docID);
+ //}
+ while (docID < nextChunkStart) {
+ int slot = docID & MASK;
+ if (docIDs[slot] == docID && counts[slot] >= dim) {
+ // This doc is still in the running...
+ // TODO: single-valued dims will always be true
+ // below; we could somehow specialize
+ if (missingDims[slot] >= dim) {
+ //if (DEBUG) {
+ // System.out.println(" set docID=" + docID + " count=" + (dim+2));
+ //}
+ missingDims[slot] = dim+1;
+ counts[slot] = dim+2;
+ } else {
+ //if (DEBUG) {
+ // System.out.println(" set docID=" + docID + " missing count=" + (dim+1));
+ //}
+ counts[slot] = dim+1;
+ }
}
+ docID = disi.nextDoc();
}
- docID = disi.nextDoc();
}
}
// Collect:
- //if (DEBUG) {
- // System.out.println(" now collect: " + filledCount + " hits");
- //}
+ //System.out.println(" now collect: " + filledCount + " hits");
for (int i=0;i<filledCount;i++) {
// NOTE: This is actually in-order collection,
// because we only accept docs originally returned by
@@ -500,7 +552,7 @@ class DrillSidewaysScorer extends Scorer
collectHit(collector, sidewaysCollectors);
} else if (counts[slot] == numDims) {
//System.out.println(" sw");
- collectNearMiss(sidewaysCollectors, missingDims[slot]);
+ collectNearMiss(sidewaysCollectors[missingDims[slot]]);
}
}
@@ -532,11 +584,34 @@ class DrillSidewaysScorer extends Scorer
}
}
- private void collectNearMiss(Collector[] sidewaysCollectors, int dim) throws IOException {
+ private void collectHit(Collector collector, Collector[] sidewaysCollectors, Collector[] sidewaysCollectors2) throws IOException {
+ //if (DEBUG) {
+ // System.out.println(" hit");
+ //}
+
+ collector.collect(collectDocID);
+ if (drillDownCollector != null) {
+ drillDownCollector.collect(collectDocID);
+ }
+
+ // TODO: we could "fix" faceting of the sideways counts
+ // to do this "union" (of the drill down hits) in the
+ // end instead:
+
+ // Tally sideways counts:
+ for (int i=0;i<sidewaysCollectors.length;i++) {
+ sidewaysCollectors[i].collect(collectDocID);
+ }
+ for (int i=0;i<sidewaysCollectors2.length;i++) {
+ sidewaysCollectors2[i].collect(collectDocID);
+ }
+ }
+
+ private void collectNearMiss(Collector sidewaysCollector) throws IOException {
//if (DEBUG) {
// System.out.println(" missingDim=" + dim);
//}
- sidewaysCollectors[dim].collect(collectDocID);
+ sidewaysCollector.collect(collectDocID);
}
@Override
@@ -575,14 +650,24 @@ class DrillSidewaysScorer extends Scorer
}
static class DocsAndCost implements Comparable<DocsAndCost> {
- // Docs matching this dim's filter:
+ // Iterator for docs matching this dim's filter, or ...
DocIdSetIterator disi;
+ // Random access bits:
+ Bits bits;
Collector sidewaysCollector;
String dim;
@Override
public int compareTo(DocsAndCost other) {
- if (disi.cost() < other.disi.cost()) {
+ if (disi == null) {
+ if (other.disi == null) {
+ return 0;
+ } else {
+ return 1;
+ }
+ } else if (other.disi == null) {
+ return -1;
+ } else if (disi.cost() < other.disi.cost()) {
return -1;
} else if (disi.cost() > other.disi.cost()) {
return 1;
Modified: lucene/dev/branches/lucene5376/lucene/facet/src/java/org/apache/lucene/facet/range/DoubleRange.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene5376/lucene/facet/src/java/org/apache/lucene/facet/range/DoubleRange.java?rev=1562127&r1=1562126&r2=1562127&view=diff
==============================================================================
--- lucene/dev/branches/lucene5376/lucene/facet/src/java/org/apache/lucene/facet/range/DoubleRange.java (original)
+++ lucene/dev/branches/lucene5376/lucene/facet/src/java/org/apache/lucene/facet/range/DoubleRange.java Tue Jan 28 17:07:19 2014
@@ -26,7 +26,6 @@ import org.apache.lucene.queries.functio
import org.apache.lucene.search.DocIdSet;
import org.apache.lucene.search.DocIdSetIterator;
import org.apache.lucene.search.Filter;
-import org.apache.lucene.search.NumericRangeFilter; // javadocs
import org.apache.lucene.util.Bits;
import org.apache.lucene.util.NumericUtils;
@@ -102,6 +101,12 @@ public final class DoubleRange extends R
@Override
public Filter getFilter(final ValueSource valueSource) {
return new Filter() {
+
+ @Override
+ public String toString() {
+ return "Filter(" + DoubleRange.this.toString();
+ }
+
@Override
public DocIdSet getDocIdSet(AtomicReaderContext context, final Bits acceptDocs) throws IOException {
@@ -117,51 +122,27 @@ public final class DoubleRange extends R
return new DocIdSet() {
@Override
- public DocIdSetIterator iterator() {
- return new DocIdSetIterator() {
- int doc = -1;
-
+ public Bits bits() {
+ return new Bits() {
@Override
- public int nextDoc() throws IOException {
- assert doc != NO_MORE_DOCS;
- while (true) {
- doc++;
- if (doc == maxDoc) {
- return doc = NO_MORE_DOCS;
- }
- if (acceptDocs != null && acceptDocs.get(doc) == false) {
- continue;
- }
- double v = values.doubleVal(doc);
- if (accept(v)) {
- return doc;
- }
+ public boolean get(int docID) {
+ if (acceptDocs != null && acceptDocs.get(docID) == false) {
+ return false;
}
+ return accept(values.doubleVal(docID));
}
@Override
- public int advance(int target) throws IOException {
- if (target == NO_MORE_DOCS) {
- doc = target;
- return doc;
- }
- doc = target-1;
- return nextDoc();
- }
-
- @Override
- public int docID() {
- return doc;
- }
-
- @Override
- public long cost() {
- // Since we do a linear scan over all
- // documents, our cost is O(maxDoc):
+ public int length() {
return maxDoc;
}
};
}
+
+ @Override
+ public DocIdSetIterator iterator() {
+ return new SlowBitsDocIdSetIterator(bits(), maxDoc, acceptDocs);
+ }
};
}
};
Modified: lucene/dev/branches/lucene5376/lucene/facet/src/java/org/apache/lucene/facet/range/LongRange.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene5376/lucene/facet/src/java/org/apache/lucene/facet/range/LongRange.java?rev=1562127&r1=1562126&r2=1562127&view=diff
==============================================================================
--- lucene/dev/branches/lucene5376/lucene/facet/src/java/org/apache/lucene/facet/range/LongRange.java (original)
+++ lucene/dev/branches/lucene5376/lucene/facet/src/java/org/apache/lucene/facet/range/LongRange.java Tue Jan 28 17:07:19 2014
@@ -26,7 +26,6 @@ import org.apache.lucene.queries.functio
import org.apache.lucene.search.DocIdSet;
import org.apache.lucene.search.DocIdSetIterator;
import org.apache.lucene.search.Filter;
-import org.apache.lucene.search.NumericRangeFilter; // javadocs
import org.apache.lucene.util.Bits;
/** Represents a range over long values. */
@@ -94,6 +93,12 @@ public final class LongRange extends Ran
@Override
public Filter getFilter(final ValueSource valueSource) {
return new Filter() {
+
+ @Override
+ public String toString() {
+ return "Filter(" + LongRange.this.toString();
+ }
+
@Override
public DocIdSet getDocIdSet(AtomicReaderContext context, final Bits acceptDocs) throws IOException {
@@ -109,51 +114,27 @@ public final class LongRange extends Ran
return new DocIdSet() {
@Override
- public DocIdSetIterator iterator() {
- return new DocIdSetIterator() {
- int doc = -1;
-
+ public Bits bits() {
+ return new Bits() {
@Override
- public int nextDoc() throws IOException {
- assert doc != NO_MORE_DOCS;
- while (true) {
- doc++;
- if (doc == maxDoc) {
- return doc = NO_MORE_DOCS;
- }
- if (acceptDocs != null && acceptDocs.get(doc) == false) {
- continue;
- }
- long v = values.longVal(doc);
- if (accept(v)) {
- return doc;
- }
+ public boolean get(int docID) {
+ if (acceptDocs != null && acceptDocs.get(docID) == false) {
+ return false;
}
+ return accept(values.longVal(docID));
}
@Override
- public int advance(int target) throws IOException {
- if (target == NO_MORE_DOCS) {
- doc = target;
- return doc;
- }
- doc = target-1;
- return nextDoc();
- }
-
- @Override
- public int docID() {
- return doc;
- }
-
- @Override
- public long cost() {
- // Since we do a linear scan over all
- // documents, our cost is O(maxDoc):
+ public int length() {
return maxDoc;
}
};
}
+
+ @Override
+ public DocIdSetIterator iterator() {
+ return new SlowBitsDocIdSetIterator(bits(), maxDoc, acceptDocs);
+ }
};
}
};
Modified: lucene/dev/branches/lucene5376/lucene/facet/src/java/org/apache/lucene/facet/range/Range.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene5376/lucene/facet/src/java/org/apache/lucene/facet/range/Range.java?rev=1562127&r1=1562126&r2=1562127&view=diff
==============================================================================
--- lucene/dev/branches/lucene5376/lucene/facet/src/java/org/apache/lucene/facet/range/Range.java (original)
+++ lucene/dev/branches/lucene5376/lucene/facet/src/java/org/apache/lucene/facet/range/Range.java Tue Jan 28 17:07:19 2014
@@ -1,8 +1,5 @@
package org.apache.lucene.facet.range;
-import org.apache.lucene.queries.function.ValueSource;
-import org.apache.lucene.search.Filter;
-
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
@@ -20,6 +17,10 @@ import org.apache.lucene.search.Filter;
* limitations under the License.
*/
+import org.apache.lucene.queries.function.ValueSource;
+import org.apache.lucene.search.Filter;
+import org.apache.lucene.search.FilteredQuery; // javadocs
+
/** Base class for a single labeled range.
*
* @lucene.experimental */
@@ -37,11 +38,13 @@ public abstract class Range {
}
/** Returns a new {@link Filter} accepting only documents
- * in this range. Note that this filter is not
- * efficient: it's a linear scan of all docs, testing
- * each value. If the {@link ValueSource} is static,
- * e.g. an indexed numeric field, then it's more
- * efficient to use {@link NumericRangeFilter}. */
+ * in this range. This filter is not general-purpose;
+ * you should either use it with {@link DrillSideways} by
+ * adding it to {@link DrillDownQuery#add}, or pass it to
+ * {@link FilteredQuery} using its {@link
+ * FilteredQuery#QUERY_FIRST_FILTER_STRATEGY}. If the
+ * {@link ValueSource} is static, e.g. an indexed numeric
+ * field, then it may be more efficient to use {@link NumericRangeFilter}. */
public abstract Filter getFilter(final ValueSource valueSource);
/** Invoke this for a useless range. */
Added: lucene/dev/branches/lucene5376/lucene/facet/src/java/org/apache/lucene/facet/range/SlowBitsDocIdSetIterator.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene5376/lucene/facet/src/java/org/apache/lucene/facet/range/SlowBitsDocIdSetIterator.java?rev=1562127&view=auto
==============================================================================
--- lucene/dev/branches/lucene5376/lucene/facet/src/java/org/apache/lucene/facet/range/SlowBitsDocIdSetIterator.java (added)
+++ lucene/dev/branches/lucene5376/lucene/facet/src/java/org/apache/lucene/facet/range/SlowBitsDocIdSetIterator.java Tue Jan 28 17:07:19 2014
@@ -0,0 +1,75 @@
+package org.apache.lucene.facet.range;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.io.IOException;
+
+import org.apache.lucene.search.DocIdSetIterator;
+import org.apache.lucene.util.Bits;
+
+/** Wraps a {@link Bits} to make a {@link DocIdSetIterator};
+ * this is typically a slow thing to do and should only be
+ * used as a last resort! */
+class SlowBitsDocIdSetIterator extends DocIdSetIterator {
+ private final Bits bits;
+ private final long cost;
+ private final int maxDoc;
+ private final Bits acceptDocs;
+ private int doc = -1;
+
+ public SlowBitsDocIdSetIterator(Bits bits, long cost, Bits acceptDocs) {
+ this.bits = bits;
+ this.cost = cost;
+ this.maxDoc = bits.length();
+ this.acceptDocs = acceptDocs;
+ }
+
+ @Override
+ public int advance(int target) throws IOException {
+ if (target == NO_MORE_DOCS) {
+ doc = target;
+ return doc;
+ }
+ doc = target-1;
+ return nextDoc();
+ }
+
+ @Override
+ public int nextDoc() throws IOException {
+ assert doc != NO_MORE_DOCS;
+ while (true) {
+ doc++;
+ if (doc == maxDoc) {
+ return doc = NO_MORE_DOCS;
+ }
+ if ((acceptDocs == null || acceptDocs.get(doc)) && bits.get(doc)) {
+ return doc;
+ }
+ }
+ }
+
+ @Override
+ public int docID() {
+ return doc;
+ }
+
+ @Override
+ public long cost() {
+ return cost;
+ }
+}
\ No newline at end of file
Modified: lucene/dev/branches/lucene5376/lucene/facet/src/test/org/apache/lucene/facet/range/TestRangeFacetCounts.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene5376/lucene/facet/src/test/org/apache/lucene/facet/range/TestRangeFacetCounts.java?rev=1562127&r1=1562126&r2=1562127&view=diff
==============================================================================
--- lucene/dev/branches/lucene5376/lucene/facet/src/test/org/apache/lucene/facet/range/TestRangeFacetCounts.java (original)
+++ lucene/dev/branches/lucene5376/lucene/facet/src/test/org/apache/lucene/facet/range/TestRangeFacetCounts.java Tue Jan 28 17:07:19 2014
@@ -51,7 +51,6 @@ import org.apache.lucene.queries.functio
import org.apache.lucene.queries.function.ValueSource;
import org.apache.lucene.queries.function.docvalues.DoubleDocValues;
import org.apache.lucene.queries.function.valuesource.FloatFieldSource;
-import org.apache.lucene.search.ConstantScoreQuery;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.MatchAllDocsQuery;
import org.apache.lucene.search.NumericRangeQuery;
@@ -229,6 +228,10 @@ public class TestRangeFacetCounts extend
IndexSearcher s = newSearcher(r);
+ if (VERBOSE) {
+ System.out.println("TEST: searcher=" + s);
+ }
+
DrillSideways ds = new DrillSideways(s, config, tr) {
@Override
@@ -765,16 +768,13 @@ public class TestRangeFacetCounts extend
Document doc = new Document();
writer.addDocument(doc);
-
- doc = new Document();
writer.addDocument(doc);
-
- doc = new Document();
writer.addDocument(doc);
+ // Test wants 3 docs in one segment:
writer.forceMerge(1);
- ValueSource vs = new ValueSource() {
+ final ValueSource vs = new ValueSource() {
@SuppressWarnings("rawtypes")
@Override
public FunctionValues getValues(Map ignored, AtomicReaderContext ignored2) {
@@ -801,6 +801,8 @@ public class TestRangeFacetCounts extend
throw new UnsupportedOperationException();
}
};
+
+ FacetsConfig config = new FacetsConfig();
FacetsCollector fc = new FacetsCollector();
@@ -808,18 +810,44 @@ public class TestRangeFacetCounts extend
IndexSearcher s = newSearcher(r);
s.search(new MatchAllDocsQuery(), fc);
- Facets facets = new DoubleRangeFacetCounts("field", vs, fc,
+ final DoubleRange[] ranges = new DoubleRange[] {
new DoubleRange("< 1", 0.0, true, 1.0, false),
new DoubleRange("< 2", 0.0, true, 2.0, false),
new DoubleRange("< 5", 0.0, true, 5.0, false),
new DoubleRange("< 10", 0.0, true, 10.0, false),
new DoubleRange("< 20", 0.0, true, 20.0, false),
- new DoubleRange("< 50", 0.0, true, 50.0, false));
+ new DoubleRange("< 50", 0.0, true, 50.0, false)};
+
+ Facets facets = new DoubleRangeFacetCounts("field", vs, fc, ranges);
assertEquals("dim=field path=[] value=3 childCount=6\n < 1 (0)\n < 2 (1)\n < 5 (3)\n < 10 (3)\n < 20 (3)\n < 50 (3)\n", facets.getTopChildren(10, "field").toString());
- // Test drill-down:
- assertEquals(1, s.search(new ConstantScoreQuery(new DoubleRange("< 2", 0.0, true, 2.0, false).getFilter(vs)), 10).totalHits);
+ DrillDownQuery ddq = new DrillDownQuery(config);
+ ddq.add("field", ranges[1].getFilter(vs));
+
+ // Test simple drill-down:
+ assertEquals(1, s.search(ddq, 10).totalHits);
+
+ // Test drill-sideways after drill-down
+ DrillSideways ds = new DrillSideways(s, config, (TaxonomyReader) null) {
+
+ @Override
+ protected Facets buildFacetsResult(FacetsCollector drillDowns, FacetsCollector[] drillSideways, String[] drillSidewaysDims) throws IOException {
+ assert drillSideways.length == 1;
+ return new DoubleRangeFacetCounts("field", vs, drillSideways[0], ranges);
+ }
+
+ @Override
+ protected boolean scoreSubDocsAtOnce() {
+ return random().nextBoolean();
+ }
+ };
+
+
+ DrillSidewaysResult dsr = ds.search(ddq, 10);
+ assertEquals(1, dsr.hits.totalHits);
+ assertEquals("dim=field path=[] value=3 childCount=6\n < 1 (0)\n < 2 (1)\n < 5 (3)\n < 10 (3)\n < 20 (3)\n < 50 (3)\n",
+ dsr.facets.getTopChildren(10, "field").toString());
IOUtils.close(r, writer, dir);
}