xiangfu0 commented on code in PR #19297:
URL: https://github.com/apache/pinot/pull/19297#discussion_r3865278773


##########
pinot-core/src/main/java/org/apache/pinot/core/operator/filter/ExactVectorScanFilterOperator.java:
##########
@@ -74,28 +79,21 @@ public class ExactVectorScanFilterOperator extends 
BaseFilterOperator {
   /// @param predicate the vector similarity predicate containing query vector 
and top-K
   /// @param column the column name (for logging and explain)
   /// @param numDocs the total number of documents in the segment
-  public ExactVectorScanFilterOperator(ForwardIndexReader<?> 
forwardIndexReader,
-      VectorSimilarityPredicate predicate, String column, int numDocs) {
-    this(forwardIndexReader, predicate, column, numDocs, null, 
"vector_index_missing",
-        VectorSearchParams.DEFAULT);
-  }
-
+  /// @param vectorIndexConfig vector index configuration, used to resolve the 
distance function (may be null)
+  /// @param fallbackReason why this scan runs instead of an ANN search, 
reported in explain output
+  /// @param searchParams vector search parameters from query options
+  /// @param candidateScope documents this scan may consider, or null to scan 
the whole segment
   public ExactVectorScanFilterOperator(ForwardIndexReader<?> 
forwardIndexReader,
       VectorSimilarityPredicate predicate, String column, int numDocs, 
@Nullable VectorIndexConfig vectorIndexConfig,
-      String fallbackReason) {
-    this(forwardIndexReader, predicate, column, numDocs, vectorIndexConfig, 
fallbackReason,
-        VectorSearchParams.DEFAULT);
-  }
-
-  public ExactVectorScanFilterOperator(ForwardIndexReader<?> 
forwardIndexReader,
-      VectorSimilarityPredicate predicate, String column, int numDocs, 
@Nullable VectorIndexConfig vectorIndexConfig,
-      String fallbackReason, VectorSearchParams searchParams) {
+      String fallbackReason, VectorSearchParams searchParams,
+      @Nullable VectorCandidateScope candidateScope) {

Review Comment:
   Reformatted — the parameter list now packs to the line limit instead of 
breaking early.
   
   _🤖 Addressed by [Claude Code](https://claude.com/claude-code)_



##########
pinot-core/src/main/java/org/apache/pinot/core/operator/filter/VectorCandidateScope.java:
##########
@@ -0,0 +1,66 @@
+/**
+ * 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.pinot.core.operator.filter;
+
+import com.google.common.base.Preconditions;
+import org.roaringbitmap.buffer.ImmutableRoaringBitmap;
+
+
+/// Immutable set of document IDs a vector predicate is allowed to consider as 
candidates.
+///
+/// Vector top-K is not monotonic: unlike an ordinary predicate, restricting 
the corpus changes which documents win
+/// the top-K contest, so a document set that defines what the query may see 
must be applied *before* candidate
+/// generation rather than intersected with the result afterwards. That 
contract is the entire meaning of this type.
+/// It deliberately says nothing about where the restriction came from -- 
callers such as [FilterPlanNode] decide
+/// that, today from the segment's queryable-document snapshot.
+///
+/// Contrast with the optimizer-selected metadata bitmap passed to
+/// [VectorSimilarityFilterOperator#setPreFilterBitmap]: that one is optional, 
because post-filtering an ordinary
+/// predicate is already correct and pre-filtering only improves recall.
+///
+/// The factory owns the single detached copy of the supplied bitmap, so 
operators can share a scope freely and only
+/// allocate again when they intersect it with an independent optional filter.
+public final class VectorCandidateScope {

Review Comment:
   Agreed — removed the class and pass the bitmap directly.
   
   To answer the question: there was a planned extension (a follow-up makes the 
mutable HNSW index filter-aware, which needs the candidate set bounded by the 
published-document watermark as well). But in this PR the type holds exactly 
one bitmap, so it is speculative generality here. If that second field actually 
materializes, introducing a type then will be a smaller and better-justified 
change than keeping an empty wrapper now.
   
   _🤖 Addressed by [Claude Code](https://claude.com/claude-code)_



##########
pinot-core/src/main/java/org/apache/pinot/core/operator/filter/VectorCandidateScope.java:
##########
@@ -0,0 +1,66 @@
+/**
+ * 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.pinot.core.operator.filter;
+
+import com.google.common.base.Preconditions;
+import org.roaringbitmap.buffer.ImmutableRoaringBitmap;
+
+
+/// Immutable set of document IDs a vector predicate is allowed to consider as 
candidates.
+///
+/// Vector top-K is not monotonic: unlike an ordinary predicate, restricting 
the corpus changes which documents win
+/// the top-K contest, so a document set that defines what the query may see 
must be applied *before* candidate
+/// generation rather than intersected with the result afterwards. That 
contract is the entire meaning of this type.
+/// It deliberately says nothing about where the restriction came from -- 
callers such as [FilterPlanNode] decide
+/// that, today from the segment's queryable-document snapshot.
+///
+/// Contrast with the optimizer-selected metadata bitmap passed to
+/// [VectorSimilarityFilterOperator#setPreFilterBitmap]: that one is optional, 
because post-filtering an ordinary
+/// predicate is already correct and pre-filtering only improves recall.
+///
+/// The factory owns the single detached copy of the supplied bitmap, so 
operators can share a scope freely and only
+/// allocate again when they intersect it with an independent optional filter.
+public final class VectorCandidateScope {
+  private final ImmutableRoaringBitmap _requiredDocIds;
+
+  private VectorCandidateScope(ImmutableRoaringBitmap requiredDocIds) {
+    // Detach from the caller's bitmap so candidate generation and the outer 
valid-document AND observe the same
+    // document set for the whole query. Copying into array-backed containers 
keeps per-document `contains` checks
+    // cheap, which matters because filtered graph traversal probes this 
bitmap once per visited node.
+    _requiredDocIds = 
requiredDocIds.toMutableRoaringBitmap().toImmutableRoaringBitmap();
+  }
+
+  /// Creates a scope from the document IDs the query is allowed to consider.
+  public static VectorCandidateScope of(ImmutableRoaringBitmap requiredDocIds) 
{

Review Comment:
   Moot now — the class is gone entirely and the bitmap is passed directly, so 
there is no factory or constructor to choose between.
   
   _🤖 Addressed by [Claude Code](https://claude.com/claude-code)_



##########
pinot-core/src/main/java/org/apache/pinot/core/operator/filter/VectorCandidateScope.java:
##########
@@ -0,0 +1,66 @@
+/**
+ * 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.pinot.core.operator.filter;
+
+import com.google.common.base.Preconditions;
+import org.roaringbitmap.buffer.ImmutableRoaringBitmap;
+
+
+/// Immutable set of document IDs a vector predicate is allowed to consider as 
candidates.
+///
+/// Vector top-K is not monotonic: unlike an ordinary predicate, restricting 
the corpus changes which documents win
+/// the top-K contest, so a document set that defines what the query may see 
must be applied *before* candidate
+/// generation rather than intersected with the result afterwards. That 
contract is the entire meaning of this type.
+/// It deliberately says nothing about where the restriction came from -- 
callers such as [FilterPlanNode] decide
+/// that, today from the segment's queryable-document snapshot.
+///
+/// Contrast with the optimizer-selected metadata bitmap passed to
+/// [VectorSimilarityFilterOperator#setPreFilterBitmap]: that one is optional, 
because post-filtering an ordinary
+/// predicate is already correct and pre-filtering only improves recall.
+///
+/// The factory owns the single detached copy of the supplied bitmap, so 
operators can share a scope freely and only
+/// allocate again when they intersect it with an independent optional filter.
+public final class VectorCandidateScope {
+  private final ImmutableRoaringBitmap _requiredDocIds;
+
+  private VectorCandidateScope(ImmutableRoaringBitmap requiredDocIds) {
+    // Detach from the caller's bitmap so candidate generation and the outer 
valid-document AND observe the same
+    // document set for the whole query. Copying into array-backed containers 
keeps per-document `contains` checks
+    // cheap, which matters because filtered graph traversal probes this 
bitmap once per visited node.
+    _requiredDocIds = 
requiredDocIds.toMutableRoaringBitmap().toImmutableRoaringBitmap();

Review Comment:
   Removed, along with the copy it was describing.
   
   The comment was trying to justify defensively copying the caller bitmap. 
That was not worth doing: master already hands the same valid-document snapshot 
straight to BitmapBasedFilterOperator and iterates it during execution, so 
copying here bought no safety that the surrounding code does not already 
assume, and cost an allocation per segment per query. The operator now holds 
the bitmap by reference with a documented do-not-modify contract, matching that 
existing convention.
   
   _🤖 Addressed by [Claude Code](https://claude.com/claude-code)_



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to