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