From 615f45611e350c0820a4784285e5d8d4975907fb Mon Sep 17 00:00:00 2001 From: Luca Cavanna Date: Wed, 26 Apr 2023 10:24:57 +0200 Subject: [PATCH] QueryProfilerWeight to extend FilterWeight (#12242) QueryProfilerWeight should override matches and delegate to the subQueryWeight. Another way to fix this issue is to make it extend ProfileWeight and override only methods that need to have a different behaviour than delegating to the sub weight. --- .../search/QueryProfilerIndexSearcher.java | 2 +- .../sandbox/search/QueryProfilerScorer.java | 2 +- .../sandbox/search/QueryProfilerWeight.java | 22 +-- .../search/TestQueryProfilerScorer.java | 4 +- .../search/TestQueryProfilerWeight.java | 156 ++++++++++++++++++ 5 files changed, 167 insertions(+), 19 deletions(-) create mode 100644 lucene/sandbox/src/test/org/apache/lucene/sandbox/search/TestQueryProfilerWeight.java diff --git a/lucene/sandbox/src/java/org/apache/lucene/sandbox/search/QueryProfilerIndexSearcher.java b/lucene/sandbox/src/java/org/apache/lucene/sandbox/search/QueryProfilerIndexSearcher.java index 37c4698b5420..53fecb47814f 100644 --- a/lucene/sandbox/src/java/org/apache/lucene/sandbox/search/QueryProfilerIndexSearcher.java +++ b/lucene/sandbox/src/java/org/apache/lucene/sandbox/search/QueryProfilerIndexSearcher.java @@ -63,7 +63,7 @@ public Weight createWeight(Query query, ScoreMode scoreMode, float boost) throws timer.stop(); profiler.pollLast(); } - return new QueryProfilerWeight(query, weight, profile); + return new QueryProfilerWeight(weight, profile); } /** diff --git a/lucene/sandbox/src/java/org/apache/lucene/sandbox/search/QueryProfilerScorer.java b/lucene/sandbox/src/java/org/apache/lucene/sandbox/search/QueryProfilerScorer.java index 0fbdf054bf81..d99b5716827c 100644 --- a/lucene/sandbox/src/java/org/apache/lucene/sandbox/search/QueryProfilerScorer.java +++ b/lucene/sandbox/src/java/org/apache/lucene/sandbox/search/QueryProfilerScorer.java @@ -31,7 +31,7 @@ class QueryProfilerScorer extends Scorer { private final Scorer scorer; - private QueryProfilerWeight profileWeight; + private final QueryProfilerWeight profileWeight; private final QueryProfilerTimer scoreTimer, nextDocTimer, diff --git a/lucene/sandbox/src/java/org/apache/lucene/sandbox/search/QueryProfilerWeight.java b/lucene/sandbox/src/java/org/apache/lucene/sandbox/search/QueryProfilerWeight.java index 131d5c88ddba..8caaac021390 100644 --- a/lucene/sandbox/src/java/org/apache/lucene/sandbox/search/QueryProfilerWeight.java +++ b/lucene/sandbox/src/java/org/apache/lucene/sandbox/search/QueryProfilerWeight.java @@ -20,8 +20,7 @@ import java.io.IOException; import org.apache.lucene.index.LeafReaderContext; import org.apache.lucene.search.BulkScorer; -import org.apache.lucene.search.Explanation; -import org.apache.lucene.search.Query; +import org.apache.lucene.search.FilterWeight; import org.apache.lucene.search.Scorer; import org.apache.lucene.search.ScorerSupplier; import org.apache.lucene.search.Weight; @@ -30,14 +29,12 @@ * Weight wrapper that will compute how much time it takes to build the {@link Scorer} and then * return a {@link Scorer} that is wrapped in order to compute timings as well. */ -class QueryProfilerWeight extends Weight { +class QueryProfilerWeight extends FilterWeight { - private final Weight subQueryWeight; private final QueryProfilerBreakdown profile; - public QueryProfilerWeight(Query query, Weight subQueryWeight, QueryProfilerBreakdown profile) { - super(query); - this.subQueryWeight = subQueryWeight; + public QueryProfilerWeight(Weight subQueryWeight, QueryProfilerBreakdown profile) { + super(subQueryWeight); this.profile = profile; } @@ -46,7 +43,7 @@ public int count(LeafReaderContext context) throws IOException { QueryProfilerTimer timer = profile.getTimer(QueryProfilerTimingType.COUNT); timer.start(); try { - return subQueryWeight.count(context); + return in.count(context); } finally { timer.stop(); } @@ -67,7 +64,7 @@ public ScorerSupplier scorerSupplier(LeafReaderContext context) throws IOExcepti timer.start(); final ScorerSupplier subQueryScorerSupplier; try { - subQueryScorerSupplier = subQueryWeight.scorerSupplier(context); + subQueryScorerSupplier = in.scorerSupplier(context); } finally { timer.stop(); } @@ -103,7 +100,7 @@ public long cost() { @Override public BulkScorer bulkScorer(LeafReaderContext context) throws IOException { // We use the default bulk scorer instead of the specialized one. The reason - // is that Lucene's BulkScorers do everything at once: finding matches, + // is that BulkScorers do everything at once: finding matches, // scoring them and calling the collector, so they make it impossible to // see where time is spent, which is the purpose of query profiling. // The default bulk scorer will pull a scorer and iterate over matches, @@ -112,11 +109,6 @@ public BulkScorer bulkScorer(LeafReaderContext context) throws IOException { return super.bulkScorer(context); } - @Override - public Explanation explain(LeafReaderContext context, int doc) throws IOException { - return subQueryWeight.explain(context, doc); - } - @Override public boolean isCacheable(LeafReaderContext ctx) { return false; diff --git a/lucene/sandbox/src/test/org/apache/lucene/sandbox/search/TestQueryProfilerScorer.java b/lucene/sandbox/src/test/org/apache/lucene/sandbox/search/TestQueryProfilerScorer.java index 870ddfa051ab..06910995977e 100644 --- a/lucene/sandbox/src/test/org/apache/lucene/sandbox/search/TestQueryProfilerScorer.java +++ b/lucene/sandbox/src/test/org/apache/lucene/sandbox/search/TestQueryProfilerScorer.java @@ -70,7 +70,7 @@ public void testPropagateMinCompetitiveScore() throws IOException { query.createWeight(new IndexSearcher(new MultiReader()), ScoreMode.TOP_SCORES, 1f); FakeScorer fakeScorer = new FakeScorer(weight); QueryProfilerBreakdown profile = new QueryProfilerBreakdown(); - QueryProfilerWeight queryProfilerWeight = new QueryProfilerWeight(query, weight, profile); + QueryProfilerWeight queryProfilerWeight = new QueryProfilerWeight(weight, profile); QueryProfilerScorer queryProfilerScorer = new QueryProfilerScorer(queryProfilerWeight, fakeScorer, profile); queryProfilerScorer.setMinCompetitiveScore(0.42f); @@ -83,7 +83,7 @@ public void testPropagateMaxScore() throws IOException { query.createWeight(new IndexSearcher(new MultiReader()), ScoreMode.TOP_SCORES, 1f); FakeScorer fakeScorer = new FakeScorer(weight); QueryProfilerBreakdown profile = new QueryProfilerBreakdown(); - QueryProfilerWeight queryProfilerWeight = new QueryProfilerWeight(query, weight, profile); + QueryProfilerWeight queryProfilerWeight = new QueryProfilerWeight(weight, profile); QueryProfilerScorer queryProfilerScorer = new QueryProfilerScorer(queryProfilerWeight, fakeScorer, profile); queryProfilerScorer.setMinCompetitiveScore(0.42f); diff --git a/lucene/sandbox/src/test/org/apache/lucene/sandbox/search/TestQueryProfilerWeight.java b/lucene/sandbox/src/test/org/apache/lucene/sandbox/search/TestQueryProfilerWeight.java new file mode 100644 index 000000000000..41b4007155be --- /dev/null +++ b/lucene/sandbox/src/test/org/apache/lucene/sandbox/search/TestQueryProfilerWeight.java @@ -0,0 +1,156 @@ +/* + * 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.sandbox.search; + +import java.io.IOException; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.search.DocIdSetIterator; +import org.apache.lucene.search.Explanation; +import org.apache.lucene.search.MatchAllDocsQuery; +import org.apache.lucene.search.Matches; +import org.apache.lucene.search.MatchesIterator; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.Scorer; +import org.apache.lucene.search.Weight; +import org.apache.lucene.tests.util.LuceneTestCase; + +public class TestQueryProfilerWeight extends LuceneTestCase { + + private static final class FakeWeight extends Weight { + FakeWeight(Query query) { + super(query); + } + + @Override + public Explanation explain(LeafReaderContext context, int doc) { + return Explanation.match(1, "fake_description"); + } + + @Override + public Scorer scorer(LeafReaderContext context) { + return new Scorer(this) { + @Override + public DocIdSetIterator iterator() { + return null; + } + + @Override + public float getMaxScore(int upTo) { + return 42f; + } + + @Override + public float score() { + return 0; + } + + @Override + public int docID() { + return 0; + } + }; + } + + @Override + public boolean isCacheable(LeafReaderContext ctx) { + return false; + } + + @Override + public Matches matches(LeafReaderContext context, int doc) { + return new Matches() { + @Override + public MatchesIterator getMatches(String field) { + return new MatchesIterator() { + @Override + public boolean next() { + return false; + } + + @Override + public int startPosition() { + return 42; + } + + @Override + public int endPosition() { + return 43; + } + + @Override + public int startOffset() { + return 44; + } + + @Override + public int endOffset() { + return 45; + } + + @Override + public MatchesIterator getSubMatches() { + return null; + } + + @Override + public Query getQuery() { + return parentQuery; + } + }; + } + + @Override + public Collection getSubMatches() { + return Collections.emptyList(); + } + + @Override + public Iterator iterator() { + return null; + } + }; + } + } + + public void testPropagateMatches() throws IOException { + Query query = new MatchAllDocsQuery(); + Weight fakeWeight = new FakeWeight(query); + QueryProfilerBreakdown profile = new QueryProfilerBreakdown(); + QueryProfilerWeight profileWeight = new QueryProfilerWeight(fakeWeight, profile); + assertEquals(42, profileWeight.matches(null, 1).getMatches("some_field").startPosition()); + } + + public void testPropagateExplain() throws IOException { + Query query = new MatchAllDocsQuery(); + Weight fakeWeight = new FakeWeight(query); + QueryProfilerBreakdown profile = new QueryProfilerBreakdown(); + QueryProfilerWeight profileWeight = new QueryProfilerWeight(fakeWeight, profile); + assertEquals("fake_description", profileWeight.explain(null, 1).getDescription()); + } + + public void testPropagateScorer() throws IOException { + Query query = new MatchAllDocsQuery(); + Weight fakeWeight = new FakeWeight(query); + QueryProfilerBreakdown profile = new QueryProfilerBreakdown(); + QueryProfilerWeight profileWeight = new QueryProfilerWeight(fakeWeight, profile); + assertEquals(42f, profileWeight.scorer(null).getMaxScore(DocIdSetIterator.NO_MORE_DOCS), 0f); + } +}