pylucene 3.5.0-3
[pylucene.git] / lucene-java-3.5.0 / lucene / src / test-framework / java / org / apache / lucene / index / RandomIndexWriter.java
1 package org.apache.lucene.index;
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.Closeable;
21 import java.io.IOException;
22 import java.util.Collection;
23 import java.util.Collections;
24 import java.util.Random;
25
26 import org.apache.lucene.analysis.Analyzer;
27 import org.apache.lucene.analysis.MockAnalyzer;
28 import org.apache.lucene.document.Document;
29 import org.apache.lucene.index.IndexWriter; // javadoc
30 import org.apache.lucene.search.Query;
31 import org.apache.lucene.store.Directory;
32 import org.apache.lucene.util.LuceneTestCase;
33 import org.apache.lucene.util.Version;
34 import org.apache.lucene.util._TestUtil;
35
36 /** Silly class that randomizes the indexing experience.  EG
37  *  it may swap in a different merge policy/scheduler; may
38  *  commit periodically; may or may not forceMerge in the end,
39  *  may flush by doc count instead of RAM, etc. 
40  */
41
42 public class RandomIndexWriter implements Closeable {
43
44   public IndexWriter w;
45   private final Random r;
46   int docCount;
47   int flushAt;
48   private double flushAtFactor = 1.0;
49   private boolean getReaderCalled;
50
51   // Randomly calls Thread.yield so we mixup thread scheduling
52   private static final class MockIndexWriter extends IndexWriter {
53
54     private final Random r;
55
56     public MockIndexWriter(Random r,Directory dir, IndexWriterConfig conf) throws IOException {
57       super(dir, conf);
58       // must make a private random since our methods are
59       // called from different threads; else test failures may
60       // not be reproducible from the original seed
61       this.r = new Random(r.nextInt());
62     }
63
64     @Override
65     boolean testPoint(String name) {
66       if (r.nextInt(4) == 2)
67         Thread.yield();
68       return true;
69     }
70   }
71
72   /** create a RandomIndexWriter with a random config: Uses TEST_VERSION_CURRENT and Whitespace+LowercasingAnalyzer */
73   public RandomIndexWriter(Random r, Directory dir) throws IOException {
74     this(r, dir, LuceneTestCase.newIndexWriterConfig(r, LuceneTestCase.TEST_VERSION_CURRENT, new MockAnalyzer(r)));
75   }
76   
77   /** create a RandomIndexWriter with a random config: Uses TEST_VERSION_CURRENT */
78   public RandomIndexWriter(Random r, Directory dir, Analyzer a) throws IOException {
79     this(r, dir, LuceneTestCase.newIndexWriterConfig(r, LuceneTestCase.TEST_VERSION_CURRENT, a));
80   }
81   
82   /** create a RandomIndexWriter with a random config */
83   public RandomIndexWriter(Random r, Directory dir, Version v, Analyzer a) throws IOException {
84     this(r, dir, LuceneTestCase.newIndexWriterConfig(r, v, a));
85   }
86   
87   /** create a RandomIndexWriter with the provided config */
88   public RandomIndexWriter(Random r, Directory dir, IndexWriterConfig c) throws IOException {
89     this.r = r;
90     w = new MockIndexWriter(r, dir, c);
91     flushAt = _TestUtil.nextInt(r, 10, 1000);
92     if (LuceneTestCase.VERBOSE) {
93       System.out.println("RIW config=" + w.getConfig());
94     }
95   } 
96
97   /**
98    * Adds a Document.
99    * @see IndexWriter#addDocument(Document)
100    */
101   public void addDocument(final Document doc) throws IOException {
102     if (r.nextInt(5) == 3) {
103       // TODO: maybe, we should simply buffer up added docs
104       // (but we need to clone them), and only when
105       // getReader, commit, etc. are called, we do an
106       // addDocuments?  Would be better testing.
107       w.addDocuments(Collections.singletonList(doc));
108     } else {
109       w.addDocument(doc);
110     }
111     maybeCommit();
112   }
113   
114   public void addDocuments(Collection<Document> docs) throws IOException {
115     w.addDocuments(docs);
116     maybeCommit();
117   }
118
119   public void updateDocuments(Term delTerm, Collection<Document> docs) throws IOException {
120     w.updateDocuments(delTerm, docs);
121     maybeCommit();
122   }
123
124   private void maybeCommit() throws IOException {
125     if (docCount++ == flushAt) {
126       if (LuceneTestCase.VERBOSE) {
127         System.out.println("RIW.add/updateDocument: now doing a commit at docCount=" + docCount);
128       }
129       w.commit();
130       flushAt += _TestUtil.nextInt(r, (int) (flushAtFactor * 10), (int) (flushAtFactor * 1000));
131       if (flushAtFactor < 2e6) {
132         // gradually but exponentially increase time b/w flushes
133         flushAtFactor *= 1.05;
134       }
135     }
136   }
137
138   /**
139    * Updates a document.
140    * @see IndexWriter#updateDocument(Term, Document)
141    */
142   public void updateDocument(Term t, final Document doc) throws IOException {
143     if (r.nextInt(5) == 3) {
144       w.updateDocuments(t, Collections.singletonList(doc));
145     } else {
146       w.updateDocument(t, doc);
147     }
148     maybeCommit();
149   }
150   
151   public void addIndexes(Directory... dirs) throws CorruptIndexException, IOException {
152     w.addIndexes(dirs);
153   }
154   
155   public void addIndexes(IndexReader... readers) throws CorruptIndexException, IOException {
156     w.addIndexes(readers);
157   }
158
159   public void deleteDocuments(Term term) throws CorruptIndexException, IOException {
160     w.deleteDocuments(term);
161   }
162
163   public void deleteDocuments(Query q) throws CorruptIndexException, IOException {
164     w.deleteDocuments(q);
165   }
166   
167   public void commit() throws CorruptIndexException, IOException {
168     w.commit();
169   }
170   
171   public int numDocs() throws IOException {
172     return w.numDocs();
173   }
174
175   public int maxDoc() {
176     return w.maxDoc();
177   }
178
179   public void deleteAll() throws IOException {
180     w.deleteAll();
181   }
182
183   private boolean doRandomForceMerge = true;
184   private boolean doRandomForceMergeAssert = true;
185
186   public void forceMergeDeletes(boolean doWait) throws IOException {
187     w.forceMergeDeletes(doWait);
188   }
189
190   public void forceMergeDeletes() throws IOException {
191     w.forceMergeDeletes();
192   }
193
194   public void setDoRandomForceMerge(boolean v) {
195     doRandomForceMerge = v;
196   }
197
198   public void setDoRandomForceMergeAssert(boolean v) {
199     doRandomForceMergeAssert = v;
200   }
201
202   private void doRandomForceMerge() throws IOException {
203     if (doRandomForceMerge) {
204       final int segCount = w.getSegmentCount();
205       if (r.nextBoolean() || segCount == 0) {
206         // full forceMerge
207         w.forceMerge(1);
208       } else {
209         // partial forceMerge
210         final int limit = _TestUtil.nextInt(r, 1, segCount);
211         w.forceMerge(limit);
212         assert !doRandomForceMergeAssert || w.getSegmentCount() <= limit: "limit=" + limit + " actual=" + w.getSegmentCount();
213       }
214     }
215   }
216
217   public IndexReader getReader() throws IOException {
218     return getReader(true);
219   }
220
221   public IndexReader getReader(boolean applyDeletions) throws IOException {
222     getReaderCalled = true;
223     if (r.nextInt(4) == 2) {
224       doRandomForceMerge();
225     }
226     if (r.nextBoolean()) {
227       if (LuceneTestCase.VERBOSE) {
228         System.out.println("RIW.getReader: use NRT reader");
229       }
230       if (r.nextInt(5) == 1) {
231         w.commit();
232       }
233       return w.getReader(applyDeletions);
234     } else {
235       if (LuceneTestCase.VERBOSE) {
236         System.out.println("RIW.getReader: open new reader");
237       }
238       w.commit();
239       if (r.nextBoolean()) {
240         return IndexReader.open(w.getDirectory(), new KeepOnlyLastCommitDeletionPolicy(), r.nextBoolean(), _TestUtil.nextInt(r, 1, 10));
241       } else {
242         return w.getReader(applyDeletions);
243       }
244     }
245   }
246
247   /**
248    * Close this writer.
249    * @see IndexWriter#close()
250    */
251   public void close() throws IOException {
252     // if someone isn't using getReader() API, we want to be sure to
253     // forceMerge since presumably they might open a reader on the dir.
254     if (getReaderCalled == false && r.nextInt(8) == 2) {
255       doRandomForceMerge();
256     }
257     w.close();
258   }
259
260   /**
261    * Forces a forceMerge.
262    * <p>
263    * NOTE: this should be avoided in tests unless absolutely necessary,
264    * as it will result in less test coverage.
265    * @see IndexWriter#forceMerge(int)
266    */
267   public void forceMerge(int maxSegmentCount) throws IOException {
268     w.forceMerge(maxSegmentCount);
269   }
270 }