diff --git a/.gitignore b/.gitignore
index f7ee48d3..d288869d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,9 +1,14 @@
# SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION.
# SPDX-License-Identifier: Apache-2.0
+# Maven
*.jar
-target
-**/.DS_Store
-bin
+target/
+
+# IDE - IntelliJ
+.idea/
+*.iml
+
+# IDE - Eclipse
.project
cuvs-workdir
diff --git a/bench/pom.xml b/bench/pom.xml
index 04ea66c5..4271d802 100644
--- a/bench/pom.xml
+++ b/bench/pom.xml
@@ -11,7 +11,7 @@
com.nvidia.cuvs.lucene.benchmarkscuvs-lucene-benchmarks
- 26.08.0
+ 26.08.0-SNAPSHOTjarcuvs-lucene-benchmarks
@@ -85,7 +85,7 @@
com.nvidia.cuvs.lucenecuvs-lucene
- 26.08.0
+ 26.08.0-SNAPSHOTcommons-io
diff --git a/build.sh b/build.sh
index 4ec8c087..3c86351c 100755
--- a/build.sh
+++ b/build.sh
@@ -1,6 +1,6 @@
#!/bin/bash
-# SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION.
+# SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
set -e -u -o pipefail
@@ -8,7 +8,7 @@ set -e -u -o pipefail
ARGS="$*"
NUMARGS=$#
-VERSION="26.08.0" # Note: The version is updated automatically when ci/release/update-version.sh is invoked
+VERSION="26.08.0-SNAPSHOT" # Note: The version is updated automatically when ci/release/update-version.sh is invoked
GROUP_ID="com.nvidia.cuvs.lucene"
function hasArg {
diff --git a/examples/pom.xml b/examples/pom.xml
index c7191234..fd277c3e 100644
--- a/examples/pom.xml
+++ b/examples/pom.xml
@@ -11,7 +11,7 @@
com.nvidia.cuvs.lucene.examplesexamples
- 26.08.0
+ 26.08.0-SNAPSHOTexamples
@@ -100,7 +100,7 @@
com.nvidia.cuvs.lucenecuvs-lucene
- 26.08.0
+ 26.08.0-SNAPSHOT
diff --git a/examples/src/main/java/com/nvidia/cuvs/lucene/examples/IndexAndSearchonGPUExample.java b/examples/src/main/java/com/nvidia/cuvs/lucene/examples/IndexAndSearchonGPUExample.java
index 834544fe..dd920001 100644
--- a/examples/src/main/java/com/nvidia/cuvs/lucene/examples/IndexAndSearchonGPUExample.java
+++ b/examples/src/main/java/com/nvidia/cuvs/lucene/examples/IndexAndSearchonGPUExample.java
@@ -1,5 +1,5 @@
/*
- * SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION.
+ * SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package com.nvidia.cuvs.lucene.examples;
@@ -30,9 +30,12 @@
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.index.LeafReader;
import org.apache.lucene.index.LeafReaderContext;
+import org.apache.lucene.index.Term;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.KnnFloatVectorQuery;
+import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
+import org.apache.lucene.search.TermQuery;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
@@ -56,6 +59,7 @@ public static void main(String[] args) throws Exception {
final int COMMIT_FREQ = 2000;
final String ID_FIELD = "id";
final String VECTOR_FIELD = "vector_field";
+ final String STATUS_FIELD = "status";
int numDocs = 2000;
int dimension = 32;
@@ -70,6 +74,9 @@ public static void main(String[] args) throws Exception {
Document document = new Document();
document.add(new StringField(ID_FIELD, Integer.toString(i), Field.Store.YES));
document.add(new KnnFloatVectorField(VECTOR_FIELD, dataset[i], EUCLIDEAN));
+ // Alternatively flip the status's for documents as an example.
+ document.add(
+ new StringField(STATUS_FIELD, i % 2 == 0 ? "active" : "in-active", Field.Store.YES));
indexWriter.addDocument(document);
count -= 1;
if (count == 0) {
@@ -103,8 +110,11 @@ public static void main(String[] args) throws Exception {
float[] queryVector = generateDataset(random, 1, dimension)[0];
log.log(Level.FINE, "Query vector: " + Arrays.toString(queryVector));
+ // Make a filter query
+ Query filter = new TermQuery(new Term(STATUS_FIELD, "active"));
+
KnnFloatVectorQuery query =
- new GPUKnnFloatVectorQuery(VECTOR_FIELD, queryVector, topK, null, topK, 1);
+ new GPUKnnFloatVectorQuery(VECTOR_FIELD, queryVector, topK, filter, topK, 1);
TopDocs results = searcher.search(query, topK);
log.log(Level.FINE, "Search results (" + results.totalHits + " total hits):");
@@ -113,12 +123,15 @@ public static void main(String[] args) throws Exception {
ScoreDoc scoreDoc = results.scoreDocs[i];
Document doc = searcher.storedFields().document(scoreDoc.doc);
String id = doc.get(ID_FIELD);
+ String status = doc.get(STATUS_FIELD);
log.log(
Level.FINE,
" Rank "
+ (i + 1)
+ ": doc "
+ scoreDoc.doc
+ + " status "
+ + status
+ " (id="
+ id
+ "), score="
diff --git a/license-header.txt b/license-header.txt
new file mode 100644
index 00000000..f9b093f8
--- /dev/null
+++ b/license-header.txt
@@ -0,0 +1,4 @@
+/*
+ * SPDX-FileCopyrightText: Copyright (c) $YEAR, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
diff --git a/pom.xml b/pom.xml
index 14390391..0433ecc4 100644
--- a/pom.xml
+++ b/pom.xml
@@ -12,7 +12,7 @@
com.nvidia.cuvs.lucenecuvs-lucene
- 26.08.0
+ 26.08.0-SNAPSHOTcuvs-lucenejar
@@ -36,6 +36,18 @@
https://github.com/rapidsai/cuvs-lucene
+
+
+ rapidsai
+ rapidsai
+ cuvs@rapids.ai
+
+ Maintainer
+ Committer
+
+
+
+
2222
@@ -68,6 +80,12 @@
false
+
+ central-snapshots
+ https://central.sonatype.com/repository/maven-snapshots/
+ true
+ false
+
@@ -129,10 +147,17 @@
com.nvidia.cuvscuvs-java
- 26.08.0
+ 26.08.0-SNAPSHOT
+
+
+ central
+ https://central.sonatype.com/repository/maven-snapshots/
+
+
+
@@ -158,6 +183,9 @@
truefalse
+
+ ${project.basedir}/license-header.txt
+
@@ -219,6 +247,7 @@
jar
+ com.nvidia.cuvs.luceneall,-missing${project.build.directory}/javadocs
diff --git a/src/main/java/com/nvidia/cuvs/lucene/CuVS2510GPUVectorsFormat.java b/src/main/java/com/nvidia/cuvs/lucene/CuVS2510GPUVectorsFormat.java
index ccc61eae..1abb9e58 100644
--- a/src/main/java/com/nvidia/cuvs/lucene/CuVS2510GPUVectorsFormat.java
+++ b/src/main/java/com/nvidia/cuvs/lucene/CuVS2510GPUVectorsFormat.java
@@ -1,5 +1,5 @@
/*
- * SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION.
+ * SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package com.nvidia.cuvs.lucene;
@@ -7,6 +7,7 @@
import static com.nvidia.cuvs.lucene.ThreadLocalCuVSResourcesProvider.assertIsSupported;
import com.nvidia.cuvs.LibraryException;
+import com.nvidia.cuvs.spi.CuVSProvider;
import java.io.IOException;
import org.apache.lucene.codecs.KnnVectorsFormat;
import org.apache.lucene.codecs.KnnVectorsReader;
@@ -39,6 +40,7 @@ public class CuVS2510GPUVectorsFormat extends KnnVectorsFormat {
static {
try {
+ CuVSProvider.provider().enableRMMAsyncMemory();
LUCENE_PROVIDER = LuceneProvider.getInstance("99");
FLAT_VECTORS_FORMAT =
LUCENE_PROVIDER.getLuceneFlatVectorsFormatInstance(DefaultFlatVectorScorer.INSTANCE);
diff --git a/src/main/java/com/nvidia/cuvs/lucene/CuVS2510GPUVectorsReader.java b/src/main/java/com/nvidia/cuvs/lucene/CuVS2510GPUVectorsReader.java
index c783660d..86955beb 100644
--- a/src/main/java/com/nvidia/cuvs/lucene/CuVS2510GPUVectorsReader.java
+++ b/src/main/java/com/nvidia/cuvs/lucene/CuVS2510GPUVectorsReader.java
@@ -1,5 +1,5 @@
/*
- * SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION.
+ * SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package com.nvidia.cuvs.lucene;
@@ -443,6 +443,9 @@ public void search(String field, float[] target, KnnCollector knnCollector, Bits
new CagraSearchParams.Builder()
.withItopkSize(Math.max(collector.getiTopK(), topK))
.withSearchWidth(collector.getSearchWidth())
+ .withThreadBlockSize(collector.getThreadBlockSize())
+ .withMaxIterations(collector.getMaxIterations())
+ .withAlgo(collector.getSearchAlgo())
.build();
} else {
// Setting itopK as topK because in any case iTopK should be ATLEAST equal to topK
@@ -600,6 +603,37 @@ private static void checkVersion(int versionMeta, int versionVectorData, IndexIn
}
}
+ /**
+ * Maps a local segment ordinal to a Lucene doc ID within this segment.
+ *
+ *
Used by {@link GPUKnnFloatVectorQuery} after a multi-segment GPU search to convert
+ * select_k result ordinals to doc IDs before adding {@code docBase}.
+ *
+ * @param field the vector field name
+ * @param ordinal the local ordinal returned by CAGRA
+ * @return the Lucene doc ID within this segment
+ * @throws IOException if the vector values cannot be read
+ */
+ public int ordToDoc(String field, int ordinal) throws IOException {
+ return flatVectorsReader.getFloatVectorValues(field).ordToDoc(ordinal);
+ }
+
+ /**
+ * Returns the {@link CagraIndex} for the given field, or {@code null} if unavailable
+ * (e.g., during a merge or when the field is missing).
+ *
+ * @param field the vector field name
+ * @return the CAGRA index, or {@code null}
+ */
+ public CagraIndex getCagraIndexForField(String field) {
+ if (cuvsIndices == null) return null;
+ FieldInfo info = fieldInfos.fieldInfo(field);
+ if (info == null) return null;
+ GPUIndex gpuIndex = cuvsIndices.get(info.number);
+ if (gpuIndex == null) return null;
+ return gpuIndex.getCagraIndex();
+ }
+
/**
* Gets the instance of FieldInfos.
*
diff --git a/src/main/java/com/nvidia/cuvs/lucene/FilterBitsetCache.java b/src/main/java/com/nvidia/cuvs/lucene/FilterBitsetCache.java
new file mode 100644
index 00000000..3b269798
--- /dev/null
+++ b/src/main/java/com/nvidia/cuvs/lucene/FilterBitsetCache.java
@@ -0,0 +1,64 @@
+/*
+ * SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+package com.nvidia.cuvs.lucene;
+
+import com.nvidia.cuvs.FilterBitsetHandle;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import org.apache.lucene.search.Query;
+
+/**
+ * Shared LRU cache mapping (filter Query, per-segment reader keys, field) → {@link
+ * FilterBitsetHandle}.
+ *
+ *
Host-side cache holding packed bitset arrays; the device-side upload is managed inside
+ * {@link FilterBitsetHandle} itself (per-thread LRU). Entries are evicted in LRU order and closed
+ * on eviction to free the host arrays and signal the device cache.
+ */
+final class FilterBitsetCache {
+
+ private static final int MAX_HOST_ENTRIES = 16;
+
+ private record FilterCacheKey(Query filter, List