Index: src/java/org/apache/lucene/search/FilteredTermEnum.java =================================================================== --- src/java/org/apache/lucene/search/FilteredTermEnum.java (revision 764815) +++ src/java/org/apache/lucene/search/FilteredTermEnum.java (working copy) @@ -26,9 +26,12 @@

Term enumerations are always ordered by Term.compareTo(). Each term in the enumeration is greater than all that precede it. */ public abstract class FilteredTermEnum extends TermEnum { - private Term currentTerm = null; - private TermEnum actualEnum = null; + /** the current term */ + protected Term currentTerm = null; + /** the delegate enum - to set this member use {@link #setEnum} */ + protected TermEnum actualEnum = null; + public FilteredTermEnum() {} /** Equality compare on the term */ @@ -40,6 +43,10 @@ /** Indicates the end of the enumeration has been reached */ protected abstract boolean endEnum(); + /** + * use this method to set the actual TermEnum (e.g. in ctor), + * it will be automatically positioned on the first matching term. + */ protected void setEnum(TermEnum actualEnum) throws IOException { this.actualEnum = actualEnum; // Find the first term that matches @@ -54,7 +61,8 @@ * Returns -1 if no Term matches or all terms have been enumerated. */ public int docFreq() { - if (actualEnum == null) return -1; + if (currentTerm == null) return -1; + assert actualEnum != null; return actualEnum.docFreq(); } @@ -85,7 +93,7 @@ /** Closes the enumeration to further activity, freeing resources. */ public void close() throws IOException { - actualEnum.close(); + if (actualEnum != null) actualEnum.close(); currentTerm = null; actualEnum = null; } Index: src/java/org/apache/lucene/search/MultiTermQuery.java =================================================================== --- src/java/org/apache/lucene/search/MultiTermQuery.java (revision 764815) +++ src/java/org/apache/lucene/search/MultiTermQuery.java (working copy) @@ -45,6 +45,7 @@ public abstract class MultiTermQuery extends Query { protected Term term; protected boolean constantScoreRewrite = false; + int lastNumberOfTerms = -1; /** Constructs a query for terms matching term. */ public MultiTermQuery(Term term) { @@ -67,18 +68,35 @@ protected abstract FilteredTermEnum getEnum(IndexReader reader) throws IOException; + /** + * Expert: Return the number of unique terms visited during the last execution of the query. + * If there are many of them, you may consider using another query type + * or optimize your term count in index. + * This method is not thread safe, be sure to only call it when no query is running! + * The results are only valid, if the test was done on a one-segment index (optimized), + * as search distributes the query to each single segment and the term count is reset + * for each segment and you only see a value for the last (and smallest segment). + * @throws IllegalStateException if query was not yet rewritten/executed. + */ + public int getLastNumberOfTerms() { + if (lastNumberOfTerms < 0) throw new IllegalStateException(); + return lastNumberOfTerms; + } + protected Filter getFilter() { return new MultiTermFilter(this); } public Query rewrite(IndexReader reader) throws IOException { if (!constantScoreRewrite) { + lastNumberOfTerms = 0; FilteredTermEnum enumerator = getEnum(reader); BooleanQuery query = new BooleanQuery(true); try { do { Term t = enumerator.term(); if (t != null) { + lastNumberOfTerms++; TermQuery tq = new TermQuery(t); // found a match tq.setBoost(getBoost() * enumerator.difference()); // set the boost query.add(tq, BooleanClause.Occur.SHOULD); // add to query @@ -150,14 +168,15 @@ MultiTermQuery mtq; abstract class TermGenerator { - public void generate(IndexReader reader) throws IOException { - TermEnum enumerator = mtq.getEnum(reader); + public void generate(IndexReader reader, TermEnum enumerator) throws IOException { + mtq.lastNumberOfTerms = 0; TermDocs termDocs = reader.termDocs(); try { do { Term term = enumerator.term(); if (term == null) break; + mtq.lastNumberOfTerms++; termDocs.seek(term); while (termDocs.next()) { handleDoc(termDocs.doc()); @@ -165,7 +184,6 @@ } while (enumerator.next()); } finally { termDocs.close(); - enumerator.close(); } } abstract public void handleDoc(int doc); @@ -176,28 +194,42 @@ } public BitSet bits(IndexReader reader) throws IOException { - final BitSet bitSet = new BitSet(reader.maxDoc()); - new TermGenerator() { - public void handleDoc(int doc) { - bitSet.set(doc); - } - }.generate(reader); - return bitSet; + final TermEnum enumerator = mtq.getEnum(reader); + try { + final BitSet bitSet = new BitSet(reader.maxDoc()); + new TermGenerator() { + public void handleDoc(int doc) { + bitSet.set(doc); + } + }.generate(reader, enumerator); + return bitSet; + } finally { + enumerator.close(); + } } public DocIdSet getDocIdSet(IndexReader reader) throws IOException { - final OpenBitSet bitSet = new OpenBitSet(reader.maxDoc()); - new TermGenerator() { - public void handleDoc(int doc) { - bitSet.set(doc); + final TermEnum enumerator = mtq.getEnum(reader); + try { + // if current term in enum is null, the enum is empty -> shortcut + if (enumerator.term() == null) { + mtq.lastNumberOfTerms = 0; + return DocIdSet.EMPTY_DOCIDSET; } - }.generate(reader); - - return bitSet; + // else fill into a OpenBitSet + final OpenBitSet bitSet = new OpenBitSet(reader.maxDoc()); + new TermGenerator() { + public void handleDoc(int doc) { + bitSet.set(doc); + } + }.generate(reader, enumerator); + return bitSet; + } finally { + enumerator.close(); + } } public boolean equals(Object o) { - if (this == o) return true; if (!(o instanceof MultiTermFilter))