| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.lucene.index; |
| |
| |
| import java.lang.reflect.Field; |
| import java.lang.reflect.Method; |
| import java.lang.reflect.Modifier; |
| import java.util.HashSet; |
| import java.util.Set; |
| import java.util.function.Consumer; |
| |
| import org.apache.lucene.analysis.MockAnalyzer; |
| import org.apache.lucene.codecs.Codec; |
| import org.apache.lucene.document.Document; |
| import org.apache.lucene.document.Field.Store; |
| import org.apache.lucene.index.DocumentsWriterPerThread.IndexingChain; |
| import org.apache.lucene.index.IndexWriterConfig.OpenMode; |
| import org.apache.lucene.search.IndexSearcher; |
| import org.apache.lucene.search.similarities.ClassicSimilarity; |
| import org.apache.lucene.store.Directory; |
| import org.apache.lucene.util.InfoStream; |
| import org.apache.lucene.util.LuceneTestCase; |
| import org.junit.Test; |
| |
| public class TestIndexWriterConfig extends LuceneTestCase { |
| |
| private static final class MySimilarity extends ClassicSimilarity { |
| // Does not implement anything - used only for type checking on IndexWriterConfig. |
| } |
| |
| private static final class MyIndexingChain extends IndexingChain { |
| @Override |
| DocConsumer getChain(int indexCreatedVersionMajor, SegmentInfo segmentInfo, Directory directory, |
| FieldInfos.Builder fieldInfos, LiveIndexWriterConfig indexWriterConfig, |
| Consumer<Throwable> abortingExceptionConsumer) { |
| return null; |
| } |
| // Does not implement anything - used only for type checking on IndexWriterConfig. |
| |
| } |
| |
| @Test |
| public void testDefaults() throws Exception { |
| IndexWriterConfig conf = new IndexWriterConfig(new MockAnalyzer(random())); |
| assertEquals(MockAnalyzer.class, conf.getAnalyzer().getClass()); |
| assertNull(conf.getIndexCommit()); |
| assertEquals(KeepOnlyLastCommitDeletionPolicy.class, conf.getIndexDeletionPolicy().getClass()); |
| assertEquals(ConcurrentMergeScheduler.class, conf.getMergeScheduler().getClass()); |
| assertEquals(OpenMode.CREATE_OR_APPEND, conf.getOpenMode()); |
| // we don't need to assert this, it should be unspecified |
| assertTrue(IndexSearcher.getDefaultSimilarity() == conf.getSimilarity()); |
| assertEquals(IndexWriterConfig.DEFAULT_RAM_BUFFER_SIZE_MB, conf.getRAMBufferSizeMB(), 0.0); |
| assertEquals(IndexWriterConfig.DEFAULT_MAX_BUFFERED_DOCS, conf.getMaxBufferedDocs()); |
| assertEquals(IndexWriterConfig.DEFAULT_READER_POOLING, conf.getReaderPooling()); |
| assertTrue(DocumentsWriterPerThread.defaultIndexingChain == conf.getIndexingChain()); |
| assertNull(conf.getMergedSegmentWarmer()); |
| assertEquals(TieredMergePolicy.class, conf.getMergePolicy().getClass()); |
| assertEquals(FlushByRamOrCountsPolicy.class, conf.getFlushPolicy().getClass()); |
| assertEquals(IndexWriterConfig.DEFAULT_RAM_PER_THREAD_HARD_LIMIT_MB, conf.getRAMPerThreadHardLimitMB()); |
| assertEquals(Codec.getDefault(), conf.getCodec()); |
| assertEquals(InfoStream.getDefault(), conf.getInfoStream()); |
| assertEquals(IndexWriterConfig.DEFAULT_USE_COMPOUND_FILE_SYSTEM, conf.getUseCompoundFile()); |
| assertTrue(conf.isCheckPendingFlushOnUpdate()); |
| // Sanity check - validate that all getters are covered. |
| Set<String> getters = new HashSet<>(); |
| getters.add("getAnalyzer"); |
| getters.add("getIndexCommit"); |
| getters.add("getIndexDeletionPolicy"); |
| getters.add("getMaxFieldLength"); |
| getters.add("getMergeScheduler"); |
| getters.add("getOpenMode"); |
| getters.add("getSimilarity"); |
| getters.add("getWriteLockTimeout"); |
| getters.add("getDefaultWriteLockTimeout"); |
| getters.add("getMaxBufferedDeleteTerms"); |
| getters.add("getRAMBufferSizeMB"); |
| getters.add("getMaxBufferedDocs"); |
| getters.add("getIndexingChain"); |
| getters.add("getMergedSegmentWarmer"); |
| getters.add("getMergePolicy"); |
| getters.add("getReaderPooling"); |
| getters.add("getIndexerThreadPool"); |
| getters.add("getFlushPolicy"); |
| getters.add("getRAMPerThreadHardLimitMB"); |
| getters.add("getCodec"); |
| getters.add("getInfoStream"); |
| getters.add("getUseCompoundFile"); |
| getters.add("isCheckPendingFlushOnUpdate"); |
| getters.add("getSoftDeletesField"); |
| |
| for (Method m : IndexWriterConfig.class.getDeclaredMethods()) { |
| if (m.getDeclaringClass() == IndexWriterConfig.class && m.getName().startsWith("get")) { |
| assertTrue("method " + m.getName() + " is not tested for defaults", getters.contains(m.getName())); |
| } |
| } |
| } |
| |
| @Test |
| public void testSettersChaining() throws Exception { |
| // Ensures that every setter returns IndexWriterConfig to allow chaining. |
| HashSet<String> liveSetters = new HashSet<>(); |
| HashSet<String> allSetters = new HashSet<>(); |
| for (Method m : IndexWriterConfig.class.getDeclaredMethods()) { |
| if (m.getName().startsWith("set") && !Modifier.isStatic(m.getModifiers())) { |
| allSetters.add(m.getName()); |
| // setters overridden from LiveIndexWriterConfig are returned twice, once with |
| // IndexWriterConfig return type and second with LiveIndexWriterConfig. The ones |
| // from LiveIndexWriterConfig are marked 'synthetic', so just collect them and |
| // assert in the end that we also received them from IWC. |
| if (m.isSynthetic()) { |
| liveSetters.add(m.getName()); |
| } else { |
| assertEquals("method " + m.getName() + " does not return IndexWriterConfig", |
| IndexWriterConfig.class, m.getReturnType()); |
| } |
| } |
| } |
| for (String setter : liveSetters) { |
| assertTrue("setter method not overridden by IndexWriterConfig: " + setter, allSetters.contains(setter)); |
| } |
| } |
| |
| @Test |
| public void testReuse() throws Exception { |
| Directory dir = newDirectory(); |
| // test that IWC cannot be reused across two IWs |
| IndexWriterConfig conf = newIndexWriterConfig(null); |
| new RandomIndexWriter(random(), dir, conf).close(); |
| |
| // this should fail |
| expectThrows(IllegalStateException.class, () -> { |
| assertNotNull(new RandomIndexWriter(random(), dir, conf)); |
| }); |
| |
| dir.close(); |
| } |
| |
| @Test |
| public void testOverrideGetters() throws Exception { |
| // Test that IndexWriterConfig overrides all getters, so that javadocs |
| // contain all methods for the users. Also, ensures that IndexWriterConfig |
| // doesn't declare getters that are not declared on LiveIWC. |
| HashSet<String> liveGetters = new HashSet<>(); |
| for (Method m : LiveIndexWriterConfig.class.getDeclaredMethods()) { |
| if (m.getName().startsWith("get") && !Modifier.isStatic(m.getModifiers())) { |
| liveGetters.add(m.getName()); |
| } |
| } |
| |
| for (Method m : IndexWriterConfig.class.getDeclaredMethods()) { |
| if (m.getName().startsWith("get") && !Modifier.isStatic(m.getModifiers())) { |
| assertEquals("method " + m.getName() + " not overrided by IndexWriterConfig", |
| IndexWriterConfig.class, m.getDeclaringClass()); |
| assertTrue("method " + m.getName() + " not declared on LiveIndexWriterConfig", |
| liveGetters.contains(m.getName())); |
| } |
| } |
| } |
| |
| @Test |
| public void testConstants() throws Exception { |
| // Tests that the values of the constants does not change |
| assertEquals(-1, IndexWriterConfig.DISABLE_AUTO_FLUSH); |
| assertEquals(IndexWriterConfig.DISABLE_AUTO_FLUSH, IndexWriterConfig.DEFAULT_MAX_BUFFERED_DELETE_TERMS); |
| assertEquals(IndexWriterConfig.DISABLE_AUTO_FLUSH, IndexWriterConfig.DEFAULT_MAX_BUFFERED_DOCS); |
| assertEquals(16.0, IndexWriterConfig.DEFAULT_RAM_BUFFER_SIZE_MB, 0.0); |
| assertEquals(true, IndexWriterConfig.DEFAULT_READER_POOLING); |
| assertEquals(true, IndexWriterConfig.DEFAULT_USE_COMPOUND_FILE_SYSTEM); |
| } |
| |
| @Test |
| public void testToString() throws Exception { |
| String str = new IndexWriterConfig(new MockAnalyzer(random())).toString(); |
| for (Field f : IndexWriterConfig.class.getDeclaredFields()) { |
| int modifiers = f.getModifiers(); |
| if (Modifier.isStatic(modifiers) && Modifier.isFinal(modifiers)) { |
| // Skip static final fields, they are only constants |
| continue; |
| } else if ("indexingChain".equals(f.getName())) { |
| // indexingChain is a package-private setting and thus is not output by |
| // toString. |
| continue; |
| } |
| if (f.getName().equals("inUseByIndexWriter")) { |
| continue; |
| } |
| assertTrue(f.getName() + " not found in toString", str.indexOf(f.getName()) != -1); |
| } |
| } |
| |
| @Test |
| public void testInvalidValues() throws Exception { |
| IndexWriterConfig conf = new IndexWriterConfig(new MockAnalyzer(random())); |
| |
| // Test IndexDeletionPolicy |
| assertEquals(KeepOnlyLastCommitDeletionPolicy.class, conf.getIndexDeletionPolicy().getClass()); |
| conf.setIndexDeletionPolicy(new SnapshotDeletionPolicy(null)); |
| assertEquals(SnapshotDeletionPolicy.class, conf.getIndexDeletionPolicy().getClass()); |
| expectThrows(IllegalArgumentException.class, () -> { |
| conf.setIndexDeletionPolicy(null); |
| }); |
| |
| // Test MergeScheduler |
| assertEquals(ConcurrentMergeScheduler.class, conf.getMergeScheduler().getClass()); |
| conf.setMergeScheduler(new SerialMergeScheduler()); |
| assertEquals(SerialMergeScheduler.class, conf.getMergeScheduler().getClass()); |
| expectThrows(IllegalArgumentException.class, () -> { |
| conf.setMergeScheduler(null); |
| }); |
| |
| // Test Similarity: |
| // we shouldnt assert what the default is, just that it's not null. |
| assertTrue(IndexSearcher.getDefaultSimilarity() == conf.getSimilarity()); |
| conf.setSimilarity(new MySimilarity()); |
| assertEquals(MySimilarity.class, conf.getSimilarity().getClass()); |
| expectThrows(IllegalArgumentException.class, () -> { |
| conf.setSimilarity(null); |
| }); |
| |
| // Test IndexingChain |
| assertTrue(DocumentsWriterPerThread.defaultIndexingChain == conf.getIndexingChain()); |
| |
| expectThrows(IllegalArgumentException.class, () -> { |
| conf.setMaxBufferedDocs(1); |
| }); |
| |
| expectThrows(IllegalArgumentException.class, () -> { |
| // Disable both MAX_BUF_DOCS and RAM_SIZE_MB |
| conf.setMaxBufferedDocs(4); |
| conf.setRAMBufferSizeMB(IndexWriterConfig.DISABLE_AUTO_FLUSH); |
| conf.setMaxBufferedDocs(IndexWriterConfig.DISABLE_AUTO_FLUSH); |
| }); |
| |
| conf.setRAMBufferSizeMB(IndexWriterConfig.DEFAULT_RAM_BUFFER_SIZE_MB); |
| conf.setMaxBufferedDocs(IndexWriterConfig.DEFAULT_MAX_BUFFERED_DOCS); |
| expectThrows(IllegalArgumentException.class, () -> { |
| conf.setRAMBufferSizeMB(IndexWriterConfig.DISABLE_AUTO_FLUSH); |
| }); |
| |
| expectThrows(IllegalArgumentException.class, () -> { |
| conf.setRAMPerThreadHardLimitMB(2048); |
| }); |
| |
| expectThrows(IllegalArgumentException.class, () -> { |
| conf.setRAMPerThreadHardLimitMB(0); |
| }); |
| |
| // Test MergePolicy |
| assertEquals(TieredMergePolicy.class, conf.getMergePolicy().getClass()); |
| conf.setMergePolicy(new LogDocMergePolicy()); |
| assertEquals(LogDocMergePolicy.class, conf.getMergePolicy().getClass()); |
| expectThrows(IllegalArgumentException.class, () -> { |
| conf.setMergePolicy(null); |
| }); |
| } |
| |
| public void testLiveChangeToCFS() throws Exception { |
| Directory dir = newDirectory(); |
| IndexWriterConfig iwc = new IndexWriterConfig(new MockAnalyzer(random())); |
| iwc.setMergePolicy(newLogMergePolicy(true)); |
| // Start false: |
| iwc.setUseCompoundFile(false); |
| iwc.getMergePolicy().setNoCFSRatio(0.0d); |
| IndexWriter w = new IndexWriter(dir, iwc); |
| // Change to true: |
| w.getConfig().setUseCompoundFile(true); |
| |
| Document doc = new Document(); |
| doc.add(newStringField("field", "foo", Store.NO)); |
| w.addDocument(doc); |
| w.commit(); |
| assertTrue("Expected CFS after commit", w.newestSegment().info.getUseCompoundFile()); |
| |
| doc.add(newStringField("field", "foo", Store.NO)); |
| w.addDocument(doc); |
| w.commit(); |
| w.forceMerge(1); |
| w.commit(); |
| |
| // no compound files after merge |
| assertFalse("Expected Non-CFS after merge", w.newestSegment().info.getUseCompoundFile()); |
| |
| MergePolicy lmp = w.getConfig().getMergePolicy(); |
| lmp.setNoCFSRatio(1.0); |
| lmp.setMaxCFSSegmentSizeMB(Double.POSITIVE_INFINITY); |
| |
| w.addDocument(doc); |
| w.forceMerge(1); |
| w.commit(); |
| assertTrue("Expected CFS after merge", w.newestSegment().info.getUseCompoundFile()); |
| w.close(); |
| dir.close(); |
| } |
| |
| } |