diff --git a/server/src/main/java/org/elasticsearch/search/profile/AbstractProfileBreakdown.java b/server/src/main/java/org/elasticsearch/search/profile/AbstractProfileBreakdown.java index f49ad4a8718..654c67af444 100644 --- a/server/src/main/java/org/elasticsearch/search/profile/AbstractProfileBreakdown.java +++ b/server/src/main/java/org/elasticsearch/search/profile/AbstractProfileBreakdown.java @@ -49,6 +49,10 @@ public abstract class AbstractProfileBreakdown> { return timings[timing.ordinal()]; } + public void setTimer(T timing, Timer timer) { + timings[timing.ordinal()] = timer; + } + /** Convert this record to a map from timingType to times. */ public Map toTimingMap() { Map map = new HashMap<>(); diff --git a/server/src/main/java/org/elasticsearch/search/profile/query/ProfileScorer.java b/server/src/main/java/org/elasticsearch/search/profile/query/ProfileScorer.java index 7899750461e..ab8fb5dbcae 100644 --- a/server/src/main/java/org/elasticsearch/search/profile/query/ProfileScorer.java +++ b/server/src/main/java/org/elasticsearch/search/profile/query/ProfileScorer.java @@ -19,7 +19,9 @@ package org.elasticsearch.search.profile.query; +import org.apache.lucene.search.ConstantScoreQuery; import org.apache.lucene.search.DocIdSetIterator; +import org.apache.lucene.search.Scorable; import org.apache.lucene.search.Scorer; import org.apache.lucene.search.TwoPhaseIterator; import org.apache.lucene.search.Weight; @@ -36,7 +38,10 @@ final class ProfileScorer extends Scorer { private final Scorer scorer; private ProfileWeight profileWeight; + private final Timer scoreTimer, nextDocTimer, advanceTimer, matchTimer, shallowAdvanceTimer, computeMaxScoreTimer; + private final boolean isConstantScoreQuery; + ProfileScorer(ProfileWeight w, Scorer scorer, QueryProfileBreakdown profile) throws IOException { super(w); @@ -48,6 +53,26 @@ final class ProfileScorer extends Scorer { matchTimer = profile.getTimer(QueryTimingType.MATCH); shallowAdvanceTimer = profile.getTimer(QueryTimingType.SHALLOW_ADVANCE); computeMaxScoreTimer = profile.getTimer(QueryTimingType.COMPUTE_MAX_SCORE); + ProfileScorer profileScorer = null; + if (w.getQuery() instanceof ConstantScoreQuery && scorer instanceof ProfileScorer) { + //Case when we have a totalHits query and it is not cached + profileScorer = (ProfileScorer) scorer; + } else if (w.getQuery() instanceof ConstantScoreQuery && scorer.getChildren().size() == 1) { + //Case when we have a top N query. If the scorer has no children, it is because it is cached + //and in that case we do not do any special treatment + Scorable childScorer = scorer.getChildren().iterator().next().child; + if (childScorer instanceof ProfileScorer) { + profileScorer = (ProfileScorer) childScorer; + } + } + if (profileScorer != null) { + isConstantScoreQuery = true; + profile.setTimer(QueryTimingType.NEXT_DOC, profileScorer.nextDocTimer); + profile.setTimer(QueryTimingType.ADVANCE, profileScorer.advanceTimer); + profile.setTimer(QueryTimingType.MATCH, profileScorer.matchTimer); + } else { + isConstantScoreQuery = false; + } } @Override @@ -77,6 +102,9 @@ final class ProfileScorer extends Scorer { @Override public DocIdSetIterator iterator() { + if (isConstantScoreQuery) { + return scorer.iterator(); + } final DocIdSetIterator in = scorer.iterator(); return new DocIdSetIterator() { @@ -114,6 +142,9 @@ final class ProfileScorer extends Scorer { @Override public TwoPhaseIterator twoPhaseIterator() { + if (isConstantScoreQuery) { + return scorer.twoPhaseIterator(); + } final TwoPhaseIterator in = scorer.twoPhaseIterator(); if (in == null) { return null; diff --git a/server/src/test/java/org/elasticsearch/search/profile/query/QueryProfilerTests.java b/server/src/test/java/org/elasticsearch/search/profile/query/QueryProfilerTests.java index fd924ce07ca..ba58a79953b 100644 --- a/server/src/test/java/org/elasticsearch/search/profile/query/QueryProfilerTests.java +++ b/server/src/test/java/org/elasticsearch/search/profile/query/QueryProfilerTests.java @@ -28,10 +28,12 @@ import org.apache.lucene.index.IndexWriter; import org.apache.lucene.index.LeafReaderContext; import org.apache.lucene.index.RandomIndexWriter; import org.apache.lucene.index.Term; +import org.apache.lucene.search.ConstantScoreQuery; import org.apache.lucene.search.Explanation; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.LeafCollector; import org.apache.lucene.search.Query; +import org.apache.lucene.search.QueryCachingPolicy; import org.apache.lucene.search.RandomApproximationQuery; import org.apache.lucene.search.ScoreMode; import org.apache.lucene.search.Scorer; @@ -118,6 +120,209 @@ public class QueryProfilerTests extends ESTestCase { assertThat(rewriteTime, greaterThan(0L)); } + public void testConstantScoreQuery() throws IOException { + QueryProfiler profiler = new QueryProfiler(); + searcher.setProfiler(profiler); + Query query = new ConstantScoreQuery(new TermQuery(new Term("foo", "bar"))); + searcher.search(query, 1); + List results = profiler.getTree(); + assertEquals(1, results.size()); + Map breakdownConstantScoreQuery = results.get(0).getTimeBreakdown(); + assertEquals(1, results.get(0).getProfiledChildren().size()); + Map breakdownTermQuery = results.get(0).getProfiledChildren().get(0).getTimeBreakdown(); + + assertThat(breakdownConstantScoreQuery.get(QueryTimingType.CREATE_WEIGHT.toString()).longValue(), greaterThan(0L)); + assertThat(breakdownConstantScoreQuery.get(QueryTimingType.BUILD_SCORER.toString()).longValue(), greaterThan(0L)); + assertThat(breakdownConstantScoreQuery.get(QueryTimingType.NEXT_DOC.toString()).longValue(), greaterThan(0L)); + assertThat(breakdownConstantScoreQuery.get(QueryTimingType.ADVANCE.toString()).longValue(), equalTo(0L)); + assertThat(breakdownConstantScoreQuery.get(QueryTimingType.SCORE.toString()).longValue(), greaterThan(0L)); + assertThat(breakdownConstantScoreQuery.get(QueryTimingType.MATCH.toString()).longValue(), equalTo(0L)); + + assertThat(breakdownConstantScoreQuery.get(QueryTimingType.CREATE_WEIGHT.toString() + "_count").longValue(), greaterThan(0L)); + assertThat(breakdownConstantScoreQuery.get(QueryTimingType.BUILD_SCORER.toString() + "_count").longValue(), greaterThan(0L)); + assertThat(breakdownConstantScoreQuery.get(QueryTimingType.NEXT_DOC.toString() + "_count").longValue(), greaterThan(0L)); + assertThat(breakdownConstantScoreQuery.get(QueryTimingType.ADVANCE.toString() + "_count").longValue(), equalTo(0L)); + assertThat(breakdownConstantScoreQuery.get(QueryTimingType.SCORE.toString() + "_count").longValue(), greaterThan(0L)); + assertThat(breakdownConstantScoreQuery.get(QueryTimingType.MATCH.toString() + "_count").longValue(), equalTo(0L)); + + assertThat(breakdownTermQuery.get(QueryTimingType.CREATE_WEIGHT.toString()).longValue(), greaterThan(0L)); + assertThat(breakdownTermQuery.get(QueryTimingType.BUILD_SCORER.toString()).longValue(), greaterThan(0L)); + assertThat(breakdownTermQuery.get(QueryTimingType.NEXT_DOC.toString()).longValue(), greaterThan(0L)); + assertThat(breakdownTermQuery.get(QueryTimingType.ADVANCE.toString()).longValue(), equalTo(0L)); + assertThat(breakdownTermQuery.get(QueryTimingType.SCORE.toString()).longValue(), equalTo(0L)); + assertThat(breakdownTermQuery.get(QueryTimingType.MATCH.toString()).longValue(), equalTo(0L)); + + assertThat(breakdownTermQuery.get(QueryTimingType.CREATE_WEIGHT.toString() + "_count").longValue(), greaterThan(0L)); + assertThat(breakdownTermQuery.get(QueryTimingType.BUILD_SCORER.toString() + "_count").longValue(), greaterThan(0L)); + assertThat(breakdownTermQuery.get(QueryTimingType.NEXT_DOC.toString() + "_count").longValue(), greaterThan(0L)); + assertThat(breakdownTermQuery.get(QueryTimingType.ADVANCE.toString() + "_count").longValue(), equalTo(0L)); + assertThat(breakdownTermQuery.get(QueryTimingType.SCORE.toString() + "_count").longValue(), equalTo(0L)); + assertThat(breakdownTermQuery.get(QueryTimingType.MATCH.toString() + "_count").longValue(), equalTo(0L)); + + assertEquals(breakdownConstantScoreQuery.get(QueryTimingType.NEXT_DOC.toString()).longValue(), + breakdownTermQuery.get(QueryTimingType.NEXT_DOC.toString()).longValue()); + + long rewriteTime = profiler.getRewriteTime(); + assertThat(rewriteTime, greaterThan(0L)); + } + + public void testConstantScoreTotalHitsBeingCachedQuery() throws IOException { + Query query = new ConstantScoreQuery(new TermQuery(new Term("foo", "bar"))); + //clean cache and make sure queries will be cached + searcher.setQueryCache(IndexSearcher.getDefaultQueryCache()); + searcher.setQueryCachingPolicy(ALWAYS_CACHE_POLICY); + + QueryProfiler profiler = new QueryProfiler(); + searcher.setProfiler(profiler); + TotalHitCountCollector collector = new TotalHitCountCollector(); + searcher.search(query, collector); + + List results = profiler.getTree(); + assertEquals(1, results.size()); + Map breakdownConstantScoreQuery = results.get(0).getTimeBreakdown(); + assertEquals(1, results.get(0).getProfiledChildren().size()); + Map breakdownTermQuery = results.get(0).getProfiledChildren().get(0).getTimeBreakdown(); + //In this case scorers for constant score query and term query are disconnected. + assertThat(breakdownConstantScoreQuery.get(QueryTimingType.CREATE_WEIGHT.toString()).longValue(), greaterThan(0L)); + assertThat(breakdownConstantScoreQuery.get(QueryTimingType.BUILD_SCORER.toString()).longValue(), greaterThan(0L)); + assertThat(breakdownConstantScoreQuery.get(QueryTimingType.NEXT_DOC.toString()).longValue(), greaterThan(0L)); + assertThat(breakdownConstantScoreQuery.get(QueryTimingType.ADVANCE.toString()).longValue(), equalTo(0L)); + assertThat(breakdownConstantScoreQuery.get(QueryTimingType.SCORE.toString()).longValue(), equalTo(0L)); + assertThat(breakdownConstantScoreQuery.get(QueryTimingType.MATCH.toString()).longValue(), equalTo(0L)); + + assertThat(breakdownConstantScoreQuery.get(QueryTimingType.CREATE_WEIGHT.toString() + "_count").longValue(), greaterThan(0L)); + assertThat(breakdownConstantScoreQuery.get(QueryTimingType.BUILD_SCORER.toString() + "_count").longValue(), greaterThan(0L)); + assertThat(breakdownConstantScoreQuery.get(QueryTimingType.NEXT_DOC.toString() + "_count").longValue(), greaterThan(0L)); + assertThat(breakdownConstantScoreQuery.get(QueryTimingType.ADVANCE.toString() + "_count").longValue(), equalTo(0L)); + assertThat(breakdownConstantScoreQuery.get(QueryTimingType.SCORE.toString() + "_count").longValue(), equalTo(0L)); + assertThat(breakdownConstantScoreQuery.get(QueryTimingType.MATCH.toString() + "_count").longValue(), equalTo(0L)); + + assertThat(breakdownTermQuery.get(QueryTimingType.CREATE_WEIGHT.toString()).longValue(), greaterThan(0L)); + assertThat(breakdownTermQuery.get(QueryTimingType.BUILD_SCORER.toString()).longValue(), greaterThan(0L)); + assertThat(breakdownTermQuery.get(QueryTimingType.NEXT_DOC.toString()).longValue(), greaterThan(0L)); + assertThat(breakdownTermQuery.get(QueryTimingType.ADVANCE.toString()).longValue(), equalTo(0L)); + assertThat(breakdownTermQuery.get(QueryTimingType.SCORE.toString()).longValue(), equalTo(0L)); + assertThat(breakdownTermQuery.get(QueryTimingType.MATCH.toString()).longValue(), equalTo(0L)); + + assertThat(breakdownTermQuery.get(QueryTimingType.CREATE_WEIGHT.toString() + "_count").longValue(), greaterThan(0L)); + assertThat(breakdownTermQuery.get(QueryTimingType.BUILD_SCORER.toString() + "_count").longValue(), greaterThan(0L)); + assertThat(breakdownTermQuery.get(QueryTimingType.NEXT_DOC.toString() + "_count").longValue(), greaterThan(0L)); + assertThat(breakdownTermQuery.get(QueryTimingType.ADVANCE.toString() + "_count").longValue(), equalTo(0L)); + assertThat(breakdownTermQuery.get(QueryTimingType.SCORE.toString() + "_count").longValue(), equalTo(0L)); + assertThat(breakdownTermQuery.get(QueryTimingType.MATCH.toString() + "_count").longValue(), equalTo(0L)); + + long rewriteTime = profiler.getRewriteTime(); + assertThat(rewriteTime, greaterThan(0L)); + } + + public void testConstantScoreTotalHitsNotCachedQuery() throws IOException { + Query query = new ConstantScoreQuery(new TermQuery(new Term("foo", "bar"))); + + //clean cache and make sure queries will not be cached + searcher.setQueryCache(IndexSearcher.getDefaultQueryCache()); + searcher.setQueryCachingPolicy(NEVER_CACHE_POLICY); + + QueryProfiler profiler = new QueryProfiler(); + searcher.setProfiler(profiler); + TotalHitCountCollector collector = new TotalHitCountCollector(); + searcher.search(query, collector); + + List results = profiler.getTree(); + assertEquals(1, results.size()); + Map breakdownConstantScoreQuery = results.get(0).getTimeBreakdown(); + assertEquals(1, results.get(0).getProfiledChildren().size()); + Map breakdownTermQuery = results.get(0).getProfiledChildren().get(0).getTimeBreakdown(); + //Timing from the scorer of term query are inherited by constant score query scorer. + assertThat(breakdownConstantScoreQuery.get(QueryTimingType.CREATE_WEIGHT.toString()).longValue(), greaterThan(0L)); + assertThat(breakdownConstantScoreQuery.get(QueryTimingType.BUILD_SCORER.toString()).longValue(), greaterThan(0L)); + assertThat(breakdownConstantScoreQuery.get(QueryTimingType.NEXT_DOC.toString()).longValue(), greaterThan(0L)); + assertThat(breakdownConstantScoreQuery.get(QueryTimingType.ADVANCE.toString()).longValue(), equalTo(0L)); + assertThat(breakdownConstantScoreQuery.get(QueryTimingType.SCORE.toString()).longValue(), equalTo(0L)); + assertThat(breakdownConstantScoreQuery.get(QueryTimingType.MATCH.toString()).longValue(), equalTo(0L)); + + assertThat(breakdownConstantScoreQuery.get(QueryTimingType.CREATE_WEIGHT.toString() + "_count").longValue(), greaterThan(0L)); + assertThat(breakdownConstantScoreQuery.get(QueryTimingType.BUILD_SCORER.toString() + "_count").longValue(), greaterThan(0L)); + assertThat(breakdownConstantScoreQuery.get(QueryTimingType.NEXT_DOC.toString() + "_count").longValue(), greaterThan(0L)); + assertThat(breakdownConstantScoreQuery.get(QueryTimingType.ADVANCE.toString() + "_count").longValue(), equalTo(0L)); + assertThat(breakdownConstantScoreQuery.get(QueryTimingType.SCORE.toString() + "_count").longValue(), equalTo(0L)); + assertThat(breakdownConstantScoreQuery.get(QueryTimingType.MATCH.toString() + "_count").longValue(), equalTo(0L)); + + assertThat(breakdownTermQuery.get(QueryTimingType.BUILD_SCORER.toString()).longValue(), greaterThan(0L)); + assertThat(breakdownTermQuery.get(QueryTimingType.NEXT_DOC.toString()).longValue(), greaterThan(0L)); + assertThat(breakdownTermQuery.get(QueryTimingType.ADVANCE.toString()).longValue(), equalTo(0L)); + assertThat(breakdownTermQuery.get(QueryTimingType.SCORE.toString()).longValue(), equalTo(0L)); + assertThat(breakdownTermQuery.get(QueryTimingType.MATCH.toString()).longValue(), equalTo(0L)); + + assertThat(breakdownTermQuery.get(QueryTimingType.CREATE_WEIGHT.toString() + "_count").longValue(), greaterThan(0L)); + assertThat(breakdownTermQuery.get(QueryTimingType.BUILD_SCORER.toString() + "_count").longValue(), greaterThan(0L)); + assertThat(breakdownTermQuery.get(QueryTimingType.NEXT_DOC.toString() + "_count").longValue(), greaterThan(0L)); + assertThat(breakdownTermQuery.get(QueryTimingType.ADVANCE.toString() + "_count").longValue(), equalTo(0L)); + assertThat(breakdownTermQuery.get(QueryTimingType.SCORE.toString() + "_count").longValue(), equalTo(0L)); + assertThat(breakdownTermQuery.get(QueryTimingType.MATCH.toString() + "_count").longValue(), equalTo(0L)); + + + assertEquals(breakdownConstantScoreQuery.get(QueryTimingType.NEXT_DOC.toString()).longValue(), + breakdownTermQuery.get(QueryTimingType.NEXT_DOC.toString()).longValue()); + + + long rewriteTime = profiler.getRewriteTime(); + assertThat(rewriteTime, greaterThan(0L)); + } + + public void testConstantScoreTotalHitsCachedQuery() throws IOException { + Query query = new ConstantScoreQuery(new TermQuery(new Term("foo", "bar"))); + + //clean cache and make sure queries will be cached + searcher.setQueryCache(IndexSearcher.getDefaultQueryCache()); + searcher.setQueryCachingPolicy(ALWAYS_CACHE_POLICY); + //Put query on cache + TotalHitCountCollector collector = new TotalHitCountCollector(); + searcher.search(query, collector); + + QueryProfiler profiler = new QueryProfiler(); + searcher.setProfiler(profiler); + collector = new TotalHitCountCollector(); + searcher.search(query, collector); + + List results = profiler.getTree(); + assertEquals(1, results.size()); + Map breakdownConstantScoreQuery = results.get(0).getTimeBreakdown(); + assertEquals(1, results.get(0).getProfiledChildren().size()); + Map breakdownTermQuery = results.get(0).getProfiledChildren().get(0).getTimeBreakdown(); + + assertThat(breakdownConstantScoreQuery.get(QueryTimingType.CREATE_WEIGHT.toString()).longValue(), greaterThan(0L)); + assertThat(breakdownConstantScoreQuery.get(QueryTimingType.BUILD_SCORER.toString()).longValue(), greaterThan(0L)); + assertThat(breakdownConstantScoreQuery.get(QueryTimingType.NEXT_DOC.toString()).longValue(), greaterThan(0L)); + assertThat(breakdownConstantScoreQuery.get(QueryTimingType.ADVANCE.toString()).longValue(), equalTo(0L)); + assertThat(breakdownConstantScoreQuery.get(QueryTimingType.SCORE.toString()).longValue(), equalTo(0L)); + assertThat(breakdownConstantScoreQuery.get(QueryTimingType.MATCH.toString()).longValue(), equalTo(0L)); + + assertThat(breakdownConstantScoreQuery.get(QueryTimingType.CREATE_WEIGHT.toString() + "_count").longValue(), greaterThan(0L)); + assertThat(breakdownConstantScoreQuery.get(QueryTimingType.BUILD_SCORER.toString() + "_count").longValue(), greaterThan(0L)); + assertThat(breakdownConstantScoreQuery.get(QueryTimingType.NEXT_DOC.toString() + "_count").longValue(), greaterThan(0L)); + assertThat(breakdownConstantScoreQuery.get(QueryTimingType.ADVANCE.toString() + "_count").longValue(), equalTo(0L)); + assertThat(breakdownConstantScoreQuery.get(QueryTimingType.SCORE.toString() + "_count").longValue(), equalTo(0L)); + assertThat(breakdownConstantScoreQuery.get(QueryTimingType.MATCH.toString() + "_count").longValue(), equalTo(0L)); + + assertThat(breakdownTermQuery.get(QueryTimingType.CREATE_WEIGHT.toString()).longValue(), greaterThan(0L)); + assertThat(breakdownTermQuery.get(QueryTimingType.BUILD_SCORER.toString()).longValue(), equalTo(0L)); + assertThat(breakdownTermQuery.get(QueryTimingType.NEXT_DOC.toString()).longValue(), equalTo(0L)); + assertThat(breakdownTermQuery.get(QueryTimingType.ADVANCE.toString()).longValue(), equalTo(0L)); + assertThat(breakdownTermQuery.get(QueryTimingType.SCORE.toString()).longValue(), equalTo(0L)); + assertThat(breakdownTermQuery.get(QueryTimingType.MATCH.toString()).longValue(), equalTo(0L)); + + assertThat(breakdownTermQuery.get(QueryTimingType.CREATE_WEIGHT.toString() + "_count").longValue(), greaterThan(0L)); + assertThat(breakdownTermQuery.get(QueryTimingType.BUILD_SCORER.toString() + "_count").longValue(), equalTo(0L)); + assertThat(breakdownTermQuery.get(QueryTimingType.NEXT_DOC.toString() + "_count").longValue(), equalTo(0L)); + assertThat(breakdownTermQuery.get(QueryTimingType.ADVANCE.toString() + "_count").longValue(), equalTo(0L)); + assertThat(breakdownTermQuery.get(QueryTimingType.SCORE.toString() + "_count").longValue(), equalTo(0L)); + assertThat(breakdownTermQuery.get(QueryTimingType.MATCH.toString() + "_count").longValue(), equalTo(0L)); + + long rewriteTime = profiler.getRewriteTime(); + assertThat(rewriteTime, greaterThan(0L)); + } + + public void testNoScoring() throws IOException { QueryProfiler profiler = new QueryProfiler(); searcher.setProfiler(profiler); @@ -276,4 +481,29 @@ public class QueryProfilerTests extends ESTestCase { reader.close(); dir.close(); } + + private static final QueryCachingPolicy ALWAYS_CACHE_POLICY = new QueryCachingPolicy() { + + @Override + public void onUse(Query query) {} + + @Override + public boolean shouldCache(Query query) throws IOException { + return true; + } + + }; + + private static final QueryCachingPolicy NEVER_CACHE_POLICY = new QueryCachingPolicy() { + + @Override + public void onUse(Query query) {} + + @Override + public boolean shouldCache(Query query) throws IOException { + return false; + } + + }; + }