jaepil commented on code in PR #15948:
URL: https://github.com/apache/lucene/pull/15948#discussion_r3165371883


##########
lucene/core/src/java/org/apache/lucene/search/BayesianScoreEstimator.java:
##########
@@ -0,0 +1,228 @@
+/*
+ * 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.search;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Random;
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.StoredFields;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.util.ArrayUtil;
+
+/**
+ * Estimates {@link BayesianScoreQuery} parameters (alpha, beta, base rate) 
from corpus statistics
+ * via pseudo-query sampling.
+ *
+ * <p>The estimation algorithm:
+ *
+ * <ol>
+ *   <li>Sample N documents randomly from the index
+ *   <li>For each document, create a pseudo-query from its first few tokens in 
the target field
+ *   <li>Run each pseudo-query via BM25 and collect the score distribution
+ *   <li>Estimate: beta = median(scores), alpha = 1 / std(scores)
+ *   <li>Estimate base rate: mean fraction of documents scoring above the 95th 
percentile
+ * </ol>
+ *
+ * @lucene.experimental
+ */
+public class BayesianScoreEstimator {

Review Comment:
   Great questions — let me take them in reverse order, since the lifecycle 
question (#3) is the most fundamental and the API choice follows from it.
   
   **On lifecycle (#3):** The estimated parameters are corpus-level statistics. 
α and β are derived from the BM25 score distribution's center and spread, and 
the base rate is a global prior. None of them depend on the user query, so the 
natural lifecycle is **per-IndexReader (per-commit)**, not per-query. 
Estimation runs ~50 pseudo-queries × top-K collection, which is fine once per 
reader but prohibitive on every query.
   
   **On putting estimation inside `rewrite()` (#1 and #2):** I'm a bit hesitant 
for a few reasons:
   
   1. `rewrite()` is generally expected to be cheap and stats-driven, not to 
perform I/O of this magnitude (reading stored fields, running 50 inner 
searches, sorting score arrays).
   2. Even with a fixed seed, lazy estimation in `rewrite()` would need a 
reader-keyed cache to avoid redoing the work — otherwise every `rewrite()` call 
repeats the sampling.
   3. It blurs query identity: `equals`/`hashCode` of an unestimated query vs. 
its rewritten form needs careful handling, especially for the query-cache layer.
   
   **What I'd propose instead:** keep the explicit `Parameters` constructor as 
the primary, deterministic API, and add a convenience factory:
   
   ```java
   public static Query BayesianScoreQuery.withAutoCalibration(
       IndexSearcher searcher, String field, Query inner) throws IOException;
   ```
   
   Internally this memoizes `Parameters` keyed by 
`IndexReader.CacheHelper#getKey()`, so estimation runs once per reader and is 
cleaned up automatically when the reader closes. The user gets the "just works" 
ergonomics without overloading `rewrite()` with sampling I/O.
   
   Structurally this follows the same precedent as `KnnFloatVectorQuery` / 
`KnnByteVectorQuery`: some queries inherently need a reader-bound resolution 
step, and Lucene already accommodates that. The difference here is that we 
resolve **eagerly at construction time** rather than lazily in `rewrite()`, 
since calibration parameters are reusable across many inner queries against the 
same reader (whereas a kNN result set is tied to a specific query vector and 
isn't).
   
   Happy to push this as a follow-up commit if the direction makes sense.



-- 
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