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 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(); + } + }; + } } }