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

QueryUtils.java

package org.apache.lucene.search;

import junit.framework.TestCase;

import java.io.IOException;

/**
 * Copyright 2005 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.
 */


/**
 * @author yonik
 */
public class QueryUtils {

  /** Check the types of things query objects should be able to do. */
  public static void check(Query q) {
    checkHashEquals(q);
  }

  /** check very basic hashCode and equals */
  public static void checkHashEquals(Query q) {
    Query q2 = (Query)q.clone();
    checkEqual(q,q2);

    Query q3 = (Query)q.clone();
    q3.setBoost(7.21792348f);
    checkUnequal(q,q3);

    // test that a class check is done so that no exception is thrown
    // in the implementation of equals()
    Query whacky = new Query() {
      public String toString(String field) {
        return "My Whacky Query";
      }
    };
    whacky.setBoost(q.getBoost());
    checkUnequal(q, whacky);
  }

  public static void checkEqual(Query q1, Query q2) {
    TestCase.assertEquals(q1, q2);
    TestCase.assertEquals(q1.hashCode(), q2.hashCode());
  }

  public static void checkUnequal(Query q1, Query q2) {
    TestCase.assertTrue(!q1.equals(q2));
    TestCase.assertTrue(!q2.equals(q1));

    // possible this test can fail on a hash collision... if that
    // happens, please change test to use a different example.
    TestCase.assertTrue(q1.hashCode() != q2.hashCode());
  }
  
  /** deep check that explanations of a query 'score' correctly */
  public static void checkExplanations (final Query q, final Searcher s) throws IOException {
    CheckHits.checkExplanations(q, null, s, true);
  }
  
  /** 
   * various query sanity checks on a searcher, including explanation checks.
   * @see #checkExplanations
   * @see #checkSkipTo
   * @see #check(Query)
   */
  public static void check(Query q1, Searcher s) {
    try {
      check(q1);
      if (s!=null) {
        if (s instanceof IndexSearcher) {
          IndexSearcher is = (IndexSearcher)s;
          checkFirstSkipTo(q1,is);
          checkSkipTo(q1,is);
        }
        checkExplanations(q1,s);
      }
    } catch (IOException e) {
      throw new RuntimeException(e);
    }
  }

  /** alternate scorer skipTo(),skipTo(),next(),next(),skipTo(),skipTo(), etc
   * and ensure a hitcollector receives same docs and scores
   */
  public static void checkSkipTo(final Query q, final IndexSearcher s) throws IOException {
    //System.out.println("Checking "+q);
    
    if (BooleanQuery.getAllowDocsOutOfOrder()) return;  // in this case order of skipTo() might differ from that of next().

    final int skip_op = 0;
    final int next_op = 1;
    final int orders [][] = {
        {next_op},
        {skip_op},
        {skip_op, next_op},
        {next_op, skip_op},
        {skip_op, skip_op, next_op, next_op},
        {next_op, next_op, skip_op, skip_op},
        {skip_op, skip_op, skip_op, next_op, next_op},
    };
    for (int k = 0; k < orders.length; k++) {
      final int order[] = orders[k];
      //System.out.print("Order:");for (int i = 0; i < order.length; i++) System.out.print(order[i]==skip_op ? " skip()":" next()"); System.out.println();
      final int opidx[] = {0};

      final Weight w = q.weight(s);
      final Scorer scorer = w.scorer(s.getIndexReader());
      
      // FUTURE: ensure scorer.doc()==-1

      final int[] sdoc = new int[] {-1};
      final float maxDiff = 1e-5f;
      s.search(q,new HitCollector() {
        public void collect(int doc, float score) {
          try {
            int op = order[(opidx[0]++)%order.length];
            //System.out.println(op==skip_op ? "skip("+(sdoc[0]+1)+")":"next()");
            boolean more = op==skip_op ? scorer.skipTo(sdoc[0]+1) : scorer.next();
            sdoc[0] = scorer.doc();
            float scorerScore = scorer.score();
            float scorerScore2 = scorer.score();
            float scoreDiff = Math.abs(score-scorerScore);
            float scorerDiff = Math.abs(scorerScore2-scorerScore);
            if (!more || doc != sdoc[0] || scoreDiff>maxDiff || scorerDiff>maxDiff) {
              StringBuffer sbord = new StringBuffer();
              for (int i = 0; i < order.length; i++) 
                sbord.append(order[i]==skip_op ? " skip()":" next()");
              throw new RuntimeException("ERROR matching docs:"
                  +"\n\t"+(doc!=sdoc[0]?"--> ":"")+"doc="+sdoc[0]
                  +"\n\t"+(!more?"--> ":"")+"tscorer.more=" + more 
                  +"\n\t"+(scoreDiff>maxDiff?"--> ":"")+"scorerScore="+scorerScore+" scoreDiff="+scoreDiff + " maxDiff="+maxDiff
                  +"\n\t"+(scorerDiff>maxDiff?"--> ":"")+"scorerScore2="+scorerScore2+" scorerDiff="+scorerDiff
                  +"\n\thitCollector.doc=" + doc + " score="+score
                  +"\n\t Scorer=" + scorer
                  +"\n\t Query=" + q + "  "+q.getClass().getName()
                  +"\n\t Searcher=" + s
                  +"\n\t Order=" + sbord
                  +"\n\t Op=" + (op==skip_op ? " skip()":" next()")
              );
            }
          } catch (IOException e) {
            throw new RuntimeException(e);
          }
        }
      });
      
      // make sure next call to scorer is false.
      int op = order[(opidx[0]++)%order.length];
      //System.out.println(op==skip_op ? "last: skip()":"last: next()");
      boolean more = op==skip_op ? scorer.skipTo(sdoc[0]+1) : scorer.next();
      TestCase.assertFalse(more);
    }
  }
    
  // check that first skip on just created scorers always goes to the right doc
  private static void checkFirstSkipTo(final Query q, final IndexSearcher s) throws IOException {
    //System.out.println("checkFirstSkipTo: "+q);
    final float maxDiff = 1e-5f;
    final int lastDoc[] = {-1};
    s.search(q,new HitCollector() {
      public void collect(int doc, float score) {
        //System.out.println("doc="+doc);
        try {
          for (int i=lastDoc[0]+1; i<=doc; i++) {
            Weight w = q.weight(s);
            Scorer scorer = w.scorer(s.getIndexReader());
            TestCase.assertTrue("query collected "+doc+" but skipTo("+i+") says no more docs!",scorer.skipTo(i));
            TestCase.assertEquals("query collected "+doc+" but skipTo("+i+") got to "+scorer.doc(),doc,scorer.doc());
            float skipToScore = scorer.score();
            TestCase.assertEquals("unstable skipTo("+i+") score!",skipToScore,scorer.score(),maxDiff); 
            TestCase.assertEquals("query assigned doc "+doc+" a score of <"+score+"> but skipTo("+i+") has <"+skipToScore+">!",score,skipToScore,maxDiff);
          }
          lastDoc[0] = doc;
        } catch (IOException e) {
          throw new RuntimeException(e);
        }
      }
    });
    Weight w = q.weight(s);
    Scorer scorer = w.scorer(s.getIndexReader());
    boolean more = scorer.skipTo(lastDoc[0]+1);
    if (more) 
      TestCase.assertFalse("query's last doc was "+lastDoc[0]+" but skipTo("+(lastDoc[0]+1)+") got to "+scorer.doc(),more);
  }
}