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.Closeable;
21 import java.io.IOException;
22 import java.util.Collection;
23 import java.util.Collections;
24 import java.util.Random;
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;
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 optimize in the end,
39 * may flush by doc count instead of RAM, etc.
42 public class RandomIndexWriter implements Closeable {
45 private final Random r;
48 private double flushAtFactor = 1.0;
49 private boolean getReaderCalled;
51 // Randomly calls Thread.yield so we mixup thread scheduling
52 private static final class MockIndexWriter extends IndexWriter {
54 private final Random r;
56 public MockIndexWriter(Random r,Directory dir, IndexWriterConfig conf) throws IOException {
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());
65 boolean testPoint(String name) {
66 if (r.nextInt(4) == 2)
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)));
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));
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));
87 /** create a RandomIndexWriter with the provided config */
88 public RandomIndexWriter(Random r, Directory dir, IndexWriterConfig c) throws IOException {
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());
99 * @see IndexWriter#addDocument(Document)
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));
114 public void addDocuments(Collection<Document> docs) throws IOException {
115 w.addDocuments(docs);
119 public void updateDocuments(Term delTerm, Collection<Document> docs) throws IOException {
120 w.updateDocuments(delTerm, docs);
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);
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;
139 * Updates a document.
140 * @see IndexWriter#updateDocument(Term, Document)
142 public void updateDocument(Term t, final Document doc) throws IOException {
143 if (r.nextInt(5) == 3) {
144 w.updateDocuments(t, Collections.singletonList(doc));
146 w.updateDocument(t, doc);
151 public void addIndexes(Directory... dirs) throws CorruptIndexException, IOException {
155 public void deleteDocuments(Term term) throws CorruptIndexException, IOException {
156 w.deleteDocuments(term);
159 public void deleteDocuments(Query q) throws CorruptIndexException, IOException {
160 w.deleteDocuments(q);
163 public void commit() throws CorruptIndexException, IOException {
167 public int numDocs() throws IOException {
171 public int maxDoc() {
175 public void deleteAll() throws IOException {
179 private boolean doRandomOptimize = true;
180 private boolean doRandomOptimizeAssert = true;
182 public void setDoRandomOptimize(boolean v) {
183 doRandomOptimize = v;
186 public void setDoRandomOptimizeAssert(boolean v) {
187 doRandomOptimizeAssert = v;
190 private void doRandomOptimize() throws IOException {
191 if (doRandomOptimize) {
192 final int segCount = w.getSegmentCount();
193 if (r.nextBoolean() || segCount == 0) {
198 final int limit = _TestUtil.nextInt(r, 1, segCount);
200 assert !doRandomOptimizeAssert || w.getSegmentCount() <= limit: "limit=" + limit + " actual=" + w.getSegmentCount();
205 public IndexReader getReader() throws IOException {
206 return getReader(true);
209 public IndexReader getReader(boolean applyDeletions) throws IOException {
210 getReaderCalled = true;
211 if (r.nextInt(4) == 2) {
214 if (r.nextBoolean()) {
215 if (LuceneTestCase.VERBOSE) {
216 System.out.println("RIW.getReader: use NRT reader");
218 if (r.nextInt(5) == 1) {
221 return w.getReader(applyDeletions);
223 if (LuceneTestCase.VERBOSE) {
224 System.out.println("RIW.getReader: open new reader");
227 if (r.nextBoolean()) {
228 return IndexReader.open(w.getDirectory(), new KeepOnlyLastCommitDeletionPolicy(), r.nextBoolean(), _TestUtil.nextInt(r, 1, 10));
230 return w.getReader(applyDeletions);
237 * @see IndexWriter#close()
239 public void close() throws IOException {
240 // if someone isn't using getReader() API, we want to be sure to
241 // maybeOptimize since presumably they might open a reader on the dir.
242 if (getReaderCalled == false && r.nextInt(8) == 2) {
249 * Forces an optimize.
251 * NOTE: this should be avoided in tests unless absolutely necessary,
252 * as it will result in less test coverage.
253 * @see IndexWriter#optimize()
255 public void optimize() throws IOException {