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.store.Directory;
31 import org.apache.lucene.util.LuceneTestCase;
32 import org.apache.lucene.util.Version;
33 import org.apache.lucene.util._TestUtil;
35 /** Silly class that randomizes the indexing experience. EG
36 * it may swap in a different merge policy/scheduler; may
37 * commit periodically; may or may not optimize in the end,
38 * may flush by doc count instead of RAM, etc.
41 public class RandomIndexWriter implements Closeable {
44 private final Random r;
47 private double flushAtFactor = 1.0;
48 private boolean getReaderCalled;
50 // Randomly calls Thread.yield so we mixup thread scheduling
51 private static final class MockIndexWriter extends IndexWriter {
53 private final Random r;
55 public MockIndexWriter(Random r,Directory dir, IndexWriterConfig conf) throws IOException {
57 // must make a private random since our methods are
58 // called from different threads; else test failures may
59 // not be reproducible from the original seed
60 this.r = new Random(r.nextInt());
64 boolean testPoint(String name) {
65 if (r.nextInt(4) == 2)
71 /** create a RandomIndexWriter with a random config: Uses TEST_VERSION_CURRENT and Whitespace+LowercasingAnalyzer */
72 public RandomIndexWriter(Random r, Directory dir) throws IOException {
73 this(r, dir, LuceneTestCase.newIndexWriterConfig(r, LuceneTestCase.TEST_VERSION_CURRENT, new MockAnalyzer(r)));
76 /** create a RandomIndexWriter with a random config: Uses TEST_VERSION_CURRENT */
77 public RandomIndexWriter(Random r, Directory dir, Analyzer a) throws IOException {
78 this(r, dir, LuceneTestCase.newIndexWriterConfig(r, LuceneTestCase.TEST_VERSION_CURRENT, a));
81 /** create a RandomIndexWriter with a random config */
82 public RandomIndexWriter(Random r, Directory dir, Version v, Analyzer a) throws IOException {
83 this(r, dir, LuceneTestCase.newIndexWriterConfig(r, v, a));
86 /** create a RandomIndexWriter with the provided config */
87 public RandomIndexWriter(Random r, Directory dir, IndexWriterConfig c) throws IOException {
89 w = new MockIndexWriter(r, dir, c);
90 flushAt = _TestUtil.nextInt(r, 10, 1000);
91 if (LuceneTestCase.VERBOSE) {
92 System.out.println("RIW config=" + w.getConfig());
98 * @see IndexWriter#addDocument(Document)
100 public void addDocument(final Document doc) throws IOException {
101 if (r.nextInt(5) == 3) {
102 // TODO: maybe, we should simply buffer up added docs
103 // (but we need to clone them), and only when
104 // getReader, commit, etc. are called, we do an
105 // addDocuments? Would be better testing.
106 w.addDocuments(Collections.singletonList(doc));
113 public void addDocuments(Collection<Document> docs) throws IOException {
114 w.addDocuments(docs);
118 public void updateDocuments(Term delTerm, Collection<Document> docs) throws IOException {
119 w.updateDocuments(delTerm, docs);
123 private void maybeCommit() throws IOException {
124 if (docCount++ == flushAt) {
125 if (LuceneTestCase.VERBOSE) {
126 System.out.println("RIW.add/updateDocument: now doing a commit at docCount=" + docCount);
129 flushAt += _TestUtil.nextInt(r, (int) (flushAtFactor * 10), (int) (flushAtFactor * 1000));
130 if (flushAtFactor < 2e6) {
131 // gradually but exponentially increase time b/w flushes
132 flushAtFactor *= 1.05;
138 * Updates a document.
139 * @see IndexWriter#updateDocument(Term, Document)
141 public void updateDocument(Term t, final Document doc) throws IOException {
142 if (r.nextInt(5) == 3) {
143 w.updateDocuments(t, Collections.singletonList(doc));
145 w.updateDocument(t, doc);
150 public void addIndexes(Directory... dirs) throws CorruptIndexException, IOException {
154 public void deleteDocuments(Term term) throws CorruptIndexException, IOException {
155 w.deleteDocuments(term);
158 public void commit() throws CorruptIndexException, IOException {
162 public int numDocs() throws IOException {
166 public int maxDoc() {
170 public void deleteAll() throws IOException {
174 private boolean doRandomOptimize = true;
176 public void setDoRandomOptimize(boolean v) {
177 doRandomOptimize = v;
180 private void doRandomOptimize() throws IOException {
181 if (doRandomOptimize) {
182 final int segCount = w.getSegmentCount();
183 if (r.nextBoolean() || segCount == 0) {
188 final int limit = _TestUtil.nextInt(r, 1, segCount);
190 assert w.getSegmentCount() <= limit: "limit=" + limit + " actual=" + w.getSegmentCount();
195 public IndexReader getReader() throws IOException {
196 return getReader(true);
199 public IndexReader getReader(boolean applyDeletions) throws IOException {
200 getReaderCalled = true;
201 if (r.nextInt(4) == 2) {
204 if (r.nextBoolean()) {
205 if (LuceneTestCase.VERBOSE) {
206 System.out.println("RIW.getReader: use NRT reader");
208 return w.getReader(applyDeletions);
210 if (LuceneTestCase.VERBOSE) {
211 System.out.println("RIW.getReader: open new reader");
214 return IndexReader.open(w.getDirectory(), new KeepOnlyLastCommitDeletionPolicy(), r.nextBoolean(), _TestUtil.nextInt(r, 1, 10));
220 * @see IndexWriter#close()
222 public void close() throws IOException {
223 // if someone isn't using getReader() API, we want to be sure to
224 // maybeOptimize since presumably they might open a reader on the dir.
225 if (getReaderCalled == false && r.nextInt(8) == 2) {
232 * Forces an optimize.
234 * NOTE: this should be avoided in tests unless absolutely necessary,
235 * as it will result in less test coverage.
236 * @see IndexWriter#optimize()
238 public void optimize() throws IOException {