diff --git a/lucene/CHANGES.txt b/lucene/CHANGES.txt
index caa9eaf38503..ba06947b41d4 100644
--- a/lucene/CHANGES.txt
+++ b/lucene/CHANGES.txt
@@ -31,6 +31,8 @@ API Changes
* GITHUB#14844: Change IndexInput.updateReadAdvice to take an IOContext instead (Simon Cooper)
+* GITHUB#14996: BitSet no longer implements Bits. (Adrien Grand)
+
New Features
---------------------
* GITHUB#14097: Binary partitioning merge policy over float-valued vector field. (Mike Sokolov)
diff --git a/lucene/MIGRATE.md b/lucene/MIGRATE.md
index 3c7d5017c15d..5981c32a0d1f 100644
--- a/lucene/MIGRATE.md
+++ b/lucene/MIGRATE.md
@@ -35,6 +35,11 @@ long maxRamBytesUsed = 50 * 1024 * 1024; // 50MB
IndexSearcher.setDefaultQueryCache(new LRUQueryCache(maxCachedQueries, maxRamBytesUsed));
```
+### BitSet no longer implements Bits
+
+You should call `BitSet#asReadOnlyBits` if you would like to use a `BitSet` as
+`Bits`.
+
## Migration from Lucene 9.x to Lucene 10.0
### DataInput#readVLong() may now read negative vlongs
diff --git a/lucene/backward-codecs/src/java/org/apache/lucene/backward_codecs/lucene91/Lucene91HnswVectorsReader.java b/lucene/backward-codecs/src/java/org/apache/lucene/backward_codecs/lucene91/Lucene91HnswVectorsReader.java
index 9bb320e71287..745f45ac8754 100644
--- a/lucene/backward-codecs/src/java/org/apache/lucene/backward_codecs/lucene91/Lucene91HnswVectorsReader.java
+++ b/lucene/backward-codecs/src/java/org/apache/lucene/backward_codecs/lucene91/Lucene91HnswVectorsReader.java
@@ -250,11 +250,13 @@ public void search(String field, float[] target, KnnCollector knnCollector, Acce
RandomVectorScorer scorer =
defaultFlatVectorScorer.getRandomVectorScorer(
fieldEntry.similarityFunction, vectorValues, target);
+ HnswGraph graph = getGraph(fieldEntry);
HnswGraphSearcher.search(
scorer,
new OrdinalTranslatedKnnCollector(knnCollector, vectorValues::ordToDoc),
- getGraph(fieldEntry),
- getAcceptOrds(acceptDocs.bits(), fieldEntry));
+ graph,
+ getAcceptOrds(acceptDocs.bits(), fieldEntry),
+ Math.min(graph.size(), acceptDocs.cost()));
}
@Override
diff --git a/lucene/backward-codecs/src/java/org/apache/lucene/backward_codecs/lucene92/Lucene92HnswVectorsReader.java b/lucene/backward-codecs/src/java/org/apache/lucene/backward_codecs/lucene92/Lucene92HnswVectorsReader.java
index 32dab1b6c7c7..b49e0f27ff70 100644
--- a/lucene/backward-codecs/src/java/org/apache/lucene/backward_codecs/lucene92/Lucene92HnswVectorsReader.java
+++ b/lucene/backward-codecs/src/java/org/apache/lucene/backward_codecs/lucene92/Lucene92HnswVectorsReader.java
@@ -247,11 +247,13 @@ public void search(String field, float[] target, KnnCollector knnCollector, Acce
RandomVectorScorer scorer =
defaultFlatVectorScorer.getRandomVectorScorer(
fieldEntry.similarityFunction, vectorValues, target);
+ HnswGraph graph = getGraph(fieldEntry);
HnswGraphSearcher.search(
scorer,
new OrdinalTranslatedKnnCollector(knnCollector, vectorValues::ordToDoc),
- getGraph(fieldEntry),
- vectorValues.getAcceptOrds(acceptDocs.bits()));
+ graph,
+ vectorValues.getAcceptOrds(acceptDocs.bits()),
+ Math.min(graph.size(), acceptDocs.cost()));
}
@Override
diff --git a/lucene/backward-codecs/src/java/org/apache/lucene/backward_codecs/lucene94/Lucene94HnswVectorsReader.java b/lucene/backward-codecs/src/java/org/apache/lucene/backward_codecs/lucene94/Lucene94HnswVectorsReader.java
index 5b937375f1af..6fcba737a436 100644
--- a/lucene/backward-codecs/src/java/org/apache/lucene/backward_codecs/lucene94/Lucene94HnswVectorsReader.java
+++ b/lucene/backward-codecs/src/java/org/apache/lucene/backward_codecs/lucene94/Lucene94HnswVectorsReader.java
@@ -281,11 +281,13 @@ public void search(String field, float[] target, KnnCollector knnCollector, Acce
RandomVectorScorer scorer =
defaultFlatVectorScorer.getRandomVectorScorer(
fieldEntry.similarityFunction, vectorValues, target);
+ HnswGraph graph = getGraph(fieldEntry);
HnswGraphSearcher.search(
scorer,
new OrdinalTranslatedKnnCollector(knnCollector, vectorValues::ordToDoc),
- getGraph(fieldEntry),
- vectorValues.getAcceptOrds(acceptDocs.bits()));
+ graph,
+ vectorValues.getAcceptOrds(acceptDocs.bits()),
+ Math.min(graph.size(), acceptDocs.cost()));
}
@Override
@@ -300,11 +302,13 @@ public void search(String field, byte[] target, KnnCollector knnCollector, Accep
RandomVectorScorer scorer =
defaultFlatVectorScorer.getRandomVectorScorer(
fieldEntry.similarityFunction, vectorValues, target);
+ HnswGraph graph = getGraph(fieldEntry);
HnswGraphSearcher.search(
scorer,
new OrdinalTranslatedKnnCollector(knnCollector, vectorValues::ordToDoc),
- getGraph(fieldEntry),
- vectorValues.getAcceptOrds(acceptDocs.bits()));
+ graph,
+ vectorValues.getAcceptOrds(acceptDocs.bits()),
+ Math.min(graph.size(), acceptDocs.cost()));
}
private HnswGraph getGraph(FieldEntry entry) throws IOException {
diff --git a/lucene/backward-codecs/src/java/org/apache/lucene/backward_codecs/lucene95/Lucene95HnswVectorsReader.java b/lucene/backward-codecs/src/java/org/apache/lucene/backward_codecs/lucene95/Lucene95HnswVectorsReader.java
index 7fa6ffbaedad..d7ed4d950120 100644
--- a/lucene/backward-codecs/src/java/org/apache/lucene/backward_codecs/lucene95/Lucene95HnswVectorsReader.java
+++ b/lucene/backward-codecs/src/java/org/apache/lucene/backward_codecs/lucene95/Lucene95HnswVectorsReader.java
@@ -310,11 +310,13 @@ public void search(String field, float[] target, KnnCollector knnCollector, Acce
RandomVectorScorer scorer =
defaultFlatVectorScorer.getRandomVectorScorer(
fieldEntry.similarityFunction, vectorValues, target);
+ HnswGraph graph = getGraph(fieldEntry);
HnswGraphSearcher.search(
scorer,
new OrdinalTranslatedKnnCollector(knnCollector, vectorValues::ordToDoc),
- getGraph(fieldEntry),
- vectorValues.getAcceptOrds(acceptDocs.bits()));
+ graph,
+ vectorValues.getAcceptOrds(acceptDocs.bits()),
+ Math.min(graph.size(), acceptDocs.cost()));
}
@Override
@@ -338,11 +340,13 @@ public void search(String field, byte[] target, KnnCollector knnCollector, Accep
RandomVectorScorer scorer =
defaultFlatVectorScorer.getRandomVectorScorer(
fieldEntry.similarityFunction, vectorValues, target);
+ HnswGraph graph = getGraph(fieldEntry);
HnswGraphSearcher.search(
scorer,
new OrdinalTranslatedKnnCollector(knnCollector, vectorValues::ordToDoc),
- getGraph(fieldEntry),
- vectorValues.getAcceptOrds(acceptDocs.bits()));
+ graph,
+ vectorValues.getAcceptOrds(acceptDocs.bits()),
+ Math.min(graph.size(), acceptDocs.cost()));
}
/** Get knn graph values; used for testing */
diff --git a/lucene/core/src/java/org/apache/lucene/index/DocumentsWriterPerThread.java b/lucene/core/src/java/org/apache/lucene/index/DocumentsWriterPerThread.java
index 8b81a6a9b7fa..9fc18234be52 100644
--- a/lucene/core/src/java/org/apache/lucene/index/DocumentsWriterPerThread.java
+++ b/lucene/core/src/java/org/apache/lucene/index/DocumentsWriterPerThread.java
@@ -458,7 +458,9 @@ FlushedSegment flush(DocumentsWriter.FlushNotifications flushNotifications) thro
flushState.softDelCountOnFlush = 0;
} else {
flushState.softDelCountOnFlush =
- PendingSoftDeletes.countSoftDeletes(softDeletedDocs, flushState.liveDocs);
+ PendingSoftDeletes.countSoftDeletes(
+ softDeletedDocs,
+ flushState.liveDocs == null ? null : flushState.liveDocs.asReadOnlyBits());
assert flushState.segmentInfo.maxDoc()
>= flushState.softDelCountOnFlush + flushState.delCountOnFlush;
}
@@ -654,9 +656,11 @@ void sealFlushedSegment(
if (sortMap == null) {
bits = flushedSegment.liveDocs;
} else {
- bits = sortLiveDocs(flushedSegment.liveDocs, sortMap);
+ bits = sortLiveDocs(flushedSegment.liveDocs.asReadOnlyBits(), sortMap);
}
- codec.liveDocsFormat().writeLiveDocs(bits, directory, info, delCount, context);
+ codec
+ .liveDocsFormat()
+ .writeLiveDocs(bits.asReadOnlyBits(), directory, info, delCount, context);
newSegment.setDelCount(delCount);
newSegment.advanceDelGen();
}
diff --git a/lucene/core/src/java/org/apache/lucene/index/FieldUpdatesBuffer.java b/lucene/core/src/java/org/apache/lucene/index/FieldUpdatesBuffer.java
index a4d38dcd2030..9a16be777902 100644
--- a/lucene/core/src/java/org/apache/lucene/index/FieldUpdatesBuffer.java
+++ b/lucene/core/src/java/org/apache/lucene/index/FieldUpdatesBuffer.java
@@ -315,7 +315,8 @@ class BufferedUpdateIterator {
this.lookAheadTermIterator =
termSortState != null ? termValues.iterator(termSortState) : null;
this.byteValuesIterator = isNumeric ? null : byteValues.iterator();
- updatesWithValue = hasValues == null ? new Bits.MatchAllBits(numUpdates) : hasValues;
+ updatesWithValue =
+ hasValues == null ? new Bits.MatchAllBits(numUpdates) : hasValues.asReadOnlyBits();
}
/**
diff --git a/lucene/core/src/java/org/apache/lucene/index/SoftDeletesDirectoryReaderWrapper.java b/lucene/core/src/java/org/apache/lucene/index/SoftDeletesDirectoryReaderWrapper.java
index 0d9aafbb4998..eeebc14d7a84 100644
--- a/lucene/core/src/java/org/apache/lucene/index/SoftDeletesDirectoryReaderWrapper.java
+++ b/lucene/core/src/java/org/apache/lucene/index/SoftDeletesDirectoryReaderWrapper.java
@@ -133,17 +133,18 @@ static LeafReader wrap(LeafReader reader, String field) throws IOException {
return reader;
}
Bits liveDocs = reader.getLiveDocs();
- final FixedBitSet bits;
+ final FixedBitSet bitSet;
if (liveDocs != null) {
- bits = FixedBitSet.copyOf(liveDocs);
+ bitSet = FixedBitSet.copyOf(liveDocs);
} else {
- bits = new FixedBitSet(reader.maxDoc());
- bits.set(0, reader.maxDoc());
+ bitSet = new FixedBitSet(reader.maxDoc());
+ bitSet.set(0, reader.maxDoc());
}
- int numSoftDeletes = PendingSoftDeletes.applySoftDeletes(iterator, bits);
+ int numSoftDeletes = PendingSoftDeletes.applySoftDeletes(iterator, bitSet);
if (numSoftDeletes == 0) {
return reader;
}
+ Bits bits = bitSet.asReadOnlyBits();
int numDeletes = reader.numDeletedDocs() + numSoftDeletes;
int numDocs = reader.maxDoc() - numDeletes;
assert assertDocCounts(numDocs, numSoftDeletes, reader);
@@ -188,11 +189,11 @@ private static boolean assertDocCounts(
static final class SoftDeletesFilterLeafReader extends FilterLeafReader {
private final LeafReader reader;
- private final FixedBitSet bits;
+ private final Bits bits;
private final int numDocs;
private final CacheHelper readerCacheHelper;
- private SoftDeletesFilterLeafReader(LeafReader reader, FixedBitSet bits, int numDocs) {
+ private SoftDeletesFilterLeafReader(LeafReader reader, Bits bits, int numDocs) {
super(reader);
this.reader = reader;
this.bits = bits;
@@ -226,11 +227,11 @@ public CacheHelper getReaderCacheHelper() {
static final class SoftDeletesFilterCodecReader extends FilterCodecReader {
private final LeafReader reader;
- private final FixedBitSet bits;
+ private final Bits bits;
private final int numDocs;
private final CacheHelper readerCacheHelper;
- private SoftDeletesFilterCodecReader(CodecReader reader, FixedBitSet bits, int numDocs) {
+ private SoftDeletesFilterCodecReader(CodecReader reader, Bits bits, int numDocs) {
super(reader);
this.reader = reader;
this.bits = bits;
diff --git a/lucene/core/src/java/org/apache/lucene/index/SoftDeletesRetentionMergePolicy.java b/lucene/core/src/java/org/apache/lucene/index/SoftDeletesRetentionMergePolicy.java
index 7f426bafe9c2..e6359082a49d 100644
--- a/lucene/core/src/java/org/apache/lucene/index/SoftDeletesRetentionMergePolicy.java
+++ b/lucene/core/src/java/org/apache/lucene/index/SoftDeletesRetentionMergePolicy.java
@@ -137,7 +137,7 @@ public int length() {
+ " maxDoc: "
+ reader.maxDoc();
return FilterCodecReader.wrapLiveDocs(
- reader, cloneLiveDocs, reader.numDocs() + numExtraLiveDocs);
+ reader, cloneLiveDocs.asReadOnlyBits(), reader.numDocs() + numExtraLiveDocs);
} else {
return reader;
}
diff --git a/lucene/core/src/java/org/apache/lucene/search/AcceptDocs.java b/lucene/core/src/java/org/apache/lucene/search/AcceptDocs.java
index 175078434efe..d3a4f95f5665 100644
--- a/lucene/core/src/java/org/apache/lucene/search/AcceptDocs.java
+++ b/lucene/core/src/java/org/apache/lucene/search/AcceptDocs.java
@@ -127,11 +127,7 @@ private static class BitsAcceptDocs extends AcceptDocs {
"Bits length = " + bits.length() + " != maxDoc = " + maxDoc);
}
this.bits = bits;
- if (bits instanceof BitSet bitSet) {
- this.maxDoc = Objects.requireNonNull(bitSet).cardinality();
- } else {
- this.maxDoc = maxDoc;
- }
+ this.maxDoc = maxDoc;
}
@Override
@@ -141,9 +137,6 @@ public Bits bits() {
@Override
public DocIdSetIterator iterator() {
- if (bits instanceof BitSet bitSet) {
- return new BitSetIterator(bitSet, maxDoc);
- }
return AcceptDocs.getFilteredDocIdSetIterator(DocIdSetIterator.all(maxDoc), bits);
}
@@ -184,7 +177,7 @@ private void createBitSetAcceptDocsIfNecessary() throws IOException {
@Override
public Bits bits() throws IOException {
createBitSetAcceptDocsIfNecessary();
- return acceptBitSet;
+ return acceptBitSet.asReadOnlyBits();
}
@Override
diff --git a/lucene/core/src/java/org/apache/lucene/util/BitSet.java b/lucene/core/src/java/org/apache/lucene/util/BitSet.java
index 347e05d2e523..1518fe029b60 100644
--- a/lucene/core/src/java/org/apache/lucene/util/BitSet.java
+++ b/lucene/core/src/java/org/apache/lucene/util/BitSet.java
@@ -24,7 +24,10 @@
*
* @lucene.internal
*/
-public abstract class BitSet implements Bits, Accountable {
+// NOTE: It doesn't implement `Bits` on purpose to avoid mistakenly increasing polymorphism by
+// passing either a BitSet or the result of BitSet#asReadOnlyBits to functions that take a `Bits`
+// parameter.
+public abstract class BitSet implements Accountable {
/**
* Build a {@link BitSet} from the content of the provided {@link DocIdSetIterator}. NOTE: this
@@ -53,6 +56,18 @@ public void clear() {
clear(0, length());
}
+ /** Returns the number of bits in this set */
+ public abstract int length();
+
+ /**
+ * Returns the value of the bit with the specified index.
+ *
+ * @param index index, should be non-negative and < {@link #length()}. The result of passing
+ * negative or out of bounds values is undefined by this interface, just don't do it!
+ * @return true if the bit is set, false otherwise.
+ */
+ public abstract boolean get(int index);
+
/** Set the bit at i. */
public abstract void set(int i);
@@ -111,6 +126,14 @@ protected final void checkUnpositioned(DocIdSetIterator iter) {
}
}
+ /**
+ * Convert this instance to read-only {@link Bits}. This is useful in the case that this {@link
+ * BitSet} is returned as a {@link Bits} instance, to make sure that consumers may not get write
+ * access back by casting to a {@link BitSet}. NOTE: Changes to this {@link BitSet} will be
+ * reflected on the returned {@link Bits}.
+ */
+ public abstract Bits asReadOnlyBits();
+
/**
* Does in-place OR of the bits provided by the iterator. The state of the iterator after this
* operation terminates is undefined.
diff --git a/lucene/core/src/java/org/apache/lucene/util/FixedBitSet.java b/lucene/core/src/java/org/apache/lucene/util/FixedBitSet.java
index e896bff8721a..60a388891b91 100644
--- a/lucene/core/src/java/org/apache/lucene/util/FixedBitSet.java
+++ b/lucene/core/src/java/org/apache/lucene/util/FixedBitSet.java
@@ -784,48 +784,21 @@ public int hashCode() {
public static FixedBitSet copyOf(Bits bits) {
if (bits instanceof FixedBits fixedBits) {
// restore the original FixedBitSet
- bits = fixedBits.bitSet;
+ return fixedBits.bitSet.clone();
}
- if (bits instanceof FixedBitSet) {
- return ((FixedBitSet) bits).clone();
- } else {
- int length = bits.length();
- FixedBitSet bitSet = new FixedBitSet(length);
- bitSet.set(0, length);
- for (int i = 0; i < length; ++i) {
- if (bits.get(i) == false) {
- bitSet.clear(i);
- }
- }
- return bitSet;
- }
+ int length = bits.length();
+ FixedBitSet bitSet = new FixedBitSet(length);
+ bitSet.set(0, length);
+ bits.applyMask(bitSet, 0);
+ return bitSet;
}
- /**
- * Convert this instance to read-only {@link Bits}. This is useful in the case that this {@link
- * FixedBitSet} is returned as a {@link Bits} instance, to make sure that consumers may not get
- * write access back by casting to a {@link FixedBitSet}. NOTE: Changes to this {@link
- * FixedBitSet} will be reflected on the returned {@link Bits}.
- */
+ @Override
public Bits asReadOnlyBits() {
return new FixedBits(bits, numBits);
}
- @Override
- public void applyMask(FixedBitSet bitSet, int offset) {
- // Note: Some scorers don't track maxDoc and may thus call this method with an offset that is
- // beyond bitSet.length()
- int length = Math.min(bitSet.length(), length() - offset);
- if (length >= 0) {
- andRange(this, offset, bitSet, 0, length);
- }
- if (length < bitSet.length()
- && bitSet.nextSetBit(Math.max(0, length)) != DocIdSetIterator.NO_MORE_DOCS) {
- throw new IllegalArgumentException("Some bits are set beyond the end of live docs");
- }
- }
-
/**
* For each set bit from {@code from} inclusive to {@code to} exclusive, add {@code base} to the
* bit index and call {@code consumer} on it. This is internally used by queries that use bit sets
diff --git a/lucene/core/src/java/org/apache/lucene/util/FixedBits.java b/lucene/core/src/java/org/apache/lucene/util/FixedBits.java
index 0f0bac82765c..725bccba4723 100644
--- a/lucene/core/src/java/org/apache/lucene/util/FixedBits.java
+++ b/lucene/core/src/java/org/apache/lucene/util/FixedBits.java
@@ -16,6 +16,8 @@
*/
package org.apache.lucene.util;
+import org.apache.lucene.search.DocIdSetIterator;
+
/** Immutable twin of FixedBitSet. */
final class FixedBits implements Bits {
@@ -31,8 +33,17 @@ public boolean get(int index) {
}
@Override
- public void applyMask(FixedBitSet dest, int offset) {
- bitSet.applyMask(dest, offset);
+ public void applyMask(FixedBitSet bitSet, int offset) {
+ // Note: Some scorers don't track maxDoc and may thus call this method with an offset that is
+ // beyond bitSet.length()
+ int length = Math.min(bitSet.length(), length() - offset);
+ if (length >= 0) {
+ FixedBitSet.andRange(this.bitSet, offset, bitSet, 0, length);
+ }
+ if (length < bitSet.length()
+ && bitSet.nextSetBit(Math.max(0, length)) != DocIdSetIterator.NO_MORE_DOCS) {
+ throw new IllegalArgumentException("Some bits are set beyond the end of live docs");
+ }
}
@Override
diff --git a/lucene/core/src/java/org/apache/lucene/util/SparseFixedBitSet.java b/lucene/core/src/java/org/apache/lucene/util/SparseFixedBitSet.java
index 3104948cc4c6..e87986e05778 100644
--- a/lucene/core/src/java/org/apache/lucene/util/SparseFixedBitSet.java
+++ b/lucene/core/src/java/org/apache/lucene/util/SparseFixedBitSet.java
@@ -562,6 +562,11 @@ public void or(DocIdSetIterator it) throws IOException {
}
}
+ @Override
+ public Bits asReadOnlyBits() {
+ return new SparseFixedBits(this);
+ }
+
@Override
public long ramBytesUsed() {
return ramBytesUsed;
diff --git a/lucene/core/src/java/org/apache/lucene/util/SparseFixedBits.java b/lucene/core/src/java/org/apache/lucene/util/SparseFixedBits.java
new file mode 100644
index 000000000000..2369aae838a0
--- /dev/null
+++ b/lucene/core/src/java/org/apache/lucene/util/SparseFixedBits.java
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+package org.apache.lucene.util;
+
+/** Unmodifiable {@link Bits} view on a {@link SparseFixedBitSet}. */
+final class SparseFixedBits implements Bits {
+
+ final SparseFixedBitSet bitSet;
+
+ SparseFixedBits(SparseFixedBitSet bitSet) {
+ this.bitSet = bitSet;
+ }
+
+ @Override
+ public boolean get(int index) {
+ return bitSet.get(index);
+ }
+
+ @Override
+ public int length() {
+ return bitSet.length();
+ }
+}
diff --git a/lucene/core/src/java/org/apache/lucene/util/hnsw/HnswGraphBuilder.java b/lucene/core/src/java/org/apache/lucene/util/hnsw/HnswGraphBuilder.java
index cd437069ff24..98ff5054c987 100644
--- a/lucene/core/src/java/org/apache/lucene/util/hnsw/HnswGraphBuilder.java
+++ b/lucene/core/src/java/org/apache/lucene/util/hnsw/HnswGraphBuilder.java
@@ -32,6 +32,7 @@
import org.apache.lucene.search.KnnCollector;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.search.knn.KnnSearchStrategy;
+import org.apache.lucene.util.Bits;
import org.apache.lucene.util.FixedBitSet;
import org.apache.lucene.util.InfoStream;
import org.apache.lucene.util.hnsw.HnswUtil.Component;
@@ -464,6 +465,7 @@ private void connectComponents() throws IOException {
private boolean connectComponents(int level) throws IOException {
FixedBitSet notFullyConnected = new FixedBitSet(hnsw.size());
+ Bits notFullyConnectedAsBits = notFullyConnected.asReadOnlyBits();
int maxConn = M;
if (level == 0) {
maxConn *= 2;
@@ -501,7 +503,7 @@ private boolean connectComponents(int level) throws IOException {
scorer.setScoringOrdinal(c.start());
// find the closest node in the largest component to the lowest-numbered node in this
// component that has room to make a connection
- graphSearcher.searchLevel(beam, scorer, level, eps, hnsw, notFullyConnected);
+ graphSearcher.searchLevel(beam, scorer, level, eps, hnsw, notFullyConnectedAsBits);
boolean linked = false;
while (beam.size() > 0) {
int c0node = beam.popNode();
diff --git a/lucene/core/src/java/org/apache/lucene/util/hnsw/HnswGraphSearcher.java b/lucene/core/src/java/org/apache/lucene/util/hnsw/HnswGraphSearcher.java
index cd9b013d27ac..969984605718 100644
--- a/lucene/core/src/java/org/apache/lucene/util/hnsw/HnswGraphSearcher.java
+++ b/lucene/core/src/java/org/apache/lucene/util/hnsw/HnswGraphSearcher.java
@@ -82,28 +82,6 @@ public HnswGraphSearcher(NeighborQueue candidates, BitSet visited) {
this.visited = visited;
}
- /**
- * See {@link HnswGraphSearcher#search(RandomVectorScorer, KnnCollector, HnswGraph, Bits, int)}
- *
- * @param scorer the scorer to compare the query with the nodes
- * @param knnCollector a hnsw knn collector of top knn results to be returned
- * @param graph the graph values. May represent the entire graph, or a level in a hierarchical
- * graph.
- * @param acceptOrds {@link Bits} that represents the allowed document ordinals to match, or
- * {@code null} if they are all allowed to match.
- */
- public static void search(
- RandomVectorScorer scorer, KnnCollector knnCollector, HnswGraph graph, Bits acceptOrds)
- throws IOException {
- int filteredDocCount = 0;
- if (acceptOrds instanceof BitSet bitSet) {
- // Use approximate cardinality as this is good enough, but ensure we don't exceed the graph
- // size as that is illogical
- filteredDocCount = Math.min(bitSet.approximateCardinality(), graph.size());
- }
- search(scorer, knnCollector, graph, acceptOrds, filteredDocCount);
- }
-
/**
* Searches the HNSW graph for the nearest neighbors of a query vector. If entry points are
* directly provided via the knnCollector, then the search will be initialized at those points.
diff --git a/lucene/core/src/test/org/apache/lucene/index/TestSegmentMerger.java b/lucene/core/src/test/org/apache/lucene/index/TestSegmentMerger.java
index 432f02580605..e090f7cf9cd1 100644
--- a/lucene/core/src/test/org/apache/lucene/index/TestSegmentMerger.java
+++ b/lucene/core/src/test/org/apache/lucene/index/TestSegmentMerger.java
@@ -179,7 +179,7 @@ public void testBuildDocMap() {
}
}
- final PackedLongValues docMap = MergeState.removeDeletes(maxDoc, liveDocs);
+ final PackedLongValues docMap = MergeState.removeDeletes(maxDoc, liveDocs.asReadOnlyBits());
// assert the mapping is compact
for (int i = 0, del = 0; i < maxDoc; ++i) {
diff --git a/lucene/core/src/test/org/apache/lucene/search/TestDenseConjunctionBulkScorer.java b/lucene/core/src/test/org/apache/lucene/search/TestDenseConjunctionBulkScorer.java
index ff220e2d0e2b..3d8b40f584e8 100644
--- a/lucene/core/src/test/org/apache/lucene/search/TestDenseConjunctionBulkScorer.java
+++ b/lucene/core/src/test/org/apache/lucene/search/TestDenseConjunctionBulkScorer.java
@@ -112,7 +112,7 @@ public void collect(int doc) throws IOException {
result.set(doc);
}
},
- acceptDocs,
+ acceptDocs.asReadOnlyBits(),
0,
DocIdSetIterator.NO_MORE_DOCS);
@@ -128,7 +128,7 @@ public void collect(int doc) throws IOException {
maxDoc,
0f);
CountingLeafCollector collector = new CountingLeafCollector();
- scorer.score(collector, acceptDocs, 0, DocIdSetIterator.NO_MORE_DOCS);
+ scorer.score(collector, acceptDocs.asReadOnlyBits(), 0, DocIdSetIterator.NO_MORE_DOCS);
assertEquals(acceptDocs.cardinality(), collector.count);
}
@@ -365,7 +365,7 @@ public void collect(int doc) throws IOException {
result.set(doc);
}
},
- acceptDocs,
+ acceptDocs.asReadOnlyBits(),
0,
DocIdSetIterator.NO_MORE_DOCS);
@@ -379,7 +379,7 @@ public void collect(int doc) throws IOException {
maxDoc,
0f);
CountingLeafCollector collector = new CountingLeafCollector();
- scorer.score(collector, acceptDocs, 0, DocIdSetIterator.NO_MORE_DOCS);
+ scorer.score(collector, acceptDocs.asReadOnlyBits(), 0, DocIdSetIterator.NO_MORE_DOCS);
assertEquals(acceptDocs.cardinality(), collector.count);
}
@@ -459,7 +459,7 @@ public void collect(int doc) throws IOException {
result.set(doc);
}
},
- acceptDocs,
+ acceptDocs.asReadOnlyBits(),
0,
DocIdSetIterator.NO_MORE_DOCS);
@@ -477,7 +477,7 @@ public void collect(int doc) throws IOException {
maxDoc,
0f);
CountingLeafCollector collector = new CountingLeafCollector();
- scorer.score(collector, acceptDocs, 0, DocIdSetIterator.NO_MORE_DOCS);
+ scorer.score(collector, acceptDocs.asReadOnlyBits(), 0, DocIdSetIterator.NO_MORE_DOCS);
assertEquals(expected.cardinality(), collector.count);
}
@@ -626,7 +626,7 @@ public void collect(int doc) throws IOException {
result.set(doc);
}
},
- acceptDocs,
+ acceptDocs.asReadOnlyBits(),
0,
DocIdSetIterator.NO_MORE_DOCS);
@@ -643,7 +643,7 @@ public void collect(int doc) throws IOException {
Collections.shuffle(clauses, random());
scorer = new DenseConjunctionBulkScorer(clauses, Collections.emptyList(), maxDoc, 0f);
CountingLeafCollector collector = new CountingLeafCollector();
- scorer.score(collector, acceptDocs, 0, DocIdSetIterator.NO_MORE_DOCS);
+ scorer.score(collector, acceptDocs.asReadOnlyBits(), 0, DocIdSetIterator.NO_MORE_DOCS);
assertEquals(expected.cardinality(), collector.count);
}
@@ -732,7 +732,7 @@ public void collect(int doc) throws IOException {
result.set(doc);
}
},
- acceptDocs,
+ acceptDocs.asReadOnlyBits(),
0,
DocIdSetIterator.NO_MORE_DOCS);
@@ -748,7 +748,7 @@ public void collect(int doc) throws IOException {
Collections.shuffle(clauses, random());
scorer = new DenseConjunctionBulkScorer(clauses, Collections.emptyList(), maxDoc, 0f);
CountingLeafCollector collector = new CountingLeafCollector();
- scorer.score(collector, acceptDocs, 0, DocIdSetIterator.NO_MORE_DOCS);
+ scorer.score(collector, acceptDocs.asReadOnlyBits(), 0, DocIdSetIterator.NO_MORE_DOCS);
assertEquals(expected.cardinality(), collector.count);
}
@@ -854,7 +854,7 @@ public void collect(int doc) throws IOException {
result.set(doc);
}
},
- acceptDocs,
+ acceptDocs.asReadOnlyBits(),
0,
DocIdSetIterator.NO_MORE_DOCS);
@@ -876,7 +876,7 @@ public void collect(int doc) throws IOException {
maxDoc,
0f);
CountingLeafCollector collector = new CountingLeafCollector();
- scorer.score(collector, acceptDocs, 0, DocIdSetIterator.NO_MORE_DOCS);
+ scorer.score(collector, acceptDocs.asReadOnlyBits(), 0, DocIdSetIterator.NO_MORE_DOCS);
assertEquals(expected.cardinality(), collector.count);
}
diff --git a/lucene/core/src/test/org/apache/lucene/search/TestScorerUtil.java b/lucene/core/src/test/org/apache/lucene/search/TestScorerUtil.java
index fb3f031ef8d1..579ab2dc1692 100644
--- a/lucene/core/src/test/org/apache/lucene/search/TestScorerUtil.java
+++ b/lucene/core/src/test/org/apache/lucene/search/TestScorerUtil.java
@@ -44,7 +44,7 @@ public class TestScorerUtil extends LuceneTestCase {
public void testLikelyFixedBits() throws IOException {
assertNull(ScorerUtil.likelyLiveDocs(null));
- Bits bits1 = new SparseFixedBitSet(10);
+ Bits bits1 = new SparseFixedBitSet(10).asReadOnlyBits();
assertNotSame(bits1, ScorerUtil.likelyLiveDocs(bits1));
Bits bits2 = new Bits.MatchAllBits(10);
assertNotSame(bits2, ScorerUtil.likelyLiveDocs(bits2));
diff --git a/lucene/core/src/test/org/apache/lucene/util/TestFixedBitSet.java b/lucene/core/src/test/org/apache/lucene/util/TestFixedBitSet.java
index efdebee71585..2d8ce69d148d 100644
--- a/lucene/core/src/test/org/apache/lucene/util/TestFixedBitSet.java
+++ b/lucene/core/src/test/org/apache/lucene/util/TestFixedBitSet.java
@@ -576,14 +576,13 @@ public void testCopyOf() {
for (int e : bits) {
fixedBitSet.set(e);
}
- for (boolean readOnly : new boolean[] {false, true}) {
- Bits bitsToCopy = readOnly ? fixedBitSet.asReadOnlyBits() : fixedBitSet;
- FixedBitSet mutableCopy = FixedBitSet.copyOf(bitsToCopy);
- assertNotSame(mutableCopy, bitsToCopy);
- assertEquals(mutableCopy, fixedBitSet);
- }
- final Bits bitsToCopy =
+ Bits bitsToCopy1 = fixedBitSet.asReadOnlyBits();
+ FixedBitSet mutableCopy1 = FixedBitSet.copyOf(bitsToCopy1);
+ assertNotSame(mutableCopy1, bitsToCopy1);
+ assertEquals(mutableCopy1, fixedBitSet);
+
+ final Bits bitsToCopy2 =
new Bits() {
@Override
@@ -596,11 +595,11 @@ public int length() {
return fixedBitSet.length();
}
};
- FixedBitSet mutableCopy = FixedBitSet.copyOf(bitsToCopy);
+ FixedBitSet mutableCopy2 = FixedBitSet.copyOf(bitsToCopy2);
- assertNotSame(bitsToCopy, mutableCopy);
- assertNotSame(fixedBitSet, mutableCopy);
- assertEquals(mutableCopy, fixedBitSet);
+ assertNotSame(bitsToCopy2, mutableCopy2);
+ assertNotSame(fixedBitSet, mutableCopy2);
+ assertEquals(mutableCopy2, fixedBitSet);
}
public void testAsBits() {
@@ -609,7 +608,6 @@ public void testAsBits() {
set.set(4);
set.set(9);
Bits bits = set.asReadOnlyBits();
- assertFalse(bits instanceof FixedBitSet);
assertEquals(set.length(), bits.length());
for (int i = 0; i < set.length(); ++i) {
assertEquals(set.get(i), bits.get(i));
diff --git a/lucene/core/src/test/org/apache/lucene/util/hnsw/HnswGraphTestCase.java b/lucene/core/src/test/org/apache/lucene/util/hnsw/HnswGraphTestCase.java
index be099398ba6a..cf1b464ff6ae 100644
--- a/lucene/core/src/test/org/apache/lucene/util/hnsw/HnswGraphTestCase.java
+++ b/lucene/core/src/test/org/apache/lucene/util/hnsw/HnswGraphTestCase.java
@@ -518,7 +518,7 @@ public void testSearchWithSelectiveAcceptOrds() throws IOException {
buildScorer(vectors, getTargetVector()),
numAccepted,
hnsw,
- acceptOrds,
+ acceptOrds.asReadOnlyBits(),
Integer.MAX_VALUE);
TopDocs nodes = nn.topDocs();
assertEquals(numAccepted, nodes.scoreDocs.length);
@@ -773,7 +773,11 @@ public TopDocs topDocs() {
}
};
HnswGraphSearcher.search(
- buildScorer(vectorValues, target), collector, hnsw, new BitSet.MatchAllBits(numVectors));
+ buildScorer(vectorValues, target),
+ collector,
+ hnsw,
+ new Bits.MatchAllBits(numVectors),
+ hnsw.size());
assertEquals(numVectors, collector.numCollected());
}
@@ -1096,7 +1100,8 @@ public int numLevels() {
RandomVectorScorer queryScorer = buildScorer(docVectors, vectorValue(queryVectors, 0));
KnnCollector collector = new TopKnnCollector(topK, Integer.MAX_VALUE);
- HnswGraphSearcher.search(queryScorer, collector, singleLevelGraph, null);
+ HnswGraphSearcher.search(
+ queryScorer, collector, singleLevelGraph, null, singleLevelGraph.size());
// Check that we visit all nodes
assertEquals(graph.size(), collector.visitedCount());
@@ -1320,7 +1325,7 @@ private static Bits createRandomAcceptOrds(int startIndex, int length) {
bits.set(i);
}
}
- return bits;
+ return bits.asReadOnlyBits();
}
static float[] randomVector(Random random, int dim) {
diff --git a/lucene/core/src/test/org/apache/lucene/util/hnsw/TestHnswFloatVectorGraph.java b/lucene/core/src/test/org/apache/lucene/util/hnsw/TestHnswFloatVectorGraph.java
index 52d1da3dfa83..f85a448815fc 100644
--- a/lucene/core/src/test/org/apache/lucene/util/hnsw/TestHnswFloatVectorGraph.java
+++ b/lucene/core/src/test/org/apache/lucene/util/hnsw/TestHnswFloatVectorGraph.java
@@ -132,7 +132,11 @@ public void testSearchWithSkewedAcceptOrds() throws IOException {
}
KnnCollector nn =
HnswGraphSearcher.search(
- buildScorer(vectors, getTargetVector()), 10, hnsw, acceptOrds, Integer.MAX_VALUE);
+ buildScorer(vectors, getTargetVector()),
+ 10,
+ hnsw,
+ acceptOrds.asReadOnlyBits(),
+ Integer.MAX_VALUE);
TopDocs nodes = nn.topDocs();
assertEquals("Number of found results is not equal to [10].", 10, nodes.scoreDocs.length);
diff --git a/lucene/grouping/src/java/org/apache/lucene/search/grouping/GroupingSearch.java b/lucene/grouping/src/java/org/apache/lucene/search/grouping/GroupingSearch.java
index ce97e2a87055..66154d934483 100644
--- a/lucene/grouping/src/java/org/apache/lucene/search/grouping/GroupingSearch.java
+++ b/lucene/grouping/src/java/org/apache/lucene/search/grouping/GroupingSearch.java
@@ -162,7 +162,9 @@ protected TopGroups groupByFieldOrFunction(
matchingGroups = allGroups ? allGroupsCollector.getGroups() : Collections.emptyList();
matchingGroupHeads =
allGroupHeads
- ? allGroupHeadsCollector.retrieveGroupHeads(searcher.getIndexReader().maxDoc())
+ ? allGroupHeadsCollector
+ .retrieveGroupHeads(searcher.getIndexReader().maxDoc())
+ .asReadOnlyBits()
: new Bits.MatchNoBits(searcher.getIndexReader().maxDoc());
Collection topSearchGroups = firstPassCollector.getTopGroups(groupOffset);
diff --git a/lucene/grouping/src/test/org/apache/lucene/search/grouping/TestAllGroupHeadsCollector.java b/lucene/grouping/src/test/org/apache/lucene/search/grouping/TestAllGroupHeadsCollector.java
index 2e54b1158f2e..8bd163bc630f 100644
--- a/lucene/grouping/src/test/org/apache/lucene/search/grouping/TestAllGroupHeadsCollector.java
+++ b/lucene/grouping/src/test/org/apache/lucene/search/grouping/TestAllGroupHeadsCollector.java
@@ -50,7 +50,7 @@
import org.apache.lucene.tests.index.RandomIndexWriter;
import org.apache.lucene.tests.util.LuceneTestCase;
import org.apache.lucene.tests.util.TestUtil;
-import org.apache.lucene.util.Bits;
+import org.apache.lucene.util.BitSet;
import org.apache.lucene.util.BytesRef;
import org.apache.lucene.util.FixedBitSet;
@@ -431,10 +431,9 @@ private boolean arrayContains(int[] expected, int[] actual) {
return true;
}
- private boolean openBitSetContains(int[] expectedDocs, Bits actual, int maxDoc)
+ private boolean openBitSetContains(int[] expectedDocs, BitSet actual, int maxDoc)
throws IOException {
- assert actual instanceof FixedBitSet;
- if (expectedDocs.length != ((FixedBitSet) actual).cardinality()) {
+ if (expectedDocs.length != actual.cardinality()) {
return false;
}
diff --git a/lucene/join/src/test/org/apache/lucene/search/join/TestBlockJoinSelector.java b/lucene/join/src/test/org/apache/lucene/search/join/TestBlockJoinSelector.java
index bcf1abffe252..a478c8c5d9e5 100644
--- a/lucene/join/src/test/org/apache/lucene/search/join/TestBlockJoinSelector.java
+++ b/lucene/join/src/test/org/apache/lucene/search/join/TestBlockJoinSelector.java
@@ -57,7 +57,8 @@ public void testDocsWithValue() {
childDocsWithValue.set(8);
childDocsWithValue.set(16);
- final Bits docsWithValue = BlockJoinSelector.wrap(childDocsWithValue, parents, children);
+ final Bits docsWithValue =
+ BlockJoinSelector.wrap(childDocsWithValue.asReadOnlyBits(), parents, children);
assertFalse(docsWithValue.get(0));
assertTrue(docsWithValue.get(5));
assertFalse(docsWithValue.get(6));
@@ -259,7 +260,7 @@ public void testNumericSelector() throws Exception {
final NumericDocValues mins =
BlockJoinSelector.wrap(
- DocValues.singleton(new CannedNumericDocValues(longs, docsWithValue)),
+ DocValues.singleton(new CannedNumericDocValues(longs, docsWithValue.asReadOnlyBits())),
BlockJoinSelector.Type.MIN,
parents,
toIter(children));
@@ -271,7 +272,7 @@ public void testNumericSelector() throws Exception {
final NumericDocValues maxs =
BlockJoinSelector.wrap(
- DocValues.singleton(new CannedNumericDocValues(longs, docsWithValue)),
+ DocValues.singleton(new CannedNumericDocValues(longs, docsWithValue.asReadOnlyBits())),
BlockJoinSelector.Type.MAX,
parents,
toIter(children));
diff --git a/lucene/join/src/test/org/apache/lucene/search/join/TestBlockJoinValidation.java b/lucene/join/src/test/org/apache/lucene/search/join/TestBlockJoinValidation.java
index ffe4c3840d5f..37c32be27721 100644
--- a/lucene/join/src/test/org/apache/lucene/search/join/TestBlockJoinValidation.java
+++ b/lucene/join/src/test/org/apache/lucene/search/join/TestBlockJoinValidation.java
@@ -40,7 +40,7 @@
import org.apache.lucene.tests.analysis.MockAnalyzer;
import org.apache.lucene.tests.util.LuceneTestCase;
import org.apache.lucene.tests.util.TestUtil;
-import org.apache.lucene.util.Bits;
+import org.apache.lucene.util.BitSet;
public class TestBlockJoinValidation extends LuceneTestCase {
@@ -131,7 +131,7 @@ public void testAdvanceValidationForToChildBjq() throws Exception {
indexSearcher.createWeight(
indexSearcher.rewrite(blockJoinQuery), org.apache.lucene.search.ScoreMode.COMPLETE, 1);
Scorer scorer = weight.scorer(context);
- final Bits parentDocs = parentsFilter.getBitSet(context);
+ final BitSet parentDocs = parentsFilter.getBitSet(context);
int target;
do {
diff --git a/lucene/misc/src/java/org/apache/lucene/misc/index/IndexRearranger.java b/lucene/misc/src/java/org/apache/lucene/misc/index/IndexRearranger.java
index 7c3bd53cece9..900dcc8f6135 100644
--- a/lucene/misc/src/java/org/apache/lucene/misc/index/IndexRearranger.java
+++ b/lucene/misc/src/java/org/apache/lucene/misc/index/IndexRearranger.java
@@ -234,7 +234,7 @@ private static void applyDeletes(
private static void applyDeletesToOneSegment(
IndexWriter writer, CodecReader segmentReader, DocumentSelector selector) throws IOException {
- Bits deletedDocs = selector.getFilteredDocs(segmentReader);
+ Bits deletedDocs = selector.getFilteredDocs(segmentReader).asReadOnlyBits();
for (int docid = 0; docid < segmentReader.maxDoc(); ++docid) {
if (deletedDocs.get(docid)) {
if (writer.tryDeleteDocument(segmentReader, docid) == -1) {
@@ -247,13 +247,14 @@ private static void applyDeletesToOneSegment(
private static class DocSelectorFilteredCodecReader extends FilterCodecReader {
- BitSet filteredLiveDocs;
+ Bits filteredLiveDocs;
int numDocs;
public DocSelectorFilteredCodecReader(CodecReader in, DocumentSelector selector)
throws IOException {
super(in);
- filteredLiveDocs = selector.getFilteredDocs(in);
+ BitSet filteredLiveDocs = selector.getFilteredDocs(in);
+ this.filteredLiveDocs = filteredLiveDocs.asReadOnlyBits();
numDocs = filteredLiveDocs.cardinality();
}
diff --git a/lucene/misc/src/java/org/apache/lucene/misc/index/MultiPassIndexSplitter.java b/lucene/misc/src/java/org/apache/lucene/misc/index/MultiPassIndexSplitter.java
index b3ec2f7d461b..9a2b4740997c 100644
--- a/lucene/misc/src/java/org/apache/lucene/misc/index/MultiPassIndexSplitter.java
+++ b/lucene/misc/src/java/org/apache/lucene/misc/index/MultiPassIndexSplitter.java
@@ -230,6 +230,7 @@ final List extends FakeDeleteLeafIndexReader> getSequentialSubReadersWrapper()
private static final class FakeDeleteLeafIndexReader extends FilterCodecReader {
FixedBitSet liveDocs;
+ Bits readOnlyLiveDocs;
public FakeDeleteLeafIndexReader(CodecReader reader) {
super(reader);
@@ -255,6 +256,7 @@ public void undeleteAll() {
// mark all docs as valid
liveDocs.set(0, maxDoc);
}
+ readOnlyLiveDocs = liveDocs.asReadOnlyBits();
}
public void deleteDocument(int n) {
@@ -263,7 +265,7 @@ public void deleteDocument(int n) {
@Override
public Bits getLiveDocs() {
- return liveDocs;
+ return readOnlyLiveDocs;
}
@Override
diff --git a/lucene/misc/src/java/org/apache/lucene/misc/index/PKIndexSplitter.java b/lucene/misc/src/java/org/apache/lucene/misc/index/PKIndexSplitter.java
index 8caa9d6b5e9e..91b659c067bb 100644
--- a/lucene/misc/src/java/org/apache/lucene/misc/index/PKIndexSplitter.java
+++ b/lucene/misc/src/java/org/apache/lucene/misc/index/PKIndexSplitter.java
@@ -161,7 +161,7 @@ public DocumentFilteredLeafIndexReader(
}
}
- this.liveDocs = bits;
+ this.liveDocs = bits.asReadOnlyBits();
this.numDocs = bits.cardinality();
}
diff --git a/lucene/sandbox/src/java/org/apache/lucene/sandbox/codecs/faiss/FaissLibraryNativeImpl.java b/lucene/sandbox/src/java/org/apache/lucene/sandbox/codecs/faiss/FaissLibraryNativeImpl.java
index 08ec8264131c..268d7f9e0a80 100644
--- a/lucene/sandbox/src/java/org/apache/lucene/sandbox/codecs/faiss/FaissLibraryNativeImpl.java
+++ b/lucene/sandbox/src/java/org/apache/lucene/sandbox/codecs/faiss/FaissLibraryNativeImpl.java
@@ -319,8 +319,7 @@ public void search(float[] query, KnnCollector knnCollector, AcceptDocs acceptDo
FixedBitSet fixedBitSet =
switch (acceptDocs.bits()) {
case null -> null;
- case FixedBitSet bitSet -> bitSet;
- // TODO: Add optimized case for SparseFixedBitSet
+ // TODO: can we avoid copying when bits are instances of (Sparse)FixedBits?
case Bits bits -> FixedBitSet.copyOf(bits);
};
diff --git a/lucene/sandbox/src/java/org/apache/lucene/sandbox/codecs/idversion/IDVersionPostingsFormat.java b/lucene/sandbox/src/java/org/apache/lucene/sandbox/codecs/idversion/IDVersionPostingsFormat.java
index de42862cc659..9efe12fa0227 100644
--- a/lucene/sandbox/src/java/org/apache/lucene/sandbox/codecs/idversion/IDVersionPostingsFormat.java
+++ b/lucene/sandbox/src/java/org/apache/lucene/sandbox/codecs/idversion/IDVersionPostingsFormat.java
@@ -79,7 +79,9 @@ public IDVersionPostingsFormat(int minTermsInBlock, int maxTermsInBlock) {
@Override
public FieldsConsumer fieldsConsumer(SegmentWriteState state) throws IOException {
- PostingsWriterBase postingsWriter = new IDVersionPostingsWriter(state.liveDocs);
+ PostingsWriterBase postingsWriter =
+ new IDVersionPostingsWriter(
+ state.liveDocs == null ? null : state.liveDocs.asReadOnlyBits());
try {
return new VersionBlockTreeTermsWriter(
state, postingsWriter, minTermsInBlock, maxTermsInBlock);
diff --git a/lucene/spatial-extras/src/test/org/apache/lucene/spatial/prefix/TestNumberRangeFacets.java b/lucene/spatial-extras/src/test/org/apache/lucene/spatial/prefix/TestNumberRangeFacets.java
index 168319f0f33c..62433745c70a 100644
--- a/lucene/spatial-extras/src/test/org/apache/lucene/spatial/prefix/TestNumberRangeFacets.java
+++ b/lucene/spatial-extras/src/test/org/apache/lucene/spatial/prefix/TestNumberRangeFacets.java
@@ -216,8 +216,9 @@ public void test() throws IOException {
}
private Bits searchForDocBits(Query query) throws IOException {
- return indexSearcher.search(
- query, FixedBitSetCollector.createManager(indexSearcher.getIndexReader().maxDoc()));
+ return indexSearcher
+ .search(query, FixedBitSetCollector.createManager(indexSearcher.getIndexReader().maxDoc()))
+ .asReadOnlyBits();
}
private void preQueryHavoc() {
diff --git a/lucene/suggest/src/test/org/apache/lucene/search/suggest/document/TestPrefixCompletionQuery.java b/lucene/suggest/src/test/org/apache/lucene/search/suggest/document/TestPrefixCompletionQuery.java
index 6670a0b78538..da3bfd0c7a0a 100644
--- a/lucene/suggest/src/test/org/apache/lucene/search/suggest/document/TestPrefixCompletionQuery.java
+++ b/lucene/suggest/src/test/org/apache/lucene/search/suggest/document/TestPrefixCompletionQuery.java
@@ -96,7 +96,7 @@ public Bits getBits(final LeafReaderContext context) throws IOException {
}
}
}
- return bits;
+ return bits.asReadOnlyBits();
}
}
diff --git a/lucene/test-framework/src/java/org/apache/lucene/tests/index/BaseLiveDocsFormatTestCase.java b/lucene/test-framework/src/java/org/apache/lucene/tests/index/BaseLiveDocsFormatTestCase.java
index 64d3549c5743..c6aaee53400d 100644
--- a/lucene/test-framework/src/java/org/apache/lucene/tests/index/BaseLiveDocsFormatTestCase.java
+++ b/lucene/test-framework/src/java/org/apache/lucene/tests/index/BaseLiveDocsFormatTestCase.java
@@ -104,9 +104,9 @@ private void testSerialization(int maxDoc, int numLiveDocs, boolean fixedBitSet)
final Bits bits;
if (fixedBitSet) {
- bits = liveDocs;
+ bits = liveDocs.asReadOnlyBits();
} else {
- // Make sure the impl doesn't only work with a FixedBitSet
+ // Make sure the impl doesn't only work with FixedBitSet#asReadOnlyBits
bits =
new Bits() {
diff --git a/lucene/test-framework/src/java/org/apache/lucene/tests/util/BaseBitSetTestCase.java b/lucene/test-framework/src/java/org/apache/lucene/tests/util/BaseBitSetTestCase.java
index 205d8861322a..1d89a08cb275 100644
--- a/lucene/test-framework/src/java/org/apache/lucene/tests/util/BaseBitSetTestCase.java
+++ b/lucene/test-framework/src/java/org/apache/lucene/tests/util/BaseBitSetTestCase.java
@@ -27,6 +27,7 @@
import org.apache.lucene.util.BitDocIdSet;
import org.apache.lucene.util.BitSet;
import org.apache.lucene.util.BitSetIterator;
+import org.apache.lucene.util.Bits;
import org.apache.lucene.util.FixedBitSet;
import org.apache.lucene.util.RoaringDocIdSet;
import org.apache.lucene.util.SparseFixedBitSet;
@@ -348,5 +349,21 @@ public int nextSetBit(int start, int upperBound) {
}
return next;
}
+
+ @Override
+ public Bits asReadOnlyBits() {
+ return new Bits() {
+
+ @Override
+ public boolean get(int index) {
+ return JavaUtilBitSet.this.get(index);
+ }
+
+ @Override
+ public int length() {
+ return JavaUtilBitSet.this.length();
+ }
+ };
+ }
}
}