Repository navigation
Support rescoring with expand_nested_docs on the Lucene engine (#3125) - #3579
Conversation
PR Reviewer Guide 🔍(Review updated until commit 0d6910f)Here are some key observations to aid the review process:
|
PR Code Suggestions ✨Latest suggestions up to 0d6910f Explore these optional code suggestions:
Previous suggestionsSuggestions up to commit 0d6910f
Suggestions up to commit 72111ec
Suggestions up to commit 92d780a
Suggestions up to commit 2591782
Suggestions up to commit ad0bf3f
|
|
Congratulations @benkim1028 for First PR !! |
|
Persistent review updated to latest commit 3d88dd1 |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #3579 +/- ##
============================================
+ Coverage 83.06% 83.11% +0.05%
- Complexity 4968 5015 +47
============================================
Files 489 489
Lines 17740 17793 +53
Branches 2389 2399 +10
============================================
+ Hits 14735 14788 +53
+ Misses 2106 2100 -6
- Partials 899 905 +6 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
Persistent review updated to latest commit 84441fc |
| .build(); | ||
| TopDocs leafTopDocs = searcher.searchLeaf(leafReaderContext, exactSearcherContext); | ||
| for (ScoreDoc scoreDoc : leafTopDocs.scoreDocs) { | ||
| scoreDoc.shardIndex = leafOrd; |
There was a problem hiding this comment.
Can you pls help me understand why are we setting this shardIndex ?
Won't TopDocs.merge(k, perLeafTopDocs) on the next line already sets scoreDoc.shardIndex, can you pls confirm ?
There was a problem hiding this comment.
As far as I understood, there is no downstream method that sets shardIndex. I tested without this line. There was no doc returned even though there were 6 docs and 2 parents should have been returned.
There was a problem hiding this comment.
can we add comment on why we are doing this?
There was a problem hiding this comment.
Yes. I added the comment above.
| internalNestedKnnVectorQuery.getParentFilter(), | ||
| queryFilter | ||
| ); | ||
| final ExactSearcher.ExactSearcherContext exactSearcherContext = ExactSearcher.ExactSearcherContext.builder() |
There was a problem hiding this comment.
Looks like you missed the profile breakdown for the exact search per leaf
There was a problem hiding this comment.
Got it I will add that.
There was a problem hiding this comment.
Added profile breakdown to all exact search. Tested and verified that {"exact_search" : 1143583, "exact_search_count" : 4} is added to the ExpandNestedDocsQuery's breakdown.
| ); | ||
| final ExactSearcher.ExactSearcherContext exactSearcherContext = ExactSearcher.ExactSearcherContext.builder() | ||
| .matchedDocsIterator(allSiblings) | ||
| .numberOfMatchedDocs(allSiblings.cost()) |
There was a problem hiding this comment.
Looks like a major chunk of this rescore logic duplicates RescoreKNNVectorQuery.searchLeaf, can you see if we can extract this into a util method ?
There was a problem hiding this comment.
Sounds good. Will see if there is simple way to extract them out to a single util function.
There was a problem hiding this comment.
I extracted them to a single util function. But I think it did not add too much value. Let me know if you think reverting this change is better.
| private static final int DIMENSION = 3; | ||
| private static final int CHILDREN_PER_PARENT = 3; | ||
|
|
||
| /** |
There was a problem hiding this comment.
can you add an IT to test filter + expand_nested_docs + rescore ?
There was a problem hiding this comment.
Sounds good. Will add that.
There was a problem hiding this comment.
Added two new tests covering both parent filter and child filter.
- testExpandNestedDocs_whenRescoreEnabledWithParentFilter_thenReturnKMatchingParentsWithAllChildren
- testExpandNestedDocs_whenRescoreEnabledWithChildFilter_thenReturnOnlyMatchingChildren
|
Persistent review updated to latest commit a14f4c3 |
a14f4c3 to
5006617
Compare
|
Persistent review updated to latest commit 5006617 |
|
Persistent review updated to latest commit 0cc7c13 |
|
Persistent review updated to latest commit b75ba15 |
|
Persistent review updated to latest commit 393554a |
naveentatikonda
left a comment
There was a problem hiding this comment.
LGTM!
But, @benkim1028 as discussed offline please refactor this as a followup task using the wrapper approach by breaking them down into individual components and reuse them for both Lucene and NativeEngine query to avoid code duplication. Also, please create a github issue for tracking it. Thanks!
|
Persistent review updated to latest commit a1da462 |
@naveentatikonda An issue has been created for the refactoring: #3619 |
|
Persistent review updated to latest commit ad0bf3f |
|
Persistent review updated to latest commit 2591782 |
2591782 to
2faf128
Compare
PR Code Analyzer ❗AI-powered 'Code-Diff-Analyzer' found issues on commit 2faf128. ⛔ Hard block: Issues at High severity or above will block this PR from merging. 'Diff too large, requires skip by maintainers after manual review' Pull Requests Author(s): Please update your Pull Request according to the report above. Repository Maintainer(s): You can Thanks. |
The merge-base changed after approval.
|
Persistent review updated to latest commit 92d780a |
…earch-project#3125) With rescoring enabled, expand_nested_docs on the Lucene engine cut to k after expanding children, so k counted child documents rather than parent documents: a single parent could consume the whole budget and the remaining parents were dropped. KNNQueryFactory also silently skipped the rescore wrapper whenever expand_nested_docs was set. ExpandNestedDocsQuery now owns the rescore stage, in the same order NativeEngineKnnVectorQuery uses: approximate search over the oversampled candidate pool, a full precision rescore that collapses each parent group to its best child and cuts to k parents, and only then the child expansion. The expansion also scores children on full precision vectors when rescoring is enabled, so a quantized child score no longer leaks into the parent through the nested score mode. The collapsing pass tags each hit with its leaf ordinal in shardIndex, because TopDocs#merge flattens the per leaf results and the expansion has to regroup them by leaf. Removes the short circuit in KNNQueryFactory and the dead expandNestedDocs branch in OSDiversifyingChildrenFloatKnnVectorQuery. The full precision rescore of a leaf is extracted into QueryUtils#rescoreLeafWithFullPrecision and shared by RescoreKNNVectorQuery and both ExpandNestedDocsQuery stages. Profiling: all exact searches in ExpandNestedDocsQuery are timed under KNNQueryTimingType.EXACT_SEARCH, and the query is registered in KNNPlugin#getQueryProfileMetricsProvider. The registration is required, because without it a profiled query fails looking up the timer. Testing: unit tests for the rescore ordering, empty leaves, query equality and profiling. ExpandNestedDocsWithRescoreIT covers the issue's scenario on a 4x on_disk index, which resolves to the Lucene engine, asserting parent ids and exact full precision scores. It also covers many parents, rescore disabled, an explicit oversample factor, parent and child filters, and the Profile API breakdown. Signed-off-by: Ben Kim <kimsong@amazon.com>
92d780a to
72111ec
Compare
|
Persistent review updated to latest commit 72111ec |
Signed-off-by: Ben Kim <benkim1028@gmail.com>
|
Persistent review updated to latest commit 0d6910f |
|
Persistent review updated to latest commit 0d6910f |
Description
expand_nested_docscombined with rescoring on the Lucene engine did not return all matching documents (#3125). Withk=2the response came back with 1 parent instead of 2 — a single parent's children filled the whole result and the other parents were dropped.Root cause:
ExpandNestedDocsQueryexpanded child documents before rescoring, so the final reduction tokcounted child rows instead of parent rows. Onmainthis was worked around by silently dropping rescoring for this combination entirely — so parents were also selected on quantized distances, and child scores were quantized.Fix: make
ExpandNestedDocsQueryown the rescore stage and run it before expansion — the same orderingNativeEngineKnnVectorQueryalready uses for the faiss/nmslib engines. The one rule: keep the list at one row per parent document until after the final top-k cut.Pipeline before (Lucene engine, rescoring requested):
k← counts child rows, so it drops parentsmain, rescoring was skipped entirely → parents chosen on quantized distances, child scores quantizedPipeline after (mirrors
NativeEngineKnnVectorQuery):getAllSiblings, fromNativeEngineKnnVectorQuery.doRescoreExactSearcherwithparentsFilterset, fromdoRescorekparents — one row per parent, so the cut counts documents — mirrors native'sResultUtil.reduceToTopKkwinners to all their children —getAllSiblings, fromNativeEngineKnnVectorQuery.retrieveAllretrieveAllk— mirrors native'sgetMergeTopNTesting
Verified against the running cluster with the issue's exact repro:
lucene_4xnow returns 2 parents × 6 children with scores byte-identical to a full-precision faiss baseline.Related Issues
Resolves #3125
Check List
ExpandNestedDocsWithRescoreIT--signoff.By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.