LUCENE-2881: Track FieldInfos per segment

git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/trunk@1073110 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Michael Busch 2011-02-21 18:50:39 +00:00
parent 11bc341413
commit 26f104d35c
33 changed files with 633 additions and 335 deletions

View File

@ -160,6 +160,11 @@ Changes in Runtime Behavior
LogMergePolicy impls, and call setRequireContiguousMerge(true). LogMergePolicy impls, and call setRequireContiguousMerge(true).
(Mike McCandless) (Mike McCandless)
* LUCENE-2881: FieldInfos is now tracked per segment. Before it was tracked
per IndexWriter session, which resulted in FieldInfos that had the FieldInfo
properties from all previous segments combined. The corresponding file format
changes are backwards-compatible. (Michael Busch)
API Changes API Changes
* LUCENE-2302, LUCENE-1458, LUCENE-2111, LUCENE-2514: Terms are no longer * LUCENE-2302, LUCENE-1458, LUCENE-2111, LUCENE-2514: Terms are no longer

View File

@ -27,7 +27,8 @@ abstract class DocConsumerPerThread {
* DocumentsWriter.DocWriter and return it. * DocumentsWriter.DocWriter and return it.
* DocumentsWriter then calls finish() on this object * DocumentsWriter then calls finish() on this object
* when it's its turn. */ * when it's its turn. */
abstract DocumentsWriter.DocWriter processDocument() throws IOException; abstract DocumentsWriter.DocWriter processDocument(FieldInfos fieldInfos) throws IOException;
abstract void doAfterFlush();
abstract void abort(); abstract void abort();
} }

View File

@ -22,9 +22,6 @@ import java.util.Collection;
import java.util.Map; import java.util.Map;
abstract class DocFieldConsumer { abstract class DocFieldConsumer {
FieldInfos fieldInfos;
/** Called when DocumentsWriter decides to create a new /** Called when DocumentsWriter decides to create a new
* segment */ * segment */
abstract void flush(Map<DocFieldConsumerPerThread,Collection<DocFieldConsumerPerField>> threadsAndFields, SegmentWriteState state) throws IOException; abstract void flush(Map<DocFieldConsumerPerThread,Collection<DocFieldConsumerPerField>> threadsAndFields, SegmentWriteState state) throws IOException;
@ -39,8 +36,4 @@ abstract class DocFieldConsumer {
* The consumer should free RAM, if possible, returning * The consumer should free RAM, if possible, returning
* true if any RAM was in fact freed. */ * true if any RAM was in fact freed. */
abstract boolean freeRAM(); abstract boolean freeRAM();
void setFieldInfos(FieldInfos fieldInfos) {
this.fieldInfos = fieldInfos;
} }
}

View File

@ -34,16 +34,13 @@ import java.util.HashMap;
final class DocFieldProcessor extends DocConsumer { final class DocFieldProcessor extends DocConsumer {
final DocumentsWriter docWriter; final DocumentsWriter docWriter;
final FieldInfos fieldInfos;
final DocFieldConsumer consumer; final DocFieldConsumer consumer;
final StoredFieldsWriter fieldsWriter; final StoredFieldsWriter fieldsWriter;
public DocFieldProcessor(DocumentsWriter docWriter, DocFieldConsumer consumer) { public DocFieldProcessor(DocumentsWriter docWriter, DocFieldConsumer consumer) {
this.docWriter = docWriter; this.docWriter = docWriter;
this.consumer = consumer; this.consumer = consumer;
fieldInfos = docWriter.getFieldInfos(); fieldsWriter = new StoredFieldsWriter(docWriter);
consumer.setFieldInfos(fieldInfos);
fieldsWriter = new StoredFieldsWriter(docWriter, fieldInfos);
} }
@Override @Override
@ -53,7 +50,6 @@ final class DocFieldProcessor extends DocConsumer {
for ( DocConsumerPerThread thread : threads) { for ( DocConsumerPerThread thread : threads) {
DocFieldProcessorPerThread perThread = (DocFieldProcessorPerThread) thread; DocFieldProcessorPerThread perThread = (DocFieldProcessorPerThread) thread;
childThreadsAndFields.put(perThread.consumer, perThread.fields()); childThreadsAndFields.put(perThread.consumer, perThread.fields());
perThread.trimFields(state);
} }
fieldsWriter.flush(state); fieldsWriter.flush(state);
consumer.flush(childThreadsAndFields, state); consumer.flush(childThreadsAndFields, state);
@ -63,7 +59,7 @@ final class DocFieldProcessor extends DocConsumer {
// FreqProxTermsWriter does this with // FreqProxTermsWriter does this with
// FieldInfo.storePayload. // FieldInfo.storePayload.
final String fileName = IndexFileNames.segmentFileName(state.segmentName, "", IndexFileNames.FIELD_INFOS_EXTENSION); final String fileName = IndexFileNames.segmentFileName(state.segmentName, "", IndexFileNames.FIELD_INFOS_EXTENSION);
fieldInfos.write(state.directory, fileName); state.fieldInfos.write(state.directory, fileName);
} }
@Override @Override

View File

@ -41,14 +41,13 @@ final class DocFieldProcessorPerThread extends DocConsumerPerThread {
float docBoost; float docBoost;
int fieldGen; int fieldGen;
final DocFieldProcessor docFieldProcessor; final DocFieldProcessor docFieldProcessor;
final FieldInfos fieldInfos;
final DocFieldConsumerPerThread consumer; final DocFieldConsumerPerThread consumer;
// Holds all fields seen in current doc // Holds all fields seen in current doc
DocFieldProcessorPerField[] fields = new DocFieldProcessorPerField[1]; DocFieldProcessorPerField[] fields = new DocFieldProcessorPerField[1];
int fieldCount; int fieldCount;
// Hash table for all fields ever seen // Hash table for all fields seen in current segment
DocFieldProcessorPerField[] fieldHash = new DocFieldProcessorPerField[2]; DocFieldProcessorPerField[] fieldHash = new DocFieldProcessorPerField[2];
int hashMask = 1; int hashMask = 1;
int totalFieldCount; int totalFieldCount;
@ -60,7 +59,6 @@ final class DocFieldProcessorPerThread extends DocConsumerPerThread {
public DocFieldProcessorPerThread(DocumentsWriterThreadState threadState, DocFieldProcessor docFieldProcessor) throws IOException { public DocFieldProcessorPerThread(DocumentsWriterThreadState threadState, DocFieldProcessor docFieldProcessor) throws IOException {
this.docState = threadState.docState; this.docState = threadState.docState;
this.docFieldProcessor = docFieldProcessor; this.docFieldProcessor = docFieldProcessor;
this.fieldInfos = docFieldProcessor.fieldInfos;
this.consumer = docFieldProcessor.consumer.addThread(this); this.consumer = docFieldProcessor.consumer.addThread(this);
fieldsWriter = docFieldProcessor.fieldsWriter.addThread(docState); fieldsWriter = docFieldProcessor.fieldsWriter.addThread(docState);
} }
@ -75,6 +73,7 @@ final class DocFieldProcessorPerThread extends DocConsumerPerThread {
field = next; field = next;
} }
} }
doAfterFlush();
fieldsWriter.abort(); fieldsWriter.abort();
consumer.abort(); consumer.abort();
} }
@ -92,45 +91,15 @@ final class DocFieldProcessorPerThread extends DocConsumerPerThread {
return fields; return fields;
} }
/** If there are fields we've seen but did not see again /** In flush we reset the fieldHash to not maintain per-field state
* in the last run, then free them up. */ * across segments */
@Override
void trimFields(SegmentWriteState state) { void doAfterFlush() {
fieldHash = new DocFieldProcessorPerField[2];
for(int i=0;i<fieldHash.length;i++) { hashMask = 1;
DocFieldProcessorPerField perField = fieldHash[i]; totalFieldCount = 0;
DocFieldProcessorPerField lastPerField = null;
while (perField != null) {
if (perField.lastGen == -1) {
// This field was not seen since the previous
// flush, so, free up its resources now
// Unhash
if (lastPerField == null)
fieldHash[i] = perField.next;
else
lastPerField.next = perField.next;
if (state.infoStream != null) {
state.infoStream.println(" purge field=" + perField.fieldInfo.name);
} }
totalFieldCount--;
} else {
// Reset
perField.lastGen = -1;
lastPerField = perField;
}
perField = perField.next;
}
}
}
private void rehash() { private void rehash() {
final int newHashSize = (fieldHash.length*2); final int newHashSize = (fieldHash.length*2);
assert newHashSize > fieldHash.length; assert newHashSize > fieldHash.length;
@ -155,7 +124,7 @@ final class DocFieldProcessorPerThread extends DocConsumerPerThread {
} }
@Override @Override
public DocumentsWriter.DocWriter processDocument() throws IOException { public DocumentsWriter.DocWriter processDocument(FieldInfos fieldInfos) throws IOException {
consumer.startDocument(); consumer.startDocument();
fieldsWriter.startDocument(); fieldsWriter.startDocument();

View File

@ -39,13 +39,6 @@ final class DocInverter extends DocFieldConsumer {
this.endConsumer = endConsumer; this.endConsumer = endConsumer;
} }
@Override
void setFieldInfos(FieldInfos fieldInfos) {
super.setFieldInfos(fieldInfos);
consumer.setFieldInfos(fieldInfos);
endConsumer.setFieldInfos(fieldInfos);
}
@Override @Override
void flush(Map<DocFieldConsumerPerThread, Collection<DocFieldConsumerPerField>> threadsAndFields, SegmentWriteState state) throws IOException { void flush(Map<DocFieldConsumerPerThread, Collection<DocFieldConsumerPerField>> threadsAndFields, SegmentWriteState state) throws IOException {

View File

@ -279,12 +279,13 @@ final class DocumentsWriter {
private int maxBufferedDocs = IndexWriterConfig.DEFAULT_MAX_BUFFERED_DOCS; private int maxBufferedDocs = IndexWriterConfig.DEFAULT_MAX_BUFFERED_DOCS;
private boolean closed; private boolean closed;
private final FieldInfos fieldInfos; private FieldInfos fieldInfos;
private final BufferedDeletesStream bufferedDeletesStream; private final BufferedDeletesStream bufferedDeletesStream;
private final IndexWriter.FlushControl flushControl; 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.directory = directory;
this.writer = writer; this.writer = writer;
this.similarityProvider = writer.getConfig().getSimilarityProvider(); this.similarityProvider = writer.getConfig().getSimilarityProvider();
@ -350,10 +351,6 @@ final class DocumentsWriter {
return doFlush; return doFlush;
} }
public FieldInfos getFieldInfos() {
return fieldInfos;
}
/** If non-null, various details of indexing are printed /** If non-null, various details of indexing are printed
* here. */ * here. */
synchronized void setInfoStream(PrintStream infoStream) { synchronized void setInfoStream(PrintStream infoStream) {
@ -482,9 +479,14 @@ final class DocumentsWriter {
private void doAfterFlush() throws IOException { private void doAfterFlush() throws IOException {
// All ThreadStates should be idle when we are called // All ThreadStates should be idle when we are called
assert allThreadsIdle(); assert allThreadsIdle();
for (DocumentsWriterThreadState threadState : threadStates) {
threadState.consumer.doAfterFlush();
}
threadBindings.clear(); threadBindings.clear();
waitQueue.reset(); waitQueue.reset();
segment = null; segment = null;
fieldInfos = fieldInfos.newFieldInfosWithGlobalFieldNumberMap();
numDocs = 0; numDocs = 0;
nextDocID = 0; nextDocID = 0;
bufferIsFull = false; bufferIsFull = false;
@ -602,7 +604,7 @@ final class DocumentsWriter {
pendingDeletes.docIDs.clear(); 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<DocConsumerPerThread> threads = new HashSet<DocConsumerPerThread>(); Collection<DocConsumerPerThread> threads = new HashSet<DocConsumerPerThread>();
for (DocumentsWriterThreadState threadState : threadStates) { for (DocumentsWriterThreadState threadState : threadStates) {
@ -613,7 +615,7 @@ final class DocumentsWriter {
consumer.flush(threads, flushState); consumer.flush(threads, flushState);
newSegment.setHasVectors(flushState.hasVectors); newSegment.clearFilesCache();
if (infoStream != null) { if (infoStream != null) {
message("new segment has " + (flushState.hasVectors ? "vectors" : "no vectors")); message("new segment has " + (flushState.hasVectors ? "vectors" : "no vectors"));
@ -796,7 +798,7 @@ final class DocumentsWriter {
// work // work
final DocWriter perDoc; final DocWriter perDoc;
try { try {
perDoc = state.consumer.processDocument(); perDoc = state.consumer.processDocument(fieldInfos);
} finally { } finally {
docState.clear(); docState.clear();
} }

View File

@ -32,7 +32,7 @@ public final class FieldInfo {
public boolean omitTermFreqAndPositions; public boolean omitTermFreqAndPositions;
public boolean storePayloads; // whether this field stores payloads together with term positions 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, FieldInfo(String na, boolean tk, int nu, boolean storeTermVector,
boolean storePositionWithTermVector, boolean storeOffsetWithTermVector, boolean storePositionWithTermVector, boolean storeOffsetWithTermVector,
@ -57,10 +57,21 @@ public final class FieldInfo {
} }
} }
public void setCodecId(int codecId) {
assert this.codecId == -1 : "CodecId can only be set once.";
this.codecId = codecId;
}
public int getCodecId() {
return codecId;
}
@Override @Override
public Object clone() { public Object clone() {
return new FieldInfo(name, isIndexed, number, storeTermVector, storePositionWithTermVector, FieldInfo clone = new FieldInfo(name, isIndexed, number, storeTermVector, storePositionWithTermVector,
storeOffsetWithTermVector, omitNorms, storePayloads, omitTermFreqAndPositions); storeOffsetWithTermVector, omitNorms, storePayloads, omitTermFreqAndPositions);
clone.codecId = this.codecId;
return clone;
} }
void update(boolean isIndexed, boolean storeTermVector, boolean storePositionWithTermVector, void update(boolean isIndexed, boolean storeTermVector, boolean storePositionWithTermVector,

View File

@ -17,6 +17,16 @@ package org.apache.lucene.index;
* limitations under the License. * limitations under the License.
*/ */
import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import org.apache.lucene.document.Document; import org.apache.lucene.document.Document;
import org.apache.lucene.document.Fieldable; import org.apache.lucene.document.Fieldable;
import org.apache.lucene.store.Directory; import org.apache.lucene.store.Directory;
@ -24,9 +34,6 @@ import org.apache.lucene.store.IndexInput;
import org.apache.lucene.store.IndexOutput; import org.apache.lucene.store.IndexOutput;
import org.apache.lucene.util.StringHelper; import org.apache.lucene.util.StringHelper;
import java.io.IOException;
import java.util.*;
/** Access to the Fieldable Info file that describes document fields and whether or /** Access to the Fieldable Info file that describes document fields and whether or
* not they are indexed. Each segment has a separate Fieldable Info file. Objects * not they are indexed. Each segment has a separate Fieldable Info file. Objects
* of this class are thread-safe for multiple readers, but only one thread can * of this class are thread-safe for multiple readers, but only one thread can
@ -34,7 +41,72 @@ import java.util.*;
* accessing this object. * accessing this object.
* @lucene.experimental * @lucene.experimental
*/ */
public final class FieldInfos { public final class FieldInfos implements Iterable<FieldInfo> {
private static final class FieldNumberBiMap {
private final Map<Integer,String> numberToName;
private final Map<String,Integer> nameToNumber;
private FieldNumberBiMap() {
this.nameToNumber = new HashMap<String, Integer>();
this.numberToName = new HashMap<Integer, String>();
}
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<FieldInfo> {
private final SortedMap<Integer,FieldInfo> byNumber = new TreeMap<Integer,FieldInfo>();
private final HashMap<String,FieldInfo> byName = new HashMap<String,FieldInfo>();
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<FieldInfo> iterator() {
return byNumber.values().iterator();
}
}
// First used in 2.9; prior to 2.9 there was no format header // 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_START = -2;
@ -53,11 +125,18 @@ public final class FieldInfos {
static final byte STORE_PAYLOADS = 0x20; static final byte STORE_PAYLOADS = 0x20;
static final byte OMIT_TERM_FREQ_AND_POSITIONS = 0x40; static final byte OMIT_TERM_FREQ_AND_POSITIONS = 0x40;
private final ArrayList<FieldInfo> byNumber = new ArrayList<FieldInfo>(); private final FieldNumberBiMap globalFieldNumbers;
private final HashMap<String,FieldInfo> byName = new HashMap<String,FieldInfo>(); private final FieldInfoBiMap localFieldInfos;
private int format; private int format;
public FieldInfos() { public FieldInfos() {
this(new FieldNumberBiMap());
}
private FieldInfos(FieldNumberBiMap globalFieldNumbers) {
this.globalFieldNumbers = globalFieldNumbers;
this.localFieldInfos = new FieldInfoBiMap();
} }
/** /**
@ -68,6 +147,7 @@ public final class FieldInfos {
* @throws IOException * @throws IOException
*/ */
public FieldInfos(Directory d, String name) throws IOException { public FieldInfos(Directory d, String name) throws IOException {
this(new FieldNumberBiMap());
IndexInput input = d.openInput(name); IndexInput input = d.openInput(name);
try { try {
read(input, name); read(input, name);
@ -76,17 +156,27 @@ public final class FieldInfos {
} }
} }
private static final int findNextAvailableFieldNumber(int nextPreferredNumber, Set<Integer> unavailableNumbers) {
while (unavailableNumbers.contains(nextPreferredNumber)) {
nextPreferredNumber++;
}
return nextPreferredNumber;
}
public FieldInfos newFieldInfosWithGlobalFieldNumberMap() {
return new FieldInfos(this.globalFieldNumbers);
}
/** /**
* Returns a deep clone of this FieldInfos instance. * Returns a deep clone of this FieldInfos instance.
*/ */
@Override @Override
synchronized public Object clone() { synchronized public Object clone() {
FieldInfos fis = new FieldInfos(); FieldInfos fis = new FieldInfos(globalFieldNumbers);
final int numField = byNumber.size(); for (FieldInfo fi : this) {
for(int i=0;i<numField;i++) { FieldInfo clone = (FieldInfo) (fi).clone();
FieldInfo fi = (FieldInfo) ( byNumber.get(i)).clone(); fis.localFieldInfos.put(clone);
fis.byNumber.add(fi);
fis.byName.put(fi.name, fi);
} }
return fis; return fis;
} }
@ -102,9 +192,7 @@ public final class FieldInfos {
/** Returns true if any fields do not omitTermFreqAndPositions */ /** Returns true if any fields do not omitTermFreqAndPositions */
public boolean hasProx() { public boolean hasProx() {
final int numFields = byNumber.size(); for (FieldInfo fi : this) {
for(int i=0;i<numFields;i++) {
final FieldInfo fi = fieldInfo(i);
if (fi.isIndexed && !fi.omitTermFreqAndPositions) { if (fi.isIndexed && !fi.omitTermFreqAndPositions) {
return true; return true;
} }
@ -215,9 +303,28 @@ public final class FieldInfos {
synchronized public FieldInfo add(String name, boolean isIndexed, boolean storeTermVector, synchronized public FieldInfo add(String name, boolean isIndexed, boolean storeTermVector,
boolean storePositionWithTermVector, boolean storeOffsetWithTermVector, boolean storePositionWithTermVector, boolean storeOffsetWithTermVector,
boolean omitNorms, boolean storePayloads, boolean omitTermFreqAndPositions) { boolean omitNorms, boolean storePayloads, boolean omitTermFreqAndPositions) {
return addOrUpdateInternal(name, -1, isIndexed, storeTermVector, storePositionWithTermVector,
storeOffsetWithTermVector, omitNorms, storePayloads, omitTermFreqAndPositions);
}
synchronized private FieldInfo addOrUpdateInternal(String name, int preferredFieldNumber, boolean isIndexed,
boolean storeTermVector, boolean storePositionWithTermVector, boolean storeOffsetWithTermVector,
boolean omitNorms, boolean storePayloads, boolean omitTermFreqAndPositions) {
FieldInfo fi = fieldInfo(name); FieldInfo fi = fieldInfo(name);
if (fi == null) { if (fi == null) {
return addInternal(name, isIndexed, storeTermVector, storePositionWithTermVector, storeOffsetWithTermVector, omitNorms, storePayloads, omitTermFreqAndPositions); if (preferredFieldNumber == -1) {
preferredFieldNumber = findNextAvailableFieldNumber(localFieldInfos.nextAvailableNumber, localFieldInfos.byNumber.keySet());
localFieldInfos.nextAvailableNumber = preferredFieldNumber;
}
// get a global number for this field
int fieldNumber = globalFieldNumbers.addOrGet(name, localFieldInfos, preferredFieldNumber);
if (localFieldInfos.get(fieldNumber) != null) {
// fall back if the global number is already taken
fieldNumber = preferredFieldNumber;
}
return addInternal(name, fieldNumber, isIndexed, storeTermVector, storePositionWithTermVector, storeOffsetWithTermVector, omitNorms, storePayloads, omitTermFreqAndPositions);
} else { } else {
fi.update(isIndexed, storeTermVector, storePositionWithTermVector, storeOffsetWithTermVector, omitNorms, storePayloads, omitTermFreqAndPositions); fi.update(isIndexed, storeTermVector, storePositionWithTermVector, storeOffsetWithTermVector, omitNorms, storePayloads, omitTermFreqAndPositions);
} }
@ -225,20 +332,27 @@ public final class FieldInfos {
} }
synchronized public FieldInfo add(FieldInfo fi) { synchronized public FieldInfo add(FieldInfo fi) {
return add(fi.name, fi.isIndexed, fi.storeTermVector, int preferredFieldNumber = fi.number;
FieldInfo other = localFieldInfos.get(preferredFieldNumber);
if (other == null || !other.name.equals(fi.name)) {
preferredFieldNumber = -1;
}
return addOrUpdateInternal(fi.name, preferredFieldNumber, fi.isIndexed, fi.storeTermVector,
fi.storePositionWithTermVector, fi.storeOffsetWithTermVector, fi.storePositionWithTermVector, fi.storeOffsetWithTermVector,
fi.omitNorms, fi.storePayloads, fi.omitNorms, fi.storePayloads,
fi.omitTermFreqAndPositions); fi.omitTermFreqAndPositions);
} }
private FieldInfo addInternal(String name, boolean isIndexed, private FieldInfo addInternal(String name, int fieldNumber, boolean isIndexed,
boolean storeTermVector, boolean storePositionWithTermVector, boolean storeTermVector, boolean storePositionWithTermVector,
boolean storeOffsetWithTermVector, boolean omitNorms, boolean storePayloads, boolean omitTermFreqAndPositions) { boolean storeOffsetWithTermVector, boolean omitNorms, boolean storePayloads, boolean omitTermFreqAndPositions) {
name = StringHelper.intern(name); name = StringHelper.intern(name);
FieldInfo fi = new FieldInfo(name, isIndexed, byNumber.size(), storeTermVector, storePositionWithTermVector, globalFieldNumbers.setIfNotSet(fieldNumber, name);
FieldInfo fi = new FieldInfo(name, isIndexed, fieldNumber, storeTermVector, storePositionWithTermVector,
storeOffsetWithTermVector, omitNorms, storePayloads, omitTermFreqAndPositions); storeOffsetWithTermVector, omitNorms, storePayloads, omitTermFreqAndPositions);
byNumber.add(fi);
byName.put(name, fi); assert localFieldInfos.get(fi.number) == null;
localFieldInfos.put(fi);
return fi; return fi;
} }
@ -248,7 +362,7 @@ public final class FieldInfos {
} }
public FieldInfo fieldInfo(String fieldName) { public FieldInfo fieldInfo(String fieldName) {
return byName.get(fieldName); return localFieldInfos.get(fieldName);
} }
/** /**
@ -270,16 +384,20 @@ public final class FieldInfos {
* doesn't exist. * doesn't exist.
*/ */
public FieldInfo fieldInfo(int fieldNumber) { public FieldInfo fieldInfo(int fieldNumber) {
return (fieldNumber >= 0) ? byNumber.get(fieldNumber) : null; return (fieldNumber >= 0) ? localFieldInfos.get(fieldNumber) : null;
}
public Iterator<FieldInfo> iterator() {
return localFieldInfos.iterator();
} }
public int size() { public int size() {
return byNumber.size(); return localFieldInfos.size();
} }
public boolean hasVectors() { public boolean hasVectors() {
for (int i = 0; i < size(); i++) { for (FieldInfo fi : this) {
if (fieldInfo(i).storeTermVector) { if (fi.storeTermVector) {
return true; return true;
} }
} }
@ -287,8 +405,8 @@ public final class FieldInfos {
} }
public boolean hasNorms() { public boolean hasNorms() {
for (int i = 0; i < size(); i++) { for (FieldInfo fi : this) {
if (!fieldInfo(i).omitNorms) { if (!fi.omitNorms) {
return true; return true;
} }
} }
@ -307,8 +425,7 @@ public final class FieldInfos {
public void write(IndexOutput output) throws IOException { public void write(IndexOutput output) throws IOException {
output.writeVInt(FORMAT_CURRENT); output.writeVInt(FORMAT_CURRENT);
output.writeVInt(size()); output.writeVInt(size());
for (int i = 0; i < size(); i++) { for (FieldInfo fi : this) {
FieldInfo fi = fieldInfo(i);
byte bits = 0x0; byte bits = 0x0;
if (fi.isIndexed) bits |= IS_INDEXED; if (fi.isIndexed) bits |= IS_INDEXED;
if (fi.storeTermVector) bits |= STORE_TERMVECTOR; if (fi.storeTermVector) bits |= STORE_TERMVECTOR;
@ -318,7 +435,8 @@ public final class FieldInfos {
if (fi.storePayloads) bits |= STORE_PAYLOADS; if (fi.storePayloads) bits |= STORE_PAYLOADS;
if (fi.omitTermFreqAndPositions) bits |= OMIT_TERM_FREQ_AND_POSITIONS; if (fi.omitTermFreqAndPositions) bits |= OMIT_TERM_FREQ_AND_POSITIONS;
output.writeString(fi.name); output.writeString(fi.name);
output.writeInt(fi.codecId); output.writeInt(fi.number);
output.writeInt(fi.getCodecId());
output.writeByte(bits); output.writeByte(bits);
} }
} }
@ -338,6 +456,7 @@ public final class FieldInfos {
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
String name = StringHelper.intern(input.readString()); String name = StringHelper.intern(input.readString());
// if this is a previous format codec 0 will be preflex! // 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; final int codecId = format <= FORMAT_PER_FIELD_CODEC? input.readInt():0;
byte bits = input.readByte(); byte bits = input.readByte();
boolean isIndexed = (bits & IS_INDEXED) != 0; boolean isIndexed = (bits & IS_INDEXED) != 0;
@ -347,8 +466,8 @@ public final class FieldInfos {
boolean omitNorms = (bits & OMIT_NORMS) != 0; boolean omitNorms = (bits & OMIT_NORMS) != 0;
boolean storePayloads = (bits & STORE_PAYLOADS) != 0; boolean storePayloads = (bits & STORE_PAYLOADS) != 0;
boolean omitTermFreqAndPositions = (bits & OMIT_TERM_FREQ_AND_POSITIONS) != 0; boolean omitTermFreqAndPositions = (bits & OMIT_TERM_FREQ_AND_POSITIONS) != 0;
final FieldInfo addInternal = addInternal(name, isIndexed, storeTermVector, storePositionsWithTermVector, storeOffsetWithTermVector, omitNorms, storePayloads, omitTermFreqAndPositions); final FieldInfo addInternal = addInternal(name, fieldNumber, isIndexed, storeTermVector, storePositionsWithTermVector, storeOffsetWithTermVector, omitNorms, storePayloads, omitTermFreqAndPositions);
addInternal.codecId = codecId; addInternal.setCodecId(codecId);
} }
if (input.getFilePointer() != input.length()) { if (input.getFilePointer() != input.length()) {

View File

@ -45,14 +45,12 @@ final class FieldsWriter {
// If null - we were supplied with streams, if notnull - we manage them ourselves // If null - we were supplied with streams, if notnull - we manage them ourselves
private Directory directory; private Directory directory;
private String segment; private String segment;
private FieldInfos fieldInfos;
private IndexOutput fieldsStream; private IndexOutput fieldsStream;
private IndexOutput indexStream; private IndexOutput indexStream;
FieldsWriter(Directory directory, String segment, FieldInfos fn) throws IOException { FieldsWriter(Directory directory, String segment) throws IOException {
this.directory = directory; this.directory = directory;
this.segment = segment; this.segment = segment;
fieldInfos = fn;
boolean success = false; boolean success = false;
try { try {
@ -70,10 +68,9 @@ final class FieldsWriter {
} }
} }
FieldsWriter(IndexOutput fdx, IndexOutput fdt, FieldInfos fn) { FieldsWriter(IndexOutput fdx, IndexOutput fdt) {
directory = null; directory = null;
segment = null; segment = null;
fieldInfos = fn;
fieldsStream = fdt; fieldsStream = fdt;
indexStream = fdx; indexStream = fdx;
} }
@ -166,7 +163,7 @@ final class FieldsWriter {
assert fieldsStream.getFilePointer() == position; assert fieldsStream.getFilePointer() == position;
} }
final void addDocument(Document doc) throws IOException { final void addDocument(Document doc, FieldInfos fieldInfos) throws IOException {
indexStream.writeLong(fieldsStream.getFilePointer()); indexStream.writeLong(fieldsStream.getFilePointer());
int storedCount = 0; int storedCount = 0;

View File

@ -38,7 +38,6 @@ import org.apache.lucene.document.Document;
import org.apache.lucene.index.IndexWriterConfig.OpenMode; import org.apache.lucene.index.IndexWriterConfig.OpenMode;
import org.apache.lucene.index.PayloadProcessorProvider.DirPayloadProcessor; import org.apache.lucene.index.PayloadProcessorProvider.DirPayloadProcessor;
import org.apache.lucene.index.codecs.CodecProvider; import org.apache.lucene.index.codecs.CodecProvider;
import org.apache.lucene.index.codecs.DefaultSegmentInfosWriter;
import org.apache.lucene.search.Query; import org.apache.lucene.search.Query;
import org.apache.lucene.store.AlreadyClosedException; import org.apache.lucene.store.AlreadyClosedException;
import org.apache.lucene.store.BufferedIndexInput; import org.apache.lucene.store.BufferedIndexInput;
@ -221,6 +220,7 @@ public class IndexWriter implements Closeable {
volatile long pendingCommitChangeCount; volatile long pendingCommitChangeCount;
final SegmentInfos segmentInfos; // the segments final SegmentInfos segmentInfos; // the segments
final FieldInfos fieldInfos;
private DocumentsWriter docWriter; private DocumentsWriter docWriter;
private IndexFileDeleter deleter; private IndexFileDeleter deleter;
@ -791,7 +791,10 @@ public class IndexWriter implements Closeable {
setRollbackSegmentInfos(segmentInfos); 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); docWriter.setInfoStream(infoStream);
// Default deleter (for backwards compatibility) is // Default deleter (for backwards compatibility) is
@ -854,23 +857,14 @@ public class IndexWriter implements Closeable {
private FieldInfos getCurrentFieldInfos() throws IOException { private FieldInfos getCurrentFieldInfos() throws IOException {
final FieldInfos fieldInfos; final FieldInfos fieldInfos;
if (segmentInfos.size() > 0) { 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(); fieldInfos = new FieldInfos();
for(SegmentInfo info : segmentInfos) { for(SegmentInfo info : segmentInfos) {
final FieldInfos segFieldInfos = getFieldInfos(info); final FieldInfos segFieldInfos = getFieldInfos(info);
final int fieldCount = segFieldInfos.size(); for (FieldInfo fi : segFieldInfos) {
for(int fieldNumber=0;fieldNumber<fieldCount;fieldNumber++) { fieldInfos.add(fi);
fieldInfos.add(segFieldInfos.fieldInfo(fieldNumber));
} }
} }
} else { } else {
// Already a 4.0 index; just seed the FieldInfos
// from the last segment
fieldInfos = getFieldInfos(segmentInfos.info(segmentInfos.size()-1));
}
} else {
fieldInfos = new FieldInfos(); fieldInfos = new FieldInfos();
} }
return fieldInfos; return fieldInfos;
@ -2272,7 +2266,7 @@ public class IndexWriter implements Closeable {
String mergedName = newSegmentName(); String mergedName = newSegmentName();
SegmentMerger merger = new SegmentMerger(directory, termIndexInterval, SegmentMerger merger = new SegmentMerger(directory, termIndexInterval,
mergedName, null, codecs, payloadProcessorProvider, mergedName, null, codecs, payloadProcessorProvider,
((FieldInfos) docWriter.getFieldInfos().clone())); fieldInfos.newFieldInfosWithGlobalFieldNumberMap());
for (IndexReader reader : readers) // add new indexes for (IndexReader reader : readers) // add new indexes
merger.add(reader); merger.add(reader);
@ -2280,8 +2274,8 @@ public class IndexWriter implements Closeable {
int docCount = merger.merge(); // merge 'em int docCount = merger.merge(); // merge 'em
SegmentInfo info = new SegmentInfo(mergedName, docCount, directory, SegmentInfo info = new SegmentInfo(mergedName, docCount, directory,
false, merger.fieldInfos().hasProx(), merger.getSegmentCodecs(), false, merger.getSegmentCodecs(),
merger.fieldInfos().hasVectors()); merger.fieldInfos());
setDiagnostics(info, "addIndexes(IndexReader...)"); setDiagnostics(info, "addIndexes(IndexReader...)");
boolean useCompoundFile; boolean useCompoundFile;
@ -3014,7 +3008,7 @@ public class IndexWriter implements Closeable {
// Bind a new segment name here so even with // Bind a new segment name here so even with
// ConcurrentMergePolicy we keep deterministic segment // ConcurrentMergePolicy we keep deterministic segment
// names. // names.
merge.info = new SegmentInfo(newSegmentName(), 0, directory, false, false, null, false); merge.info = new SegmentInfo(newSegmentName(), 0, directory, false, null, fieldInfos.newFieldInfosWithGlobalFieldNumberMap());
// Lock order: IW -> BD // Lock order: IW -> BD
final BufferedDeletesStream.ApplyDeletesResult result = bufferedDeletesStream.applyDeletes(readerPool, merge.segments); final BufferedDeletesStream.ApplyDeletesResult result = bufferedDeletesStream.applyDeletes(readerPool, merge.segments);
@ -3165,7 +3159,7 @@ public class IndexWriter implements Closeable {
SegmentMerger merger = new SegmentMerger(directory, termIndexInterval, mergedName, merge, SegmentMerger merger = new SegmentMerger(directory, termIndexInterval, mergedName, merge,
codecs, payloadProcessorProvider, codecs, payloadProcessorProvider,
((FieldInfos) docWriter.getFieldInfos().clone())); merge.info.getFieldInfos());
if (infoStream != null) { if (infoStream != null) {
message("merging " + merge.segString(directory) + " mergeVectors=" + merger.fieldInfos().hasVectors()); message("merging " + merge.segString(directory) + " mergeVectors=" + merger.fieldInfos().hasVectors());
@ -3174,7 +3168,8 @@ public class IndexWriter implements Closeable {
merge.readers = new ArrayList<SegmentReader>(); merge.readers = new ArrayList<SegmentReader>();
merge.readerClones = new ArrayList<SegmentReader>(); merge.readerClones = new ArrayList<SegmentReader>();
merge.info.setHasVectors(merger.fieldInfos().hasVectors()); merge.info.clearFilesCache();
// This is try/finally to make sure merger's readers are // This is try/finally to make sure merger's readers are
// closed: // closed:
@ -3230,7 +3225,7 @@ public class IndexWriter implements Closeable {
// because codec must know if prox was written for // because codec must know if prox was written for
// this segment: // this segment:
//System.out.println("merger set hasProx=" + merger.hasProx() + " seg=" + merge.info.name); //System.out.println("merger set hasProx=" + merger.hasProx() + " seg=" + merge.info.name);
merge.info.setHasProx(merger.fieldInfos().hasProx()); merge.info.clearFilesCache();
boolean useCompoundFile; boolean useCompoundFile;
synchronized (this) { // Guard segmentInfos synchronized (this) { // Guard segmentInfos

View File

@ -35,10 +35,4 @@ abstract class InvertedDocConsumer {
/** Attempt to free RAM, returning true if any RAM was /** Attempt to free RAM, returning true if any RAM was
* freed */ * freed */
abstract boolean freeRAM(); abstract boolean freeRAM();
FieldInfos fieldInfos;
void setFieldInfos(FieldInfos fieldInfos) {
this.fieldInfos = fieldInfos;
} }
}

View File

@ -25,5 +25,4 @@ abstract class InvertedDocEndConsumer {
abstract InvertedDocEndConsumerPerThread addThread(DocInverterPerThread docInverterPerThread); abstract InvertedDocEndConsumerPerThread addThread(DocInverterPerThread docInverterPerThread);
abstract void flush(Map<InvertedDocEndConsumerPerThread,Collection<InvertedDocEndConsumerPerField>> threadsAndFields, SegmentWriteState state) throws IOException; abstract void flush(Map<InvertedDocEndConsumerPerThread,Collection<InvertedDocEndConsumerPerField>> threadsAndFields, SegmentWriteState state) throws IOException;
abstract void abort(); abstract void abort();
abstract void setFieldInfos(FieldInfos fieldInfos);
} }

View File

@ -36,7 +36,6 @@ import org.apache.lucene.store.IndexOutput;
final class NormsWriter extends InvertedDocEndConsumer { final class NormsWriter extends InvertedDocEndConsumer {
private FieldInfos fieldInfos;
@Override @Override
public InvertedDocEndConsumerPerThread addThread(DocInverterPerThread docInverterPerThread) { public InvertedDocEndConsumerPerThread addThread(DocInverterPerThread docInverterPerThread) {
return new NormsWriterPerThread(docInverterPerThread, this); return new NormsWriterPerThread(docInverterPerThread, this);
@ -48,11 +47,6 @@ final class NormsWriter extends InvertedDocEndConsumer {
// We only write the _X.nrm file at flush // We only write the _X.nrm file at flush
void files(Collection<String> files) {} void files(Collection<String> files) {}
@Override
void setFieldInfos(FieldInfos fieldInfos) {
this.fieldInfos = fieldInfos;
}
/** Produce _X.nrm if any document had a field with norms /** Produce _X.nrm if any document had a field with norms
* not disabled */ * not disabled */
@Override @Override
@ -60,7 +54,7 @@ final class NormsWriter extends InvertedDocEndConsumer {
final Map<FieldInfo,List<NormsWriterPerField>> byField = new HashMap<FieldInfo,List<NormsWriterPerField>>(); final Map<FieldInfo,List<NormsWriterPerField>> byField = new HashMap<FieldInfo,List<NormsWriterPerField>>();
if (!fieldInfos.hasNorms()) { if (!state.fieldInfos.hasNorms()) {
return; return;
} }
@ -96,15 +90,10 @@ final class NormsWriter extends InvertedDocEndConsumer {
try { try {
normsOut.writeBytes(SegmentMerger.NORMS_HEADER, 0, SegmentMerger.NORMS_HEADER.length); normsOut.writeBytes(SegmentMerger.NORMS_HEADER, 0, SegmentMerger.NORMS_HEADER.length);
final int numField = fieldInfos.size();
int normCount = 0; int normCount = 0;
for(int fieldNumber=0;fieldNumber<numField;fieldNumber++) { for (FieldInfo fi : state.fieldInfos) {
List<NormsWriterPerField> toMerge = byField.get(fi);
final FieldInfo fieldInfo = fieldInfos.fieldInfo(fieldNumber);
List<NormsWriterPerField> toMerge = byField.get(fieldInfo);
int upto = 0; int upto = 0;
if (toMerge != null) { if (toMerge != null) {
@ -158,7 +147,7 @@ final class NormsWriter extends InvertedDocEndConsumer {
// Fill final hole with defaultNorm // Fill final hole with defaultNorm
for(;upto<state.numDocs;upto++) for(;upto<state.numDocs;upto++)
normsOut.writeByte((byte) 0); normsOut.writeByte((byte) 0);
} else if (fieldInfo.isIndexed && !fieldInfo.omitNorms) { } else if (fi.isIndexed && !fi.omitNorms) {
normCount++; normCount++;
// Fill entire field with default norm: // Fill entire field with default norm:
for(;upto<state.numDocs;upto++) for(;upto<state.numDocs;upto++)

View File

@ -67,7 +67,7 @@ final class PerFieldCodecWrapper extends Codec {
@Override @Override
public TermsConsumer addField(FieldInfo field) throws IOException { 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); return fields.addField(field);
} }
@ -100,18 +100,16 @@ final class PerFieldCodecWrapper extends Codec {
public FieldsReader(Directory dir, FieldInfos fieldInfos, SegmentInfo si, public FieldsReader(Directory dir, FieldInfos fieldInfos, SegmentInfo si,
int readBufferSize, int indexDivisor) throws IOException { int readBufferSize, int indexDivisor) throws IOException {
final int fieldCount = fieldInfos.size();
final Map<Codec, FieldsProducer> producers = new HashMap<Codec, FieldsProducer>(); final Map<Codec, FieldsProducer> producers = new HashMap<Codec, FieldsProducer>();
boolean success = false; boolean success = false;
try { try {
for (int i = 0; i < fieldCount; i++) { for (FieldInfo fi : fieldInfos) {
FieldInfo fi = fieldInfos.fieldInfo(i);
if (fi.isIndexed) { // TODO this does not work for non-indexed fields if (fi.isIndexed) { // TODO this does not work for non-indexed fields
fields.add(fi.name); fields.add(fi.name);
Codec codec = segmentCodecs.codecs[fi.codecId]; Codec codec = segmentCodecs.codecs[fi.getCodecId()];
if (!producers.containsKey(codec)) { if (!producers.containsKey(codec)) {
producers.put(codec, codec.fieldsProducer(new SegmentReadState(dir, 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)); codecs.put(fi.name, producers.get(codec));
} }

View File

@ -74,22 +74,20 @@ final class SegmentCodecs implements Cloneable {
} }
static SegmentCodecs build(FieldInfos infos, CodecProvider provider) { static SegmentCodecs build(FieldInfos infos, CodecProvider provider) {
final int size = infos.size();
final Map<Codec, Integer> codecRegistry = new IdentityHashMap<Codec, Integer>(); final Map<Codec, Integer> codecRegistry = new IdentityHashMap<Codec, Integer>();
final ArrayList<Codec> codecs = new ArrayList<Codec>(); final ArrayList<Codec> codecs = new ArrayList<Codec>();
for (int i = 0; i < size; i++) { for (FieldInfo fi : infos) {
final FieldInfo info = infos.fieldInfo(i); if (fi.isIndexed) {
if (info.isIndexed) {
final Codec fieldCodec = provider.lookup(provider final Codec fieldCodec = provider.lookup(provider
.getFieldCodec(info.name)); .getFieldCodec(fi.name));
Integer ord = codecRegistry.get(fieldCodec); Integer ord = codecRegistry.get(fieldCodec);
if (ord == null) { if (ord == null) {
ord = Integer.valueOf(codecs.size()); ord = Integer.valueOf(codecs.size());
codecRegistry.put(fieldCodec, ord); codecRegistry.put(fieldCodec, ord);
codecs.add(fieldCodec); codecs.add(fieldCodec);
} }
info.codecId = ord.intValue(); fi.setCodecId(ord.intValue());
} }
} }
return new SegmentCodecs(provider, codecs.toArray(Codec.EMPTY)); return new SegmentCodecs(provider, codecs.toArray(Codec.EMPTY));

View File

@ -17,21 +17,22 @@ package org.apache.lucene.index;
* limitations under the License. * limitations under the License.
*/ */
import org.apache.lucene.store.Directory; import java.io.IOException;
import org.apache.lucene.store.IndexOutput; import java.util.ArrayList;
import org.apache.lucene.store.IndexInput; import java.util.HashMap;
import org.apache.lucene.util.Constants; import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.apache.lucene.index.codecs.Codec; import org.apache.lucene.index.codecs.Codec;
import org.apache.lucene.index.codecs.CodecProvider; import org.apache.lucene.index.codecs.CodecProvider;
import org.apache.lucene.index.codecs.DefaultSegmentInfosWriter; import org.apache.lucene.index.codecs.DefaultSegmentInfosWriter;
import java.io.IOException; import org.apache.lucene.store.Directory;
import java.util.Arrays; import org.apache.lucene.store.IndexInput;
import java.util.List; import org.apache.lucene.store.IndexOutput;
import java.util.Map; import org.apache.lucene.util.Constants;
import java.util.Set;
import java.util.HashSet;
import java.util.HashMap;
import java.util.ArrayList;
/** /**
* Information about a segment such as it's name, directory, and files related * Information about a segment such as it's name, directory, and files related
@ -41,6 +42,9 @@ import java.util.ArrayList;
*/ */
public final class SegmentInfo { 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 NO = -1; // e.g. no norms; no deletes;
static final int YES = 1; // e.g. have norms; have 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. static final int WITHOUT_GEN = 0; // a file name that has no GEN in it.
@ -62,7 +66,7 @@ public final class SegmentInfo {
* - NO says this field has no separate norms * - NO says this field has no separate norms
* >= YES says this field has separate norms with the specified generation * >= YES says this field has separate norms with the specified generation
*/ */
private long[] normGen; private Map<Integer,Long> normGen;
private boolean isCompoundFile; private boolean isCompoundFile;
@ -80,9 +84,15 @@ public final class SegmentInfo {
private int delCount; // How many deleted docs in this segment 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 SegmentCodecs segmentCodecs;
@ -100,7 +110,7 @@ public final class SegmentInfo {
private long bufferedDeletesGen; private long bufferedDeletesGen;
public SegmentInfo(String name, int docCount, Directory dir, boolean isCompoundFile, public SegmentInfo(String name, int docCount, Directory dir, boolean isCompoundFile,
boolean hasProx, SegmentCodecs segmentCodecs, boolean hasVectors) { SegmentCodecs segmentCodecs, FieldInfos fieldInfos) {
this.name = name; this.name = name;
this.docCount = docCount; this.docCount = docCount;
this.dir = dir; this.dir = dir;
@ -108,18 +118,17 @@ public final class SegmentInfo {
this.isCompoundFile = isCompoundFile; this.isCompoundFile = isCompoundFile;
this.docStoreOffset = -1; this.docStoreOffset = -1;
this.docStoreSegment = name; this.docStoreSegment = name;
this.hasProx = hasProx;
this.segmentCodecs = segmentCodecs; this.segmentCodecs = segmentCodecs;
this.hasVectors = hasVectors;
delCount = 0; delCount = 0;
version = Constants.LUCENE_MAIN_VERSION; version = Constants.LUCENE_MAIN_VERSION;
this.fieldInfos = fieldInfos;
} }
/** /**
* Copy everything from src SegmentInfo into our instance. * Copy everything from src SegmentInfo into our instance.
*/ */
void reset(SegmentInfo src) { void reset(SegmentInfo src) {
clearFiles(); clearFilesCache();
version = src.version; version = src.version;
name = src.name; name = src.name;
docCount = src.docCount; docCount = src.docCount;
@ -130,11 +139,14 @@ public final class SegmentInfo {
docStoreIsCompoundFile = src.docStoreIsCompoundFile; docStoreIsCompoundFile = src.docStoreIsCompoundFile;
hasVectors = src.hasVectors; hasVectors = src.hasVectors;
hasProx = src.hasProx; hasProx = src.hasProx;
fieldInfos = src.fieldInfos == null ? null : (FieldInfos) src.fieldInfos.clone();
if (src.normGen == null) { if (src.normGen == null) {
normGen = null; normGen = null;
} else { } else {
normGen = new long[src.normGen.length]; normGen = new HashMap<Integer, Long>(src.normGen.size());
System.arraycopy(src.normGen, 0, normGen, 0, src.normGen.length); for (Entry<Integer,Long> entry : src.normGen.entrySet()) {
normGen.put(entry.getKey(), entry.getValue());
}
} }
isCompoundFile = src.isCompoundFile; isCompoundFile = src.isCompoundFile;
delCount = src.delCount; delCount = src.delCount;
@ -184,17 +196,35 @@ public final class SegmentInfo {
if (numNormGen == NO) { if (numNormGen == NO) {
normGen = null; normGen = null;
} else { } else {
normGen = new long[numNormGen]; normGen = new HashMap<Integer, Long>();
for(int j=0;j<numNormGen;j++) { for(int j=0;j<numNormGen;j++) {
normGen[j] = input.readLong(); int fieldNumber = j;
if (format <= DefaultSegmentInfosWriter.FORMAT_4_0) {
fieldNumber = input.readInt();
} }
normGen.put(fieldNumber, input.readLong());
}
} }
isCompoundFile = input.readByte() == YES; isCompoundFile = input.readByte() == YES;
Directory dir0 = dir;
if (isCompoundFile) {
dir0 = new CompoundFileReader(dir, IndexFileNames.segmentFileName(name, "", IndexFileNames.COMPOUND_FILE_EXTENSION));
}
try {
fieldInfos = new FieldInfos(dir0, IndexFileNames.segmentFileName(name, "", IndexFileNames.FIELD_INFOS_EXTENSION));
} finally {
if (dir != dir0) {
dir0.close();
}
}
delCount = input.readInt(); delCount = input.readInt();
assert delCount <= docCount; assert delCount <= docCount;
hasProx = input.readByte() == YES; hasProx = input.readByte();
// System.out.println(Thread.currentThread().getName() + ": si.read hasProx=" + hasProx + " seg=" + name); // System.out.println(Thread.currentThread().getName() + ": si.read hasProx=" + hasProx + " seg=" + name);
segmentCodecs = new SegmentCodecs(codecs); segmentCodecs = new SegmentCodecs(codecs);
@ -208,7 +238,7 @@ public final class SegmentInfo {
diagnostics = input.readStringStringMap(); diagnostics = input.readStringStringMap();
if (format <= DefaultSegmentInfosWriter.FORMAT_HAS_VECTORS) { if (format <= DefaultSegmentInfosWriter.FORMAT_HAS_VECTORS) {
hasVectors = input.readByte() == 1; hasVectors = input.readByte();
} else { } else {
final String storesSegment; final String storesSegment;
final String ext; final String ext;
@ -229,7 +259,11 @@ public final class SegmentInfo {
dirToTest = dir; dirToTest = dir;
} }
try { try {
hasVectors = dirToTest.fileExists(IndexFileNames.segmentFileName(storesSegment, "", IndexFileNames.VECTORS_INDEX_EXTENSION)); if (dirToTest.fileExists(IndexFileNames.segmentFileName(storesSegment, "", IndexFileNames.VECTORS_INDEX_EXTENSION))) {
hasVectors = YES;
} else {
hasVectors = NO;
}
} finally { } finally {
if (isCompoundFile) { if (isCompoundFile) {
dirToTest.close(); dirToTest.close();
@ -274,13 +308,13 @@ public final class SegmentInfo {
} }
} }
public boolean getHasVectors() throws IOException { public boolean getHasVectors() {
return hasVectors; return hasVectors == CHECK_FIELDINFOS ?
(fieldInfos == null ? true : fieldInfos.hasVectors()) : hasVectors == YES;
} }
public void setHasVectors(boolean v) { public FieldInfos getFieldInfos() {
hasVectors = v; return fieldInfos;
clearFiles();
} }
public boolean hasDeletions() { public boolean hasDeletions() {
@ -298,17 +332,18 @@ public final class SegmentInfo {
} else { } else {
delGen++; delGen++;
} }
clearFiles(); clearFilesCache();
} }
void clearDelGen() { void clearDelGen() {
delGen = NO; delGen = NO;
clearFiles(); clearFilesCache();
} }
@Override @Override
public Object clone() { public Object clone() {
SegmentInfo si = new SegmentInfo(name, docCount, dir, isCompoundFile, hasProx, segmentCodecs, false); SegmentInfo si = new SegmentInfo(name, docCount, dir, isCompoundFile, segmentCodecs,
fieldInfos == null ? null : (FieldInfos) fieldInfos.clone());
si.docStoreOffset = docStoreOffset; si.docStoreOffset = docStoreOffset;
si.docStoreSegment = docStoreSegment; si.docStoreSegment = docStoreSegment;
si.docStoreIsCompoundFile = docStoreIsCompoundFile; si.docStoreIsCompoundFile = docStoreIsCompoundFile;
@ -316,8 +351,12 @@ public final class SegmentInfo {
si.delCount = delCount; si.delCount = delCount;
si.diagnostics = new HashMap<String, String>(diagnostics); si.diagnostics = new HashMap<String, String>(diagnostics);
if (normGen != null) { if (normGen != null) {
si.normGen = normGen.clone(); si.normGen = new HashMap<Integer, Long>();
for (Entry<Integer,Long> entry : normGen.entrySet()) {
si.normGen.put(entry.getKey(), entry.getValue());
} }
}
si.hasProx = hasProx;
si.hasVectors = hasVectors; si.hasVectors = hasVectors;
si.version = version; si.version = version;
return si; return si;
@ -339,7 +378,12 @@ public final class SegmentInfo {
* @param fieldNumber the field index to check * @param fieldNumber the field index to check
*/ */
public boolean hasSeparateNorms(int fieldNumber) { 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;
} }
/** /**
@ -349,7 +393,7 @@ public final class SegmentInfo {
if (normGen == null) { if (normGen == null) {
return false; return false;
} else { } else {
for (long fieldNormGen : normGen) { for (long fieldNormGen : normGen.values()) {
if (fieldNormGen >= YES) { if (fieldNormGen >= YES) {
return true; return true;
} }
@ -359,10 +403,9 @@ public final class SegmentInfo {
return false; 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 if (normGen == null) { // normGen is null if this segments file hasn't had any norms set against it yet
normGen = new long[numFields]; normGen = new HashMap<Integer, Long>();
Arrays.fill(normGen, NO);
} }
} }
@ -373,12 +416,13 @@ public final class SegmentInfo {
* @param fieldIndex field whose norm file will be rewritten * @param fieldIndex field whose norm file will be rewritten
*/ */
void advanceNormGen(int fieldIndex) { void advanceNormGen(int fieldIndex) {
if (normGen[fieldIndex] == NO) { Long gen = normGen.get(fieldIndex);
normGen[fieldIndex] = YES; if (gen == null || gen.longValue() == NO) {
normGen.put(fieldIndex, new Long(YES));
} else { } else {
normGen[fieldIndex]++; normGen.put(fieldIndex, gen+1);
} }
clearFiles(); clearFilesCache();
} }
/** /**
@ -388,7 +432,7 @@ public final class SegmentInfo {
*/ */
public String getNormFileName(int number) { public String getNormFileName(int number) {
if (hasSeparateNorms(number)) { if (hasSeparateNorms(number)) {
return IndexFileNames.fileNameFromGeneration(name, "s" + number, normGen[number]); return IndexFileNames.fileNameFromGeneration(name, "s" + number, normGen.get(number));
} else { } else {
// single file for all norms // single file for all norms
return IndexFileNames.fileNameFromGeneration(name, IndexFileNames.NORMS_EXTENSION, WITHOUT_GEN); return IndexFileNames.fileNameFromGeneration(name, IndexFileNames.NORMS_EXTENSION, WITHOUT_GEN);
@ -403,7 +447,7 @@ public final class SegmentInfo {
*/ */
void setUseCompoundFile(boolean isCompoundFile) { void setUseCompoundFile(boolean isCompoundFile) {
this.isCompoundFile = isCompoundFile; this.isCompoundFile = isCompoundFile;
clearFiles(); clearFilesCache();
} }
/** /**
@ -433,7 +477,7 @@ public final class SegmentInfo {
void setDocStoreIsCompoundFile(boolean v) { void setDocStoreIsCompoundFile(boolean v) {
docStoreIsCompoundFile = v; docStoreIsCompoundFile = v;
clearFiles(); clearFilesCache();
} }
public String getDocStoreSegment() { public String getDocStoreSegment() {
@ -446,14 +490,14 @@ public final class SegmentInfo {
void setDocStoreOffset(int offset) { void setDocStoreOffset(int offset) {
docStoreOffset = offset; docStoreOffset = offset;
clearFiles(); clearFilesCache();
} }
void setDocStore(int offset, String segment, boolean isCompoundFile) { void setDocStore(int offset, String segment, boolean isCompoundFile) {
docStoreOffset = offset; docStoreOffset = offset;
docStoreSegment = segment; docStoreSegment = segment;
docStoreIsCompoundFile = isCompoundFile; docStoreIsCompoundFile = isCompoundFile;
clearFiles(); clearFilesCache();
} }
/** Save this segment's info. */ /** Save this segment's info. */
@ -474,27 +518,24 @@ public final class SegmentInfo {
if (normGen == null) { if (normGen == null) {
output.writeInt(NO); output.writeInt(NO);
} else { } else {
output.writeInt(normGen.length); output.writeInt(normGen.size());
for (long fieldNormGen : normGen) { for (Entry<Integer,Long> entry : normGen.entrySet()) {
output.writeLong(fieldNormGen); output.writeInt(entry.getKey());
output.writeLong(entry.getValue());
} }
} }
output.writeByte((byte) (isCompoundFile ? YES : NO)); output.writeByte((byte) (isCompoundFile ? YES : NO));
output.writeInt(delCount); output.writeInt(delCount);
output.writeByte((byte) (hasProx ? 1:0)); output.writeByte((byte) hasProx);
segmentCodecs.write(output); segmentCodecs.write(output);
output.writeStringStringMap(diagnostics); output.writeStringStringMap(diagnostics);
output.writeByte((byte) (hasVectors ? 1 : 0)); output.writeByte((byte) hasVectors);
}
void setHasProx(boolean hasProx) {
this.hasProx = hasProx;
clearFiles();
} }
public boolean getHasProx() { public boolean getHasProx() {
return hasProx; return hasProx == CHECK_FIELDINFOS ?
(fieldInfos == null ? true : fieldInfos.hasProx()) : hasProx == YES;
} }
/** Can only be called once. */ /** Can only be called once. */
@ -550,7 +591,7 @@ public final class SegmentInfo {
} else { } else {
fileSet.add(IndexFileNames.segmentFileName(docStoreSegment, "", IndexFileNames.FIELDS_INDEX_EXTENSION)); fileSet.add(IndexFileNames.segmentFileName(docStoreSegment, "", IndexFileNames.FIELDS_INDEX_EXTENSION));
fileSet.add(IndexFileNames.segmentFileName(docStoreSegment, "", IndexFileNames.FIELDS_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_INDEX_EXTENSION));
fileSet.add(IndexFileNames.segmentFileName(docStoreSegment, "", IndexFileNames.VECTORS_DOCUMENTS_EXTENSION)); fileSet.add(IndexFileNames.segmentFileName(docStoreSegment, "", IndexFileNames.VECTORS_DOCUMENTS_EXTENSION));
fileSet.add(IndexFileNames.segmentFileName(docStoreSegment, "", IndexFileNames.VECTORS_FIELDS_EXTENSION)); fileSet.add(IndexFileNames.segmentFileName(docStoreSegment, "", IndexFileNames.VECTORS_FIELDS_EXTENSION));
@ -559,7 +600,7 @@ public final class SegmentInfo {
} else if (!useCompoundFile) { } else if (!useCompoundFile) {
fileSet.add(IndexFileNames.segmentFileName(name, "", IndexFileNames.FIELDS_INDEX_EXTENSION)); fileSet.add(IndexFileNames.segmentFileName(name, "", IndexFileNames.FIELDS_INDEX_EXTENSION));
fileSet.add(IndexFileNames.segmentFileName(name, "", IndexFileNames.FIELDS_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_INDEX_EXTENSION));
fileSet.add(IndexFileNames.segmentFileName(name, "", IndexFileNames.VECTORS_DOCUMENTS_EXTENSION)); fileSet.add(IndexFileNames.segmentFileName(name, "", IndexFileNames.VECTORS_DOCUMENTS_EXTENSION));
fileSet.add(IndexFileNames.segmentFileName(name, "", IndexFileNames.VECTORS_FIELDS_EXTENSION)); fileSet.add(IndexFileNames.segmentFileName(name, "", IndexFileNames.VECTORS_FIELDS_EXTENSION));
@ -572,11 +613,11 @@ public final class SegmentInfo {
} }
if (normGen != null) { if (normGen != null) {
for (int i = 0; i < normGen.length; i++) { for (Entry<Integer,Long> entry : normGen.entrySet()) {
long gen = normGen[i]; long gen = entry.getValue();
if (gen >= YES) { if (gen >= YES) {
// Definitely a separate norm file, with generation: // 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 @@ public final class SegmentInfo {
/* Called whenever any change is made that affects which /* Called whenever any change is made that affects which
* files this segment has. */ * files this segment has. */
private void clearFiles() { void clearFilesCache() {
files = null; files = null;
sizeInBytesNoStore = -1; sizeInBytesNoStore = -1;
sizeInBytesWithStore = -1; sizeInBytesWithStore = -1;
@ -623,7 +664,7 @@ public final class SegmentInfo {
if (this.dir != dir) { if (this.dir != dir) {
s.append('x'); s.append('x');
} }
if (hasVectors) { if (getHasVectors()) {
s.append('v'); s.append('v');
} }
s.append(docCount); s.append(docCount);

View File

@ -17,26 +17,26 @@ package org.apache.lucene.index;
* limitations under the License. * limitations under the License.
*/ */
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintStream;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Vector;
import org.apache.lucene.index.codecs.CodecProvider;
import org.apache.lucene.index.codecs.SegmentInfosReader;
import org.apache.lucene.index.codecs.SegmentInfosWriter;
import org.apache.lucene.store.Directory; import org.apache.lucene.store.Directory;
import org.apache.lucene.store.IndexInput; import org.apache.lucene.store.IndexInput;
import org.apache.lucene.store.IndexOutput; import org.apache.lucene.store.IndexOutput;
import org.apache.lucene.store.NoSuchDirectoryException; import org.apache.lucene.store.NoSuchDirectoryException;
import org.apache.lucene.index.codecs.CodecProvider;
import org.apache.lucene.index.codecs.SegmentInfosReader;
import org.apache.lucene.index.codecs.SegmentInfosWriter;
import org.apache.lucene.util.ThreadInterruptedException; import org.apache.lucene.util.ThreadInterruptedException;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintStream;
import java.util.Vector;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.HashMap;
import java.util.Map;
/** /**
* A collection of segmentInfo objects with methods for operating on * A collection of segmentInfo objects with methods for operating on
* those segments in relation to the file system. * those segments in relation to the file system.

View File

@ -26,16 +26,16 @@ import java.util.List;
import org.apache.lucene.document.Document; import org.apache.lucene.document.Document;
import org.apache.lucene.index.IndexReader.FieldOption; import org.apache.lucene.index.IndexReader.FieldOption;
import org.apache.lucene.index.MergePolicy.MergeAbortedException; import org.apache.lucene.index.MergePolicy.MergeAbortedException;
import org.apache.lucene.index.codecs.CodecProvider;
import org.apache.lucene.index.codecs.Codec; import org.apache.lucene.index.codecs.Codec;
import org.apache.lucene.index.codecs.MergeState; import org.apache.lucene.index.codecs.CodecProvider;
import org.apache.lucene.index.codecs.FieldsConsumer; import org.apache.lucene.index.codecs.FieldsConsumer;
import org.apache.lucene.index.codecs.MergeState;
import org.apache.lucene.store.Directory; import org.apache.lucene.store.Directory;
import org.apache.lucene.store.IndexInput; import org.apache.lucene.store.IndexInput;
import org.apache.lucene.store.IndexOutput; import org.apache.lucene.store.IndexOutput;
import org.apache.lucene.util.Bits; import org.apache.lucene.util.Bits;
import org.apache.lucene.util.ReaderUtil;
import org.apache.lucene.util.MultiBits; import org.apache.lucene.util.MultiBits;
import org.apache.lucene.util.ReaderUtil;
/** /**
* The SegmentMerger class combines two or more Segments, represented by an IndexReader ({@link #add}, * The SegmentMerger class combines two or more Segments, represented by an IndexReader ({@link #add},
@ -75,8 +75,8 @@ final class SegmentMerger {
this.payloadProcessorProvider = payloadProcessorProvider; this.payloadProcessorProvider = payloadProcessorProvider;
directory = dir; directory = dir;
this.codecs = codecs; this.codecs = codecs;
this.fieldInfos = fieldInfos;
segment = name; segment = name;
this.fieldInfos = fieldInfos;
if (merge != null) { if (merge != null) {
checkAbort = new MergeState.CheckAbort(merge, directory); checkAbort = new MergeState.CheckAbort(merge, directory);
} else { } else {
@ -180,9 +180,8 @@ final class SegmentMerger {
SegmentReader segmentReader = (SegmentReader) reader; SegmentReader segmentReader = (SegmentReader) reader;
boolean same = true; boolean same = true;
FieldInfos segmentFieldInfos = segmentReader.fieldInfos(); FieldInfos segmentFieldInfos = segmentReader.fieldInfos();
int numFieldInfos = segmentFieldInfos.size(); for (FieldInfo fi : segmentFieldInfos) {
for (int j = 0; same && j < numFieldInfos; j++) { same = fieldInfos.fieldName(fi.number).equals(fi.name);
same = fieldInfos.fieldName(j).equals(segmentFieldInfos.fieldName(j));
} }
if (same) { if (same) {
matchingSegmentReaders[i] = segmentReader; matchingSegmentReaders[i] = segmentReader;
@ -208,9 +207,8 @@ final class SegmentMerger {
if (reader instanceof SegmentReader) { if (reader instanceof SegmentReader) {
SegmentReader segmentReader = (SegmentReader) reader; SegmentReader segmentReader = (SegmentReader) reader;
FieldInfos readerFieldInfos = segmentReader.fieldInfos(); FieldInfos readerFieldInfos = segmentReader.fieldInfos();
int numReaderFieldInfos = readerFieldInfos.size(); for (FieldInfo fi : readerFieldInfos) {
for (int j = 0; j < numReaderFieldInfos; j++) { fieldInfos.add(fi);
fieldInfos.add(readerFieldInfos.fieldInfo(j));
} }
} else { } else {
addIndexed(reader, fieldInfos, reader.getFieldNames(FieldOption.TERMVECTOR_WITH_POSITION_OFFSET), true, true, true, false, false); addIndexed(reader, fieldInfos, reader.getFieldNames(FieldOption.TERMVECTOR_WITH_POSITION_OFFSET), true, true, true, false, false);
@ -224,13 +222,13 @@ final class SegmentMerger {
} }
} }
final SegmentCodecs codecInfo = SegmentCodecs.build(fieldInfos, this.codecs); final SegmentCodecs codecInfo = SegmentCodecs.build(fieldInfos, this.codecs);
fieldInfos.write(directory, segment + ".fnm"); fieldInfos.write(directory, segment + "." + IndexFileNames.FIELD_INFOS_EXTENSION);
int docCount = 0; int docCount = 0;
setMatchingSegmentReaders(); setMatchingSegmentReaders();
final FieldsWriter fieldsWriter = new FieldsWriter(directory, segment, fieldInfos); final FieldsWriter fieldsWriter = new FieldsWriter(directory, segment);
try { try {
int idx = 0; int idx = 0;
@ -312,7 +310,7 @@ final class SegmentMerger {
// NOTE: it's very important to first assign to doc then pass it to // NOTE: it's very important to first assign to doc then pass it to
// termVectorsWriter.addAllDocVectors; see LUCENE-1282 // termVectorsWriter.addAllDocVectors; see LUCENE-1282
Document doc = reader.document(j); Document doc = reader.document(j);
fieldsWriter.addDocument(doc); fieldsWriter.addDocument(doc, fieldInfos);
docCount++; docCount++;
checkAbort.work(300); checkAbort.work(300);
} }
@ -339,7 +337,7 @@ final class SegmentMerger {
// NOTE: it's very important to first assign to doc then pass it to // NOTE: it's very important to first assign to doc then pass it to
// termVectorsWriter.addAllDocVectors; see LUCENE-1282 // termVectorsWriter.addAllDocVectors; see LUCENE-1282
Document doc = reader.document(docCount); Document doc = reader.document(docCount);
fieldsWriter.addDocument(doc); fieldsWriter.addDocument(doc, fieldInfos);
checkAbort.work(300); checkAbort.work(300);
} }
} }
@ -574,8 +572,7 @@ final class SegmentMerger {
private void mergeNorms() throws IOException { private void mergeNorms() throws IOException {
IndexOutput output = null; IndexOutput output = null;
try { try {
for (int i = 0, numFieldInfos = fieldInfos.size(); i < numFieldInfos; i++) { for (FieldInfo fi : fieldInfos) {
final FieldInfo fi = fieldInfos.fieldInfo(i);
if (fi.isIndexed && !fi.omitNorms) { if (fi.isIndexed && !fi.omitNorms) {
if (output == null) { if (output == null) {
output = directory.createOutput(IndexFileNames.segmentFileName(segment, "", IndexFileNames.NORMS_EXTENSION)); output = directory.createOutput(IndexFileNames.segmentFileName(segment, "", IndexFileNames.NORMS_EXTENSION));

View File

@ -22,23 +22,22 @@ import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import org.apache.lucene.document.Document; import org.apache.lucene.document.Document;
import org.apache.lucene.document.FieldSelector; import org.apache.lucene.document.FieldSelector;
import org.apache.lucene.index.codecs.FieldsProducer;
import org.apache.lucene.store.BufferedIndexInput; import org.apache.lucene.store.BufferedIndexInput;
import org.apache.lucene.store.Directory; import org.apache.lucene.store.Directory;
import org.apache.lucene.store.IndexInput; import org.apache.lucene.store.IndexInput;
import org.apache.lucene.store.IndexOutput; import org.apache.lucene.store.IndexOutput;
import org.apache.lucene.util.BitVector; import org.apache.lucene.util.BitVector;
import org.apache.lucene.util.Bits; 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.BytesRef;
import org.apache.lucene.util.CloseableThreadLocal;
/** /**
* @lucene.experimental * @lucene.experimental
@ -120,7 +119,7 @@ public class SegmentReader extends IndexReader implements Cloneable {
} }
cfsDir = dir0; cfsDir = dir0;
fieldInfos = new FieldInfos(cfsDir, IndexFileNames.segmentFileName(segment, "", IndexFileNames.FIELD_INFOS_EXTENSION)); fieldInfos = si.getFieldInfos();
this.termsIndexDivisor = termsIndexDivisor; this.termsIndexDivisor = termsIndexDivisor;
@ -598,12 +597,12 @@ public class SegmentReader extends IndexReader implements Cloneable {
&& (!si.hasDeletions() || this.si.getDelFileName().equals(si.getDelFileName())); && (!si.hasDeletions() || this.si.getDelFileName().equals(si.getDelFileName()));
boolean normsUpToDate = true; boolean normsUpToDate = true;
boolean[] fieldNormsChanged = new boolean[core.fieldInfos.size()]; Set<Integer> fieldNormsChanged = new HashSet<Integer>();
final int fieldCount = core.fieldInfos.size(); for (FieldInfo fi : core.fieldInfos) {
for (int i = 0; i < fieldCount; i++) { int fieldNumber = fi.number;
if (!this.si.getNormFileName(i).equals(si.getNormFileName(i))) { if (!this.si.getNormFileName(fieldNumber).equals(si.getNormFileName(fieldNumber))) {
normsUpToDate = false; normsUpToDate = false;
fieldNormsChanged[i] = true; fieldNormsChanged.add(fieldNumber);
} }
} }
@ -659,11 +658,10 @@ public class SegmentReader extends IndexReader implements Cloneable {
clone.norms = new HashMap<String,Norm>(); clone.norms = new HashMap<String,Norm>();
// Clone norms // Clone norms
for (int i = 0; i < fieldNormsChanged.length; i++) { for (FieldInfo fi : core.fieldInfos) {
// Clone unchanged norms to the cloned reader // Clone unchanged norms to the cloned reader
if (doClone || !fieldNormsChanged[i]) { if (doClone || !fieldNormsChanged.contains(fi.number)) {
final String curField = core.fieldInfos.fieldInfo(i).name; final String curField = fi.name;
Norm norm = this.norms.get(curField); Norm norm = this.norms.get(curField);
if (norm != null) if (norm != null)
clone.norms.put(curField, (Norm) norm.clone()); clone.norms.put(curField, (Norm) norm.clone());
@ -735,7 +733,7 @@ public class SegmentReader extends IndexReader implements Cloneable {
} }
if (normsDirty) { // re-write norms if (normsDirty) { // re-write norms
si.initNormGen(core.fieldInfos.size()); si.initNormGen();
for (final Norm norm : norms.values()) { for (final Norm norm : norms.values()) {
if (norm.dirty) { if (norm.dirty) {
norm.reWrite(si); norm.reWrite(si);
@ -880,8 +878,7 @@ public class SegmentReader extends IndexReader implements Cloneable {
ensureOpen(); ensureOpen();
Set<String> fieldSet = new HashSet<String>(); Set<String> fieldSet = new HashSet<String>();
for (int i = 0; i < core.fieldInfos.size(); i++) { for (FieldInfo fi : core.fieldInfos) {
FieldInfo fi = core.fieldInfos.fieldInfo(i);
if (fieldOption == IndexReader.FieldOption.ALL) { if (fieldOption == IndexReader.FieldOption.ALL) {
fieldSet.add(fi.name); fieldSet.add(fi.name);
} }
@ -959,8 +956,7 @@ public class SegmentReader extends IndexReader implements Cloneable {
private void openNorms(Directory cfsDir, int readBufferSize) throws IOException { private void openNorms(Directory cfsDir, int readBufferSize) throws IOException {
long nextNormSeek = SegmentMerger.NORMS_HEADER.length; //skip header (header unused for now) long nextNormSeek = SegmentMerger.NORMS_HEADER.length; //skip header (header unused for now)
int maxDoc = maxDoc(); int maxDoc = maxDoc();
for (int i = 0; i < core.fieldInfos.size(); i++) { for (FieldInfo fi : core.fieldInfos) {
FieldInfo fi = core.fieldInfos.fieldInfo(i);
if (norms.containsKey(fi.name)) { if (norms.containsKey(fi.name)) {
// in case this SegmentReader is being re-opened, we might be able to // in case this SegmentReader is being re-opened, we might be able to
// reuse some norm instances and skip loading them here // reuse some norm instances and skip loading them here

View File

@ -27,15 +27,13 @@ final class StoredFieldsWriter {
FieldsWriter fieldsWriter; FieldsWriter fieldsWriter;
final DocumentsWriter docWriter; final DocumentsWriter docWriter;
final FieldInfos fieldInfos;
int lastDocID; int lastDocID;
PerDoc[] docFreeList = new PerDoc[1]; PerDoc[] docFreeList = new PerDoc[1];
int freeCount; int freeCount;
public StoredFieldsWriter(DocumentsWriter docWriter, FieldInfos fieldInfos) { public StoredFieldsWriter(DocumentsWriter docWriter) {
this.docWriter = docWriter; this.docWriter = docWriter;
this.fieldInfos = fieldInfos;
} }
public StoredFieldsWriterPerThread addThread(DocumentsWriter.DocState docState) throws IOException { public StoredFieldsWriterPerThread addThread(DocumentsWriter.DocState docState) throws IOException {
@ -62,7 +60,7 @@ final class StoredFieldsWriter {
private synchronized void initFieldsWriter() throws IOException { private synchronized void initFieldsWriter() throws IOException {
if (fieldsWriter == null) { if (fieldsWriter == null) {
fieldsWriter = new FieldsWriter(docWriter.directory, docWriter.getSegment(), fieldInfos); fieldsWriter = new FieldsWriter(docWriter.directory, docWriter.getSegment());
lastDocID = 0; lastDocID = 0;
} }
} }

View File

@ -32,7 +32,7 @@ final class StoredFieldsWriterPerThread {
public StoredFieldsWriterPerThread(DocumentsWriter.DocState docState, StoredFieldsWriter storedFieldsWriter) throws IOException { public StoredFieldsWriterPerThread(DocumentsWriter.DocState docState, StoredFieldsWriter storedFieldsWriter) throws IOException {
this.storedFieldsWriter = storedFieldsWriter; this.storedFieldsWriter = storedFieldsWriter;
this.docState = docState; this.docState = docState;
localFieldsWriter = new FieldsWriter((IndexOutput) null, (IndexOutput) null, storedFieldsWriter.fieldInfos); localFieldsWriter = new FieldsWriter((IndexOutput) null, (IndexOutput) null);
} }
public void startDocument() { public void startDocument() {

View File

@ -56,12 +56,6 @@ final class TermsHash extends InvertedDocConsumer {
return new TermsHashPerThread(docInverterPerThread, this, nextTermsHash, primaryPerThread); return new TermsHashPerThread(docInverterPerThread, this, nextTermsHash, primaryPerThread);
} }
@Override
void setFieldInfos(FieldInfos fieldInfos) {
this.fieldInfos = fieldInfos;
consumer.setFieldInfos(fieldInfos);
}
@Override @Override
public void abort() { public void abort() {
consumer.abort(); consumer.abort();

View File

@ -25,10 +25,4 @@ abstract class TermsHashConsumer {
abstract TermsHashConsumerPerThread addThread(TermsHashPerThread perThread); abstract TermsHashConsumerPerThread addThread(TermsHashPerThread perThread);
abstract void flush(Map<TermsHashConsumerPerThread,Collection<TermsHashConsumerPerField>> threadsAndFields, final SegmentWriteState state) throws IOException; abstract void flush(Map<TermsHashConsumerPerThread,Collection<TermsHashConsumerPerField>> threadsAndFields, final SegmentWriteState state) throws IOException;
abstract void abort(); abstract void abort();
FieldInfos fieldInfos;
void setFieldInfos(FieldInfos fieldInfos) {
this.fieldInfos = fieldInfos;
} }
}

View File

@ -19,14 +19,15 @@ package org.apache.lucene.index.codecs.preflex;
import java.io.IOException; import java.io.IOException;
import java.util.Collection; import java.util.Collection;
import java.util.Iterator;
import java.util.TreeMap;
import java.util.HashMap;
import java.util.Map;
import java.util.Comparator; import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
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.DocsAndPositionsEnum;
import org.apache.lucene.index.DocsEnum;
import org.apache.lucene.index.FieldInfo; import org.apache.lucene.index.FieldInfo;
import org.apache.lucene.index.FieldInfos; import org.apache.lucene.index.FieldInfos;
import org.apache.lucene.index.FieldsEnum; import org.apache.lucene.index.FieldsEnum;
@ -35,7 +36,6 @@ import org.apache.lucene.index.SegmentInfo;
import org.apache.lucene.index.Term; import org.apache.lucene.index.Term;
import org.apache.lucene.index.Terms; import org.apache.lucene.index.Terms;
import org.apache.lucene.index.TermsEnum; import org.apache.lucene.index.TermsEnum;
import org.apache.lucene.index.CompoundFileReader;
import org.apache.lucene.index.codecs.FieldsProducer; import org.apache.lucene.index.codecs.FieldsProducer;
import org.apache.lucene.store.Directory; import org.apache.lucene.store.Directory;
import org.apache.lucene.store.IndexInput; import org.apache.lucene.store.IndexInput;
@ -94,13 +94,11 @@ public class PreFlexFields extends FieldsProducer {
// so that if an index update removes them we'll still have them // so that if an index update removes them we'll still have them
freqStream = dir.openInput(info.name + ".frq", readBufferSize); freqStream = dir.openInput(info.name + ".frq", readBufferSize);
boolean anyProx = false; boolean anyProx = false;
final int numFields = fieldInfos.size(); for (FieldInfo fi : fieldInfos) {
for(int i=0;i<numFields;i++) { if (fi.isIndexed) {
final FieldInfo fieldInfo = fieldInfos.fieldInfo(i); fields.put(fi.name, fi);
if (fieldInfo.isIndexed) { preTerms.put(fi.name, new PreTerms(fi));
fields.put(fieldInfo.name, fieldInfo); if (!fi.omitTermFreqAndPositions) {
preTerms.put(fieldInfo.name, new PreTerms(fieldInfo));
if (!fieldInfo.omitTermFreqAndPositions) {
anyProx = true; anyProx = true;
} }
} }

View File

@ -528,10 +528,9 @@ public class TestBackwardsCompatibility extends LuceneTestCase {
CompoundFileReader cfsReader = new CompoundFileReader(dir, "_0.cfs"); CompoundFileReader cfsReader = new CompoundFileReader(dir, "_0.cfs");
FieldInfos fieldInfos = new FieldInfos(cfsReader, "_0.fnm"); FieldInfos fieldInfos = new FieldInfos(cfsReader, "_0.fnm");
int contentFieldIndex = -1; int contentFieldIndex = -1;
for(int i=0;i<fieldInfos.size();i++) { for (FieldInfo fi : fieldInfos) {
FieldInfo fi = fieldInfos.fieldInfo(i);
if (fi.name.equals("content")) { if (fi.name.equals("content")) {
contentFieldIndex = i; contentFieldIndex = fi.number;
break; break;
} }
} }

View File

@ -23,14 +23,14 @@ import java.util.HashSet;
import org.apache.lucene.analysis.MockAnalyzer; import org.apache.lucene.analysis.MockAnalyzer;
import org.apache.lucene.document.Document; import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field.Store;
import org.apache.lucene.document.Field; import org.apache.lucene.document.Field;
import org.apache.lucene.document.Field.Store;
import org.apache.lucene.index.codecs.CodecProvider; import org.apache.lucene.index.codecs.CodecProvider;
import org.apache.lucene.index.codecs.FieldsConsumer; import org.apache.lucene.index.codecs.FieldsConsumer;
import org.apache.lucene.index.codecs.FieldsProducer; import org.apache.lucene.index.codecs.FieldsProducer;
import org.apache.lucene.index.codecs.PostingsConsumer; import org.apache.lucene.index.codecs.PostingsConsumer;
import org.apache.lucene.index.codecs.TermsConsumer;
import org.apache.lucene.index.codecs.TermStats; import org.apache.lucene.index.codecs.TermStats;
import org.apache.lucene.index.codecs.TermsConsumer;
import org.apache.lucene.index.codecs.mocksep.MockSepCodec; import org.apache.lucene.index.codecs.mocksep.MockSepCodec;
import org.apache.lucene.index.codecs.preflex.PreFlexCodec; import org.apache.lucene.index.codecs.preflex.PreFlexCodec;
import org.apache.lucene.search.DocIdSetIterator; import org.apache.lucene.search.DocIdSetIterator;
@ -238,9 +238,9 @@ public class TestCodecs extends LuceneTestCase {
final FieldData[] fields = new FieldData[] {field}; final FieldData[] fields = new FieldData[] {field};
final Directory dir = newDirectory(); final Directory dir = newDirectory();
FieldInfos clonedFieldInfos = (FieldInfos) fieldInfos.clone();
this.write(fieldInfos, dir, fields, true); this.write(fieldInfos, dir, fields, true);
final SegmentInfo si = new SegmentInfo(SEGMENT, 10000, dir, false, true, SegmentCodecs.build(fieldInfos, CodecProvider.getDefault()), fieldInfos.hasVectors()); final SegmentInfo si = new SegmentInfo(SEGMENT, 10000, dir, false, SegmentCodecs.build(clonedFieldInfos, CodecProvider.getDefault()), clonedFieldInfos);
si.setHasProx(false);
final FieldsProducer reader = si.getSegmentCodecs().codec().fieldsProducer(new SegmentReadState(dir, si, fieldInfos, 64, IndexReader.DEFAULT_TERMS_INDEX_DIVISOR)); final FieldsProducer reader = si.getSegmentCodecs().codec().fieldsProducer(new SegmentReadState(dir, si, fieldInfos, 64, IndexReader.DEFAULT_TERMS_INDEX_DIVISOR));
@ -289,8 +289,10 @@ public class TestCodecs extends LuceneTestCase {
if (VERBOSE) { if (VERBOSE) {
System.out.println("TEST: now write postings"); System.out.println("TEST: now write postings");
} }
FieldInfos clonedFieldInfos = (FieldInfos) fieldInfos.clone();
this.write(fieldInfos, dir, fields, false); this.write(fieldInfos, dir, fields, false);
final SegmentInfo si = new SegmentInfo(SEGMENT, 10000, dir, false, true, SegmentCodecs.build(fieldInfos, CodecProvider.getDefault()), fieldInfos.hasVectors()); final SegmentInfo si = new SegmentInfo(SEGMENT, 10000, dir, false, SegmentCodecs.build(clonedFieldInfos, CodecProvider.getDefault()), clonedFieldInfos);
if (VERBOSE) { if (VERBOSE) {
System.out.println("TEST: now read postings"); System.out.println("TEST: now read postings");
@ -440,7 +442,7 @@ public class TestCodecs extends LuceneTestCase {
final FieldData field = fields[TestCodecs.random.nextInt(fields.length)]; final FieldData field = fields[TestCodecs.random.nextInt(fields.length)];
final TermsEnum termsEnum = termsDict.terms(field.fieldInfo.name).iterator(); final TermsEnum termsEnum = termsDict.terms(field.fieldInfo.name).iterator();
if (si.getSegmentCodecs().codecs[field.fieldInfo.codecId] instanceof PreFlexCodec) { if (si.getSegmentCodecs().codecs[field.fieldInfo.getCodecId()] instanceof PreFlexCodec) {
// code below expects unicode sort order // code below expects unicode sort order
continue; continue;
} }
@ -594,7 +596,7 @@ public class TestCodecs extends LuceneTestCase {
final FieldsConsumer consumer = state.segmentCodecs.codec().fieldsConsumer(state); final FieldsConsumer consumer = state.segmentCodecs.codec().fieldsConsumer(state);
Arrays.sort(fields); Arrays.sort(fields);
for (final FieldData field : fields) { for (final FieldData field : fields) {
if (!allowPreFlex && codecInfo.codecs[field.fieldInfo.codecId] instanceof PreFlexCodec) { if (!allowPreFlex && codecInfo.codecs[field.fieldInfo.getCodecId()] instanceof PreFlexCodec) {
// code below expects unicode sort order // code below expects unicode sort order
continue; continue;
} }

View File

@ -0,0 +1,222 @@
package org.apache.lucene.index;
/**
* 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.
*/
import org.apache.lucene.analysis.MockAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
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.store.Directory;
import org.apache.lucene.util.LuceneTestCase;
import org.junit.Test;
public class TestConsistentFieldNumbers extends LuceneTestCase {
@Test
public void testSameFieldNumbersAcrossSegments() throws Exception {
for (int i = 0; i < 2; i++) {
Directory dir = newDirectory();
IndexWriter writer = new IndexWriter(dir, newIndexWriterConfig( TEST_VERSION_CURRENT, new MockAnalyzer()).setMergePolicy(NoMergePolicy.COMPOUND_FILES));
Document d1 = new Document();
d1.add(new Field("f1", "first field", Store.YES, Index.ANALYZED, TermVector.NO));
d1.add(new Field("f2", "second field", Store.YES, Index.ANALYZED, TermVector.NO));
writer.addDocument(d1);
if (i == 1) {
writer.close();
writer = new IndexWriter(dir, newIndexWriterConfig( TEST_VERSION_CURRENT, new MockAnalyzer()).setMergePolicy(NoMergePolicy.COMPOUND_FILES));
} else {
writer.commit();
}
Document d2 = new Document();
d2.add(new Field("f2", "second field", Store.YES, Index.ANALYZED, TermVector.NO));
d2.add(new Field("f1", "first field", Store.YES, Index.ANALYZED, TermVector.YES));
d2.add(new Field("f3", "third field", Store.YES, Index.ANALYZED, TermVector.NO));
d2.add(new Field("f4", "fourth field", Store.YES, Index.ANALYZED, TermVector.NO));
writer.addDocument(d2);
writer.close();
SegmentInfos sis = new SegmentInfos();
sis.read(dir);
assertEquals(2, sis.size());
FieldInfos fis1 = sis.info(0).getFieldInfos();
FieldInfos fis2 = sis.info(1).getFieldInfos();
assertEquals("f1", fis1.fieldInfo(0).name);
assertEquals("f2", fis1.fieldInfo(1).name);
assertEquals("f1", fis2.fieldInfo(0).name);
assertEquals("f2", fis2.fieldInfo(1).name);
assertEquals("f3", fis2.fieldInfo(2).name);
assertEquals("f4", fis2.fieldInfo(3).name);
writer = new IndexWriter(dir, newIndexWriterConfig( TEST_VERSION_CURRENT, new MockAnalyzer()));
writer.optimize();
writer.close();
sis = new SegmentInfos();
sis.read(dir);
assertEquals(1, sis.size());
FieldInfos fis3 = sis.info(0).getFieldInfos();
assertEquals("f1", fis3.fieldInfo(0).name);
assertEquals("f2", fis3.fieldInfo(1).name);
assertEquals("f3", fis3.fieldInfo(2).name);
assertEquals("f4", fis3.fieldInfo(3).name);
dir.close();
}
}
@Test
public void testAddIndexes() throws Exception {
Directory dir1 = newDirectory();
Directory dir2 = newDirectory();
IndexWriter writer = new IndexWriter(dir1, newIndexWriterConfig( TEST_VERSION_CURRENT, new MockAnalyzer()).setMergePolicy(NoMergePolicy.COMPOUND_FILES));
Document d1 = new Document();
d1.add(new Field("f1", "first field", Store.YES, Index.ANALYZED, TermVector.NO));
d1.add(new Field("f2", "second field", Store.YES, Index.ANALYZED, TermVector.NO));
writer.addDocument(d1);
writer.close();
writer = new IndexWriter(dir2, newIndexWriterConfig( TEST_VERSION_CURRENT, new MockAnalyzer()).setMergePolicy(NoMergePolicy.COMPOUND_FILES));
Document d2 = new Document();
d2.add(new Field("f2", "second field", Store.YES, Index.ANALYZED, TermVector.NO));
d2.add(new Field("f1", "first field", Store.YES, Index.ANALYZED, TermVector.YES));
d2.add(new Field("f3", "third field", Store.YES, Index.ANALYZED, TermVector.NO));
d2.add(new Field("f4", "fourth field", Store.YES, Index.ANALYZED, TermVector.NO));
writer.addDocument(d2);
writer.close();
writer = new IndexWriter(dir1, newIndexWriterConfig( TEST_VERSION_CURRENT, new MockAnalyzer()).setMergePolicy(NoMergePolicy.COMPOUND_FILES));
writer.addIndexes(dir2);
writer.close();
SegmentInfos sis = new SegmentInfos();
sis.read(dir1);
assertEquals(2, sis.size());
FieldInfos fis1 = sis.info(0).getFieldInfos();
FieldInfos fis2 = sis.info(1).getFieldInfos();
assertEquals("f1", fis1.fieldInfo(0).name);
assertEquals("f2", fis1.fieldInfo(1).name);
// make sure the ordering of the "external" segment is preserved
assertEquals("f2", fis2.fieldInfo(0).name);
assertEquals("f1", fis2.fieldInfo(1).name);
assertEquals("f3", fis2.fieldInfo(2).name);
assertEquals("f4", fis2.fieldInfo(3).name);
writer = new IndexWriter(dir1, newIndexWriterConfig( TEST_VERSION_CURRENT, new MockAnalyzer()));
writer.optimize();
writer.close();
sis = new SegmentInfos();
sis.read(dir1);
assertEquals(1, sis.size());
FieldInfos fis3 = sis.info(0).getFieldInfos();
// after merging the ordering should be identical to the first segment
assertEquals("f1", fis3.fieldInfo(0).name);
assertEquals("f2", fis3.fieldInfo(1).name);
assertEquals("f3", fis3.fieldInfo(2).name);
assertEquals("f4", fis3.fieldInfo(3).name);
dir1.close();
dir2.close();
}
@Test
public void testManyFields() throws Exception {
final int NUM_DOCS = 2000;
final int MAX_FIELDS = 50;
int[][] docs = new int[NUM_DOCS][4];
for (int i = 0; i < docs.length; i++) {
for (int j = 0; j < docs[i].length;j++) {
docs[i][j] = random.nextInt(MAX_FIELDS);
}
}
Directory dir = newDirectory();
IndexWriter writer = new IndexWriter(dir, newIndexWriterConfig( TEST_VERSION_CURRENT, new MockAnalyzer()));
for (int i = 0; i < NUM_DOCS; i++) {
Document d = new Document();
for (int j = 0; j < docs[i].length; j++) {
d.add(getField(docs[i][j]));
}
writer.addDocument(d);
}
writer.optimize();
writer.close();
SegmentInfos sis = new SegmentInfos();
sis.read(dir);
for (SegmentInfo si : sis) {
FieldInfos fis = si.getFieldInfos();
for (FieldInfo fi : fis) {
Field expected = getField(Integer.parseInt(fi.name));
assertEquals(expected.isIndexed(), fi.isIndexed);
assertEquals(expected.isTermVectorStored(), fi.storeTermVector);
assertEquals(expected.isStorePositionWithTermVector(), fi.storePositionWithTermVector);
assertEquals(expected.isStoreOffsetWithTermVector(), fi.storeOffsetWithTermVector);
}
}
dir.close();
}
private Field getField(int number) {
int mode = number % 16;
String fieldName = "" + number;
switch (mode) {
case 0: return new Field(fieldName, "some text", Store.YES, Index.ANALYZED, TermVector.NO);
case 1: return new Field(fieldName, "some text", Store.NO, Index.ANALYZED, TermVector.NO);
case 2: return new Field(fieldName, "some text", Store.YES, Index.NOT_ANALYZED, TermVector.NO);
case 3: return new Field(fieldName, "some text", Store.NO, Index.NOT_ANALYZED, TermVector.NO);
case 4: return new Field(fieldName, "some text", Store.YES, Index.ANALYZED, TermVector.WITH_OFFSETS);
case 5: return new Field(fieldName, "some text", Store.NO, Index.ANALYZED, TermVector.WITH_OFFSETS);
case 6: return new Field(fieldName, "some text", Store.YES, Index.NOT_ANALYZED, TermVector.WITH_OFFSETS);
case 7: return new Field(fieldName, "some text", Store.NO, Index.NOT_ANALYZED, TermVector.WITH_OFFSETS);
case 8: return new Field(fieldName, "some text", Store.YES, Index.ANALYZED, TermVector.WITH_POSITIONS);
case 9: return new Field(fieldName, "some text", Store.NO, Index.ANALYZED, TermVector.WITH_POSITIONS);
case 10: return new Field(fieldName, "some text", Store.YES, Index.NOT_ANALYZED, TermVector.WITH_POSITIONS);
case 11: return new Field(fieldName, "some text", Store.NO, Index.NOT_ANALYZED, TermVector.WITH_POSITIONS);
case 12: return new Field(fieldName, "some text", Store.YES, Index.ANALYZED, TermVector.WITH_POSITIONS_OFFSETS);
case 13: return new Field(fieldName, "some text", Store.NO, Index.ANALYZED, TermVector.WITH_POSITIONS_OFFSETS);
case 14: return new Field(fieldName, "some text", Store.YES, Index.NOT_ANALYZED, TermVector.WITH_POSITIONS_OFFSETS);
case 15: return new Field(fieldName, "some text", Store.NO, Index.NOT_ANALYZED, TermVector.WITH_POSITIONS_OFFSETS);
default: return null;
}
}
}

View File

@ -204,8 +204,8 @@ public class TestDoc extends LuceneTestCase {
r2.close(); r2.close();
final SegmentInfo info = new SegmentInfo(merged, si1.docCount + si2.docCount, si1.dir, final SegmentInfo info = new SegmentInfo(merged, si1.docCount + si2.docCount, si1.dir,
false, merger.fieldInfos().hasProx(), merger.getSegmentCodecs(), false, merger.getSegmentCodecs(),
merger.fieldInfos().hasVectors()); merger.fieldInfos());
if (useCompoundFile) { if (useCompoundFile) {
Collection<String> filesToDelete = merger.createCompoundFile(merged + ".cfs", info); Collection<String> filesToDelete = merger.createCompoundFile(merged + ".cfs", info);

View File

@ -25,20 +25,20 @@ import org.apache.lucene.analysis.MockAnalyzer;
import org.apache.lucene.analysis.MockTokenizer; import org.apache.lucene.analysis.MockTokenizer;
import org.apache.lucene.analysis.TokenFilter; import org.apache.lucene.analysis.TokenFilter;
import org.apache.lucene.analysis.TokenStream; 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.PayloadAttribute;
import org.apache.lucene.analysis.tokenattributes.PositionIncrementAttribute; 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.Document;
import org.apache.lucene.document.Field; 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.Index;
import org.apache.lucene.document.Field.Store; import org.apache.lucene.document.Field.Store;
import org.apache.lucene.document.Field.TermVector; import org.apache.lucene.document.Field.TermVector;
import org.apache.lucene.document.Fieldable;
import org.apache.lucene.store.Directory; import org.apache.lucene.store.Directory;
import org.apache.lucene.util.AttributeSource; import org.apache.lucene.util.AttributeSource;
import org.apache.lucene.util.BytesRef;
import org.apache.lucene.util.LuceneTestCase; import org.apache.lucene.util.LuceneTestCase;
import org.apache.lucene.util._TestUtil; import org.apache.lucene.util._TestUtil;
import org.apache.lucene.util.BytesRef;
public class TestDocumentWriter extends LuceneTestCase { public class TestDocumentWriter extends LuceneTestCase {
private Directory dir; private Directory dir;
@ -98,8 +98,7 @@ public class TestDocumentWriter extends LuceneTestCase {
// test that the norms are not present in the segment if // test that the norms are not present in the segment if
// omitNorms is true // omitNorms is true
for (int i = 0; i < reader.core.fieldInfos.size(); i++) { for (FieldInfo fi : reader.core.fieldInfos) {
FieldInfo fi = reader.core.fieldInfos.fieldInfo(i);
if (fi.isIndexed) { if (fi.isIndexed) {
assertTrue(fi.omitNorms == !reader.hasNorms(fi.name)); assertTrue(fi.omitNorms == !reader.hasNorms(fi.name));
} }

View File

@ -92,10 +92,9 @@ public class TestIndexFileDeleter extends LuceneTestCase {
CompoundFileReader cfsReader = new CompoundFileReader(dir, "_2.cfs"); CompoundFileReader cfsReader = new CompoundFileReader(dir, "_2.cfs");
FieldInfos fieldInfos = new FieldInfos(cfsReader, "_2.fnm"); FieldInfos fieldInfos = new FieldInfos(cfsReader, "_2.fnm");
int contentFieldIndex = -1; int contentFieldIndex = -1;
for(i=0;i<fieldInfos.size();i++) { for (FieldInfo fi : fieldInfos) {
FieldInfo fi = fieldInfos.fieldInfo(i);
if (fi.name.equals("content")) { if (fi.name.equals("content")) {
contentFieldIndex = i; contentFieldIndex = fi.number;
break; break;
} }
} }

View File

@ -216,7 +216,7 @@ public class TestPerFieldCodecSupport extends LuceneTestCase {
IndexFileNames.FIELD_INFOS_EXTENSION)); IndexFileNames.FIELD_INFOS_EXTENSION));
FieldInfo fieldInfo = fieldInfos.fieldInfo(field); FieldInfo fieldInfo = fieldInfos.fieldInfo(field);
assertEquals("faild for segment index: " + i, codec[i], assertEquals("faild for segment index: " + i, codec[i],
codecInfo.codecs[fieldInfo.codecId]); codecInfo.codecs[fieldInfo.getCodecId()]);
} }
} }

View File

@ -79,8 +79,8 @@ public class TestSegmentMerger extends LuceneTestCase {
int docsMerged = merger.merge(); int docsMerged = merger.merge();
assertTrue(docsMerged == 2); assertTrue(docsMerged == 2);
//Should be able to open a new SegmentReader against the new directory //Should be able to open a new SegmentReader against the new directory
SegmentReader mergedReader = SegmentReader.get(false, mergedDir, new SegmentInfo(mergedSegment, docsMerged, mergedDir, false, merger.fieldInfos().hasProx(), SegmentReader mergedReader = SegmentReader.get(false, mergedDir, new SegmentInfo(mergedSegment, docsMerged, mergedDir, false,
merger.getSegmentCodecs(), merger.fieldInfos().hasVectors()), merger.getSegmentCodecs(), merger.fieldInfos()),
BufferedIndexInput.BUFFER_SIZE, true, IndexReader.DEFAULT_TERMS_INDEX_DIVISOR); BufferedIndexInput.BUFFER_SIZE, true, IndexReader.DEFAULT_TERMS_INDEX_DIVISOR);
assertTrue(mergedReader != null); assertTrue(mergedReader != null);