Index: lucene/src/test/org/apache/lucene/index/TestIndexWriter.java =================================================================== --- lucene/src/test/org/apache/lucene/index/TestIndexWriter.java (revision 1174709) +++ lucene/src/test/org/apache/lucene/index/TestIndexWriter.java (working copy) @@ -38,8 +38,10 @@ import org.apache.lucene.document.Document; import org.apache.lucene.document.Field; import org.apache.lucene.document.FieldType; +import org.apache.lucene.document.IndexDocValuesField; import org.apache.lucene.document.TextField; import org.apache.lucene.index.IndexWriterConfig.OpenMode; +import org.apache.lucene.index.codecs.CodecProvider; import org.apache.lucene.search.DocIdSetIterator; import org.apache.lucene.search.FieldCache; import org.apache.lucene.search.IndexSearcher; @@ -1870,4 +1872,33 @@ w.close(); d.close(); } + + public void testMultiValuedIndexDocValuesField() throws Exception { + assumeFalse("cannot work with preflex codec", CodecProvider.getDefault().getDefaultFieldCodec().equals("PreFlex")); + Directory d = newDirectory(); + RandomIndexWriter w = new RandomIndexWriter(random, d); + Document doc = new Document(); + IndexDocValuesField f = new IndexDocValuesField("field"); + f.setInt(17); + // Index doc values are single-valued so we should not + // be able to add same field more than once: + doc.add(f); + doc.add(f); + try { + w.addDocument(doc); + fail("didn't hit expected exception"); + } catch (IllegalArgumentException iae) { + // expected + } + + doc = new Document(); + doc.add(f); + w.addDocument(doc); + w.optimize(); + IndexReader r = w.getReader(); + w.close(); + assertEquals(17, r.getSequentialSubReaders()[0].perDocValues().docValues("field").load().getInt(0)); + r.close(); + d.close(); + } } Index: lucene/src/java/org/apache/lucene/index/DocFieldProcessor.java =================================================================== --- lucene/src/java/org/apache/lucene/index/DocFieldProcessor.java (revision 1174709) +++ lucene/src/java/org/apache/lucene/index/DocFieldProcessor.java (working copy) @@ -83,9 +83,9 @@ // FieldInfo.storePayload. final String fileName = IndexFileNames.segmentFileName(state.segmentName, "", IndexFileNames.FIELD_INFOS_EXTENSION); state.fieldInfos.write(state.directory, fileName); - for (DocValuesConsumer consumers : docValues.values()) { - consumers.finish(state.numDocs); - }; + for (DocValuesConsumerAndDocID consumers : docValues.values()) { + consumers.docValuesConsumer.finish(state.numDocs); + } // close perDocConsumer during flush to ensure all files are flushed due to PerCodec CFS IOUtils.close(perDocConsumers.values()); } @@ -297,14 +297,28 @@ } } - final private Map docValues = new HashMap(); + private static class DocValuesConsumerAndDocID { + public int docID; + final DocValuesConsumer docValuesConsumer; + + public DocValuesConsumerAndDocID(DocValuesConsumer docValuesConsumer) { + this.docValuesConsumer = docValuesConsumer; + } + } + + final private Map docValues = new HashMap(); final private Map perDocConsumers = new HashMap(); DocValuesConsumer docValuesConsumer(DocState docState, FieldInfo fieldInfo) throws IOException { - DocValuesConsumer docValuesConsumer = docValues.get(fieldInfo.name); - if (docValuesConsumer != null) { - return docValuesConsumer; + DocValuesConsumerAndDocID docValuesConsumerAndDocID = docValues.get(fieldInfo.name); + if (docValuesConsumerAndDocID != null) { + if (docState.docID == docValuesConsumerAndDocID.docID) { + throw new IllegalArgumentException("IndexDocValuesField \"" + fieldInfo.name + "\" appears more than once in this document (only one value is allowed, per field)"); + } + assert docValuesConsumerAndDocID.docID < docState.docID; + docValuesConsumerAndDocID.docID = docState.docID; + return docValuesConsumerAndDocID.docValuesConsumer; } PerDocConsumer perDocConsumer = perDocConsumers.get(fieldInfo.getCodecId()); if (perDocConsumer == null) { @@ -316,6 +330,7 @@ perDocConsumers.put(Integer.valueOf(fieldInfo.getCodecId()), perDocConsumer); } boolean success = false; + DocValuesConsumer docValuesConsumer = null; try { docValuesConsumer = perDocConsumer.addValuesField(fieldInfo); fieldInfo.commitDocValues(); @@ -325,7 +340,10 @@ fieldInfo.revertUncommitted(); } } - docValues.put(fieldInfo.name, docValuesConsumer); + + docValuesConsumerAndDocID = new DocValuesConsumerAndDocID(docValuesConsumer); + docValuesConsumerAndDocID.docID = docState.docID; + docValues.put(fieldInfo.name, docValuesConsumerAndDocID); return docValuesConsumer; } }