Index: src/test/org/apache/lucene/index/TestDoc.java =================================================================== --- src/test/org/apache/lucene/index/TestDoc.java (revision 1072815) +++ src/test/org/apache/lucene/index/TestDoc.java (working copy) @@ -204,8 +204,8 @@ r2.close(); final SegmentInfo info = new SegmentInfo(merged, si1.docCount + si2.docCount, si1.dir, - false, merger.fieldInfos().hasProx(), merger.getSegmentCodecs(), - merger.fieldInfos().hasVectors()); + false, merger.getSegmentCodecs(), + merger.fieldInfos()); if (useCompoundFile) { Collection filesToDelete = merger.createCompoundFile(merged + ".cfs", info); Index: src/test/org/apache/lucene/index/TestDocumentWriter.java =================================================================== --- src/test/org/apache/lucene/index/TestDocumentWriter.java (revision 1072815) +++ src/test/org/apache/lucene/index/TestDocumentWriter.java (working copy) @@ -25,20 +25,20 @@ import org.apache.lucene.analysis.MockTokenizer; import org.apache.lucene.analysis.TokenFilter; import org.apache.lucene.analysis.TokenStream; +import org.apache.lucene.analysis.tokenattributes.CharTermAttribute; import org.apache.lucene.analysis.tokenattributes.PayloadAttribute; import org.apache.lucene.analysis.tokenattributes.PositionIncrementAttribute; -import org.apache.lucene.analysis.tokenattributes.CharTermAttribute; import org.apache.lucene.document.Document; import org.apache.lucene.document.Field; -import org.apache.lucene.document.Fieldable; import org.apache.lucene.document.Field.Index; import org.apache.lucene.document.Field.Store; import org.apache.lucene.document.Field.TermVector; +import org.apache.lucene.document.Fieldable; import org.apache.lucene.store.Directory; import org.apache.lucene.util.AttributeSource; +import org.apache.lucene.util.BytesRef; import org.apache.lucene.util.LuceneTestCase; import org.apache.lucene.util._TestUtil; -import org.apache.lucene.util.BytesRef; public class TestDocumentWriter extends LuceneTestCase { private Directory dir; @@ -98,8 +98,7 @@ // test that the norms are not present in the segment if // omitNorms is true - for (int i = 0; i < reader.core.fieldInfos.size(); i++) { - FieldInfo fi = reader.core.fieldInfos.fieldInfo(i); + for (FieldInfo fi : reader.core.fieldInfos) { if (fi.isIndexed) { assertTrue(fi.omitNorms == !reader.hasNorms(fi.name)); } Index: src/test/org/apache/lucene/index/TestIndexFileDeleter.java =================================================================== --- src/test/org/apache/lucene/index/TestIndexFileDeleter.java (revision 1072815) +++ src/test/org/apache/lucene/index/TestIndexFileDeleter.java (working copy) @@ -92,10 +92,9 @@ CompoundFileReader cfsReader = new CompoundFileReader(dir, "_2.cfs"); FieldInfos fieldInfos = new FieldInfos(cfsReader, "_2.fnm"); int contentFieldIndex = -1; - for(i=0;i { + private static final class FieldNumberBiMap { + private final Map numberToName; + private final Map nameToNumber; + private FieldNumberBiMap() { + this.nameToNumber = new HashMap(); + this.numberToName = new HashMap(); + } + + synchronized int addOrGet(String fieldName, FieldInfoBiMap fieldInfoMap, int preferredFieldNumber) { + Integer fieldNumber = nameToNumber.get(fieldName); + if (fieldNumber == null) { + if (!numberToName.containsKey(preferredFieldNumber)) { + // cool - we can use this number globally + fieldNumber = preferredFieldNumber; + } else { + fieldNumber = findNextAvailableFieldNumber(preferredFieldNumber + 1, numberToName.keySet()); + } + + numberToName.put(fieldNumber, fieldName); + nameToNumber.put(fieldName, fieldNumber); + } + + return fieldNumber; + } + + synchronized void setIfNotSet(int fieldNumber, String fieldName) { + if (!numberToName.containsKey(fieldNumber) && !nameToNumber.containsKey(fieldName)) { + numberToName.put(fieldNumber, fieldName); + nameToNumber.put(fieldName, fieldNumber); + } + } + } + + private static final class FieldInfoBiMap implements Iterable { + private final SortedMap byNumber = new TreeMap(); + private final HashMap byName = new HashMap(); + private int nextAvailableNumber = 0; + + public void put(FieldInfo fi) { + assert !byNumber.containsKey(fi.number); + assert !byName.containsKey(fi.name); + + byNumber.put(fi.number, fi); + byName.put(fi.name, fi); + } + + public FieldInfo get(String fieldName) { + return byName.get(fieldName); + } + + public FieldInfo get(int fieldNumber) { + return byNumber.get(fieldNumber); + } + + public int size() { + assert byNumber.size() == byName.size(); + return byNumber.size(); + } + + @Override + public Iterator iterator() { + return byNumber.values().iterator(); + } + } + // First used in 2.9; prior to 2.9 there was no format header public static final int FORMAT_START = -2; public static final int FORMAT_PER_FIELD_CODEC = -3; @@ -53,13 +125,20 @@ static final byte STORE_PAYLOADS = 0x20; static final byte OMIT_TERM_FREQ_AND_POSITIONS = 0x40; - private final ArrayList byNumber = new ArrayList(); - private final HashMap byName = new HashMap(); + private final FieldNumberBiMap globalFieldNumbers; + private final FieldInfoBiMap localFieldInfos; + private int format; public FieldInfos() { + this(new FieldNumberBiMap()); } + private FieldInfos(FieldNumberBiMap globalFieldNumbers) { + this.globalFieldNumbers = globalFieldNumbers; + this.localFieldInfos = new FieldInfoBiMap(); + } + /** * Construct a FieldInfos object using the directory and the name of the file * IndexInput @@ -68,6 +147,7 @@ * @throws IOException */ public FieldInfos(Directory d, String name) throws IOException { + this(new FieldNumberBiMap()); IndexInput input = d.openInput(name); try { read(input, name); @@ -76,17 +156,27 @@ } } + private static final int findNextAvailableFieldNumber(int nextPreferredNumber, Set unavailableNumbers) { + while (unavailableNumbers.contains(nextPreferredNumber)) { + nextPreferredNumber++; + } + + return nextPreferredNumber; + } + + public FieldInfos newFieldInfosWithGlobalFieldNumberMap() { + return new FieldInfos(this.globalFieldNumbers); + } + /** * Returns a deep clone of this FieldInfos instance. */ @Override synchronized public Object clone() { - FieldInfos fis = new FieldInfos(); - final int numField = byNumber.size(); - for(int i=0;i= 0) ? byNumber.get(fieldNumber) : null; + return (fieldNumber >= 0) ? localFieldInfos.get(fieldNumber) : null; } + public Iterator iterator() { + return localFieldInfos.iterator(); + } + public int size() { - return byNumber.size(); + return localFieldInfos.size(); } public boolean hasVectors() { - for (int i = 0; i < size(); i++) { - if (fieldInfo(i).storeTermVector) { + for (FieldInfo fi : this) { + if (fi.storeTermVector) { return true; } } @@ -287,8 +405,8 @@ } public boolean hasNorms() { - for (int i = 0; i < size(); i++) { - if (!fieldInfo(i).omitNorms) { + for (FieldInfo fi : this) { + if (!fi.omitNorms) { return true; } } @@ -307,8 +425,7 @@ public void write(IndexOutput output) throws IOException { output.writeVInt(FORMAT_CURRENT); output.writeVInt(size()); - for (int i = 0; i < size(); i++) { - FieldInfo fi = fieldInfo(i); + for (FieldInfo fi : this) { byte bits = 0x0; if (fi.isIndexed) bits |= IS_INDEXED; if (fi.storeTermVector) bits |= STORE_TERMVECTOR; @@ -318,7 +435,8 @@ if (fi.storePayloads) bits |= STORE_PAYLOADS; if (fi.omitTermFreqAndPositions) bits |= OMIT_TERM_FREQ_AND_POSITIONS; output.writeString(fi.name); - output.writeInt(fi.codecId); + output.writeInt(fi.number); + output.writeInt(fi.getCodecId()); output.writeByte(bits); } } @@ -338,6 +456,7 @@ for (int i = 0; i < size; i++) { String name = StringHelper.intern(input.readString()); // if this is a previous format codec 0 will be preflex! + final int fieldNumber = format <= FORMAT_PER_FIELD_CODEC? input.readInt():i; final int codecId = format <= FORMAT_PER_FIELD_CODEC? input.readInt():0; byte bits = input.readByte(); boolean isIndexed = (bits & IS_INDEXED) != 0; @@ -347,8 +466,8 @@ boolean omitNorms = (bits & OMIT_NORMS) != 0; boolean storePayloads = (bits & STORE_PAYLOADS) != 0; boolean omitTermFreqAndPositions = (bits & OMIT_TERM_FREQ_AND_POSITIONS) != 0; - final FieldInfo addInternal = addInternal(name, isIndexed, storeTermVector, storePositionsWithTermVector, storeOffsetWithTermVector, omitNorms, storePayloads, omitTermFreqAndPositions); - addInternal.codecId = codecId; + final FieldInfo addInternal = addInternal(name, fieldNumber, isIndexed, storeTermVector, storePositionsWithTermVector, storeOffsetWithTermVector, omitNorms, storePayloads, omitTermFreqAndPositions); + addInternal.setCodecId(codecId); } if (input.getFilePointer() != input.length()) { Index: src/java/org/apache/lucene/index/FieldsWriter.java =================================================================== --- src/java/org/apache/lucene/index/FieldsWriter.java (revision 1072815) +++ src/java/org/apache/lucene/index/FieldsWriter.java (working copy) @@ -45,14 +45,12 @@ // If null - we were supplied with streams, if notnull - we manage them ourselves private Directory directory; private String segment; - private FieldInfos fieldInfos; private IndexOutput fieldsStream; private IndexOutput indexStream; - FieldsWriter(Directory directory, String segment, FieldInfos fn) throws IOException { + FieldsWriter(Directory directory, String segment) throws IOException { this.directory = directory; this.segment = segment; - fieldInfos = fn; boolean success = false; try { @@ -70,10 +68,9 @@ } } - FieldsWriter(IndexOutput fdx, IndexOutput fdt, FieldInfos fn) { + FieldsWriter(IndexOutput fdx, IndexOutput fdt) { directory = null; segment = null; - fieldInfos = fn; fieldsStream = fdt; indexStream = fdx; } @@ -166,7 +163,7 @@ assert fieldsStream.getFilePointer() == position; } - final void addDocument(Document doc) throws IOException { + final void addDocument(Document doc, FieldInfos fieldInfos) throws IOException { indexStream.writeLong(fieldsStream.getFilePointer()); int storedCount = 0; Index: src/java/org/apache/lucene/index/SegmentInfo.java =================================================================== --- src/java/org/apache/lucene/index/SegmentInfo.java (revision 1072815) +++ src/java/org/apache/lucene/index/SegmentInfo.java (working copy) @@ -17,22 +17,23 @@ * limitations under the License. */ -import org.apache.lucene.store.Directory; -import org.apache.lucene.store.IndexOutput; -import org.apache.lucene.store.IndexInput; -import org.apache.lucene.util.Constants; -import org.apache.lucene.index.codecs.Codec; -import org.apache.lucene.index.codecs.CodecProvider; -import org.apache.lucene.index.codecs.DefaultSegmentInfosWriter; import java.io.IOException; -import java.util.Arrays; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.Set; -import java.util.HashSet; -import java.util.HashMap; -import java.util.ArrayList; +import org.apache.lucene.index.codecs.Codec; +import org.apache.lucene.index.codecs.CodecProvider; +import org.apache.lucene.index.codecs.DefaultSegmentInfosWriter; +import org.apache.lucene.store.Directory; +import org.apache.lucene.store.IndexInput; +import org.apache.lucene.store.IndexOutput; +import org.apache.lucene.util.Constants; + /** * Information about a segment such as it's name, directory, and files related * to the segment. @@ -41,6 +42,9 @@ */ public final class SegmentInfo { + @Deprecated + // remove with hasVector and hasProx + static final int CHECK_FIELDINFOS = -2; // hasVector and hasProx use this for bw compatibility static final int NO = -1; // e.g. no norms; no deletes; static final int YES = 1; // e.g. have norms; have deletes; static final int WITHOUT_GEN = 0; // a file name that has no GEN in it. @@ -62,7 +66,7 @@ * - NO says this field has no separate norms * >= YES says this field has separate norms with the specified generation */ - private long[] normGen; + private Map normGen; private boolean isCompoundFile; @@ -80,10 +84,16 @@ private int delCount; // How many deleted docs in this segment - private boolean hasProx; // True if this segment has any fields with omitTermFreqAndPositions==false + @Deprecated + // remove when we don't have to support old indexes anymore that had this field + private int hasProx = CHECK_FIELDINFOS; // True if this segment has any fields with omitTermFreqAndPositions==false - private boolean hasVectors; // True if this segment wrote term vectors + @Deprecated + // remove when we don't have to support old indexes anymore that had this field + private int hasVectors = CHECK_FIELDINFOS; // True if this segment wrote term vectors + private FieldInfos fieldInfos; + private SegmentCodecs segmentCodecs; private Map diagnostics; @@ -100,7 +110,7 @@ private long bufferedDeletesGen; public SegmentInfo(String name, int docCount, Directory dir, boolean isCompoundFile, - boolean hasProx, SegmentCodecs segmentCodecs, boolean hasVectors) { + SegmentCodecs segmentCodecs, FieldInfos fieldInfos) { this.name = name; this.docCount = docCount; this.dir = dir; @@ -108,18 +118,17 @@ this.isCompoundFile = isCompoundFile; this.docStoreOffset = -1; this.docStoreSegment = name; - this.hasProx = hasProx; this.segmentCodecs = segmentCodecs; - this.hasVectors = hasVectors; delCount = 0; version = Constants.LUCENE_MAIN_VERSION; + this.fieldInfos = fieldInfos; } /** * Copy everything from src SegmentInfo into our instance. */ void reset(SegmentInfo src) { - clearFiles(); + clearFilesCache(); version = src.version; name = src.name; docCount = src.docCount; @@ -130,12 +139,15 @@ docStoreIsCompoundFile = src.docStoreIsCompoundFile; hasVectors = src.hasVectors; hasProx = src.hasProx; + fieldInfos = src.fieldInfos == null ? null : (FieldInfos) src.fieldInfos.clone(); if (src.normGen == null) { normGen = null; } else { - normGen = new long[src.normGen.length]; - System.arraycopy(src.normGen, 0, normGen, 0, src.normGen.length); + normGen = new HashMap(src.normGen.size()); + for (Entry entry : src.normGen.entrySet()) { + normGen.put(entry.getKey(), entry.getValue()); } + } isCompoundFile = src.isCompoundFile; delCount = src.delCount; segmentCodecs = src.segmentCodecs; @@ -184,17 +196,35 @@ if (numNormGen == NO) { normGen = null; } else { - normGen = new long[numNormGen]; + normGen = new HashMap(); for(int j=0;j(diagnostics); if (normGen != null) { - si.normGen = normGen.clone(); + si.normGen = new HashMap(); + for (Entry entry : normGen.entrySet()) { + si.normGen.put(entry.getKey(), entry.getValue()); } + } + si.hasProx = hasProx; si.hasVectors = hasVectors; si.version = version; return si; @@ -339,9 +378,14 @@ * @param fieldNumber the field index to check */ public boolean hasSeparateNorms(int fieldNumber) { - return normGen != null && normGen[fieldNumber] != NO; + if (normGen == null) { + return false; } + Long gen = normGen.get(fieldNumber); + return gen != null && gen.longValue() != NO; + } + /** * Returns true if any fields in this segment have separate norms. */ @@ -349,7 +393,7 @@ if (normGen == null) { return false; } else { - for (long fieldNormGen : normGen) { + for (long fieldNormGen : normGen.values()) { if (fieldNormGen >= YES) { return true; } @@ -359,10 +403,9 @@ return false; } - void initNormGen(int numFields) { + void initNormGen() { if (normGen == null) { // normGen is null if this segments file hasn't had any norms set against it yet - normGen = new long[numFields]; - Arrays.fill(normGen, NO); + normGen = new HashMap(); } } @@ -373,12 +416,13 @@ * @param fieldIndex field whose norm file will be rewritten */ void advanceNormGen(int fieldIndex) { - if (normGen[fieldIndex] == NO) { - normGen[fieldIndex] = YES; + Long gen = normGen.get(fieldIndex); + if (gen == null || gen.longValue() == NO) { + normGen.put(fieldIndex, new Long(YES)); } else { - normGen[fieldIndex]++; + normGen.put(fieldIndex, gen+1); } - clearFiles(); + clearFilesCache(); } /** @@ -388,7 +432,7 @@ */ public String getNormFileName(int number) { if (hasSeparateNorms(number)) { - return IndexFileNames.fileNameFromGeneration(name, "s" + number, normGen[number]); + return IndexFileNames.fileNameFromGeneration(name, "s" + number, normGen.get(number)); } else { // single file for all norms return IndexFileNames.fileNameFromGeneration(name, IndexFileNames.NORMS_EXTENSION, WITHOUT_GEN); @@ -403,7 +447,7 @@ */ void setUseCompoundFile(boolean isCompoundFile) { this.isCompoundFile = isCompoundFile; - clearFiles(); + clearFilesCache(); } /** @@ -433,7 +477,7 @@ void setDocStoreIsCompoundFile(boolean v) { docStoreIsCompoundFile = v; - clearFiles(); + clearFilesCache(); } public String getDocStoreSegment() { @@ -446,14 +490,14 @@ void setDocStoreOffset(int offset) { docStoreOffset = offset; - clearFiles(); + clearFilesCache(); } void setDocStore(int offset, String segment, boolean isCompoundFile) { docStoreOffset = offset; docStoreSegment = segment; docStoreIsCompoundFile = isCompoundFile; - clearFiles(); + clearFilesCache(); } /** Save this segment's info. */ @@ -474,27 +518,24 @@ if (normGen == null) { output.writeInt(NO); } else { - output.writeInt(normGen.length); - for (long fieldNormGen : normGen) { - output.writeLong(fieldNormGen); + output.writeInt(normGen.size()); + for (Entry entry : normGen.entrySet()) { + output.writeInt(entry.getKey()); + output.writeLong(entry.getValue()); } } output.writeByte((byte) (isCompoundFile ? YES : NO)); output.writeInt(delCount); - output.writeByte((byte) (hasProx ? 1:0)); + output.writeByte((byte) hasProx); segmentCodecs.write(output); output.writeStringStringMap(diagnostics); - output.writeByte((byte) (hasVectors ? 1 : 0)); + output.writeByte((byte) hasVectors); } - void setHasProx(boolean hasProx) { - this.hasProx = hasProx; - clearFiles(); - } - public boolean getHasProx() { - return hasProx; + return hasProx == CHECK_FIELDINFOS ? + (fieldInfos == null ? true : fieldInfos.hasProx()) : hasProx == YES; } /** Can only be called once. */ @@ -550,7 +591,7 @@ } else { fileSet.add(IndexFileNames.segmentFileName(docStoreSegment, "", IndexFileNames.FIELDS_INDEX_EXTENSION)); fileSet.add(IndexFileNames.segmentFileName(docStoreSegment, "", IndexFileNames.FIELDS_EXTENSION)); - if (hasVectors) { + if (getHasVectors()) { fileSet.add(IndexFileNames.segmentFileName(docStoreSegment, "", IndexFileNames.VECTORS_INDEX_EXTENSION)); fileSet.add(IndexFileNames.segmentFileName(docStoreSegment, "", IndexFileNames.VECTORS_DOCUMENTS_EXTENSION)); fileSet.add(IndexFileNames.segmentFileName(docStoreSegment, "", IndexFileNames.VECTORS_FIELDS_EXTENSION)); @@ -559,7 +600,7 @@ } else if (!useCompoundFile) { fileSet.add(IndexFileNames.segmentFileName(name, "", IndexFileNames.FIELDS_INDEX_EXTENSION)); fileSet.add(IndexFileNames.segmentFileName(name, "", IndexFileNames.FIELDS_EXTENSION)); - if (hasVectors) { + if (getHasVectors()) { fileSet.add(IndexFileNames.segmentFileName(name, "", IndexFileNames.VECTORS_INDEX_EXTENSION)); fileSet.add(IndexFileNames.segmentFileName(name, "", IndexFileNames.VECTORS_DOCUMENTS_EXTENSION)); fileSet.add(IndexFileNames.segmentFileName(name, "", IndexFileNames.VECTORS_FIELDS_EXTENSION)); @@ -572,11 +613,11 @@ } if (normGen != null) { - for (int i = 0; i < normGen.length; i++) { - long gen = normGen[i]; + for (Entry entry : normGen.entrySet()) { + long gen = entry.getValue(); if (gen >= YES) { // Definitely a separate norm file, with generation: - fileSet.add(IndexFileNames.fileNameFromGeneration(name, IndexFileNames.SEPARATE_NORMS_EXTENSION + i, gen)); + fileSet.add(IndexFileNames.fileNameFromGeneration(name, IndexFileNames.SEPARATE_NORMS_EXTENSION + entry.getKey(), gen)); } } } @@ -588,7 +629,7 @@ /* Called whenever any change is made that affects which * files this segment has. */ - private void clearFiles() { + void clearFilesCache() { files = null; sizeInBytesNoStore = -1; sizeInBytesWithStore = -1; @@ -623,7 +664,7 @@ if (this.dir != dir) { s.append('x'); } - if (hasVectors) { + if (getHasVectors()) { s.append('v'); } s.append(docCount); Index: src/java/org/apache/lucene/index/InvertedDocConsumer.java =================================================================== --- src/java/org/apache/lucene/index/InvertedDocConsumer.java (revision 1072815) +++ src/java/org/apache/lucene/index/InvertedDocConsumer.java (working copy) @@ -35,10 +35,4 @@ /** Attempt to free RAM, returning true if any RAM was * freed */ abstract boolean freeRAM(); - - FieldInfos fieldInfos; - - void setFieldInfos(FieldInfos fieldInfos) { - this.fieldInfos = fieldInfos; } -} Index: src/java/org/apache/lucene/index/SegmentReader.java =================================================================== --- src/java/org/apache/lucene/index/SegmentReader.java (revision 1072815) +++ src/java/org/apache/lucene/index/SegmentReader.java (working copy) @@ -22,23 +22,22 @@ import java.util.Collection; import java.util.HashMap; import java.util.HashSet; - import java.util.List; import java.util.Map; import java.util.Set; - import java.util.concurrent.atomic.AtomicInteger; + import org.apache.lucene.document.Document; import org.apache.lucene.document.FieldSelector; +import org.apache.lucene.index.codecs.FieldsProducer; import org.apache.lucene.store.BufferedIndexInput; import org.apache.lucene.store.Directory; import org.apache.lucene.store.IndexInput; import org.apache.lucene.store.IndexOutput; import org.apache.lucene.util.BitVector; import org.apache.lucene.util.Bits; -import org.apache.lucene.util.CloseableThreadLocal; -import org.apache.lucene.index.codecs.FieldsProducer; import org.apache.lucene.util.BytesRef; +import org.apache.lucene.util.CloseableThreadLocal; /** * @lucene.experimental @@ -120,7 +119,7 @@ } cfsDir = dir0; - fieldInfos = new FieldInfos(cfsDir, IndexFileNames.segmentFileName(segment, "", IndexFileNames.FIELD_INFOS_EXTENSION)); + fieldInfos = si.getFieldInfos(); this.termsIndexDivisor = termsIndexDivisor; @@ -598,12 +597,12 @@ && (!si.hasDeletions() || this.si.getDelFileName().equals(si.getDelFileName())); boolean normsUpToDate = true; - boolean[] fieldNormsChanged = new boolean[core.fieldInfos.size()]; - final int fieldCount = core.fieldInfos.size(); - for (int i = 0; i < fieldCount; i++) { - if (!this.si.getNormFileName(i).equals(si.getNormFileName(i))) { + Set fieldNormsChanged = new HashSet(); + for (FieldInfo fi : core.fieldInfos) { + int fieldNumber = fi.number; + if (!this.si.getNormFileName(fieldNumber).equals(si.getNormFileName(fieldNumber))) { normsUpToDate = false; - fieldNormsChanged[i] = true; + fieldNormsChanged.add(fieldNumber); } } @@ -659,11 +658,10 @@ clone.norms = new HashMap(); // Clone norms - for (int i = 0; i < fieldNormsChanged.length; i++) { - + for (FieldInfo fi : core.fieldInfos) { // Clone unchanged norms to the cloned reader - if (doClone || !fieldNormsChanged[i]) { - final String curField = core.fieldInfos.fieldInfo(i).name; + if (doClone || !fieldNormsChanged.contains(fi.number)) { + final String curField = fi.name; Norm norm = this.norms.get(curField); if (norm != null) clone.norms.put(curField, (Norm) norm.clone()); @@ -735,7 +733,7 @@ } if (normsDirty) { // re-write norms - si.initNormGen(core.fieldInfos.size()); + si.initNormGen(); for (final Norm norm : norms.values()) { if (norm.dirty) { norm.reWrite(si); @@ -880,8 +878,7 @@ ensureOpen(); Set fieldSet = new HashSet(); - for (int i = 0; i < core.fieldInfos.size(); i++) { - FieldInfo fi = core.fieldInfos.fieldInfo(i); + for (FieldInfo fi : core.fieldInfos) { if (fieldOption == IndexReader.FieldOption.ALL) { fieldSet.add(fi.name); } @@ -959,8 +956,7 @@ private void openNorms(Directory cfsDir, int readBufferSize) throws IOException { long nextNormSeek = SegmentMerger.NORMS_HEADER.length; //skip header (header unused for now) int maxDoc = maxDoc(); - for (int i = 0; i < core.fieldInfos.size(); i++) { - FieldInfo fi = core.fieldInfos.fieldInfo(i); + for (FieldInfo fi : core.fieldInfos) { if (norms.containsKey(fi.name)) { // in case this SegmentReader is being re-opened, we might be able to // reuse some norm instances and skip loading them here Index: src/java/org/apache/lucene/index/DocFieldConsumer.java =================================================================== --- src/java/org/apache/lucene/index/DocFieldConsumer.java (revision 1072815) +++ src/java/org/apache/lucene/index/DocFieldConsumer.java (working copy) @@ -22,9 +22,6 @@ import java.util.Map; abstract class DocFieldConsumer { - - FieldInfos fieldInfos; - /** Called when DocumentsWriter decides to create a new * segment */ abstract void flush(Map> threadsAndFields, SegmentWriteState state) throws IOException; @@ -39,8 +36,4 @@ * The consumer should free RAM, if possible, returning * true if any RAM was in fact freed. */ abstract boolean freeRAM(); - - void setFieldInfos(FieldInfos fieldInfos) { - this.fieldInfos = fieldInfos; } -} Index: src/java/org/apache/lucene/index/DocFieldProcessorPerThread.java =================================================================== --- src/java/org/apache/lucene/index/DocFieldProcessorPerThread.java (revision 1072815) +++ src/java/org/apache/lucene/index/DocFieldProcessorPerThread.java (working copy) @@ -41,14 +41,13 @@ float docBoost; int fieldGen; final DocFieldProcessor docFieldProcessor; - final FieldInfos fieldInfos; final DocFieldConsumerPerThread consumer; // Holds all fields seen in current doc DocFieldProcessorPerField[] fields = new DocFieldProcessorPerField[1]; int fieldCount; - // Hash table for all fields ever seen + // Hash table for all fields seen in current segment DocFieldProcessorPerField[] fieldHash = new DocFieldProcessorPerField[2]; int hashMask = 1; int totalFieldCount; @@ -60,7 +59,6 @@ public DocFieldProcessorPerThread(DocumentsWriterThreadState threadState, DocFieldProcessor docFieldProcessor) throws IOException { this.docState = threadState.docState; this.docFieldProcessor = docFieldProcessor; - this.fieldInfos = docFieldProcessor.fieldInfos; this.consumer = docFieldProcessor.consumer.addThread(this); fieldsWriter = docFieldProcessor.fieldsWriter.addThread(docState); } @@ -75,6 +73,7 @@ field = next; } } + doAfterFlush(); fieldsWriter.abort(); consumer.abort(); } @@ -92,45 +91,15 @@ return fields; } - /** If there are fields we've seen but did not see again - * in the last run, then free them up. */ - - void trimFields(SegmentWriteState state) { - - for(int i=0;i codecRegistry = new IdentityHashMap(); final ArrayList codecs = new ArrayList(); - for (int i = 0; i < size; i++) { - final FieldInfo info = infos.fieldInfo(i); - if (info.isIndexed) { + for (FieldInfo fi : infos) { + if (fi.isIndexed) { final Codec fieldCodec = provider.lookup(provider - .getFieldCodec(info.name)); + .getFieldCodec(fi.name)); Integer ord = codecRegistry.get(fieldCodec); if (ord == null) { ord = Integer.valueOf(codecs.size()); codecRegistry.put(fieldCodec, ord); codecs.add(fieldCodec); } - info.codecId = ord.intValue(); + fi.setCodecId(ord.intValue()); } } return new SegmentCodecs(provider, codecs.toArray(Codec.EMPTY)); Index: src/java/org/apache/lucene/index/FieldInfo.java =================================================================== --- src/java/org/apache/lucene/index/FieldInfo.java (revision 1072815) +++ src/java/org/apache/lucene/index/FieldInfo.java (working copy) @@ -32,7 +32,7 @@ public boolean omitTermFreqAndPositions; public boolean storePayloads; // whether this field stores payloads together with term positions - int codecId = 0; // set inside SegmentCodecs#build() during segment flush - this is used to identify the codec used to write this field + private int codecId = -1; // set inside SegmentCodecs#build() during segment flush - this is used to identify the codec used to write this field FieldInfo(String na, boolean tk, int nu, boolean storeTermVector, boolean storePositionWithTermVector, boolean storeOffsetWithTermVector, @@ -57,10 +57,21 @@ } } + public void setCodecId(int codecId) { + assert this.codecId == -1 : "CodecId can only be set once."; + this.codecId = codecId; + } + + public int getCodecId() { + return codecId; + } + @Override public Object clone() { - return new FieldInfo(name, isIndexed, number, storeTermVector, storePositionWithTermVector, + FieldInfo clone = new FieldInfo(name, isIndexed, number, storeTermVector, storePositionWithTermVector, storeOffsetWithTermVector, omitNorms, storePayloads, omitTermFreqAndPositions); + clone.codecId = this.codecId; + return clone; } void update(boolean isIndexed, boolean storeTermVector, boolean storePositionWithTermVector, Index: src/java/org/apache/lucene/index/PerFieldCodecWrapper.java =================================================================== --- src/java/org/apache/lucene/index/PerFieldCodecWrapper.java (revision 1072815) +++ src/java/org/apache/lucene/index/PerFieldCodecWrapper.java (working copy) @@ -67,7 +67,7 @@ @Override public TermsConsumer addField(FieldInfo field) throws IOException { - final FieldsConsumer fields = consumers.get(field.codecId); + final FieldsConsumer fields = consumers.get(field.getCodecId()); return fields.addField(field); } @@ -100,18 +100,16 @@ public FieldsReader(Directory dir, FieldInfos fieldInfos, SegmentInfo si, int readBufferSize, int indexDivisor) throws IOException { - final int fieldCount = fieldInfos.size(); final Map producers = new HashMap(); boolean success = false; try { - for (int i = 0; i < fieldCount; i++) { - FieldInfo fi = fieldInfos.fieldInfo(i); + for (FieldInfo fi : fieldInfos) { if (fi.isIndexed) { // TODO this does not work for non-indexed fields fields.add(fi.name); - Codec codec = segmentCodecs.codecs[fi.codecId]; + Codec codec = segmentCodecs.codecs[fi.getCodecId()]; if (!producers.containsKey(codec)) { producers.put(codec, codec.fieldsProducer(new SegmentReadState(dir, - si, fieldInfos, readBufferSize, indexDivisor, ""+fi.codecId))); + si, fieldInfos, readBufferSize, indexDivisor, ""+fi.getCodecId()))); } codecs.put(fi.name, producers.get(codec)); } Index: src/java/org/apache/lucene/index/DocumentsWriter.java =================================================================== --- src/java/org/apache/lucene/index/DocumentsWriter.java (revision 1072815) +++ src/java/org/apache/lucene/index/DocumentsWriter.java (working copy) @@ -279,12 +279,13 @@ private int maxBufferedDocs = IndexWriterConfig.DEFAULT_MAX_BUFFERED_DOCS; private boolean closed; - private final FieldInfos fieldInfos; + private FieldInfos fieldInfos; private final BufferedDeletesStream bufferedDeletesStream; private final IndexWriter.FlushControl flushControl; - DocumentsWriter(Directory directory, IndexWriter writer, IndexingChain indexingChain, int maxThreadStates, FieldInfos fieldInfos, BufferedDeletesStream bufferedDeletesStream) throws IOException { + DocumentsWriter(Directory directory, IndexWriter writer, IndexingChain indexingChain, int maxThreadStates, FieldInfos fieldInfos, + BufferedDeletesStream bufferedDeletesStream) throws IOException { this.directory = directory; this.writer = writer; this.similarityProvider = writer.getConfig().getSimilarityProvider(); @@ -350,10 +351,6 @@ return doFlush; } - public FieldInfos getFieldInfos() { - return fieldInfos; - } - /** If non-null, various details of indexing are printed * here. */ synchronized void setInfoStream(PrintStream infoStream) { @@ -482,9 +479,14 @@ private void doAfterFlush() throws IOException { // All ThreadStates should be idle when we are called assert allThreadsIdle(); + for (DocumentsWriterThreadState threadState : threadStates) { + threadState.consumer.doAfterFlush(); + } + threadBindings.clear(); waitQueue.reset(); segment = null; + fieldInfos = fieldInfos.newFieldInfosWithGlobalFieldNumberMap(); numDocs = 0; nextDocID = 0; bufferIsFull = false; @@ -602,7 +604,7 @@ pendingDeletes.docIDs.clear(); } - newSegment = new SegmentInfo(segment, numDocs, directory, false, fieldInfos.hasProx(), flushState.segmentCodecs, false); + newSegment = new SegmentInfo(segment, numDocs, directory, false, flushState.segmentCodecs, fieldInfos); Collection threads = new HashSet(); for (DocumentsWriterThreadState threadState : threadStates) { @@ -613,7 +615,7 @@ consumer.flush(threads, flushState); - newSegment.setHasVectors(flushState.hasVectors); + newSegment.clearFilesCache(); if (infoStream != null) { message("new segment has " + (flushState.hasVectors ? "vectors" : "no vectors")); @@ -796,7 +798,7 @@ // work final DocWriter perDoc; try { - perDoc = state.consumer.processDocument(); + perDoc = state.consumer.processDocument(fieldInfos); } finally { docState.clear(); } Index: src/java/org/apache/lucene/index/IndexWriter.java =================================================================== --- src/java/org/apache/lucene/index/IndexWriter.java (revision 1072815) +++ src/java/org/apache/lucene/index/IndexWriter.java (working copy) @@ -38,7 +38,6 @@ import org.apache.lucene.index.IndexWriterConfig.OpenMode; import org.apache.lucene.index.PayloadProcessorProvider.DirPayloadProcessor; import org.apache.lucene.index.codecs.CodecProvider; -import org.apache.lucene.index.codecs.DefaultSegmentInfosWriter; import org.apache.lucene.search.Query; import org.apache.lucene.store.AlreadyClosedException; import org.apache.lucene.store.BufferedIndexInput; @@ -221,6 +220,7 @@ volatile long pendingCommitChangeCount; final SegmentInfos segmentInfos; // the segments + final FieldInfos fieldInfos; private DocumentsWriter docWriter; private IndexFileDeleter deleter; @@ -791,7 +791,10 @@ setRollbackSegmentInfos(segmentInfos); - docWriter = new DocumentsWriter(directory, this, conf.getIndexingChain(), conf.getMaxThreadStates(), getCurrentFieldInfos(), bufferedDeletesStream); + // start with previous field numbers, but new FieldInfos + fieldInfos = getCurrentFieldInfos(); + docWriter = new DocumentsWriter(directory, this, conf.getIndexingChain(), conf.getMaxThreadStates(), + fieldInfos.newFieldInfosWithGlobalFieldNumberMap(), bufferedDeletesStream); docWriter.setInfoStream(infoStream); // Default deleter (for backwards compatibility) is @@ -854,23 +857,14 @@ private FieldInfos getCurrentFieldInfos() throws IOException { final FieldInfos fieldInfos; if (segmentInfos.size() > 0) { - if (segmentInfos.getFormat() > DefaultSegmentInfosWriter.FORMAT_HAS_VECTORS) { - // Pre-4.0 index. In this case we sweep all - // segments, merging their FieldInfos: fieldInfos = new FieldInfos(); for(SegmentInfo info : segmentInfos) { final FieldInfos segFieldInfos = getFieldInfos(info); - final int fieldCount = segFieldInfos.size(); - for(int fieldNumber=0;fieldNumber BD final BufferedDeletesStream.ApplyDeletesResult result = bufferedDeletesStream.applyDeletes(readerPool, merge.segments); @@ -3165,7 +3159,7 @@ SegmentMerger merger = new SegmentMerger(directory, termIndexInterval, mergedName, merge, codecs, payloadProcessorProvider, - ((FieldInfos) docWriter.getFieldInfos().clone())); + merge.info.getFieldInfos()); if (infoStream != null) { message("merging " + merge.segString(directory) + " mergeVectors=" + merger.fieldInfos().hasVectors()); @@ -3174,8 +3168,9 @@ merge.readers = new ArrayList(); merge.readerClones = new ArrayList(); - merge.info.setHasVectors(merger.fieldInfos().hasVectors()); + merge.info.clearFilesCache(); + // This is try/finally to make sure merger's readers are // closed: boolean success = false; @@ -3230,7 +3225,7 @@ // because codec must know if prox was written for // this segment: //System.out.println("merger set hasProx=" + merger.hasProx() + " seg=" + merge.info.name); - merge.info.setHasProx(merger.fieldInfos().hasProx()); + merge.info.clearFilesCache(); boolean useCompoundFile; synchronized (this) { // Guard segmentInfos Index: src/java/org/apache/lucene/index/codecs/preflex/PreFlexFields.java =================================================================== --- src/java/org/apache/lucene/index/codecs/preflex/PreFlexFields.java (revision 1072815) +++ src/java/org/apache/lucene/index/codecs/preflex/PreFlexFields.java (working copy) @@ -19,14 +19,15 @@ import java.io.IOException; import java.util.Collection; -import java.util.Iterator; -import java.util.TreeMap; +import java.util.Comparator; import java.util.HashMap; +import java.util.Iterator; import java.util.Map; -import java.util.Comparator; +import java.util.TreeMap; -import org.apache.lucene.index.DocsEnum; +import org.apache.lucene.index.CompoundFileReader; import org.apache.lucene.index.DocsAndPositionsEnum; +import org.apache.lucene.index.DocsEnum; import org.apache.lucene.index.FieldInfo; import org.apache.lucene.index.FieldInfos; import org.apache.lucene.index.FieldsEnum; @@ -35,7 +36,6 @@ import org.apache.lucene.index.Term; import org.apache.lucene.index.Terms; import org.apache.lucene.index.TermsEnum; -import org.apache.lucene.index.CompoundFileReader; import org.apache.lucene.index.codecs.FieldsProducer; import org.apache.lucene.store.Directory; import org.apache.lucene.store.IndexInput; @@ -94,13 +94,11 @@ // so that if an index update removes them we'll still have them freqStream = dir.openInput(info.name + ".frq", readBufferSize); boolean anyProx = false; - final int numFields = fieldInfos.size(); - for(int i=0;i> threadsAndFields, SegmentWriteState state) throws IOException { Map> childThreadsAndFields = new HashMap>(); Index: src/java/org/apache/lucene/index/NormsWriter.java =================================================================== --- src/java/org/apache/lucene/index/NormsWriter.java (revision 1072815) +++ src/java/org/apache/lucene/index/NormsWriter.java (working copy) @@ -36,7 +36,6 @@ final class NormsWriter extends InvertedDocEndConsumer { - private FieldInfos fieldInfos; @Override public InvertedDocEndConsumerPerThread addThread(DocInverterPerThread docInverterPerThread) { return new NormsWriterPerThread(docInverterPerThread, this); @@ -48,11 +47,6 @@ // We only write the _X.nrm file at flush void files(Collection files) {} - @Override - void setFieldInfos(FieldInfos fieldInfos) { - this.fieldInfos = fieldInfos; - } - /** Produce _X.nrm if any document had a field with norms * not disabled */ @Override @@ -60,7 +54,7 @@ final Map> byField = new HashMap>(); - if (!fieldInfos.hasNorms()) { + if (!state.fieldInfos.hasNorms()) { return; } @@ -96,15 +90,10 @@ try { normsOut.writeBytes(SegmentMerger.NORMS_HEADER, 0, SegmentMerger.NORMS_HEADER.length); - final int numField = fieldInfos.size(); - int normCount = 0; - for(int fieldNumber=0;fieldNumber toMerge = byField.get(fieldInfo); + for (FieldInfo fi : state.fieldInfos) { + List toMerge = byField.get(fi); int upto = 0; if (toMerge != null) { @@ -158,7 +147,7 @@ // Fill final hole with defaultNorm for(;upto