~slub.team/goobi-indexserver/3.x

« back to all changes in this revision

Viewing changes to lucene/src/test-framework/java/org/apache/lucene/search/QueryUtils.java

  • Committer: Sebastian Meyer
  • Date: 2012-08-03 09:12:40 UTC
  • Revision ID: sebastian.meyer@slub-dresden.de-20120803091240-x6861b0vabq1xror
Remove Lucene and Solr source code and add patches instead
Fix Bug #985487: Auto-suggestion for the search interface

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
package org.apache.lucene.search;
2
 
 
3
 
/**
4
 
 * Licensed to the Apache Software Foundation (ASF) under one or more
5
 
 * contributor license agreements.  See the NOTICE file distributed with
6
 
 * this work for additional information regarding copyright ownership.
7
 
 * The ASF licenses this file to You under the Apache License, Version 2.0
8
 
 * (the "License"); you may not use this file except in compliance with
9
 
 * the License.  You may obtain a copy of the License at
10
 
 *
11
 
 *     http://www.apache.org/licenses/LICENSE-2.0
12
 
 *
13
 
 * Unless required by applicable law or agreed to in writing, software
14
 
 * distributed under the License is distributed on an "AS IS" BASIS,
15
 
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
 
 * See the License for the specific language governing permissions and
17
 
 * limitations under the License.
18
 
 */
19
 
 
20
 
import java.io.ByteArrayInputStream;
21
 
import java.io.ByteArrayOutputStream;
22
 
import java.io.IOException;
23
 
import java.io.ObjectInputStream;
24
 
import java.io.ObjectOutputStream;
25
 
import java.util.Random;
26
 
 
27
 
import junit.framework.Assert;
28
 
 
29
 
import org.apache.lucene.analysis.MockAnalyzer;
30
 
import org.apache.lucene.document.Document;
31
 
import org.apache.lucene.index.IndexReader;
32
 
import org.apache.lucene.index.IndexWriter;
33
 
import org.apache.lucene.index.IndexWriterConfig;
34
 
import org.apache.lucene.index.MultiReader;
35
 
import org.apache.lucene.store.Directory;
36
 
import org.apache.lucene.store.MockDirectoryWrapper;
37
 
import org.apache.lucene.store.RAMDirectory;
38
 
import org.apache.lucene.util._TestUtil;
39
 
 
40
 
import static org.apache.lucene.util.LuceneTestCase.TEST_VERSION_CURRENT;
41
 
 
42
 
 
43
 
 
44
 
 
45
 
public class QueryUtils {
46
 
 
47
 
  /** Check the types of things query objects should be able to do. */
48
 
  public static void check(Query q) {
49
 
    checkHashEquals(q);
50
 
  }
51
 
 
52
 
  /** check very basic hashCode and equals */
53
 
  public static void checkHashEquals(Query q) {
54
 
    Query q2 = (Query)q.clone();
55
 
    checkEqual(q,q2);
56
 
 
57
 
    Query q3 = (Query)q.clone();
58
 
    q3.setBoost(7.21792348f);
59
 
    checkUnequal(q,q3);
60
 
 
61
 
    // test that a class check is done so that no exception is thrown
62
 
    // in the implementation of equals()
63
 
    Query whacky = new Query() {
64
 
      @Override
65
 
      public String toString(String field) {
66
 
        return "My Whacky Query";
67
 
      }
68
 
    };
69
 
    whacky.setBoost(q.getBoost());
70
 
    checkUnequal(q, whacky);
71
 
    
72
 
    // null test
73
 
    Assert.assertFalse(q.equals(null));
74
 
  }
75
 
 
76
 
  public static void checkEqual(Query q1, Query q2) {
77
 
    Assert.assertEquals(q1, q2);
78
 
    Assert.assertEquals(q1.hashCode(), q2.hashCode());
79
 
  }
80
 
 
81
 
  public static void checkUnequal(Query q1, Query q2) {
82
 
    Assert.assertTrue(!q1.equals(q2));
83
 
    Assert.assertTrue(!q2.equals(q1));
84
 
 
85
 
    // possible this test can fail on a hash collision... if that
86
 
    // happens, please change test to use a different example.
87
 
    Assert.assertTrue(q1.hashCode() != q2.hashCode());
88
 
  }
89
 
  
90
 
  /** deep check that explanations of a query 'score' correctly */
91
 
  public static void checkExplanations (final Query q, final Searcher s) throws IOException {
92
 
    CheckHits.checkExplanations(q, null, s, true);
93
 
  }
94
 
  
95
 
  /** 
96
 
   * Various query sanity checks on a searcher, some checks are only done for
97
 
   * instanceof IndexSearcher.
98
 
   *
99
 
   * @see #check(Query)
100
 
   * @see #checkFirstSkipTo
101
 
   * @see #checkSkipTo
102
 
   * @see #checkExplanations
103
 
   * @see #checkSerialization
104
 
   * @see #checkEqual
105
 
   */
106
 
  public static void check(Random random, Query q1, Searcher s) {
107
 
    check(random, q1, s, true);
108
 
  }
109
 
  private static void check(Random random, Query q1, Searcher s, boolean wrap) {
110
 
    try {
111
 
      check(q1);
112
 
      if (s!=null) {
113
 
        if (s instanceof IndexSearcher) {
114
 
          IndexSearcher is = (IndexSearcher)s;
115
 
          checkFirstSkipTo(q1,is);
116
 
          checkSkipTo(q1,is);
117
 
          if (wrap) {
118
 
            check(random, q1, wrapUnderlyingReader(random, is, -1), false);
119
 
            check(random, q1, wrapUnderlyingReader(random, is,  0), false);
120
 
            check(random, q1, wrapUnderlyingReader(random, is, +1), false);
121
 
          }
122
 
        }
123
 
        if (wrap) {
124
 
          check(random,q1, wrapSearcher(random, s, -1), false);
125
 
          check(random,q1, wrapSearcher(random, s,  0), false);
126
 
          check(random,q1, wrapSearcher(random, s, +1), false);
127
 
        }
128
 
        checkExplanations(q1,s);
129
 
        checkSerialization(q1,s);
130
 
        
131
 
        Query q2 = (Query)q1.clone();
132
 
        checkEqual(s.rewrite(q1),
133
 
                   s.rewrite(q2));
134
 
      }
135
 
    } catch (IOException e) {
136
 
      throw new RuntimeException(e);
137
 
    }
138
 
  }
139
 
 
140
 
  /**
141
 
   * Given an IndexSearcher, returns a new IndexSearcher whose IndexReader 
142
 
   * is a MultiReader containing the Reader of the original IndexSearcher, 
143
 
   * as well as several "empty" IndexReaders -- some of which will have 
144
 
   * deleted documents in them.  This new IndexSearcher should 
145
 
   * behave exactly the same as the original IndexSearcher.
146
 
   * @param s the searcher to wrap
147
 
   * @param edge if negative, s will be the first sub; if 0, s will be in the middle, if positive s will be the last sub
148
 
   */
149
 
  public static IndexSearcher wrapUnderlyingReader(Random random, final IndexSearcher s, final int edge) 
150
 
    throws IOException {
151
 
 
152
 
    IndexReader r = s.getIndexReader();
153
 
 
154
 
    // we can't put deleted docs before the nested reader, because
155
 
    // it will throw off the docIds
156
 
    IndexReader[] readers = new IndexReader[] {
157
 
      edge < 0 ? r : IndexReader.open(makeEmptyIndex(random, 0), true),
158
 
      IndexReader.open(makeEmptyIndex(random, 0), true),
159
 
      new MultiReader(new IndexReader[] {
160
 
        IndexReader.open(makeEmptyIndex(random, edge < 0 ? 4 : 0), true),
161
 
        IndexReader.open(makeEmptyIndex(random, 0), true),
162
 
        0 == edge ? r : IndexReader.open(makeEmptyIndex(random, 0), true)
163
 
      }),
164
 
      IndexReader.open(makeEmptyIndex(random, 0 < edge ? 0 : 7), true),
165
 
      IndexReader.open(makeEmptyIndex(random, 0), true),
166
 
      new MultiReader(new IndexReader[] {
167
 
        IndexReader.open(makeEmptyIndex(random, 0 < edge ? 0 : 5), true),
168
 
        IndexReader.open(makeEmptyIndex(random, 0), true),
169
 
        0 < edge ? r : IndexReader.open(makeEmptyIndex(random, 0), true)
170
 
      })
171
 
    };
172
 
    IndexSearcher out = new IndexSearcher(new MultiReader(readers));
173
 
    out.setSimilarity(s.getSimilarity());
174
 
    return out;
175
 
  }
176
 
  /**
177
 
   * Given a Searcher, returns a new MultiSearcher wrapping the  
178
 
   * the original Searcher, 
179
 
   * as well as several "empty" IndexSearchers -- some of which will have
180
 
   * deleted documents in them.  This new MultiSearcher 
181
 
   * should behave exactly the same as the original Searcher.
182
 
   * @param s the Searcher to wrap
183
 
   * @param edge if negative, s will be the first sub; if 0, s will be in hte middle, if positive s will be the last sub
184
 
   */
185
 
  public static MultiSearcher wrapSearcher(Random random, final Searcher s, final int edge) 
186
 
    throws IOException {
187
 
 
188
 
    // we can't put deleted docs before the nested reader, because
189
 
    // it will through off the docIds
190
 
    Searcher[] searchers = new Searcher[] {
191
 
      edge < 0 ? s : new IndexSearcher(makeEmptyIndex(random, 0), true),
192
 
      new MultiSearcher(new Searcher[] {
193
 
        new IndexSearcher(makeEmptyIndex(random, edge < 0 ? 65 : 0), true),
194
 
        new IndexSearcher(makeEmptyIndex(random, 0), true),
195
 
        0 == edge ? s : new IndexSearcher(makeEmptyIndex(random, 0), true)
196
 
      }),
197
 
      new IndexSearcher(makeEmptyIndex(random, 0 < edge ? 0 : 3), true),
198
 
      new IndexSearcher(makeEmptyIndex(random, 0), true),
199
 
      new MultiSearcher(new Searcher[] {
200
 
        new IndexSearcher(makeEmptyIndex(random, 0 < edge ? 0 : 5), true),
201
 
        new IndexSearcher(makeEmptyIndex(random, 0), true),
202
 
        0 < edge ? s : new IndexSearcher(makeEmptyIndex(random, 0), true)
203
 
      })
204
 
    };
205
 
    MultiSearcher out = new MultiSearcher(searchers);
206
 
    out.setSimilarity(s.getSimilarity());
207
 
    return out;
208
 
  }
209
 
 
210
 
  private static Directory makeEmptyIndex(Random random, final int numDeletedDocs) 
211
 
    throws IOException {
212
 
    Directory d = new MockDirectoryWrapper(random, new RAMDirectory());
213
 
      IndexWriter w = new IndexWriter(d, new IndexWriterConfig(
214
 
        TEST_VERSION_CURRENT, new MockAnalyzer(random)));
215
 
 
216
 
      for (int i = 0; i < numDeletedDocs; i++) {
217
 
        w.addDocument(new Document());
218
 
      }
219
 
      w.commit();
220
 
      w.deleteDocuments( new MatchAllDocsQuery() );
221
 
      _TestUtil.keepFullyDeletedSegments(w);
222
 
      w.commit();
223
 
 
224
 
      if (0 < numDeletedDocs)
225
 
        Assert.assertTrue("writer has no deletions", w.hasDeletions());
226
 
 
227
 
      Assert.assertEquals("writer is missing some deleted docs", 
228
 
                          numDeletedDocs, w.maxDoc());
229
 
      Assert.assertEquals("writer has non-deleted docs", 
230
 
                          0, w.numDocs());
231
 
      w.close();
232
 
      IndexReader r = IndexReader.open(d, true);
233
 
      Assert.assertEquals("reader has wrong number of deleted docs", 
234
 
                          numDeletedDocs, r.numDeletedDocs());
235
 
      r.close();
236
 
      return d;
237
 
  }
238
 
  
239
 
 
240
 
  /** check that the query weight is serializable. 
241
 
   * @throws IOException if serialization check fail. 
242
 
   */
243
 
  private static void checkSerialization(Query q, Searcher s) throws IOException {
244
 
    Weight w = s.createNormalizedWeight(q);
245
 
    try {
246
 
      ByteArrayOutputStream bos = new ByteArrayOutputStream();
247
 
      ObjectOutputStream oos = new ObjectOutputStream(bos);
248
 
      oos.writeObject(w);
249
 
      oos.close();
250
 
      ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()));
251
 
      ois.readObject();
252
 
      ois.close();
253
 
      
254
 
      //skip equals() test for now - most weights don't override equals() and we won't add this just for the tests.
255
 
      //TestCase.assertEquals("writeObject(w) != w.  ("+w+")",w2,w);   
256
 
      
257
 
    } catch (Exception e) {
258
 
      IOException e2 = new IOException("Serialization failed for "+w);
259
 
      e2.initCause(e);
260
 
      throw e2;
261
 
    }
262
 
  }
263
 
 
264
 
 
265
 
  /** alternate scorer skipTo(),skipTo(),next(),next(),skipTo(),skipTo(), etc
266
 
   * and ensure a hitcollector receives same docs and scores
267
 
   */
268
 
  public static void checkSkipTo(final Query q, final IndexSearcher s) throws IOException {
269
 
    //System.out.println("Checking "+q);
270
 
    
271
 
    if (s.createNormalizedWeight(q).scoresDocsOutOfOrder()) return;  // in this case order of skipTo() might differ from that of next().
272
 
 
273
 
    final int skip_op = 0;
274
 
    final int next_op = 1;
275
 
    final int orders [][] = {
276
 
        {next_op},
277
 
        {skip_op},
278
 
        {skip_op, next_op},
279
 
        {next_op, skip_op},
280
 
        {skip_op, skip_op, next_op, next_op},
281
 
        {next_op, next_op, skip_op, skip_op},
282
 
        {skip_op, skip_op, skip_op, next_op, next_op},
283
 
    };
284
 
    for (int k = 0; k < orders.length; k++) {
285
 
 
286
 
        final int order[] = orders[k];
287
 
        // System.out.print("Order:");for (int i = 0; i < order.length; i++)
288
 
        // System.out.print(order[i]==skip_op ? " skip()":" next()");
289
 
        // System.out.println();
290
 
        final int opidx[] = { 0 };
291
 
        final int lastDoc[] = {-1};
292
 
 
293
 
        // FUTURE: ensure scorer.doc()==-1
294
 
 
295
 
        final float maxDiff = 1e-5f;
296
 
        final IndexReader lastReader[] = {null};
297
 
 
298
 
        s.search(q, new Collector() {
299
 
          private Scorer sc;
300
 
          private IndexReader reader;
301
 
          private Scorer scorer;
302
 
 
303
 
          @Override
304
 
          public void setScorer(Scorer scorer) throws IOException {
305
 
            this.sc = scorer;
306
 
          }
307
 
 
308
 
          @Override
309
 
          public void collect(int doc) throws IOException {
310
 
            float score = sc.score();
311
 
            lastDoc[0] = doc;
312
 
            try {
313
 
              if (scorer == null) {
314
 
                Weight w = s.createNormalizedWeight(q);
315
 
                scorer = w.scorer(reader, true, false);
316
 
              }
317
 
              
318
 
              int op = order[(opidx[0]++) % order.length];
319
 
              // System.out.println(op==skip_op ?
320
 
              // "skip("+(sdoc[0]+1)+")":"next()");
321
 
              boolean more = op == skip_op ? scorer.advance(scorer.docID() + 1) != DocIdSetIterator.NO_MORE_DOCS
322
 
                  : scorer.nextDoc() != DocIdSetIterator.NO_MORE_DOCS;
323
 
              int scorerDoc = scorer.docID();
324
 
              float scorerScore = scorer.score();
325
 
              float scorerScore2 = scorer.score();
326
 
              float scoreDiff = Math.abs(score - scorerScore);
327
 
              float scorerDiff = Math.abs(scorerScore2 - scorerScore);
328
 
              if (!more || doc != scorerDoc || scoreDiff > maxDiff
329
 
                  || scorerDiff > maxDiff) {
330
 
                StringBuilder sbord = new StringBuilder();
331
 
                for (int i = 0; i < order.length; i++)
332
 
                  sbord.append(order[i] == skip_op ? " skip()" : " next()");
333
 
                throw new RuntimeException("ERROR matching docs:" + "\n\t"
334
 
                    + (doc != scorerDoc ? "--> " : "") + "doc=" + doc + ", scorerDoc=" + scorerDoc
335
 
                    + "\n\t" + (!more ? "--> " : "") + "tscorer.more=" + more
336
 
                    + "\n\t" + (scoreDiff > maxDiff ? "--> " : "")
337
 
                    + "scorerScore=" + scorerScore + " scoreDiff=" + scoreDiff
338
 
                    + " maxDiff=" + maxDiff + "\n\t"
339
 
                    + (scorerDiff > maxDiff ? "--> " : "") + "scorerScore2="
340
 
                    + scorerScore2 + " scorerDiff=" + scorerDiff
341
 
                    + "\n\thitCollector.doc=" + doc + " score=" + score
342
 
                    + "\n\t Scorer=" + scorer + "\n\t Query=" + q + "  "
343
 
                    + q.getClass().getName() + "\n\t Searcher=" + s
344
 
                    + "\n\t Order=" + sbord + "\n\t Op="
345
 
                    + (op == skip_op ? " skip()" : " next()"));
346
 
              }
347
 
            } catch (IOException e) {
348
 
              throw new RuntimeException(e);
349
 
            }
350
 
          }
351
 
 
352
 
          @Override
353
 
          public void setNextReader(IndexReader reader, int docBase) throws IOException {
354
 
            // confirm that skipping beyond the last doc, on the
355
 
            // previous reader, hits NO_MORE_DOCS
356
 
            if (lastReader[0] != null) {
357
 
              final IndexReader previousReader = lastReader[0];
358
 
              Weight w = new IndexSearcher(previousReader).createNormalizedWeight(q);
359
 
              Scorer scorer = w.scorer(previousReader, true, false);
360
 
              if (scorer != null) {
361
 
                boolean more = scorer.advance(lastDoc[0] + 1) != DocIdSetIterator.NO_MORE_DOCS;
362
 
                Assert.assertFalse("query's last doc was "+ lastDoc[0] +" but skipTo("+(lastDoc[0]+1)+") got to "+scorer.docID(),more);
363
 
              }
364
 
            }
365
 
            this.reader = lastReader[0] = reader;
366
 
            this.scorer = null;
367
 
            lastDoc[0] = -1;
368
 
          }
369
 
 
370
 
          @Override
371
 
          public boolean acceptsDocsOutOfOrder() {
372
 
            return true;
373
 
          }
374
 
        });
375
 
 
376
 
        if (lastReader[0] != null) {
377
 
          // confirm that skipping beyond the last doc, on the
378
 
          // previous reader, hits NO_MORE_DOCS
379
 
          final IndexReader previousReader = lastReader[0];
380
 
          Weight w = new IndexSearcher(previousReader).createNormalizedWeight(q);
381
 
          Scorer scorer = w.scorer(previousReader, true, false);
382
 
          if (scorer != null) {
383
 
            boolean more = scorer.advance(lastDoc[0] + 1) != DocIdSetIterator.NO_MORE_DOCS;
384
 
            Assert.assertFalse("query's last doc was "+ lastDoc[0] +" but skipTo("+(lastDoc[0]+1)+") got to "+scorer.docID(),more);
385
 
          }
386
 
        }
387
 
      }
388
 
  }
389
 
    
390
 
  // check that first skip on just created scorers always goes to the right doc
391
 
  private static void checkFirstSkipTo(final Query q, final IndexSearcher s) throws IOException {
392
 
    //System.out.println("checkFirstSkipTo: "+q);
393
 
    final float maxDiff = 1e-3f;
394
 
    final int lastDoc[] = {-1};
395
 
    final IndexReader lastReader[] = {null};
396
 
 
397
 
    s.search(q,new Collector() {
398
 
      private Scorer scorer;
399
 
      private IndexReader reader;
400
 
      @Override
401
 
      public void setScorer(Scorer scorer) throws IOException {
402
 
        this.scorer = scorer;
403
 
      }
404
 
      @Override
405
 
      public void collect(int doc) throws IOException {
406
 
        //System.out.println("doc="+doc);
407
 
        float score = scorer.score();
408
 
        try {
409
 
          
410
 
          for (int i=lastDoc[0]+1; i<=doc; i++) {
411
 
            Weight w = s.createNormalizedWeight(q);
412
 
            Scorer scorer = w.scorer(reader, true, false);
413
 
            Assert.assertTrue("query collected "+doc+" but skipTo("+i+") says no more docs!",scorer.advance(i) != DocIdSetIterator.NO_MORE_DOCS);
414
 
            Assert.assertEquals("query collected "+doc+" but skipTo("+i+") got to "+scorer.docID(),doc,scorer.docID());
415
 
            float skipToScore = scorer.score();
416
 
            Assert.assertEquals("unstable skipTo("+i+") score!",skipToScore,scorer.score(),maxDiff); 
417
 
            Assert.assertEquals("query assigned doc "+doc+" a score of <"+score+"> but skipTo("+i+") has <"+skipToScore+">!",score,skipToScore,maxDiff);
418
 
          }
419
 
          lastDoc[0] = doc;
420
 
        } catch (IOException e) {
421
 
          throw new RuntimeException(e);
422
 
        }
423
 
      }
424
 
 
425
 
      @Override
426
 
      public void setNextReader(IndexReader reader, int docBase) throws IOException {
427
 
        // confirm that skipping beyond the last doc, on the
428
 
        // previous reader, hits NO_MORE_DOCS
429
 
        if (lastReader[0] != null) {
430
 
          final IndexReader previousReader = lastReader[0];
431
 
          Weight w = new IndexSearcher(previousReader).createNormalizedWeight(q);
432
 
          Scorer scorer = w.scorer(previousReader, true, false);
433
 
 
434
 
          if (scorer != null) {
435
 
            boolean more = scorer.advance(lastDoc[0] + 1) != DocIdSetIterator.NO_MORE_DOCS;
436
 
            Assert.assertFalse("query's last doc was "+ lastDoc[0] +" but skipTo("+(lastDoc[0]+1)+") got to "+scorer.docID(),more);
437
 
          }
438
 
        }
439
 
 
440
 
        this.reader = lastReader[0] = reader;
441
 
        lastDoc[0] = -1;
442
 
      }
443
 
      @Override
444
 
      public boolean acceptsDocsOutOfOrder() {
445
 
        return false;
446
 
      }
447
 
    });
448
 
 
449
 
    if (lastReader[0] != null) {
450
 
      // confirm that skipping beyond the last doc, on the
451
 
      // previous reader, hits NO_MORE_DOCS
452
 
      final IndexReader previousReader = lastReader[0];
453
 
      Weight w = new IndexSearcher(previousReader).createNormalizedWeight(q);
454
 
      Scorer scorer = w.scorer(previousReader, true, false);
455
 
      if (scorer != null) {
456
 
        boolean more = scorer.advance(lastDoc[0] + 1) != DocIdSetIterator.NO_MORE_DOCS;
457
 
        Assert.assertFalse("query's last doc was "+ lastDoc[0] +" but skipTo("+(lastDoc[0]+1)+") got to "+scorer.docID(),more);
458
 
      }
459
 
    }
460
 
  }
461
 
}