FileDocCategorySizeDatePackage
CheckHits.javaAPI DocApache Lucene 2.2.016342Sat Jun 16 22:20:28 BST 2007org.apache.lucene.search

CheckHits

public class CheckHits extends Object
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.

Fields Summary
public static float
EXPLAIN_SCORE_TOLERANCE_DELTA
Some explains methods calculate their vlaues though a slightly differnet order of operations from the acctaul scoring method ... this allows for a small amount of variation
Constructors Summary
Methods Summary
public static voidcheckDocIds(java.lang.String mes, int[] results, org.apache.lucene.search.Hits hits)
Tests that a Hits has an expected order of documents

    TestCase.assertEquals(mes + " nr of hits", results.length, hits.length());
    for (int i = 0; i < results.length; i++) {
      TestCase.assertEquals(mes + " doc nrs for hit " + i, results[i], hits.id(i));
    }
  
public static voidcheckEqual(org.apache.lucene.search.Query query, org.apache.lucene.search.Hits hits1, org.apache.lucene.search.Hits hits2)

     final float scoreTolerance = 1.0e-6f;
     if (hits1.length() != hits2.length()) {
       TestCase.fail("Unequal lengths: hits1="+hits1.length()+",hits2="+hits2.length());
     }
    for (int i = 0; i < hits1.length(); i++) {
      if (hits1.id(i) != hits2.id(i)) {
        TestCase.fail("Hit " + i + " docnumbers don't match\n"
                + hits2str(hits1, hits2,0,0)
                + "for query:" + query.toString());
      }

      if ((hits1.id(i) != hits2.id(i))
          || Math.abs(hits1.score(i) -  hits2.score(i)) > scoreTolerance)
      {
        TestCase.fail("Hit " + i + ", doc nrs " + hits1.id(i) + " and " + hits2.id(i)
                      + "\nunequal       : " + hits1.score(i)
                      + "\n           and: " + hits2.score(i)
                      + "\nfor query:" + query.toString());
      }
    }
  
public static voidcheckExplanations(org.apache.lucene.search.Query query, java.lang.String defaultFieldName, org.apache.lucene.search.Searcher searcher, boolean deep)
Asserts that the explanation value for every document matching a query corresponds with the true score. Optionally does "deep" testing of the explanation details.

see
ExplanationAsserter
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
deep indicates whether a deep comparison of sub-Explanation details should be executed


    searcher.search(query,
                    new ExplanationAsserter
                    (query, defaultFieldName, searcher, deep));

  
public static voidcheckExplanations(org.apache.lucene.search.Query query, java.lang.String defaultFieldName, org.apache.lucene.search.Searcher searcher)
Asserts that the explanation value for every document matching a query corresponds with the true score.

see
ExplanationAsserter
see
#checkExplanations(Query, String, Searcher, boolean) for a "deep" testing of the explanation details.
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

    checkExplanations(query, defaultFieldName, searcher, false);
  
public static voidcheckHitCollector(org.apache.lucene.search.Query query, java.lang.String defaultFieldName, org.apache.lucene.search.Searcher searcher, int[] results)
Tests that a query matches the an expected set of documents using a HitCollector.

Note that when using the HitCollector API, documents will be collected if they "match" regardless of what their score is.

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

    
    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);

    QueryUtils.check(query,searcher);
  
public static voidcheckHits(org.apache.lucene.search.Query query, java.lang.String defaultFieldName, org.apache.lucene.search.Searcher searcher, int[] results)
Tests that a query matches the an expected set of documents using Hits.

Note that when using the Hits API, documents will only be returned if they have a positive normalized score.

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

    if (searcher instanceof IndexSearcher) {
      QueryUtils.check(query,(IndexSearcher)searcher);
    }

    Hits hits = searcher.search(query);

    Set correct = new TreeSet();
    for (int i = 0; i < results.length; i++) {
      correct.add(new Integer(results[i]));
    }

    Set actual = new TreeSet();
    for (int i = 0; i < hits.length(); i++) {
      actual.add(new Integer(hits.id(i)));
    }

    TestCase.assertEquals(query.toString(defaultFieldName), correct, actual);

    QueryUtils.check(query,searcher);
  
public static voidcheckHitsQuery(org.apache.lucene.search.Query query, org.apache.lucene.search.Hits hits1, org.apache.lucene.search.Hits hits2, int[] results)
Tests that two queries have an expected order of documents, and that the two queries have the same score values.


    checkDocIds("hits1", results, hits1);
    checkDocIds("hits2", results, hits2);
    checkEqual(query, hits1, hits2);
  
public static voidcheckNoMatchExplanations(org.apache.lucene.search.Query q, java.lang.String defaultFieldName, org.apache.lucene.search.Searcher searcher, int[] results)
Tests that all documents up to maxDoc which are *not* in the expected result set, have an explanation which indicates no match (ie: Explanation value of 0.0f)

    
                                
        
                                                 
      

    String d = q.toString(defaultFieldName);
    Set ignore = new TreeSet();
    for (int i = 0; i < results.length; i++) {
      ignore.add(new Integer(results[i]));
    }
    
    int maxDoc = searcher.maxDoc();
    for (int doc = 0; doc < maxDoc; doc++) {
      if (ignore.contains(new Integer(doc))) continue;

      Explanation exp = searcher.explain(q, doc);
      TestCase.assertNotNull("Explanation of [["+d+"]] for #"+doc+" is null",
                             exp);
      TestCase.assertEquals("Explanation of [["+d+"]] for #"+doc+
                            " doesn't indicate non-match: " + exp.toString(),
                            0.0f, exp.getValue(), 0.0f);
    }
    
  
public static java.lang.Stringhits2str(org.apache.lucene.search.Hits hits1, org.apache.lucene.search.Hits hits2, int start, int end)

    StringBuffer sb = new StringBuffer();
    int len1=hits1==null ? 0 : hits1.length();
    int len2=hits2==null ? 0 : hits2.length();
    if (end<=0) {
      end = Math.max(len1,len2);
    }

    sb.append("Hits length1=" + len1 + "\tlength2="+len2);

    sb.append("\n");
    for (int i=start; i<end; i++) {
      sb.append("hit=" + i + ":");
      if (i<len1) {
        sb.append(" doc"+hits1.id(i) + "=" + hits1.score(i));
      } else {
        sb.append("               ");
      }
      sb.append(",\t");
      if (i<len2) {
        sb.append(" doc"+hits2.id(i) + "=" + hits2.score(i));
      }
      sb.append("\n");
    }
    return sb.toString();
  
public static java.lang.StringtopdocsString(org.apache.lucene.search.TopDocs docs, int start, int end)

    StringBuffer sb = new StringBuffer();
    sb.append("TopDocs totalHits="+docs.totalHits + " top="+docs.scoreDocs.length+"\n");
    if (end<=0) end=docs.scoreDocs.length;
    else end=Math.min(end,docs.scoreDocs.length);
    for (int i=start; i<end; i++) {
      sb.append("\t");
      sb.append(i);
      sb.append(") doc=");
      sb.append(docs.scoreDocs[i].doc);
      sb.append("\tscore=");
      sb.append(docs.scoreDocs[i].score);
      sb.append("\n");
    }
    return sb.toString();
  
public static voidverifyExplanation(java.lang.String q, int doc, float score, boolean deep, org.apache.lucene.search.Explanation expl)
Assert that an explanation has the expected score, and optionally that its sub-details max/sum/factor match to that score.

param
q String representation of the query for assertion messages
param
doc Document ID for assertion messages
param
score Real score value of doc with query q
param
deep indicates whether a deep comparison of sub-Explanation details should be executed
param
expl The Explanation to match against score

    float value = expl.getValue();
    TestCase.assertEquals(q+": score(doc="+doc+")="+score+
        " != explanationScore="+value+" Explanation: "+expl,
        score,value,EXPLAIN_SCORE_TOLERANCE_DELTA);

    if (!deep) return;

    Explanation detail[] = expl.getDetails();
    if (detail!=null) {
      if (detail.length==1) {
        // simple containment, no matter what the description says, 
        // just verify contained expl has same score
        verifyExplanation(q,doc,score,deep,detail[0]);
      } else {
        // explanation must either:
        // - end with one of: "product of:", "sum of:", "max of:", or
        // - have "max plus <x> times others" (where <x> is float).
        float x = 0;
        String descr = expl.getDescription().toLowerCase();
        boolean productOf = descr.endsWith("product of:");
        boolean sumOf = descr.endsWith("sum of:");
        boolean maxOf = descr.endsWith("max of:");
        boolean maxTimesOthers = false;
        if (!(productOf || sumOf || maxOf)) {
          // maybe 'max plus x times others'
          int k1 = descr.indexOf("max plus ");
          if (k1>=0) {
            k1 += "max plus ".length();
            int k2 = descr.indexOf(" ",k1);
            try {
              x = Float.parseFloat(descr.substring(k1,k2).trim());
              if (descr.substring(k2).trim().equals("times others of:")) {
                maxTimesOthers = true;
              }
            } catch (NumberFormatException e) {
            }
          }
        }
        TestCase.assertTrue(
            q+": multi valued explanation description=\""+descr
            +"\" must be 'max of plus x times others' or end with 'prodoct of'"
            +" or 'sum of:' or 'max of:' - "+expl,
            productOf || sumOf || maxOf || maxTimesOthers);
        float sum = 0;
        float product = 1;
        float max = 0;
        for (int i=0; i<detail.length; i++) {
          float dval = detail[i].getValue();
          verifyExplanation(q,doc,dval,deep,detail[i]);
          product *= dval;
          sum += dval;
          max = Math.max(max,dval);
        }
        float combined = 0;
        if (productOf) {
          combined = product;
        } else if (sumOf) {
          combined = sum;
        } else if (maxOf) {
          combined = max;
        } else if (maxTimesOthers) {
          combined = max + x * (sum - max);
        } else {
            TestCase.assertTrue("should never get here!",false);
        }
        TestCase.assertEquals(q+": actual subDetails combined=="+combined+
            " != value="+value+" Explanation: "+expl,
            combined,value,EXPLAIN_SCORE_TOLERANCE_DELTA);
      }
    }