From 6b2f17933b178fa0759ef384b2ff5d683b9a64c8 Mon Sep 17 00:00:00 2001 From: Marcus Date: Wed, 10 May 2023 09:10:37 -0700 Subject: [PATCH] `ToParentBlockJoinQuery` Explain Support Score Mode (#12245) * `ToParentBlockJoinQuery` Explain Support Score Mode --------- Co-authored-by: Mikhail Khludnev --- lucene/CHANGES.txt | 3 +- .../search/join/ToParentBlockJoinQuery.java | 42 +++++++++++++------ .../lucene/search/join/TestBlockJoin.java | 5 +-- 3 files changed, 32 insertions(+), 18 deletions(-) diff --git a/lucene/CHANGES.txt b/lucene/CHANGES.txt index 8c0bf2888f24..e6019e40148d 100644 --- a/lucene/CHANGES.txt +++ b/lucene/CHANGES.txt @@ -15,7 +15,8 @@ New Features Improvements --------------------- -(No changes) + +GITHUB#12245: Add support for Score Mode to `ToParentBlockJoinQuery` explain. (Marcus Eagan via Mikhail Khludnev) Optimizations --------------------- diff --git a/lucene/join/src/java/org/apache/lucene/search/join/ToParentBlockJoinQuery.java b/lucene/join/src/java/org/apache/lucene/search/join/ToParentBlockJoinQuery.java index 5640c9e4a7cd..87481f03a88f 100644 --- a/lucene/join/src/java/org/apache/lucene/search/join/ToParentBlockJoinQuery.java +++ b/lucene/join/src/java/org/apache/lucene/search/join/ToParentBlockJoinQuery.java @@ -173,7 +173,7 @@ public long cost() { public Explanation explain(LeafReaderContext context, int doc) throws IOException { BlockJoinScorer scorer = (BlockJoinScorer) scorer(context); if (scorer != null && scorer.iterator().advance(doc) == doc) { - return scorer.explain(context, in); + return scorer.explain(context, in, scoreMode); } return Explanation.noMatch("Not a match"); } @@ -392,35 +392,51 @@ private void setScoreAndFreq() throws IOException { } this.score = (float) score; } - - public Explanation explain(LeafReaderContext context, Weight childWeight) throws IOException { + /* + * This instance of Explanation requires three parameters, context, childWeight, and scoreMode. + * The scoreMode parameter considers Avg, Total, Min, Max, and None. + * */ + public Explanation explain(LeafReaderContext context, Weight childWeight, ScoreMode scoreMode) + throws IOException { int prevParentDoc = parentBits.prevSetBit(parentApproximation.docID() - 1); int start = context.docBase + prevParentDoc + 1; // +1 b/c prevParentDoc is previous parent doc int end = context.docBase + parentApproximation.docID() - 1; // -1 b/c parentDoc is parent doc Explanation bestChild = null; + Explanation worstChild = null; + int matches = 0; for (int childDoc = start; childDoc <= end; childDoc++) { Explanation child = childWeight.explain(context, childDoc - context.docBase); if (child.isMatch()) { matches++; if (bestChild == null - || child.getValue().floatValue() > bestChild.getValue().floatValue()) { + || child.getValue().doubleValue() > bestChild.getValue().doubleValue()) { bestChild = child; } + if (worstChild == null + || child.getValue().doubleValue() < worstChild.getValue().doubleValue()) { + worstChild = child; + } } } - + assert matches > 0 : "No matches should be handled before."; + Explanation subExplain = scoreMode == ScoreMode.Min ? worstChild : bestChild; return Explanation.match( - score(), - String.format( - Locale.ROOT, - "Score based on %d child docs in range from %d to %d, best match:", - matches, - start, - end), - bestChild); + this.score(), + formatScoreExplanation(matches, start, end, scoreMode), + subExplain == null ? Collections.emptyList() : Collections.singleton(subExplain)); + } + + private String formatScoreExplanation(int matches, int start, int end, ScoreMode scoreMode) { + return String.format( + Locale.ROOT, + "Score based on %d child docs in range from %d to %d, using score mode %s", + matches, + start, + end, + scoreMode); } } diff --git a/lucene/join/src/test/org/apache/lucene/search/join/TestBlockJoin.java b/lucene/join/src/test/org/apache/lucene/search/join/TestBlockJoin.java index b77e634aac20..ca4246196fce 100644 --- a/lucene/join/src/test/org/apache/lucene/search/join/TestBlockJoin.java +++ b/lucene/join/src/test/org/apache/lucene/search/join/TestBlockJoin.java @@ -277,7 +277,6 @@ public void testSimple() throws Exception { CheckHits.checkHitCollector(random(), fullQuery.build(), "country", s, new int[] {2}); TopDocs topDocs = s.search(fullQuery.build(), 1); - // assertEquals(1, results.totalHitCount); assertEquals(1, topDocs.totalHits.value); Document parentDoc = s.storedFields().document(topDocs.scoreDocs[0].doc); @@ -890,13 +889,11 @@ public void testRandom() throws Exception { Explanation explanation = joinS.explain(childJoinQuery, hit.doc); Document document = joinS.storedFields().document(hit.doc - 1); int childId = Integer.parseInt(document.get("childID")); - // System.out.println(" hit docID=" + hit.doc + " childId=" + childId + " parentId=" + - // document.get("parentID")); assertTrue(explanation.isMatch()); assertEquals(hit.score, explanation.getValue().doubleValue(), 0.0f); Matcher m = Pattern.compile( - "Score based on ([0-9]+) child docs in range from ([0-9]+) to ([0-9]+), best match:") + "Score based on ([0-9]+) child docs in range from ([0-9]+) to ([0-9]+), using score mode (None|Avg|Min|Max|Total)") .matcher(explanation.getDescription()); assertTrue("Block Join description not matches", m.matches()); assertTrue("Matched children not positive", Integer.parseInt(m.group(1)) > 0);