Mikep86 commented on code in PR #13697: URL: https://github.com/apache/lucene/pull/13697#discussion_r1745751201
########## lucene/join/src/java/org/apache/lucene/search/join/ToParentBlockJoinQuery.java: ########## @@ -440,6 +478,114 @@ private String formatScoreExplanation(int matches, int start, int end, ScoreMode } } + private abstract static class BatchAwareLeafCollector extends FilterLeafCollector { + public BatchAwareLeafCollector(LeafCollector in) { + super(in); + } + + public void endBatch() throws IOException {} + } + + private static class BlockJoinBulkScorer extends BulkScorer { + private final BulkScorer childBulkScorer; + private final ScoreMode scoreMode; + private final BitSet parents; + private final int parentsLength; + + public BlockJoinBulkScorer(BulkScorer childBulkScorer, ScoreMode scoreMode, BitSet parents) { + this.childBulkScorer = childBulkScorer; + this.scoreMode = scoreMode; + this.parents = parents; + this.parentsLength = parents.length(); + } + + @Override + public int score(LeafCollector collector, Bits acceptDocs, int min, int max) + throws IOException { + // Subtract one because max is exclusive w.r.t. score but inclusive w.r.t prevSetBit + int lastParent = parents.prevSetBit(Math.min(parentsLength, max) - 1); + int prevParent = min == 0 ? -1 : parents.prevSetBit(min - 1); + if (lastParent == prevParent) { + // No parent docs in this range. + // If we've scored the last parent in the bit set, return NO_MORE_DOCS to indicate we are + // done scoring. + return max >= parentsLength ? NO_MORE_DOCS : max; + } + + BatchAwareLeafCollector wrappedCollector = wrapCollector(collector); + childBulkScorer.score(wrappedCollector, acceptDocs, prevParent + 1, lastParent + 1); + wrappedCollector.endBatch(); + + // If we've scored the last parent in the bit set, return NO_MORE_DOCS to indicate we are done + // scoring + return lastParent + 1 >= parentsLength ? NO_MORE_DOCS : max; + } + + @Override + public long cost() { + return childBulkScorer.cost(); + } + + private BatchAwareLeafCollector wrapCollector(LeafCollector collector) { + return new BatchAwareLeafCollector(collector) { + private final Score currentParentScore = new Score(scoreMode); + private int currentParent = -1; + private Scorable scorer = null; + + @Override + public void setScorer(Scorable scorer) throws IOException { + assert scorer != null; + this.scorer = scorer; + + super.setScorer( + new Scorable() { + @Override + public float score() { + return currentParentScore.score(); + } + + @Override + public void setMinCompetitiveScore(float minScore) throws IOException { + if (scoreMode == ScoreMode.None || scoreMode == ScoreMode.Max) { + scorer.setMinCompetitiveScore(minScore); + } + } + }); Review Comment: LMKWYT of this approach. I originally added the child scorer to `currentParentScore`'s constructor and created `currentParentScore` in `setScorer`, but that didn't work because `setScorer` is potentially called multiple times. I then tried creating `currentParentScore` once, on the first call to `setScorer`, and checking that the same child scorer is passed in subsequent calls in an assertion. At least in the tests, this didn't work because the child scorer is wrapped in a different `AssertingScorable` in each call. This approach seemed like the simplest that avoids the above problems. I could go back to them though if we can assume that subsequent calls to `setScorer` will pass the same child scorer without checking in an assertion. -- 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: issues-unsubscr...@lucene.apache.org For queries about this service, please contact Infrastructure at: us...@infra.apache.org --------------------------------------------------------------------- To unsubscribe, e-mail: issues-unsubscr...@lucene.apache.org For additional commands, e-mail: issues-h...@lucene.apache.org