Index: CHANGES.txt =================================================================== --- CHANGES.txt (revision 696028) +++ CHANGES.txt (working copy) @@ -156,6 +156,11 @@ 24. LUCENE-1131: Added numDeletedDocs method to IndexReader (Otis Gospodnetic) +25. LUCENE-1382: Add an optional arbitrary String "commitUserData" to + IndexWriter.commit(), which is stored in the segments file and is + then retrievable via IndexReader.getCommitUserData instance and + static methods. (Shalin Shekhar Mangar via Mike McCandless) + Bug fixes 1. LUCENE-1134: Fixed BooleanQuery.rewrite to only optimize a single Index: src/test/org/apache/lucene/index/TestIndexWriter.java =================================================================== --- src/test/org/apache/lucene/index/TestIndexWriter.java (revision 696028) +++ src/test/org/apache/lucene/index/TestIndexWriter.java (working copy) @@ -4118,4 +4118,42 @@ _TestUtil.rmDir(indexDir); } } + + // LUCENE-1382 + public void testCommitUserData() throws IOException { + Directory dir = new MockRAMDirectory(); + IndexWriter w = new IndexWriter(dir, false, new WhitespaceAnalyzer(), IndexWriter.MaxFieldLength.LIMITED); + w.setMaxBufferedDocs(2); + for(int j=0;j<17;j++) + addDoc(w); + w.close(); + + assertEquals(null, IndexReader.getCommitUserData(dir)); + + IndexReader r = IndexReader.open(dir); + // commit(String) never called for this index + assertEquals(null, r.getCommitUserData()); + r.close(); + + w = new IndexWriter(dir, false, new WhitespaceAnalyzer(), IndexWriter.MaxFieldLength.LIMITED); + w.setMaxBufferedDocs(2); + for(int j=0;j<17;j++) + addDoc(w); + w.commit("test1"); + w.close(); + + assertEquals("test1", IndexReader.getCommitUserData(dir)); + + r = IndexReader.open(dir); + assertEquals("test1", r.getCommitUserData()); + r.close(); + + w = new IndexWriter(dir, false, new WhitespaceAnalyzer(), IndexWriter.MaxFieldLength.LIMITED); + w.optimize(); + w.close(); + + assertEquals("test1", IndexReader.getCommitUserData(dir)); + + dir.close(); + } } Index: src/java/org/apache/lucene/index/DirectoryIndexReader.java =================================================================== --- src/java/org/apache/lucene/index/DirectoryIndexReader.java (revision 696028) +++ src/java/org/apache/lucene/index/DirectoryIndexReader.java (working copy) @@ -169,6 +169,11 @@ return segmentInfos.getVersion(); } + public String getCommitUserData() { + ensureOpen(); + return segmentInfos.getUserData(); + } + /** * Check whether this IndexReader is still using the * current (i.e., most recently committed) version of the Index: src/java/org/apache/lucene/index/IndexReader.java =================================================================== --- src/java/org/apache/lucene/index/IndexReader.java (revision 696028) +++ src/java/org/apache/lucene/index/IndexReader.java (working copy) @@ -458,6 +458,24 @@ } /** + * Reads commitUserData, previously passed to {@link + * IndexWriter#commit(String)}, from current index + * segments file. This will return null if {@link + * IndexWriter#commit(String)} has never been called for + * this index. + * + * @param directory where the index resides. + * @return commit userData. + * @throws CorruptIndexException if the index is corrupt + * @throws IOException if there is a low-level IO error + * + * @see #getCommitUserData() + */ + public static String getCommitUserData(Directory directory) throws CorruptIndexException, IOException { + return SegmentInfos.readCurrentUserData(directory); + } + + /** * Version number when this IndexReader was opened. Not implemented in the IndexReader base class. * @throws UnsupportedOperationException unless overridden in subclass */ @@ -465,6 +483,18 @@ throw new UnsupportedOperationException("This reader does not support this method."); } + /** + * Retrieve the String userData optionally passed to + * IndexWriter#commit. This will return null if {@link + * IndexWriter#commit(String)} has never been called for + * this index. + * + * @see #getCommitUserData(Directory) + */ + public String getCommitUserData() { + throw new UnsupportedOperationException("This reader does not support this method."); + } + /**
For IndexReader implementations that use * TermInfosReader to read terms, this sets the * indexDivisor to subsample the number of indexed terms Index: src/java/org/apache/lucene/index/SegmentInfos.java =================================================================== --- src/java/org/apache/lucene/index/SegmentInfos.java (revision 696028) +++ src/java/org/apache/lucene/index/SegmentInfos.java (working copy) @@ -69,8 +69,11 @@ * omitTf==false) */ public static final int FORMAT_HAS_PROX = -7; + /** This format adds optional commit userData (String) storage. */ + public static final int FORMAT_USER_DATA = -8; + /* This must always point to the most recent file format. */ - static final int CURRENT_FORMAT = FORMAT_HAS_PROX; + static final int CURRENT_FORMAT = FORMAT_USER_DATA; public int counter = 0; // used to name new segments /** @@ -84,6 +87,8 @@ // or wrote; this is normally the same as generation except if // there was an IOException that had interrupted a commit + private String userData; // Opaque String that user can specify during IndexWriter.commit + /** * If non-null, information about loading segments_N files * will be printed here. @see #setInfoStream. @@ -241,6 +246,13 @@ version = input.readLong(); // read version } + if (format <= FORMAT_USER_DATA) { + if (0 == input.readByte()) + userData = null; + else + userData = input.readString(); + } + if (format <= FORMAT_CHECKSUM) { final long checksumNow = input.getChecksum(); final long checksumThen = input.readLong(); @@ -306,6 +318,12 @@ for (int i = 0; i < size(); i++) { info(i).write(output); } + if (userData == null) + output.writeByte((byte) 0); + else { + output.writeByte((byte) 1); + output.writeString(userData); + } output.prepareCommit(); success = true; pendingOutput = output; @@ -394,6 +412,18 @@ }.run()).longValue(); } + /** + * Returns userData from latest segments file + * @throws CorruptIndexException if the index is corrupt + * @throws IOException if there is a low-level IO error + */ + public static String readCurrentUserData(Directory directory) + throws CorruptIndexException, IOException { + SegmentInfos sis = new SegmentInfos(); + sis.read(directory); + return sis.getUserData(); + } + /** If non-null, information about retries when loading * the segments file will be printed to this. */ @@ -840,4 +870,12 @@ } return buffer.toString(); } + + public String getUserData() { + return userData; + } + + public void setUserData(String data) { + userData = data; + } } Index: src/java/org/apache/lucene/index/IndexWriter.java =================================================================== --- src/java/org/apache/lucene/index/IndexWriter.java (revision 696028) +++ src/java/org/apache/lucene/index/IndexWriter.java (working copy) @@ -3386,8 +3386,16 @@ flush(true, false, true); } - /**
Expert: prepare for commit. This does the first - * phase of 2-phase commit. You can only call this when + /** Expert: prepare for commit. + * @see #prepareCommit(String) */ + public final void prepareCommit() throws CorruptIndexException, IOException { + ensureOpen(); + prepareCommit(null); + } + + /**
Expert: prepare for commit, specifying + * commitUserData String. This does the first phase of + * 2-phase commit. You can only call this when * autoCommit is false. This method does all steps * necessary to commit changes since this writer was * opened: flushes pending added and deleted docs, syncs @@ -3396,17 +3404,28 @@ * #commit()} to finish the commit, or {@link * #rollback()} to revert the commit and undo all changes * done since the writer was opened.
+ * + * You can also just call {@link #commit(String)} directly + * without prepareCommit first in which case that method + * will internally call prepareCommit. * - * You can also just call {@link #commit()} directly - * without prepareCommit first in which case that method - * will internally call prepareCommit. + * @param commitUserData Opaque String that's recorded + * into the segments file in the index, and retrievable + * by {@link IndexReader#getCommitUserData}. Note that + * when IndexWriter commits itself, for example if open + * with autoCommit=true, or, during {@link #close}, the + * commitUserData is unchanged (just carried over from + * the prior commit). If this is null then the previous + * commitUserData is kept. Also, the commitUserData will + * only "stick" if there are actually changes in the + * index to commit. Therefore it's best to use this + * feature only when autoCommit is false. */ - public final void prepareCommit() throws CorruptIndexException, IOException { - ensureOpen(); - prepareCommit(false); + public final void prepareCommit(String commitUserData) throws CorruptIndexException, IOException { + prepareCommit(commitUserData, false); } - private final void prepareCommit(boolean internal) throws CorruptIndexException, IOException { + private final void prepareCommit(String commitUserData, boolean internal) throws CorruptIndexException, IOException { if (hitOOM) throw new IllegalStateException("this writer hit an OutOfMemoryError; cannot commit"); @@ -3421,11 +3440,11 @@ flush(true, true, true); - startCommit(0); + startCommit(0, commitUserData); } private void commit(long sizeInBytes) throws IOException { - startCommit(sizeInBytes); + startCommit(sizeInBytes, null); finishCommit(); } @@ -3466,8 +3485,18 @@ * consistency on such devices. */ + /** Commits all changes to the index. + * @see #commit(String) */ public final void commit() throws CorruptIndexException, IOException { + commit(null); + } + /** Commits all changes to the index, specifying a + * commitUserData String. This just calls {@link + * #prepareCommit(String)} (if you didn't already call + * it) and then {@link #finishCommit}. */ + public final void commit(String commitUserData) throws CorruptIndexException, IOException { + ensureOpen(); // Only let one thread do the prepare/finish at a time @@ -3478,7 +3507,7 @@ if (autoCommit || pendingCommit == null) { message("commit: now prepare"); - prepareCommit(true); + prepareCommit(commitUserData, true); } else message("commit: already prepared"); @@ -3496,6 +3525,7 @@ pendingCommit.finishCommit(directory); lastCommitChangeCount = pendingCommitChangeCount; segmentInfos.updateGeneration(pendingCommit); + segmentInfos.setUserData(pendingCommit.getUserData()); setRollbackSegmentInfos(pendingCommit); deleter.checkpoint(pendingCommit, true); } finally { @@ -4564,7 +4594,7 @@ * if it wasn't already. If that succeeds, then we * prepare a new segments_N file but do not fully commit * it. */ - private void startCommit(long sizeInBytes) throws IOException { + private void startCommit(long sizeInBytes, String commitUserData) throws IOException { assert testPoint("startStartCommit"); @@ -4619,6 +4649,10 @@ message("startCommit index=" + segString(segmentInfos) + " changeCount=" + changeCount); toSync = (SegmentInfos) segmentInfos.clone(); + + if (commitUserData != null) + toSync.setUserData(commitUserData); + deleter.incRef(toSync, false); myChangeCount = changeCount; } finally {