You are viewing a plain text version of this content. The canonical link for it is here.
Posted to java-commits@lucene.apache.org by ho...@apache.org on 2006/06/26 20:38:48 UTC

svn commit: r417275 - in /lucene/java/trunk: ./ src/java/org/apache/lucene/search/ src/java/org/apache/lucene/search/spans/ src/test/org/apache/lucene/search/

Author: hossman
Date: Mon Jun 26 11:38:47 2006
New Revision: 417275

URL: http://svn.apache.org/viewvc?rev=417275&view=rev
Log:
fixed bug LUCENE-451 by adding new functionality in LUCENE-605 -- new ComplexExplanation class can model matching independent of having a positive value

Added:
    lucene/java/trunk/src/java/org/apache/lucene/search/ComplexExplanation.java
Modified:
    lucene/java/trunk/CHANGES.txt
    lucene/java/trunk/src/java/org/apache/lucene/search/BooleanQuery.java
    lucene/java/trunk/src/java/org/apache/lucene/search/ConstantScoreQuery.java
    lucene/java/trunk/src/java/org/apache/lucene/search/DisjunctionMaxQuery.java
    lucene/java/trunk/src/java/org/apache/lucene/search/Explanation.java
    lucene/java/trunk/src/java/org/apache/lucene/search/MatchAllDocsQuery.java
    lucene/java/trunk/src/java/org/apache/lucene/search/MultiPhraseQuery.java
    lucene/java/trunk/src/java/org/apache/lucene/search/TermQuery.java
    lucene/java/trunk/src/java/org/apache/lucene/search/spans/SpanWeight.java
    lucene/java/trunk/src/test/org/apache/lucene/search/CheckHits.java
    lucene/java/trunk/src/test/org/apache/lucene/search/TestComplexExplanations.java
    lucene/java/trunk/src/test/org/apache/lucene/search/TestExplanations.java

Modified: lucene/java/trunk/CHANGES.txt
URL: http://svn.apache.org/viewvc/lucene/java/trunk/CHANGES.txt?rev=417275&r1=417274&r2=417275&view=diff
==============================================================================
--- lucene/java/trunk/CHANGES.txt (original)
+++ lucene/java/trunk/CHANGES.txt Mon Jun 26 11:38:47 2006
@@ -29,7 +29,12 @@
  4. LUCENE-608: Document.fields() has been deprecated and a new method 
     Document.getFields() has been added that returns a List instead of
     an Enumeration (Daniel Naber)
-       
+
+ 5. LUCENE-605: New Explanation.isMatch() method and new ComplexExplanation
+    subclass allows explain methods to produce Explanations which model
+    "matching" independent of having a positive value.
+    (Chris Hostetter)
+           
 Bug fixes
 
  1. Fixed the web application demo (built with "ant war-demo") which
@@ -60,7 +65,11 @@
  9. LUCENE-610,LUCENE-611: Simple syntax changes to allow compilation with ecj: 
     disambiguate inner class scorer's use of doc() in BooleanScorer2,
     other test code changes.  (DM Smith via Yonik Seeley)
-
+    
+10. LUCENE-451: All core query types now use ComplexExplanations so that
+    boosts of zero don't confuse the BooleanWeight explain method.
+    (Chris Hostetter)
+    
 Optimizations
 
   1. LUCENE-586: TermDocs.skipTo() is now more efficient for multi-segment

Modified: lucene/java/trunk/src/java/org/apache/lucene/search/BooleanQuery.java
URL: http://svn.apache.org/viewvc/lucene/java/trunk/src/java/org/apache/lucene/search/BooleanQuery.java?rev=417275&r1=417274&r2=417275&view=diff
==============================================================================
--- lucene/java/trunk/src/java/org/apache/lucene/search/BooleanQuery.java (original)
+++ lucene/java/trunk/src/java/org/apache/lucene/search/BooleanQuery.java Mon Jun 26 11:38:47 2006
@@ -263,7 +263,7 @@
       throws IOException {
       final int minShouldMatch =
         BooleanQuery.this.getMinimumNumberShouldMatch();
-      Explanation sumExpl = new Explanation();
+      ComplexExplanation sumExpl = new ComplexExplanation();
       sumExpl.setDescription("sum of:");
       int coord = 0;
       int maxCoord = 0;
@@ -275,14 +275,14 @@
         Weight w = (Weight)weights.elementAt(i);
         Explanation e = w.explain(reader, doc);
         if (!c.isProhibited()) maxCoord++;
-        if (e.getValue() > 0) {
+        if (e.isMatch()) {
           if (!c.isProhibited()) {
             sumExpl.addDetail(e);
             sum += e.getValue();
             coord++;
           } else {
             Explanation r =
-              new Explanation(0.0f, "match on prohibited clause");
+              new Explanation(0.0f, "match on prohibited clause (" + c.getQuery().toString() + ")");
             r.addDetail(e);
             sumExpl.addDetail(r);
             fail = true;
@@ -290,36 +290,39 @@
           if (c.getOccur().equals(Occur.SHOULD))
             shouldMatchCount++;
         } else if (c.isRequired()) {
-          Explanation r = new Explanation(0.0f, "no match on required clause");
+          Explanation r = new Explanation(0.0f, "no match on required clause (" + c.getQuery().toString() + ")");
           r.addDetail(e);
           sumExpl.addDetail(r);
           fail = true;
         }
       }
       if (fail) {
+        sumExpl.setMatch(Boolean.FALSE);
         sumExpl.setValue(0.0f);
         sumExpl.setDescription
           ("Failure to meet condition(s) of required/prohibited clause(s)");
         return sumExpl;
       } else if (shouldMatchCount < minShouldMatch) {
+        sumExpl.setMatch(Boolean.FALSE);
         sumExpl.setValue(0.0f);
         sumExpl.setDescription("Failure to match minimum number "+
                                "of optional clauses: " + minShouldMatch);
         return sumExpl;
       }
       
+      sumExpl.setMatch(0 < coord ? Boolean.TRUE : Boolean.FALSE);
       sumExpl.setValue(sum);
       
       float coordFactor = similarity.coord(coord, maxCoord);
       if (coordFactor == 1.0f)                      // coord is no-op
         return sumExpl;                             // eliminate wrapper
       else {
-        Explanation result = new Explanation();
-        result.setDescription("product of:");
+        ComplexExplanation result = new ComplexExplanation(sumExpl.isMatch(),
+                                                           sum*coordFactor,
+                                                           "product of:");
         result.addDetail(sumExpl);
         result.addDetail(new Explanation(coordFactor,
                                          "coord("+coord+"/"+maxCoord+")"));
-        result.setValue(sum*coordFactor);
         return result;
       }
     }

Added: lucene/java/trunk/src/java/org/apache/lucene/search/ComplexExplanation.java
URL: http://svn.apache.org/viewvc/lucene/java/trunk/src/java/org/apache/lucene/search/ComplexExplanation.java?rev=417275&view=auto
==============================================================================
--- lucene/java/trunk/src/java/org/apache/lucene/search/ComplexExplanation.java (added)
+++ lucene/java/trunk/src/java/org/apache/lucene/search/ComplexExplanation.java Mon Jun 26 11:38:47 2006
@@ -0,0 +1,69 @@
+package org.apache.lucene.search;
+
+/**
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed 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.util.ArrayList;
+
+/** Expert: Describes the score computation for document and query, andcan distinguish a match independent of a positive value. */
+public class ComplexExplanation extends Explanation {
+  private Boolean match;
+  
+  public ComplexExplanation() {
+    super();
+  }
+
+  public ComplexExplanation(boolean match, float value, String description) {
+    // NOTE: use of "boolean" instead of "Boolean" in params is concious
+    // choice to encourage clients to be specific.
+    super(value, description);
+    this.match = Boolean.valueOf(match);
+  }
+
+  /**
+   * The match status of this explanation node.
+   * @return May be null if match status is unknown
+   */
+  public Boolean getMatch() { return match; }
+  /**
+   * Sets the match status assigned to this explanation node.
+   * @param match May be null if match status is unknown
+   */
+  public void setMatch(Boolean match) { this.match = match; }
+  /**
+   * Indicates wether or not this Explanation models a good match.
+   *
+   * <p>
+   * If the match statis is explicitly set (ie: not null) this method
+   * uses it; otherwise it defers to the superclass.
+   * </p>
+   * @see #getMatch
+   */
+  public boolean isMatch() {
+    Boolean m = getMatch();
+    return (null != m ? m.booleanValue() : super.isMatch());
+  }
+
+  protected String getSummary() {
+    if (null == getMatch())
+      return super.getSummary();
+    
+    return getValue() + " = "
+      + (isMatch() ? "(MATCH) " : "(NON-MATCH) ")
+      + getDescription();
+  }
+  
+}

Modified: lucene/java/trunk/src/java/org/apache/lucene/search/ConstantScoreQuery.java
URL: http://svn.apache.org/viewvc/lucene/java/trunk/src/java/org/apache/lucene/search/ConstantScoreQuery.java?rev=417275&r1=417274&r2=417275&view=diff
==============================================================================
--- lucene/java/trunk/src/java/org/apache/lucene/search/ConstantScoreQuery.java (original)
+++ lucene/java/trunk/src/java/org/apache/lucene/search/ConstantScoreQuery.java Mon Jun 26 11:38:47 2006
@@ -81,18 +81,20 @@
       ConstantScorer cs = (ConstantScorer)scorer(reader);
       boolean exists = cs.bits.get(doc);
 
-      Explanation result = new Explanation();
+      ComplexExplanation result = new ComplexExplanation();
 
       if (exists) {
         result.setDescription("ConstantScoreQuery(" + filter
         + "), product of:");
         result.setValue(queryWeight);
+        result.setMatch(Boolean.TRUE);
         result.addDetail(new Explanation(getBoost(), "boost"));
         result.addDetail(new Explanation(queryNorm,"queryNorm"));
       } else {
         result.setDescription("ConstantScoreQuery(" + filter
         + ") doesn't match id " + doc);
         result.setValue(0);
+        result.setMatch(Boolean.FALSE);
       }
       return result;
     }

Modified: lucene/java/trunk/src/java/org/apache/lucene/search/DisjunctionMaxQuery.java
URL: http://svn.apache.org/viewvc/lucene/java/trunk/src/java/org/apache/lucene/search/DisjunctionMaxQuery.java?rev=417275&r1=417274&r2=417275&view=diff
==============================================================================
--- lucene/java/trunk/src/java/org/apache/lucene/search/DisjunctionMaxQuery.java (original)
+++ lucene/java/trunk/src/java/org/apache/lucene/search/DisjunctionMaxQuery.java Mon Jun 26 11:38:47 2006
@@ -138,12 +138,13 @@
     /* Explain the score we computed for doc */
     public Explanation explain(IndexReader reader, int doc) throws IOException {
       if ( disjuncts.size() == 1) return ((Weight) weights.get(0)).explain(reader,doc);
-      Explanation result = new Explanation();
+      ComplexExplanation result = new ComplexExplanation();
       float max = 0.0f, sum = 0.0f;
       result.setDescription(tieBreakerMultiplier == 0.0f ? "max of:" : "max plus " + tieBreakerMultiplier + " times others of:");
       for (int i = 0 ; i < weights.size(); i++) {
         Explanation e = ((Weight) weights.get(i)).explain(reader, doc);
-        if (e.getValue() > 0) {
+        if (e.isMatch()) {
+          result.setMatch(Boolean.TRUE);
           result.addDetail(e);
           sum += e.getValue();
           max = Math.max(max, e.getValue());

Modified: lucene/java/trunk/src/java/org/apache/lucene/search/Explanation.java
URL: http://svn.apache.org/viewvc/lucene/java/trunk/src/java/org/apache/lucene/search/Explanation.java?rev=417275&r1=417274&r2=417275&view=diff
==============================================================================
--- lucene/java/trunk/src/java/org/apache/lucene/search/Explanation.java (original)
+++ lucene/java/trunk/src/java/org/apache/lucene/search/Explanation.java Mon Jun 26 11:38:47 2006
@@ -31,6 +31,20 @@
     this.description = description;
   }
 
+  /**
+   * Indicates wether or not this Explanation models a good match.
+   *
+   * <p>
+   * By default, an Explanation represents a "match" if the value is positive.
+   * </p>
+   * @see #getValue
+   */
+  public boolean isMatch() {
+    return (0.0f < getValue());
+  }
+
+
+  
   /** The value assigned to this explanation node. */
   public float getValue() { return value; }
   /** Sets the value assigned to this explanation node. */
@@ -43,6 +57,14 @@
     this.description = description;
   }
 
+  /**
+   * A short one line summary which should contain all high level
+   * information about this Explanation, without the "Details"
+   */
+  protected String getSummary() {
+    return getValue() + " = " + getDescription();
+  }
+  
   /** The sub-nodes of this explanation node. */
   public Explanation[] getDetails() {
     if (details == null)
@@ -61,14 +83,12 @@
   public String toString() {
     return toString(0);
   }
-  private String toString(int depth) {
+  protected String toString(int depth) {
     StringBuffer buffer = new StringBuffer();
     for (int i = 0; i < depth; i++) {
       buffer.append("  ");
     }
-    buffer.append(getValue());
-    buffer.append(" = ");
-    buffer.append(getDescription());
+    buffer.append(getSummary());
     buffer.append("\n");
 
     Explanation[] details = getDetails();
@@ -88,9 +108,7 @@
     buffer.append("<ul>\n");
 
     buffer.append("<li>");
-    buffer.append(getValue());
-    buffer.append(" = ");
-    buffer.append(getDescription());
+    buffer.append(getSummary());
     buffer.append("<br />\n");
 
     Explanation[] details = getDetails();

Modified: lucene/java/trunk/src/java/org/apache/lucene/search/MatchAllDocsQuery.java
URL: http://svn.apache.org/viewvc/lucene/java/trunk/src/java/org/apache/lucene/search/MatchAllDocsQuery.java?rev=417275&r1=417274&r2=417275&view=diff
==============================================================================
--- lucene/java/trunk/src/java/org/apache/lucene/search/MatchAllDocsQuery.java (original)
+++ lucene/java/trunk/src/java/org/apache/lucene/search/MatchAllDocsQuery.java Mon Jun 26 11:38:47 2006
@@ -118,9 +118,8 @@
 
     public Explanation explain(IndexReader reader, int doc) {
       // explain query weight
-      Explanation queryExpl = new Explanation();
-      queryExpl.setDescription("MatchAllDocsQuery, product of:");
-      queryExpl.setValue(getValue());
+      Explanation queryExpl = new ComplexExplanation
+        (true, getValue(), "MatchAllDocsQuery, product of:");
       if (getBoost() != 1.0f) {
         queryExpl.addDetail(new Explanation(getBoost(),"boost"));
       }

Modified: lucene/java/trunk/src/java/org/apache/lucene/search/MultiPhraseQuery.java
URL: http://svn.apache.org/viewvc/lucene/java/trunk/src/java/org/apache/lucene/search/MultiPhraseQuery.java?rev=417275&r1=417274&r2=417275&view=diff
==============================================================================
--- lucene/java/trunk/src/java/org/apache/lucene/search/MultiPhraseQuery.java (original)
+++ lucene/java/trunk/src/java/org/apache/lucene/search/MultiPhraseQuery.java Mon Jun 26 11:38:47 2006
@@ -189,7 +189,7 @@
 
     public Explanation explain(IndexReader reader, int doc)
       throws IOException {
-      Explanation result = new Explanation();
+      ComplexExplanation result = new ComplexExplanation();
       result.setDescription("weight("+getQuery()+" in "+doc+"), product of:");
 
       Explanation idfExpl = new Explanation(idf, "idf("+getQuery()+")");
@@ -214,7 +214,7 @@
       result.addDetail(queryExpl);
 
       // explain field weight
-      Explanation fieldExpl = new Explanation();
+      ComplexExplanation fieldExpl = new ComplexExplanation();
       fieldExpl.setDescription("fieldWeight("+getQuery()+" in "+doc+
                                "), product of:");
 
@@ -230,11 +230,13 @@
       fieldNormExpl.setDescription("fieldNorm(field="+field+", doc="+doc+")");
       fieldExpl.addDetail(fieldNormExpl);
 
+      fieldExpl.setMatch(Boolean.valueOf(tfExpl.isMatch()));
       fieldExpl.setValue(tfExpl.getValue() *
                          idfExpl.getValue() *
                          fieldNormExpl.getValue());
 
       result.addDetail(fieldExpl);
+      result.setMatch(fieldExpl.getMatch());
 
       // combine them
       result.setValue(queryExpl.getValue() * fieldExpl.getValue());

Modified: lucene/java/trunk/src/java/org/apache/lucene/search/TermQuery.java
URL: http://svn.apache.org/viewvc/lucene/java/trunk/src/java/org/apache/lucene/search/TermQuery.java?rev=417275&r1=417274&r2=417275&view=diff
==============================================================================
--- lucene/java/trunk/src/java/org/apache/lucene/search/TermQuery.java (original)
+++ lucene/java/trunk/src/java/org/apache/lucene/search/TermQuery.java Mon Jun 26 11:38:47 2006
@@ -72,7 +72,7 @@
     public Explanation explain(IndexReader reader, int doc)
       throws IOException {
 
-      Explanation result = new Explanation();
+      ComplexExplanation result = new ComplexExplanation();
       result.setDescription("weight("+getQuery()+" in "+doc+"), product of:");
 
       Explanation idfExpl =
@@ -98,7 +98,7 @@
 
       // explain field weight
       String field = term.field();
-      Explanation fieldExpl = new Explanation();
+      ComplexExplanation fieldExpl = new ComplexExplanation();
       fieldExpl.setDescription("fieldWeight("+term+" in "+doc+
                                "), product of:");
 
@@ -113,13 +113,15 @@
       fieldNormExpl.setValue(fieldNorm);
       fieldNormExpl.setDescription("fieldNorm(field="+field+", doc="+doc+")");
       fieldExpl.addDetail(fieldNormExpl);
-
+      
+      fieldExpl.setMatch(Boolean.valueOf(tfExpl.isMatch()));
       fieldExpl.setValue(tfExpl.getValue() *
                          idfExpl.getValue() *
                          fieldNormExpl.getValue());
 
       result.addDetail(fieldExpl);
-
+      result.setMatch(fieldExpl.getMatch());
+      
       // combine them
       result.setValue(queryExpl.getValue() * fieldExpl.getValue());
 

Modified: lucene/java/trunk/src/java/org/apache/lucene/search/spans/SpanWeight.java
URL: http://svn.apache.org/viewvc/lucene/java/trunk/src/java/org/apache/lucene/search/spans/SpanWeight.java?rev=417275&r1=417274&r2=417275&view=diff
==============================================================================
--- lucene/java/trunk/src/java/org/apache/lucene/search/spans/SpanWeight.java (original)
+++ lucene/java/trunk/src/java/org/apache/lucene/search/spans/SpanWeight.java Mon Jun 26 11:38:47 2006
@@ -30,6 +30,7 @@
 import org.apache.lucene.search.Searcher;
 import org.apache.lucene.search.Scorer;
 import org.apache.lucene.search.Explanation;
+import org.apache.lucene.search.ComplexExplanation;
 import org.apache.lucene.search.Similarity;
 
 class SpanWeight implements Weight {
@@ -75,7 +76,7 @@
   public Explanation explain(IndexReader reader, int doc)
     throws IOException {
 
-    Explanation result = new Explanation();
+    ComplexExplanation result = new ComplexExplanation();
     result.setDescription("weight("+getQuery()+" in "+doc+"), product of:");
     String field = ((SpanQuery)getQuery()).getField();
 
@@ -114,7 +115,7 @@
     result.addDetail(queryExpl);
 
     // explain field weight
-    Explanation fieldExpl = new Explanation();
+    ComplexExplanation fieldExpl = new ComplexExplanation();
     fieldExpl.setDescription("fieldWeight("+field+":"+query.toString(field)+
                              " in "+doc+"), product of:");
 
@@ -130,11 +131,13 @@
     fieldNormExpl.setDescription("fieldNorm(field="+field+", doc="+doc+")");
     fieldExpl.addDetail(fieldNormExpl);
 
+    fieldExpl.setMatch(Boolean.valueOf(tfExpl.isMatch()));
     fieldExpl.setValue(tfExpl.getValue() *
                        idfExpl.getValue() *
                        fieldNormExpl.getValue());
 
     result.addDetail(fieldExpl);
+    result.setMatch(fieldExpl.getMatch());
 
     // combine them
     result.setValue(queryExpl.getValue() * fieldExpl.getValue());

Modified: lucene/java/trunk/src/test/org/apache/lucene/search/CheckHits.java
URL: http://svn.apache.org/viewvc/lucene/java/trunk/src/test/org/apache/lucene/search/CheckHits.java?rev=417275&r1=417274&r2=417275&view=diff
==============================================================================
--- lucene/java/trunk/src/test/org/apache/lucene/search/CheckHits.java (original)
+++ lucene/java/trunk/src/test/org/apache/lucene/search/CheckHits.java Mon Jun 26 11:38:47 2006
@@ -55,14 +55,53 @@
     }
     
   }
+  
+  /**
+   * Tests that a query matches the an expected set of documents using a
+   * HitCollector.
+   *
+   * <p>
+   * Note that when using the HitCollector API, documents will be collected
+   * if they "match" regardless of what their score is.
+   * </p>
+   * @param query the query to test
+   * @param searcher the searcher to test the query against
+   * @param defaultFieldName used for displaing the query in assertion messages
+   * @param results a list of documentIds that must match the query
+   * @see Searcher#search(Query,HitCollector)
+   * @see #checkHits
+   */
+  public static void checkHitCollector(Query query, String defaultFieldName,
+                                       Searcher searcher, int[] results)
+    throws IOException {
+    
+    Set correct = new TreeSet();
+    for (int i = 0; i < results.length; i++) {
+      correct.add(new Integer(results[i]));
+    }
     
+    final Set actual = new TreeSet();
+    searcher.search(query, new HitCollector() {
+        public void collect(int doc, float score) {
+          actual.add(new Integer(doc));
+        }
+      });
+    TestCase.assertEquals(query.toString(defaultFieldName), correct, actual);
+  }
+  
   /**
-   * Tests that a query matches the an expected set of documents
+   * Tests that a query matches the an expected set of documents using Hits.
    *
+   * <p>
+   * Note that when using the Hits API, documents will only be returned
+   * if they have a positive normalized score.
+   * </p>
    * @param query the query to test
    * @param searcher the searcher to test the query against
    * @param defaultFieldName used for displaing the query in assertion messages
    * @param results a list of documentIds that must match the query
+   * @see Searcher#search(Query)
+   * @see #checkHitCollector
    */
   public static void checkHits(
         Query query,

Modified: lucene/java/trunk/src/test/org/apache/lucene/search/TestComplexExplanations.java
URL: http://svn.apache.org/viewvc/lucene/java/trunk/src/test/org/apache/lucene/search/TestComplexExplanations.java?rev=417275&r1=417274&r2=417275&view=diff
==============================================================================
--- lucene/java/trunk/src/test/org/apache/lucene/search/TestComplexExplanations.java (original)
+++ lucene/java/trunk/src/test/org/apache/lucene/search/TestComplexExplanations.java Mon Jun 26 11:38:47 2006
@@ -17,6 +17,7 @@
  */
 
 import org.apache.lucene.search.BooleanClause.Occur;
+import org.apache.lucene.search.spans.*;
 import org.apache.lucene.store.RAMDirectory;
 
 import org.apache.lucene.index.IndexWriter;
@@ -43,6 +44,20 @@
  */
 public class TestComplexExplanations extends TestExplanations {
 
+  /**
+   * Override the Similarity used in our searcher with one that plays
+   * nice with boosts of 0.0
+   */
+  public void setUp() throws Exception {
+    super.setUp();
+    searcher.setSimilarity(new DefaultSimilarity() {
+        public float queryNorm(float sumOfSquaredWeights) {
+          return 1.0f; // / (float) Math.sqrt(1.0f + sumOfSquaredWeights);
+        }
+      });
+  }
+
+  
   public void test1() throws Exception {
     
     BooleanQuery q = new BooleanQuery();
@@ -89,9 +104,174 @@
     q.add(b, Occur.SHOULD);
     
     qtest(q, new int[] { 0,1,2 });
-    
   }
 
+  public void test2() throws Exception {
+    
+    BooleanQuery q = new BooleanQuery();
+    
+    q.add(qp.parse("\"w1 w2\"~1"), Occur.MUST);
+    q.add(snear(st("w2"),
+                sor("w5","zz"),
+                4, true),
+          Occur.SHOULD);
+    q.add(snear(sf("w3",2), st("w2"), st("w3"), 5, true),
+          Occur.SHOULD);
+    
+    Query t = new FilteredQuery(qp.parse("xx"),
+                                new ItemizedFilter(new int[] {1,3}));
+    t.setBoost(1000);
+    q.add(t, Occur.SHOULD);
+    
+    t = new ConstantScoreQuery(new ItemizedFilter(new int[] {0,2}));
+    t.setBoost(-20.0f);
+    q.add(t, Occur.SHOULD);
+    
+    DisjunctionMaxQuery dm = new DisjunctionMaxQuery(0.2f);
+    dm.add(snear(st("w2"),
+                 sor("w5","zz"),
+                 4, true));
+    dm.add(qp.parse("QQ"));
+    dm.add(qp.parse("xx yy -zz"));
+    dm.add(qp.parse("-xx -w1"));
+
+    DisjunctionMaxQuery dm2 = new DisjunctionMaxQuery(0.5f);
+    dm2.add(qp.parse("w1"));
+    dm2.add(qp.parse("w2"));
+    dm2.add(qp.parse("w3"));
+    dm.add(dm2);
+
+    q.add(dm, Occur.SHOULD);
+
+    BooleanQuery b = new BooleanQuery();
+    b.setMinimumNumberShouldMatch(2);
+    b.add(snear("w1","w2",1,true), Occur.SHOULD);
+    b.add(snear("w2","w3",1,true), Occur.SHOULD);
+    b.add(snear("w1","w3",3,true), Occur.SHOULD);
+    b.setBoost(0.0f);
+    
+    q.add(b, Occur.SHOULD);
+    
+    qtest(q, new int[] { 0,1,2 });
+  }
+  
   // :TODO: we really need more crazy complex cases.
 
+
+  // //////////////////////////////////////////////////////////////////
+
+  // The rest of these aren't that complex, but they are <i>somewhat</i>
+  // complex, and they expose weakness in dealing with queries that match
+  // with scores of 0 wrapped in other queries
+
+  public void testT3() throws Exception {
+    bqtest("w1^0.0", new int[] { 0,1,2,3 });
+  }
+
+  public void testMA3() throws Exception {
+    Query q=new MatchAllDocsQuery();
+    q.setBoost(0);
+    bqtest(q, new int[] { 0,1,2,3 });
+  }
+  
+  public void testFQ5() throws Exception {
+    bqtest(new FilteredQuery(qp.parse("xx^0"),
+                             new ItemizedFilter(new int[] {1,3})),
+           new int[] {3});
+  }
+  
+  public void testCSQ4() throws Exception {
+    Query q = new ConstantScoreQuery(new ItemizedFilter(new int[] {3}));
+    q.setBoost(0);
+    bqtest(q, new int[] {3});
+  }
+  
+  public void testDMQ10() throws Exception {
+    DisjunctionMaxQuery q = new DisjunctionMaxQuery(0.5f);
+    q.add(qp.parse("yy w5^100"));
+    q.add(qp.parse("xx^0"));
+    q.setBoost(0.0f);
+    bqtest(q, new int[] { 0,2,3 });
+  }
+  
+  public void testMPQ7() throws Exception {
+    MultiPhraseQuery q = new MultiPhraseQuery();
+    q.add(ta(new String[] {"w1"}));
+    q.add(ta(new String[] {"w2"}));
+    q.setSlop(1);
+    q.setBoost(0.0f);
+    bqtest(q, new int[] { 0,1,2 });
+  }
+  
+  public void testBQ12() throws Exception {
+    // NOTE: using qtest not bqtest
+    qtest("w1 w2^0.0", new int[] { 0,1,2,3 });
+  }
+  public void testBQ13() throws Exception {
+    // NOTE: using qtest not bqtest
+    qtest("w1 -w5^0.0", new int[] { 1,2,3 });
+  }
+  public void testBQ18() throws Exception {
+    // NOTE: using qtest not bqtest
+    qtest("+w1^0.0 w2", new int[] { 0,1,2,3 });
+  }
+  public void testBQ21() throws Exception {
+    bqtest("(+w1 w2)^0.0", new int[] { 0,1,2,3 });
+  }
+  public void testBQ22() throws Exception {
+    bqtest("(+w1^0.0 w2)^0.0", new int[] { 0,1,2,3 });
+  }
+
+  public void testST3() throws Exception {
+    SpanQuery q = st("w1");
+    q.setBoost(0);
+    bqtest(q, new int[] {0,1,2,3});
+  }
+  public void testST6() throws Exception {
+    SpanQuery q = st("xx");
+    q.setBoost(0);
+    qtest(q, new int[] {2,3});
+  }
+
+  public void testSF3() throws Exception {
+    SpanQuery q = sf(("w1"),1);
+    q.setBoost(0);
+    bqtest(q, new int[] {0,1,2,3});
+  }
+  public void testSF7() throws Exception {
+    SpanQuery q = sf(("xx"),3);
+    q.setBoost(0);
+    bqtest(q, new int[] {2,3});
+  }
+  
+  public void testSNot3() throws Exception {
+    SpanQuery q = snot(sf("w1",10),st("QQ"));
+    q.setBoost(0);
+    bqtest(q, new int[] {0,1,2,3});
+  }
+  public void testSNot6() throws Exception {
+    SpanQuery q = snot(sf("w1",10),st("xx"));
+    q.setBoost(0);
+    bqtest(q, new int[] {0,1,2,3});
+  }
+
+  public void testSNot8() throws Exception {
+    // NOTE: using qtest not bqtest
+    SpanQuery f = snear("w1","w3",10,true);
+    f.setBoost(0);
+    SpanQuery q = snot(f, st("xx"));
+    qtest(q, new int[] {0,1,3});
+  }
+  public void testSNot9() throws Exception {
+    // NOTE: using qtest not bqtest
+    SpanQuery t = st("xx");
+    t.setBoost(0);
+    SpanQuery q = snot(snear("w1","w3",10,true), t);
+    qtest(q, new int[] {0,1,3});
+  }
+
+
+  
+
+  
 }

Modified: lucene/java/trunk/src/test/org/apache/lucene/search/TestExplanations.java
URL: http://svn.apache.org/viewvc/lucene/java/trunk/src/test/org/apache/lucene/search/TestExplanations.java?rev=417275&r1=417274&r2=417275&view=diff
==============================================================================
--- lucene/java/trunk/src/test/org/apache/lucene/search/TestExplanations.java (original)
+++ lucene/java/trunk/src/test/org/apache/lucene/search/TestExplanations.java Mon Jun 26 11:38:47 2006
@@ -68,8 +68,7 @@
       writer.addDocument(doc);
     }
     writer.close();
-    searcher = new CheckHits.ExplanationAssertingSearcher(directory);
-    //searcher = new IndexSearcher(directory);
+    searcher = new IndexSearcher(directory);
   }
 
   protected String[] docFields = {
@@ -87,7 +86,9 @@
     qtest(makeQuery(queryText), expDocNrs);
   }
   public void qtest(Query q, int[] expDocNrs) throws Exception {
-    CheckHits.checkHits(q, FIELD, searcher, expDocNrs);
+    // check that the expDocNrs first, then check the explanations
+    CheckHits.checkHitCollector(q, FIELD, searcher, expDocNrs);
+    CheckHits.checkExplanations(q, FIELD, searcher);
   }
 
   /**
@@ -191,38 +192,37 @@
 
   /**
    * MACRO: Wraps a Query in a BooleanQuery so that it is optional, along
-   * with a second clause which will never match anything
+   * with a second prohibited clause which will never match anything
    */
   public Query optB(String q) throws Exception {
     return optB(makeQuery(q));
   }
   /**
    * MACRO: Wraps a Query in a BooleanQuery so that it is optional, along
-   * with a second clause which will never match anything
+   * with a second prohibited clause which will never match anything
    */
   public Query optB(Query q) throws Exception {
-    return buildWrappingB(q, BooleanClause.Occur.SHOULD);
+    BooleanQuery bq = new BooleanQuery(true);
+    bq.add(q, BooleanClause.Occur.SHOULD);
+    bq.add(new TermQuery(new Term("NEVER","MATCH")), BooleanClause.Occur.MUST_NOT);
+    return bq;
   }
   
   /**
    * MACRO: Wraps a Query in a BooleanQuery so that it is required, along
-   * with a second clause which will never match anything
+   * with a second optional clause which will match everything
    */
   public Query reqB(String q) throws Exception {
     return reqB(makeQuery(q));
   }
   /**
    * MACRO: Wraps a Query in a BooleanQuery so that it is required, along
-   * with a second clause which will never match anything
+   * with a second optional clause which will match everything
    */
   public Query reqB(Query q) throws Exception {
-    return buildWrappingB(q, BooleanClause.Occur.MUST);
-  }
-
-  private Query buildWrappingB(Query q, BooleanClause.Occur o) {
     BooleanQuery bq = new BooleanQuery(true);
-    bq.add(q, o);
-    bq.add(new TermQuery(new Term("NEVER","MATCH")), BooleanClause.Occur.MUST_NOT);
+    bq.add(q, BooleanClause.Occur.MUST);
+    bq.add(new TermQuery(new Term(FIELD,"w1")), BooleanClause.Occur.SHOULD);
     return bq;
   }