Index: lucene/core/src/java/org/apache/lucene/index/FieldsUpdate.java =================================================================== --- lucene/core/src/java/org/apache/lucene/index/FieldsUpdate.java (revision 0) +++ lucene/core/src/java/org/apache/lucene/index/FieldsUpdate.java (working copy) @@ -0,0 +1,92 @@ +package org.apache.lucene.index; + +import org.apache.lucene.analysis.Analyzer; + +/* + * 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. + */ + +public class FieldsUpdate implements Comparable { + + /** + * Specifies the operation to perform when updating fields. + */ + enum Operation { + /** + * Replace all documents matching the update criterion with a new document + * containing the given fields. + */ + REPLACE_DOCS, + + /** + * Add the given fields to all existing documents matching the update + * criterion. + */ + ADD_FIELDS, + + /** + * Use the given fields to replace fields with same names in all existing + * documents matching the update criterion. + */ + REPLACE_FIELDS + } + + final Operation operation; + final IndexDocument fields; + final Analyzer analyzer; + final int docIDUpto; + + /** + * An update of fields which is not assigned to a specific live segment. + * + * @param operation + * The type of update operation. + * @param fields + * The fields to use in the update. + * @param analyzer + * The analyzer to use in the update. + */ + public FieldsUpdate(Operation operation, IndexDocument fields, + Analyzer analyzer) { + this.operation = operation; + this.fields = fields; + this.analyzer = analyzer; + this.docIDUpto = -1; + } + + /** + * An update of fields for a specific live segment. + * + * @param other + * A non-specific update with the update data. + * @param docIDUpto + * The doc ID in the live segment up to which the update should be + * applied. + */ + public FieldsUpdate(FieldsUpdate other, int docIDUpto) { + this.operation = other.operation; + this.fields = other.fields; + this.analyzer = other.analyzer; + this.docIDUpto = docIDUpto; + } + + /* Order FrieldsUpdate by increasing docIDUpto */ + @Override + public int compareTo(FieldsUpdate other) { + return this.docIDUpto - other.docIDUpto; + } + +} Index: lucene/core/src/java/org/apache/lucene/index/IndexWriter.java =================================================================== --- lucene/core/src/java/org/apache/lucene/index/IndexWriter.java (revision 1382577) +++ lucene/core/src/java/org/apache/lucene/index/IndexWriter.java (working copy) @@ -1195,11 +1195,20 @@ * * @lucene.experimental */ - public void updateDocuments(Term delTerm, Iterable docs) throws IOException { - updateDocuments(delTerm, docs, analyzer); + public void replaceDocuments(Term delTerm, Iterable docs) throws IOException { + replaceDocuments(delTerm, docs, analyzer); } /** + * Replaced by {@link #replaceDocuments(Term, Iterable)}. + * @deprecated + */ + @Deprecated + public void updateDocuments(Term delTerm, Iterable docs) throws IOException { + replaceDocuments(delTerm, docs, analyzer); + } + + /** * Atomically deletes documents matching the provided * delTerm and adds a block of documents, analyzed using * the provided analyzer, with sequentially @@ -1213,7 +1222,7 @@ * * @lucene.experimental */ - public void updateDocuments(Term delTerm, Iterable docs, Analyzer analyzer) throws IOException { + public void replaceDocuments(Term delTerm, Iterable docs, Analyzer analyzer) throws IOException { ensureOpen(); try { boolean success = false; @@ -1237,6 +1246,95 @@ } /** + * Replaced by {@link #replaceDocuments(Term, Iterable, Analyzer)}. + * @deprecated + */ + @Deprecated + public void updateDocuments(Term delTerm, Iterable docs, Analyzer analyzer) throws IOException { + replaceDocuments(delTerm, docs, analyzer); + } + + /** + * Update fields of documents containing the given term. The operation defines + * whether the new fields are either: + * + * + * @throws CorruptIndexException if the index is corrupt + * @throws IOException if there is a low-level IO error + * + * @lucene.experimental + */ + public void updateFields(Term term, FieldsUpdate.Operation operation, IndexDocument fields) throws IOException { + updateFields(term, operation, fields, analyzer); + } + + /** + * Update fields of documents containing the given term, using the provided + * analyzer instead of the value of {@link #getAnalyzer()}. The operation + * defines whether the new fields are either: + * + * + * @throws CorruptIndexException if the index is corrupt + * @throws IOException if there is a low-level IO error + * + * @lucene.experimental + */ + public void updateFields(Term term, FieldsUpdate.Operation operation, + IndexDocument fields, Analyzer analyzer) + throws IOException { + switch (operation) { + case REPLACE_DOCS: + replaceDocument(term, fields, analyzer); + return; + + case REPLACE_FIELDS: + case ADD_FIELDS: + ensureOpen(); + try { + boolean success = false; + boolean anySegmentFlushed = false; + try { + anySegmentFlushed = docWriter.updateFields(term, + new FieldsUpdate(operation, fields, analyzer)); + success = true; + } finally { + if (!success) { + if (infoStream.isEnabled("IW")) { + infoStream.message("IW", "hit exception updating document"); + } + } + } + + if (anySegmentFlushed) { + maybeMerge(); + } + } catch (OutOfMemoryError oom) { + handleOOM(oom, "updateDocument"); + } + } + } + +/** * Deletes the document(s) containing term. * *

NOTE: if this method hits an OutOfMemoryError @@ -1410,9 +1508,21 @@ * @throws CorruptIndexException if the index is corrupt * @throws IOException if there is a low-level IO error */ + public void replaceDocument(Term term, IndexDocument doc) throws IOException { + ensureOpen(); + replaceDocument(term, doc, getAnalyzer()); + } + + /** + * Replaced by {@link #replaceDocument(Term, Iterable)}. If you + * wish to update fields of existing documents use + * {@link #updateFields(Term, UpdateFieldsOperation, Iterable}. + * @deprecated + */ + @Deprecated public void updateDocument(Term term, IndexDocument doc) throws IOException { ensureOpen(); - updateDocument(term, doc, getAnalyzer()); + replaceDocument(term, doc, getAnalyzer()); } /** @@ -1433,7 +1543,7 @@ * @throws CorruptIndexException if the index is corrupt * @throws IOException if there is a low-level IO error */ - public void updateDocument(Term term, IndexDocument doc, Analyzer analyzer) + public void replaceDocument(Term term, IndexDocument doc, Analyzer analyzer) throws IOException { ensureOpen(); try { @@ -1458,6 +1568,18 @@ } } + /** + * Replaced by {@link #replaceDocument(Term, Iterable, Analyzer)}. If you + * wish to update fields of existing documents use + * {@link #updateFields(Term, FieldsUpdate, Iterable)}. + * @deprecated + */ + @Deprecated + public void updateDocument(Term term, IndexDocument doc, Analyzer analyzer) + throws IOException { + replaceDocument(term, doc, analyzer); + } + // for test purpose final synchronized int getSegmentCount(){ return segmentInfos.size(); @@ -2123,11 +2245,11 @@ /** * Prepares the {@link SegmentInfo} for the new flushed segment and persists * the deleted documents {@link MutableBits}. Use - * {@link #publishFlushedSegment(SegmentInfoPerCommit, FrozenBufferedDeletes, FrozenBufferedDeletes)} to + * {@link #publishFlushedSegment(SegmentInfoPerCommit, FrozenBufferedChanges, FrozenBufferedChanges)} to * publish the returned {@link SegmentInfo} together with its segment private * delete packet. * - * @see #publishFlushedSegment(SegmentInfoPerCommit, FrozenBufferedDeletes, FrozenBufferedDeletes) + * @see #publishFlushedSegment(SegmentInfoPerCommit, FrozenBufferedChanges, FrozenBufferedChanges) */ SegmentInfoPerCommit prepareFlushedSegment(FlushedSegment flushedSegment) throws IOException { assert flushedSegment != null; @@ -2183,6 +2305,10 @@ newSegment.advanceDelGen(); } + if (flushedSegment.liveUpdates != null) { + // TODO : SY : write segment updates + } + success = true; } finally { if (!success) { @@ -2199,8 +2325,8 @@ return newSegment; } - synchronized void publishFrozenDeletes(FrozenBufferedDeletes packet) { - assert packet != null && packet.any(); + synchronized void publishFrozenDeletes(FrozenBufferedChanges packet) { + assert packet != null && packet.anyDeletes(); synchronized (bufferedDeletesStream) { bufferedDeletesStream.push(packet); } @@ -2215,20 +2341,20 @@ * @see #prepareFlushedSegment(DocumentsWriterPerThread.FlushedSegment) */ synchronized void publishFlushedSegment(SegmentInfoPerCommit newSegment, - FrozenBufferedDeletes packet, FrozenBufferedDeletes globalPacket) throws IOException { + FrozenBufferedChanges packet, FrozenBufferedChanges globalPacket) throws IOException { // Lock order IW -> BDS synchronized (bufferedDeletesStream) { if (infoStream.isEnabled("IW")) { infoStream.message("IW", "publishFlushedSegment"); } - if (globalPacket != null && globalPacket.any()) { + if (globalPacket != null && globalPacket.anyDeletes()) { bufferedDeletesStream.push(globalPacket); } // Publishing the segment must be synched on IW -> BDS to make the sure // that no merge prunes away the seg. private delete packet final long nextGen; - if (packet != null && packet.any()) { + if (packet != null && packet.anyDeletes()) { nextGen = bufferedDeletesStream.push(packet); } else { // Since we don't have a delete packet to apply we can get a new @@ -2422,7 +2548,7 @@ MergeState mergeState = merger.merge(); // merge 'em - SegmentInfoPerCommit infoPerCommit = new SegmentInfoPerCommit(info, 0, -1L); + SegmentInfoPerCommit infoPerCommit = new SegmentInfoPerCommit(info, 0, -1L, -1L); info.setFiles(new HashSet(trackingDir.getCreatedFiles())); trackingDir.getCreatedFiles().clear(); @@ -2496,7 +2622,7 @@ SegmentInfo newInfo = new SegmentInfo(directory, info.info.getVersion(), segName, info.info.getDocCount(), info.info.getUseCompoundFile(), info.info.getCodec(), info.info.getDiagnostics(), attributes); - SegmentInfoPerCommit newInfoPerCommit = new SegmentInfoPerCommit(newInfo, info.getDelCount(), info.getDelGen()); + SegmentInfoPerCommit newInfoPerCommit = new SegmentInfoPerCommit(newInfo, info.getDelCount(), info.getDelGen(), -1L); Set segFiles = new HashSet(); @@ -3396,7 +3522,7 @@ // names. final String mergeSegmentName = newSegmentName(); SegmentInfo si = new SegmentInfo(directory, Constants.LUCENE_MAIN_VERSION, mergeSegmentName, -1, false, codec, null, null); - merge.info = new SegmentInfoPerCommit(si, 0, -1L); + merge.info = new SegmentInfoPerCommit(si, 0, -1L, -1L); // Lock order: IW -> BD bufferedDeletesStream.prune(segmentInfos); @@ -4109,4 +4235,5 @@ return files; } + }