1 package org.apache.lucene.index;
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
11 * http://www.apache.org/licenses/LICENSE-2.0
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.
20 import java.io.IOException;
22 import org.apache.lucene.analysis.MockAnalyzer;
23 import org.apache.lucene.analysis.WhitespaceAnalyzer;
24 import org.apache.lucene.document.Document;
25 import org.apache.lucene.document.Field;
26 import org.apache.lucene.search.PhraseQuery;
27 import org.apache.lucene.search.ScoreDoc;
28 import org.apache.lucene.search.Searcher;
29 import org.apache.lucene.store.Directory;
30 import org.apache.lucene.store.IndexInput;
31 import org.apache.lucene.store.MockDirectoryWrapper;
32 import org.apache.lucene.store.RAMDirectory;
33 import org.apache.lucene.util.LuceneTestCase;
36 * Tests lazy skipping on the proximity file.
39 public class TestLazyProxSkipping extends LuceneTestCase {
40 private Searcher searcher;
41 private int seeksCounter = 0;
43 private String field = "tokens";
44 private String term1 = "xx";
45 private String term2 = "yy";
46 private String term3 = "zz";
48 private class SeekCountingDirectory extends MockDirectoryWrapper {
49 public SeekCountingDirectory(Directory delegate) {
50 super(random, delegate);
54 public IndexInput openInput(String name) throws IOException {
55 IndexInput ii = super.openInput(name);
56 if (name.endsWith(".prx")) {
57 // we decorate the proxStream with a wrapper class that allows to count the number of calls of seek()
58 ii = new SeeksCountingStream(ii);
65 private void createIndex(int numHits) throws IOException {
68 Directory directory = new SeekCountingDirectory(new RAMDirectory());
69 // note: test explicitly disables payloads
70 IndexWriter writer = new IndexWriter(
72 newIndexWriterConfig(TEST_VERSION_CURRENT, new WhitespaceAnalyzer(TEST_VERSION_CURRENT)).
73 setMaxBufferedDocs(10).
74 setMergePolicy(newLogMergePolicy(false))
76 for (int i = 0; i < numDocs; i++) {
77 Document doc = new Document();
79 if (i % (numDocs / numHits) == 0) {
80 // add a document that matches the query "term1 term2"
81 content = this.term1 + " " + this.term2;
82 } else if (i % 15 == 0) {
83 // add a document that only contains term1
84 content = this.term1 + " " + this.term1;
86 // add a document that contains term2 but not term 1
87 content = this.term3 + " " + this.term2;
90 doc.add(newField(this.field, content, Field.Store.YES, Field.Index.ANALYZED));
91 writer.addDocument(doc);
94 // make sure the index has only a single segment
98 SegmentReader reader = SegmentReader.getOnlySegmentReader(directory);
100 this.searcher = newSearcher(reader);
103 private ScoreDoc[] search() throws IOException {
104 // create PhraseQuery "term1 term2" and search
105 PhraseQuery pq = new PhraseQuery();
106 pq.add(new Term(this.field, this.term1));
107 pq.add(new Term(this.field, this.term2));
108 return this.searcher.search(pq, null, 1000).scoreDocs;
111 private void performTest(int numHits) throws IOException {
112 createIndex(numHits);
113 this.seeksCounter = 0;
114 ScoreDoc[] hits = search();
115 // verify that the right number of docs was found
116 assertEquals(numHits, hits.length);
118 // check if the number of calls of seek() does not exceed the number of hits
119 assertTrue(this.seeksCounter > 0);
120 assertTrue(this.seeksCounter <= numHits + 1);
123 public void testLazySkipping() throws IOException {
124 // test whether only the minimum amount of seeks()
132 public void testSeek() throws IOException {
133 Directory directory = newDirectory();
134 IndexWriter writer = new IndexWriter(directory, newIndexWriterConfig( TEST_VERSION_CURRENT, new MockAnalyzer(random)));
135 for (int i = 0; i < 10; i++) {
136 Document doc = new Document();
137 doc.add(newField(this.field, "a b", Field.Store.YES, Field.Index.ANALYZED));
138 writer.addDocument(doc);
142 IndexReader reader = IndexReader.open(directory, true);
143 TermPositions tp = reader.termPositions();
144 tp.seek(new Term(this.field, "b"));
145 for (int i = 0; i < 10; i++) {
147 assertEquals(tp.doc(), i);
148 assertEquals(tp.nextPosition(), 1);
150 tp.seek(new Term(this.field, "a"));
151 for (int i = 0; i < 10; i++) {
153 assertEquals(tp.doc(), i);
154 assertEquals(tp.nextPosition(), 0);
162 // Simply extends IndexInput in a way that we are able to count the number
163 // of invocations of seek()
164 class SeeksCountingStream extends IndexInput {
165 private IndexInput input;
168 SeeksCountingStream(IndexInput input) {
173 public byte readByte() throws IOException {
174 return this.input.readByte();
178 public void readBytes(byte[] b, int offset, int len) throws IOException {
179 this.input.readBytes(b, offset, len);
183 public void close() throws IOException {
188 public long getFilePointer() {
189 return this.input.getFilePointer();
193 public void seek(long pos) throws IOException {
194 TestLazyProxSkipping.this.seeksCounter++;
195 this.input.seek(pos);
199 public long length() {
200 return this.input.length();
204 public Object clone() {
205 return new SeeksCountingStream((IndexInput) this.input.clone());