package org.apache.lucene.index;
import java.io.Closeable;
import java.io.IOException;
import org.apache.lucene.codecs.FieldInfosFormat;
import org.apache.lucene.search.DocIdSetIterator;
import org.apache.lucene.search.DocValuesFieldExistsQuery;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.IOContext;
import org.apache.lucene.util.Bits;
import org.apache.lucene.util.FixedBitSet;
import org.apache.lucene.util.IOSupplier;
import org.apache.lucene.util.IOUtils;
final class PendingSoftDeletes extends PendingDeletes {
private final String field;
private long dvGeneration = -2;
private final PendingDeletes hardDeletes;
PendingSoftDeletes(String field, SegmentCommitInfo info) {
super(info, null, info.getDelCount(true) == 0);
this.field = field;
hardDeletes = new PendingDeletes(info);
}
PendingSoftDeletes(String field, SegmentReader reader, SegmentCommitInfo info) {
super(reader, info);
this.field = field;
hardDeletes = new PendingDeletes(reader, info);
}
@Override
boolean delete(int docID) throws IOException {
FixedBitSet mutableBits = getMutableBits();
if (hardDeletes.delete(docID)) {
if (mutableBits.get(docID)) {
mutableBits.clear(docID);
assert hardDeletes.delete(docID) == false;
} else {
pendingDeleteCount--;
assert assertPendingDeletes();
}
return true;
}
return false;
}
@Override
protected int numPendingDeletes() {
return super.numPendingDeletes() + hardDeletes.numPendingDeletes();
}
@Override
void onNewReader(CodecReader reader, SegmentCommitInfo info) throws IOException {
super.onNewReader(reader, info);
hardDeletes.onNewReader(reader, info);
if (dvGeneration < info.getDocValuesGen()) {
final DocIdSetIterator iterator = DocValuesFieldExistsQuery.getDocValuesDocIdSetIterator(field, reader);
int newDelCount;
if (iterator != null) {
assert info.info.maxDoc() > 0 : "maxDoc is 0";
newDelCount = applySoftDeletes(iterator, getMutableBits());
assert newDelCount >= 0 : " illegal pending delete count: " + newDelCount;
} else {
newDelCount = 0;
}
assert info.getSoftDelCount() == newDelCount : "softDeleteCount doesn't match " + info.getSoftDelCount() + " != " + newDelCount;
dvGeneration = info.getDocValuesGen();
}
assert getDelCount() <= info.info.maxDoc() : getDelCount() + " > " + info.info.maxDoc();
}
@Override
boolean writeLiveDocs(Directory dir) throws IOException {
this.info.setSoftDelCount(this.info.getSoftDelCount() + pendingDeleteCount);
super.dropChanges();
if (hardDeletes.writeLiveDocs(dir)) {
return true;
}
return false;
}
@Override
void dropChanges() {
hardDeletes.dropChanges();
}
static int applySoftDeletes(DocIdSetIterator iterator, FixedBitSet bits) throws IOException {
assert iterator != null;
int newDeletes = 0;
int docID;
DocValuesFieldUpdates.Iterator hasValue = iterator instanceof DocValuesFieldUpdates.Iterator
? (DocValuesFieldUpdates.Iterator) iterator : null;
while ((docID = iterator.nextDoc()) != DocIdSetIterator.NO_MORE_DOCS) {
if (hasValue == null || hasValue.hasValue()) {
if (bits.get(docID)) {
bits.clear(docID);
newDeletes++;
}
} else {
if (bits.get(docID) == false) {
bits.set(docID);
newDeletes--;
}
}
}
return newDeletes;
}
@Override
void onDocValuesUpdate(FieldInfo info, DocValuesFieldUpdates.Iterator iterator) throws IOException {
if (this.field.equals(info.name)) {
pendingDeleteCount += applySoftDeletes(iterator, getMutableBits());
assert assertPendingDeletes();
this.info.setSoftDelCount(this.info.getSoftDelCount() + pendingDeleteCount);
super.dropChanges();
}
assert dvGeneration < info.getDocValuesGen() : "we have seen this generation update already: " + dvGeneration + " vs. " + info.getDocValuesGen();
assert dvGeneration != -2 : "docValues generation is still uninitialized";
dvGeneration = info.getDocValuesGen();
}
private boolean assertPendingDeletes() {
assert pendingDeleteCount + info.getSoftDelCount() >= 0 : " illegal pending delete count: " + pendingDeleteCount + info.getSoftDelCount();
assert info.info.maxDoc() >= getDelCount();
return true;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("PendingSoftDeletes(seg=").append(info);
sb.append(" numPendingDeletes=").append(pendingDeleteCount);
sb.append(" field=").append(field);
sb.append(" dvGeneration=").append(dvGeneration);
sb.append(" hardDeletes=").append(hardDeletes);
return sb.toString();
}
@Override
int numDeletesToMerge(MergePolicy policy, IOSupplier<CodecReader> readerIOSupplier) throws IOException {
ensureInitialized(readerIOSupplier);
return super.numDeletesToMerge(policy, readerIOSupplier);
}
private void ensureInitialized(IOSupplier<CodecReader> readerIOSupplier) throws IOException {
if (dvGeneration == -2) {
FieldInfos fieldInfos = readFieldInfos();
FieldInfo fieldInfo = fieldInfos.fieldInfo(field);
if (fieldInfo != null && fieldInfo.getDocValuesType() != DocValuesType.NONE) {
onNewReader(readerIOSupplier.get(), info);
} else {
dvGeneration = fieldInfo == null ? -1 : fieldInfo.getDocValuesGen();
}
}
}
@Override
boolean isFullyDeleted(IOSupplier<CodecReader> readerIOSupplier) throws IOException {
ensureInitialized(readerIOSupplier);
return super.isFullyDeleted(readerIOSupplier);
}
private FieldInfos readFieldInfos() throws IOException {
SegmentInfo segInfo = info.info;
Directory dir = segInfo.dir;
if (info.hasFieldUpdates() == false) {
Closeable toClose;
if (segInfo.getUseCompoundFile()) {
toClose = dir = segInfo.getCodec().compoundFormat().getCompoundReader(segInfo.dir, segInfo, IOContext.READONCE);
} else {
toClose = null;
dir = segInfo.dir;
}
try {
return segInfo.getCodec().fieldInfosFormat().read(dir, segInfo, "", IOContext.READONCE);
} finally {
IOUtils.close(toClose);
}
} else {
FieldInfosFormat fisFormat = segInfo.getCodec().fieldInfosFormat();
final String segmentSuffix = Long.toString(info.getFieldInfosGen(), Character.MAX_RADIX);
return fisFormat.read(dir, segInfo, segmentSuffix, IOContext.READONCE);
}
}
@Override
Bits getHardLiveDocs() {
return hardDeletes.getLiveDocs();
}
@Override
boolean mustInitOnDelete() {
return liveDocsInitialized == false;
}
static int countSoftDeletes(DocIdSetIterator softDeletedDocs, Bits hardDeletes) throws IOException {
int count = 0;
if (softDeletedDocs != null) {
int doc;
while ((doc = softDeletedDocs.nextDoc()) != DocIdSetIterator.NO_MORE_DOCS) {
if (hardDeletes == null || hardDeletes.get(doc)) {
count++;
}
}
}
return count;
}
}