You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by ab...@apache.org on 2017/12/07 12:45:41 UTC

[47/50] [abbrv] lucene-solr:jira/solr-11285-sim: LUCENE-4100: Faster disjunctions when the hit count is not needed.

LUCENE-4100: Faster disjunctions when the hit count is not needed.


Project: http://git-wip-us.apache.org/repos/asf/lucene-solr/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucene-solr/commit/4fc5a872
Tree: http://git-wip-us.apache.org/repos/asf/lucene-solr/tree/4fc5a872
Diff: http://git-wip-us.apache.org/repos/asf/lucene-solr/diff/4fc5a872

Branch: refs/heads/jira/solr-11285-sim
Commit: 4fc5a872ded98522dd673de671ad28e1a6f495d7
Parents: cd30dab
Author: Adrien Grand <jp...@gmail.com>
Authored: Thu Dec 7 10:39:56 2017 +0100
Committer: Adrien Grand <jp...@gmail.com>
Committed: Thu Dec 7 10:49:39 2017 +0100

----------------------------------------------------------------------
 lucene/CHANGES.txt                              |   3 +
 .../apache/lucene/document/RangeFieldQuery.java |   3 +-
 .../SortedNumericDocValuesRangeQuery.java       |   3 +-
 .../document/SortedSetDocValuesRangeQuery.java  |   3 +-
 .../lucene/index/FrozenBufferedUpdates.java     |   3 +-
 .../java/org/apache/lucene/index/Sorter.java    |   5 +
 .../lucene/search/Boolean2ScorerSupplier.java   |  30 +-
 .../org/apache/lucene/search/BooleanQuery.java  |   6 +-
 .../org/apache/lucene/search/BooleanWeight.java |  25 +-
 .../org/apache/lucene/search/BoostQuery.java    |   4 +-
 .../apache/lucene/search/CachingCollector.java  |  13 +-
 .../org/apache/lucene/search/Collector.java     |   6 +-
 .../apache/lucene/search/ConjunctionScorer.java |  20 +
 .../lucene/search/ConstantScoreQuery.java       |  14 +-
 .../lucene/search/ConstantScoreScorer.java      |   5 +
 .../org/apache/lucene/search/DisiWrapper.java   |   3 +
 .../lucene/search/DisjunctionMaxQuery.java      |  14 +-
 .../lucene/search/DisjunctionMaxScorer.java     |   6 +
 .../lucene/search/DisjunctionSumScorer.java     |   7 +
 .../search/DocValuesFieldExistsQuery.java       |   2 +-
 .../lucene/search/DocValuesRewriteMethod.java   |   2 +-
 .../lucene/search/DoubleValuesSource.java       |   4 +-
 .../apache/lucene/search/ExactPhraseScorer.java |   5 +
 .../org/apache/lucene/search/FakeScorer.java    |   5 +
 .../apache/lucene/search/FilterCollector.java   |   4 +-
 .../org/apache/lucene/search/FilterScorer.java  |   3 +
 .../org/apache/lucene/search/FilterWeight.java  |   2 +-
 .../lucene/search/IndexOrDocValuesQuery.java    |   6 +-
 .../org/apache/lucene/search/IndexSearcher.java |  34 +-
 .../apache/lucene/search/MatchAllDocsQuery.java |   2 +-
 .../apache/lucene/search/MatchNoDocsQuery.java  |   2 +-
 .../lucene/search/MinShouldMatchSumScorer.java  |   6 +
 .../apache/lucene/search/MultiCollector.java    |  13 +-
 .../lucene/search/MultiCollectorManager.java    |  18 +-
 .../apache/lucene/search/MultiPhraseQuery.java  |   4 +-
 .../MultiTermQueryConstantScoreWrapper.java     |   4 +-
 .../lucene/search/NormsFieldExistsQuery.java    |   2 +-
 .../org/apache/lucene/search/PhraseQuery.java   |   4 +-
 .../apache/lucene/search/PointInSetQuery.java   |   2 +-
 .../apache/lucene/search/PointRangeQuery.java   |   2 +-
 .../java/org/apache/lucene/search/Query.java    |   4 +-
 .../org/apache/lucene/search/QueryCache.java    |   2 +-
 .../org/apache/lucene/search/QueryRescorer.java |   2 +-
 .../org/apache/lucene/search/ReqExclScorer.java |  11 +
 .../apache/lucene/search/ReqOptSumScorer.java   |   5 +
 .../search/ScoreCachingWrappingScorer.java      |  12 +-
 .../org/apache/lucene/search/ScoreMode.java     |  60 +++
 .../java/org/apache/lucene/search/Scorer.java   |  17 +
 .../lucene/search/SloppyPhraseScorer.java       |   5 +
 .../org/apache/lucene/search/SynonymQuery.java  |  49 +-
 .../apache/lucene/search/TermInSetQuery.java    |   4 +-
 .../org/apache/lucene/search/TermQuery.java     |  25 +-
 .../org/apache/lucene/search/TermScorer.java    |  11 +-
 .../lucene/search/TimeLimitingCollector.java    |   4 +-
 .../apache/lucene/search/TopDocsCollector.java  |   2 +-
 .../apache/lucene/search/TopFieldCollector.java |   4 +-
 .../lucene/search/TopScoreDocCollector.java     |  69 ++-
 .../lucene/search/TotalHitCountCollector.java   |   4 +-
 .../org/apache/lucene/search/WANDScorer.java    | 478 +++++++++++++++++++
 .../java/org/apache/lucene/search/Weight.java   |   2 +-
 .../org/apache/lucene/search/package-info.java  |   6 +-
 .../lucene/search/similarities/Axiomatic.java   |   6 +
 .../search/similarities/BM25Similarity.java     |   8 +-
 .../search/similarities/BooleanSimilarity.java  |   5 +
 .../search/similarities/DFISimilarity.java      |   6 +
 .../search/similarities/DFRSimilarity.java      |   8 +-
 .../search/similarities/IBSimilarity.java       |   6 +
 .../similarities/LMDirichletSimilarity.java     |   8 +-
 .../similarities/LMJelinekMercerSimilarity.java |   8 +-
 .../search/similarities/MultiSimilarity.java    |   9 +
 .../lucene/search/similarities/Similarity.java  |   7 +
 .../search/similarities/SimilarityBase.java     |  14 +-
 .../search/similarities/TFIDFSimilarity.java    |  14 +
 .../search/spans/FieldMaskingSpanQuery.java     |   5 +-
 .../lucene/search/spans/SpanBoostQuery.java     |   5 +-
 .../search/spans/SpanContainingQuery.java       |   9 +-
 .../search/spans/SpanMultiTermQueryWrapper.java |   3 +-
 .../lucene/search/spans/SpanNearQuery.java      |   9 +-
 .../lucene/search/spans/SpanNotQuery.java       |   9 +-
 .../apache/lucene/search/spans/SpanOrQuery.java |   7 +-
 .../search/spans/SpanPositionCheckQuery.java    |   7 +-
 .../apache/lucene/search/spans/SpanQuery.java   |   3 +-
 .../apache/lucene/search/spans/SpanScorer.java  |   5 +
 .../lucene/search/spans/SpanTermQuery.java      |   5 +-
 .../lucene/search/spans/SpanWithinQuery.java    |   9 +-
 .../lucene/index/TestMaxTermFrequency.java      |   4 +
 .../org/apache/lucene/index/TestOmitTf.java     |  21 +-
 .../apache/lucene/search/JustCompileSearch.java |   9 +-
 .../lucene/search/MultiCollectorTest.java       |  24 +-
 .../search/TestBoolean2ScorerSupplier.java      |  62 +--
 .../org/apache/lucene/search/TestBooleanOr.java |   6 +-
 .../apache/lucene/search/TestBooleanQuery.java  |  26 +-
 .../search/TestBooleanQueryVisitSubscorers.java |  14 +-
 .../lucene/search/TestBooleanRewrites.java      |   2 +-
 .../apache/lucene/search/TestBooleanScorer.java |  14 +-
 .../lucene/search/TestCachingCollector.java     |  11 +-
 .../lucene/search/TestConjunctionDISI.java      |   4 +
 .../apache/lucene/search/TestConjunctions.java  |   9 +-
 .../lucene/search/TestConstantScoreQuery.java   |  14 +-
 .../lucene/search/TestDisjunctionMaxQuery.java  |   4 +-
 .../lucene/search/TestDocValuesQueries.java     |   2 +-
 .../lucene/search/TestDocValuesScoring.java     |   5 +
 .../lucene/search/TestDoubleValuesSource.java   |   8 +-
 .../lucene/search/TestEarlyTermination.java     |   4 +-
 .../search/TestIndexOrDocValuesQuery.java       |   4 +-
 .../apache/lucene/search/TestLRUQueryCache.java |  14 +-
 .../lucene/search/TestMinShouldMatch2.java      |   6 +-
 .../apache/lucene/search/TestNeedsScores.java   |  22 +-
 .../apache/lucene/search/TestPointQueries.java  |  12 +-
 .../lucene/search/TestPositionIncrement.java    |   4 +-
 .../search/TestPositiveScoresOnlyCollector.java |   7 +-
 .../apache/lucene/search/TestQueryRescorer.java |   7 +-
 .../search/TestScoreCachingWrappingScorer.java  |  11 +-
 .../apache/lucene/search/TestScorerPerf.java    |   6 +-
 .../apache/lucene/search/TestSimilarity.java    |  16 +-
 .../lucene/search/TestSimilarityProvider.java   |  10 +-
 .../lucene/search/TestSloppyPhraseQuery.java    |   8 +-
 .../apache/lucene/search/TestSortRandom.java    |   2 +-
 .../lucene/search/TestSubScorerFreqs.java       |   5 +
 .../org/apache/lucene/search/TestTermQuery.java |   4 +-
 .../apache/lucene/search/TestTermScorer.java    |  14 +-
 .../search/TestTimeLimitingCollector.java       |   4 +-
 .../lucene/search/TestTopDocsCollector.java     | 206 +++++++-
 .../apache/lucene/search/TestTopDocsMerge.java  |   3 +-
 .../lucene/search/TestTopFieldCollector.java    |   9 +-
 .../TestUsageTrackingFilterCachingPolicy.java   |   2 +-
 .../apache/lucene/search/TestWANDScorer.java    | 394 +++++++++++++++
 .../search/spans/JustCompileSearchSpans.java    |   3 +-
 .../search/spans/TestFieldMaskingSpanQuery.java |  15 +-
 .../search/spans/TestNearSpansOrdered.java      |  29 +-
 .../lucene/search/spans/TestSpanCollection.java |   7 +-
 .../search/spans/TestSpanContainQuery.java      |   3 +-
 .../apache/lucene/search/spans/TestSpans.java   |  11 +-
 .../apache/lucene/expressions/FakeScorer.java   |   5 +
 .../org/apache/lucene/facet/DrillSideways.java  |  11 +-
 .../apache/lucene/facet/DrillSidewaysQuery.java |   7 +-
 .../lucene/facet/DrillSidewaysScorer.java       |   5 +
 .../apache/lucene/facet/FacetsCollector.java    |   7 +-
 .../apache/lucene/facet/range/DoubleRange.java  |   5 +-
 .../facet/range/DoubleRangeFacetCounts.java     |   3 +-
 .../apache/lucene/facet/range/LongRange.java    |   5 +-
 .../facet/range/LongRangeFacetCounts.java       |   3 +-
 .../facet/AssertingSubDocsAtOnceCollector.java  |   5 +-
 .../apache/lucene/facet/TestDrillSideways.java  |   7 +-
 .../facet/range/TestRangeFacetCounts.java       |   5 +-
 .../search/grouping/AllGroupHeadsCollector.java |   5 +-
 .../search/grouping/AllGroupsCollector.java     |   5 +-
 .../search/grouping/BlockGroupingCollector.java |   5 +-
 .../grouping/DistinctValuesCollector.java       |   5 +-
 .../lucene/search/grouping/FakeScorer.java      |   5 +
 .../grouping/FirstPassGroupingCollector.java    |   5 +-
 .../search/grouping/GroupFacetCollector.java    |   5 +-
 .../lucene/search/grouping/GroupingSearch.java  |   3 +-
 .../grouping/SecondPassGroupingCollector.java   |   5 +-
 .../lucene/search/grouping/TestGrouping.java    |   5 +-
 .../search/highlight/QueryTermExtractor.java    |   3 +-
 .../highlight/WeightedSpanTermExtractor.java    |   9 +-
 .../lucene/search/uhighlight/PhraseHelper.java  |   7 +-
 .../search/uhighlight/UnifiedHighlighter.java   |   3 +-
 .../search/highlight/HighlighterPhraseTest.java |   5 +-
 .../uhighlight/TestUnifiedHighlighterMTQ.java   |   5 +-
 .../TestUnifiedHighlighterStrictPhrases.java    |   5 +-
 .../search/join/BaseGlobalOrdinalScorer.java    |   5 +
 .../apache/lucene/search/join/FakeScorer.java   |   5 +
 .../search/join/GenericTermsCollector.java      |   4 +-
 .../search/join/GlobalOrdinalsCollector.java    |   4 +-
 .../lucene/search/join/GlobalOrdinalsQuery.java |   4 +-
 .../join/GlobalOrdinalsWithScoreCollector.java  |   8 +-
 .../join/GlobalOrdinalsWithScoreQuery.java      |   8 +-
 .../org/apache/lucene/search/join/JoinUtil.java |   8 +-
 .../join/ParentChildrenBlockJoinQuery.java      |   8 +-
 .../join/PointInSetIncludingScoreQuery.java     |   7 +-
 .../lucene/search/join/QueryBitSetProducer.java |   2 +-
 .../lucene/search/join/TermsCollector.java      |   4 +-
 .../search/join/TermsIncludingScoreQuery.java   |  11 +-
 .../search/join/TermsWithScoreCollector.java    |   4 +-
 .../search/join/ToChildBlockJoinQuery.java      |   9 +-
 .../search/join/ToParentBlockJoinQuery.java     |  15 +-
 .../lucene/search/join/TestBlockJoin.java       |  21 +-
 .../search/join/TestBlockJoinValidation.java    |   2 +-
 .../apache/lucene/search/join/TestJoinUtil.java |  28 +-
 .../apache/lucene/index/memory/MemoryIndex.java |   5 +-
 .../apache/lucene/index/PKIndexSplitter.java    |   4 +-
 .../search/DiversifiedTopDocsCollector.java     |   4 +-
 .../lucene/search/DocValuesStatsCollector.java  |   4 +-
 .../search/TestDiversifiedTopDocsCollector.java |   5 +
 .../apache/lucene/queries/BoostingQuery.java    |  19 +-
 .../apache/lucene/queries/CustomScoreQuery.java |  16 +-
 .../lucene/queries/function/BoostedQuery.java   |  14 +-
 .../queries/function/FunctionMatchQuery.java    |   3 +-
 .../lucene/queries/function/FunctionQuery.java  |   8 +-
 .../queries/function/FunctionRangeQuery.java    |   3 +-
 .../queries/function/FunctionScoreQuery.java    |  11 +-
 .../lucene/queries/function/ValueSource.java    |   5 +
 .../queries/function/ValueSourceScorer.java     |   5 +
 .../function/valuesource/QueryValueSource.java  |   3 +-
 .../queries/payloads/PayloadScoreQuery.java     |   7 +-
 .../queries/payloads/SpanPayloadCheckQuery.java |   7 +-
 .../function/TestIndexReaderFunctions.java      |   5 +-
 .../queries/payloads/TestPayloadSpans.java      |  31 +-
 .../queries/payloads/TestPayloadTermQuery.java  |   5 +-
 .../surround/query/BooleanQueryTst.java         |   5 +-
 .../document/LatLonDocValuesBoxQuery.java       |   3 +-
 .../document/LatLonDocValuesDistanceQuery.java  |   3 +-
 .../document/LatLonPointDistanceQuery.java      |   3 +-
 .../document/LatLonPointInPolygonQuery.java     |   3 +-
 .../apache/lucene/payloads/PayloadSpanUtil.java |   3 +-
 .../org/apache/lucene/search/CoveringQuery.java |   4 +-
 .../apache/lucene/search/CoveringScorer.java    |   6 +
 .../lucene/search/DocValuesNumbersQuery.java    |   2 +-
 .../lucene/search/DocValuesTermsQuery.java      |   2 +-
 .../lucene/search/TermAutomatonQuery.java       |   2 +-
 .../lucene/search/TermAutomatonScorer.java      |   6 +-
 .../sandbox/queries/FuzzyLikeThisQueryTest.java |   9 +-
 .../lucene/search/TestTermAutomatonQuery.java   |   2 +-
 .../spatial/composite/CompositeVerifyQuery.java |   5 +-
 .../composite/IntersectsRPTVerifyQuery.java     |   3 +-
 .../spatial/prefix/AbstractPrefixTreeQuery.java |   3 +-
 .../serialized/SerializedDVStrategy.java        |   3 +-
 .../spatial/vector/PointVectorStrategy.java     |   5 +-
 .../spatial/prefix/NumberRangeFacetsTest.java   |   5 +-
 .../spatial3d/PointInGeo3DShapeQuery.java       |   3 +-
 .../apache/lucene/spatial3d/TestGeo3DPoint.java |   5 +-
 .../search/suggest/document/ContextQuery.java   |   5 +-
 .../suggest/document/FuzzyCompletionQuery.java  |   3 +-
 .../suggest/document/PrefixCompletionQuery.java |   3 +-
 .../suggest/document/RegexCompletionQuery.java  |   3 +-
 .../suggest/document/SuggestIndexSearcher.java  |   2 +-
 .../document/TopSuggestDocsCollector.java       |   5 +-
 .../apache/lucene/geo/BaseGeoPointTestCase.java |  17 +-
 .../lucene/search/AssertingBulkScorer.java      |  12 +-
 .../lucene/search/AssertingCollector.java       |   2 +-
 .../lucene/search/AssertingIndexSearcher.java   |   4 +-
 .../lucene/search/AssertingLeafCollector.java   |   6 +-
 .../apache/lucene/search/AssertingQuery.java    |   4 +-
 .../apache/lucene/search/AssertingScorer.java   |  30 +-
 .../apache/lucene/search/AssertingWeight.java   |  12 +-
 .../search/BaseRangeFieldQueryTestCase.java     |   2 +-
 .../lucene/search/BulkScorerWrapperScorer.java  |   5 +
 .../org/apache/lucene/search/CheckHits.java     |   8 +-
 .../org/apache/lucene/search/QueryUtils.java    |  22 +-
 .../lucene/search/RandomApproximationQuery.java |   9 +-
 .../lucene/search/ShardSearchingTestBase.java   |   2 +-
 .../similarities/AssertingSimilarity.java       |   9 +-
 .../similarities/BaseSimilarityTestCase.java    |   2 +
 .../lucene/search/spans/AssertingSpanQuery.java |   5 +-
 .../search/TestBaseExplanationTestCase.java     |   5 +-
 .../analytics/facet/AbstractSolrQueryFacet.java |   5 +-
 .../java/org/apache/solr/ltr/LTRRescorer.java   |   5 +-
 .../org/apache/solr/ltr/LTRScoringQuery.java    |  22 +-
 .../org/apache/solr/ltr/feature/Feature.java    |   4 +
 .../solr/ltr/feature/FieldLengthFeature.java    |   5 +
 .../solr/ltr/feature/FieldValueFeature.java     |   5 +
 .../solr/ltr/feature/OriginalScoreFeature.java  |   8 +-
 .../apache/solr/ltr/feature/SolrFeature.java    |   8 +-
 .../LTRFeatureLoggerTransformerFactory.java     |   3 +-
 .../solr/ltr/TestLTRReRankingPipeline.java      |   5 +-
 .../apache/solr/ltr/TestLTRScoringQuery.java    |   3 +-
 .../solr/ltr/TestSelectiveWeightCreation.java   |   3 +-
 .../solr/handler/component/ExpandComponent.java |   9 +-
 .../solr/handler/component/QueryComponent.java  |   5 +
 .../handler/component/RealTimeGetComponent.java |   3 +-
 .../java/org/apache/solr/query/FilterQuery.java |   7 +-
 .../org/apache/solr/query/SolrRangeQuery.java   |   7 +-
 .../java/org/apache/solr/schema/LatLonType.java |   8 +-
 .../apache/solr/search/AbstractReRankQuery.java |   6 +-
 .../solr/search/CollapsingQParserPlugin.java    |  14 +-
 .../apache/solr/search/DelegatingCollector.java |   5 +-
 .../org/apache/solr/search/DocSetCollector.java |   5 +-
 .../apache/solr/search/ExportQParserPlugin.java |   6 +-
 .../src/java/org/apache/solr/search/Filter.java |   3 +-
 .../solr/search/GraphTermsQParserPlugin.java    |   5 +-
 .../apache/solr/search/HashQParserPlugin.java   |   3 +-
 .../apache/solr/search/JoinQParserPlugin.java   |   3 +-
 .../apache/solr/search/QueryWrapperFilter.java  |   3 +-
 .../org/apache/solr/search/ReRankCollector.java |   5 +-
 .../solr/search/SolrConstantScoreQuery.java     |   3 +-
 .../apache/solr/search/SolrIndexSearcher.java   |  14 +-
 .../org/apache/solr/search/WrappedQuery.java    |   5 +-
 .../facet/FacetFieldProcessorByHashDV.java      |   7 +-
 .../org/apache/solr/search/join/GraphQuery.java |   8 +-
 .../solr/search/join/GraphTermsCollector.java   |   5 +-
 .../search/join/ScoreJoinQParserPlugin.java     |  12 +-
 .../solr/search/stats/ExactStatsCache.java      |   3 +-
 .../solr/update/DeleteByQueryWrapper.java       |   5 +-
 .../solr/search/TestQueryWrapperFilter.java     |   5 +-
 .../apache/solr/search/TestRankQueryPlugin.java |  18 +-
 .../uninverting/TestFieldCacheSortRandom.java   |   3 +-
 288 files changed, 2617 insertions(+), 711 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/4fc5a872/lucene/CHANGES.txt
----------------------------------------------------------------------
diff --git a/lucene/CHANGES.txt b/lucene/CHANGES.txt
index cce2d89..bb84357 100644
--- a/lucene/CHANGES.txt
+++ b/lucene/CHANGES.txt
@@ -49,6 +49,9 @@ Optimizations
 * LUCENE-8040: Optimize IndexSearcher.collectionStatistics, avoiding MultiFields/MultiTerms
   (David Smiley, Robert Muir)
 
+* LUCENE-4100: Disjunctions now support faster collection of top hits when the
+  total hit count is not required. (Stefan Pohl, Adrien Grand, Robert Muir)
+
 ======================= Lucene 7.3.0 =======================
 
 API Changes

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/4fc5a872/lucene/core/src/java/org/apache/lucene/document/RangeFieldQuery.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/document/RangeFieldQuery.java b/lucene/core/src/java/org/apache/lucene/document/RangeFieldQuery.java
index a6cbec4..a24b7cd 100644
--- a/lucene/core/src/java/org/apache/lucene/document/RangeFieldQuery.java
+++ b/lucene/core/src/java/org/apache/lucene/document/RangeFieldQuery.java
@@ -34,6 +34,7 @@ import org.apache.lucene.search.Query;
 import org.apache.lucene.search.Scorer;
 import org.apache.lucene.search.ScorerSupplier;
 import org.apache.lucene.search.Weight;
+import org.apache.lucene.search.ScoreMode;
 import org.apache.lucene.util.DocIdSetBuilder;
 import org.apache.lucene.util.StringHelper;
 
@@ -261,7 +262,7 @@ abstract class RangeFieldQuery extends Query {
   }
 
   @Override
-  public final Weight createWeight(IndexSearcher searcher, boolean needsScores, float boost) throws IOException {
+  public final Weight createWeight(IndexSearcher searcher, ScoreMode scoreMode, float boost) throws IOException {
     return new ConstantScoreWeight(this, boost) {
 
       private IntersectVisitor getIntersectVisitor(DocIdSetBuilder result) {

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/4fc5a872/lucene/core/src/java/org/apache/lucene/document/SortedNumericDocValuesRangeQuery.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/document/SortedNumericDocValuesRangeQuery.java b/lucene/core/src/java/org/apache/lucene/document/SortedNumericDocValuesRangeQuery.java
index c7ab7fa..246b50f 100644
--- a/lucene/core/src/java/org/apache/lucene/document/SortedNumericDocValuesRangeQuery.java
+++ b/lucene/core/src/java/org/apache/lucene/document/SortedNumericDocValuesRangeQuery.java
@@ -30,6 +30,7 @@ import org.apache.lucene.search.ConstantScoreWeight;
 import org.apache.lucene.search.DocValuesFieldExistsQuery;
 import org.apache.lucene.search.IndexSearcher;
 import org.apache.lucene.search.Query;
+import org.apache.lucene.search.ScoreMode;
 import org.apache.lucene.search.Scorer;
 import org.apache.lucene.search.TwoPhaseIterator;
 import org.apache.lucene.search.Weight;
@@ -92,7 +93,7 @@ abstract class SortedNumericDocValuesRangeQuery extends Query {
   abstract SortedNumericDocValues getValues(LeafReader reader, String field) throws IOException;
 
   @Override
-  public Weight createWeight(IndexSearcher searcher, boolean needsScores, float boost) throws IOException {
+  public Weight createWeight(IndexSearcher searcher, ScoreMode scoreMode, float boost) throws IOException {
     return new ConstantScoreWeight(this, boost) {
 
       @Override

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/4fc5a872/lucene/core/src/java/org/apache/lucene/document/SortedSetDocValuesRangeQuery.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/document/SortedSetDocValuesRangeQuery.java b/lucene/core/src/java/org/apache/lucene/document/SortedSetDocValuesRangeQuery.java
index 1d0433d..de7c11b 100644
--- a/lucene/core/src/java/org/apache/lucene/document/SortedSetDocValuesRangeQuery.java
+++ b/lucene/core/src/java/org/apache/lucene/document/SortedSetDocValuesRangeQuery.java
@@ -30,6 +30,7 @@ import org.apache.lucene.search.ConstantScoreWeight;
 import org.apache.lucene.search.DocValuesFieldExistsQuery;
 import org.apache.lucene.search.IndexSearcher;
 import org.apache.lucene.search.Query;
+import org.apache.lucene.search.ScoreMode;
 import org.apache.lucene.search.Scorer;
 import org.apache.lucene.search.TwoPhaseIterator;
 import org.apache.lucene.search.Weight;
@@ -103,7 +104,7 @@ abstract class SortedSetDocValuesRangeQuery extends Query {
   abstract SortedSetDocValues getValues(LeafReader reader, String field) throws IOException;
 
   @Override
-  public Weight createWeight(IndexSearcher searcher, boolean needsScores, float boost) throws IOException {
+  public Weight createWeight(IndexSearcher searcher, ScoreMode scoreMode, float boost) throws IOException {
     return new ConstantScoreWeight(this, boost) {
       @Override
       public Scorer scorer(LeafReaderContext context) throws IOException {

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/4fc5a872/lucene/core/src/java/org/apache/lucene/index/FrozenBufferedUpdates.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/index/FrozenBufferedUpdates.java b/lucene/core/src/java/org/apache/lucene/index/FrozenBufferedUpdates.java
index c4e60db..202bf2c 100644
--- a/lucene/core/src/java/org/apache/lucene/index/FrozenBufferedUpdates.java
+++ b/lucene/core/src/java/org/apache/lucene/index/FrozenBufferedUpdates.java
@@ -32,6 +32,7 @@ import org.apache.lucene.index.DocValuesUpdate.NumericDocValuesUpdate;
 import org.apache.lucene.search.DocIdSetIterator;
 import org.apache.lucene.search.IndexSearcher;
 import org.apache.lucene.search.Query;
+import org.apache.lucene.search.ScoreMode;
 import org.apache.lucene.search.Scorer;
 import org.apache.lucene.search.Weight;
 import org.apache.lucene.store.ByteArrayDataInput;
@@ -684,7 +685,7 @@ class FrozenBufferedUpdates {
         }
         final IndexSearcher searcher = new IndexSearcher(readerContext.reader());
         searcher.setQueryCache(null);
-        final Weight weight = searcher.createNormalizedWeight(query, false);
+        final Weight weight = searcher.createNormalizedWeight(query, ScoreMode.COMPLETE_NO_SCORES);
         final Scorer scorer = weight.scorer(readerContext);
         if (scorer != null) {
           final DocIdSetIterator it = scorer.iterator();

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/4fc5a872/lucene/core/src/java/org/apache/lucene/index/Sorter.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/index/Sorter.java b/lucene/core/src/java/org/apache/lucene/index/Sorter.java
index dbb8d2b..876a034 100644
--- a/lucene/core/src/java/org/apache/lucene/index/Sorter.java
+++ b/lucene/core/src/java/org/apache/lucene/index/Sorter.java
@@ -464,6 +464,11 @@ final class Sorter {
     public float score() throws IOException {
       return score;
     }
+
+    @Override
+    public float maxScore() {
+      return Float.POSITIVE_INFINITY;
+    }
   };
   
 }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/4fc5a872/lucene/core/src/java/org/apache/lucene/search/Boolean2ScorerSupplier.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/search/Boolean2ScorerSupplier.java b/lucene/core/src/java/org/apache/lucene/search/Boolean2ScorerSupplier.java
index 68a4280..5956836 100644
--- a/lucene/core/src/java/org/apache/lucene/search/Boolean2ScorerSupplier.java
+++ b/lucene/core/src/java/org/apache/lucene/search/Boolean2ScorerSupplier.java
@@ -31,20 +31,20 @@ final class Boolean2ScorerSupplier extends ScorerSupplier {
 
   private final BooleanWeight weight;
   private final Map<BooleanClause.Occur, Collection<ScorerSupplier>> subs;
-  private final boolean needsScores;
+  private final ScoreMode scoreMode;
   private final int minShouldMatch;
   private long cost = -1;
 
   Boolean2ScorerSupplier(BooleanWeight weight,
       Map<Occur, Collection<ScorerSupplier>> subs,
-      boolean needsScores, int minShouldMatch) {
+      ScoreMode scoreMode, int minShouldMatch) {
     if (minShouldMatch < 0) {
       throw new IllegalArgumentException("minShouldMatch must be positive, but got: " + minShouldMatch);
     }
     if (minShouldMatch != 0 && minShouldMatch >= subs.get(Occur.SHOULD).size()) {
       throw new IllegalArgumentException("minShouldMatch must be strictly less than the number of SHOULD clauses");
     }
-    if (needsScores == false && minShouldMatch == 0 && subs.get(Occur.SHOULD).size() > 0
+    if (scoreMode.needsScores() == false && minShouldMatch == 0 && subs.get(Occur.SHOULD).size() > 0
         && subs.get(Occur.MUST).size() + subs.get(Occur.FILTER).size() > 0) {
       throw new IllegalArgumentException("Cannot pass purely optional clauses if scores are not needed");
     }
@@ -53,7 +53,7 @@ final class Boolean2ScorerSupplier extends ScorerSupplier {
     }
     this.weight = weight;
     this.subs = subs;
-    this.needsScores = needsScores;
+    this.scoreMode = scoreMode;
     this.minShouldMatch = minShouldMatch;
   }
 
@@ -94,7 +94,7 @@ final class Boolean2ScorerSupplier extends ScorerSupplier {
 
     // pure disjunction
     if (subs.get(Occur.FILTER).isEmpty() && subs.get(Occur.MUST).isEmpty()) {
-      return excl(opt(subs.get(Occur.SHOULD), minShouldMatch, needsScores, leadCost), subs.get(Occur.MUST_NOT), leadCost);
+      return excl(opt(subs.get(Occur.SHOULD), minShouldMatch, scoreMode, leadCost), subs.get(Occur.MUST_NOT), leadCost);
     }
 
     // conjunction-disjunction mix:
@@ -104,13 +104,13 @@ final class Boolean2ScorerSupplier extends ScorerSupplier {
 
     if (minShouldMatch > 0) {
       Scorer req = excl(req(subs.get(Occur.FILTER), subs.get(Occur.MUST), leadCost), subs.get(Occur.MUST_NOT), leadCost);
-      Scorer opt = opt(subs.get(Occur.SHOULD), minShouldMatch, needsScores, leadCost);
+      Scorer opt = opt(subs.get(Occur.SHOULD), minShouldMatch, scoreMode, leadCost);
       return new ConjunctionScorer(weight, Arrays.asList(req, opt), Arrays.asList(req, opt));
     } else {
-      assert needsScores;
+      assert scoreMode.needsScores();
       return new ReqOptSumScorer(
           excl(req(subs.get(Occur.FILTER), subs.get(Occur.MUST), leadCost), subs.get(Occur.MUST_NOT), leadCost),
-          opt(subs.get(Occur.SHOULD), minShouldMatch, needsScores, leadCost));
+          opt(subs.get(Occur.SHOULD), minShouldMatch, scoreMode, leadCost));
     }
   }
 
@@ -121,7 +121,7 @@ final class Boolean2ScorerSupplier extends ScorerSupplier {
     if (requiredNoScoring.size() + requiredScoring.size() == 1) {
       Scorer req = (requiredNoScoring.isEmpty() ? requiredScoring : requiredNoScoring).iterator().next().get(leadCost);
 
-      if (needsScores == false) {
+      if (scoreMode.needsScores() == false) {
         return req;
       }
 
@@ -134,6 +134,10 @@ final class Boolean2ScorerSupplier extends ScorerSupplier {
           public float score() throws IOException {
             return 0f;
           }
+          @Override
+          public float maxScore() {
+            return 0f;
+          }
         };
       }
 
@@ -157,12 +161,12 @@ final class Boolean2ScorerSupplier extends ScorerSupplier {
     if (prohibited.isEmpty()) {
       return main;
     } else {
-      return new ReqExclScorer(main, opt(prohibited, 1, false, leadCost));
+      return new ReqExclScorer(main, opt(prohibited, 1, ScoreMode.COMPLETE_NO_SCORES, leadCost));
     }
   }
 
   private Scorer opt(Collection<ScorerSupplier> optional, int minShouldMatch,
-      boolean needsScores, long leadCost) throws IOException {
+      ScoreMode scoreMode, long leadCost) throws IOException {
     if (optional.size() == 1) {
       return optional.iterator().next().get(leadCost);
     } else {
@@ -172,8 +176,10 @@ final class Boolean2ScorerSupplier extends ScorerSupplier {
       }
       if (minShouldMatch > 1) {
         return new MinShouldMatchSumScorer(weight, optionalScorers, minShouldMatch);
+      } else if (scoreMode == ScoreMode.TOP_SCORES) {
+        return new WANDScorer(weight, optionalScorers);
       } else {
-        return new DisjunctionSumScorer(weight, optionalScorers, needsScores);
+        return new DisjunctionSumScorer(weight, optionalScorers, scoreMode.needsScores());
       }
     }
   }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/4fc5a872/lucene/core/src/java/org/apache/lucene/search/BooleanQuery.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/search/BooleanQuery.java b/lucene/core/src/java/org/apache/lucene/search/BooleanQuery.java
index a3f5ae0..8e6df16 100644
--- a/lucene/core/src/java/org/apache/lucene/search/BooleanQuery.java
+++ b/lucene/core/src/java/org/apache/lucene/search/BooleanQuery.java
@@ -196,12 +196,12 @@ public class BooleanQuery extends Query implements Iterable<BooleanClause> {
   }
 
   @Override
-  public Weight createWeight(IndexSearcher searcher, boolean needsScores, float boost) throws IOException {
+  public Weight createWeight(IndexSearcher searcher, ScoreMode scoreMode, float boost) throws IOException {
     BooleanQuery query = this;
-    if (needsScores == false) {
+    if (scoreMode.needsScores() == false) {
       query = rewriteNoScoring();
     }
-    return new BooleanWeight(query, searcher, needsScores, boost);
+    return new BooleanWeight(query, searcher, scoreMode, boost);
   }
 
   @Override

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/4fc5a872/lucene/core/src/java/org/apache/lucene/search/BooleanWeight.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/search/BooleanWeight.java b/lucene/core/src/java/org/apache/lucene/search/BooleanWeight.java
index ed729c7..900a77f 100644
--- a/lucene/core/src/java/org/apache/lucene/search/BooleanWeight.java
+++ b/lucene/core/src/java/org/apache/lucene/search/BooleanWeight.java
@@ -42,16 +42,16 @@ final class BooleanWeight extends Weight {
   final BooleanQuery query;
   
   final ArrayList<Weight> weights;
-  final boolean needsScores;
+  final ScoreMode scoreMode;
 
-  BooleanWeight(BooleanQuery query, IndexSearcher searcher, boolean needsScores, float boost) throws IOException {
+  BooleanWeight(BooleanQuery query, IndexSearcher searcher, ScoreMode scoreMode, float boost) throws IOException {
     super(query);
     this.query = query;
-    this.needsScores = needsScores;
-    this.similarity = searcher.getSimilarity(needsScores);
+    this.scoreMode = scoreMode;
+    this.similarity = searcher.getSimilarity(scoreMode.needsScores());
     weights = new ArrayList<>();
     for (BooleanClause c : query) {
-      Weight w = searcher.createWeight(c.getQuery(), needsScores && c.isScoring(), boost);
+      Weight w = searcher.createWeight(c.getQuery(), c.isScoring() ? scoreMode : ScoreMode.COMPLETE_NO_SCORES, boost);
       weights.add(w);
     }
   }
@@ -60,7 +60,7 @@ final class BooleanWeight extends Weight {
   public void extractTerms(Set<Term> terms) {
     int i = 0;
     for (BooleanClause clause : query) {
-      if (clause.isScoring() || (needsScores == false && clause.isProhibited() == false)) {
+      if (clause.isScoring() || (scoreMode.needsScores() == false && clause.isProhibited() == false)) {
         weights.get(i).extractTerms(terms);
       }
       i++;
@@ -178,7 +178,7 @@ final class BooleanWeight extends Weight {
       return optional.get(0);
     }
 
-    return new BooleanScorer(this, optional, Math.max(1, query.getMinimumNumberShouldMatch()), needsScores);
+    return new BooleanScorer(this, optional, Math.max(1, query.getMinimumNumberShouldMatch()), scoreMode.needsScores());
   }
 
   // Return a BulkScorer for the required clauses only,
@@ -201,7 +201,7 @@ final class BooleanWeight extends Weight {
         // no matches
         return null;
       }
-      if (c.isScoring() == false && needsScores) {
+      if (c.isScoring() == false && scoreMode.needsScores()) {
         scorer = disableScoring(scorer);
       }
     }
@@ -285,6 +285,11 @@ final class BooleanWeight extends Weight {
 
   @Override
   public BulkScorer bulkScorer(LeafReaderContext context) throws IOException {
+    if (scoreMode == ScoreMode.TOP_SCORES) {
+      // If only the top docs are requested, use the default bulk scorer
+      // so that we can dynamically prune non-competitive hits.
+      return super.bulkScorer(context);
+    }
     final BulkScorer bulkScorer = booleanScorer(context);
     if (bulkScorer != null) {
       // bulk scoring is applicable, use it
@@ -361,11 +366,11 @@ final class BooleanWeight extends Weight {
     }
 
     // we don't need scores, so if we have required clauses, drop optional clauses completely
-    if (!needsScores && minShouldMatch == 0 && scorers.get(Occur.MUST).size() + scorers.get(Occur.FILTER).size() > 0) {
+    if (scoreMode.needsScores() == false && minShouldMatch == 0 && scorers.get(Occur.MUST).size() + scorers.get(Occur.FILTER).size() > 0) {
       scorers.get(Occur.SHOULD).clear();
     }
 
-    return new Boolean2ScorerSupplier(this, scorers, needsScores, minShouldMatch);
+    return new Boolean2ScorerSupplier(this, scorers, scoreMode, minShouldMatch);
   }
 
 }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/4fc5a872/lucene/core/src/java/org/apache/lucene/search/BoostQuery.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/search/BoostQuery.java b/lucene/core/src/java/org/apache/lucene/search/BoostQuery.java
index 0cfb0d2..2c2cb78 100644
--- a/lucene/core/src/java/org/apache/lucene/search/BoostQuery.java
+++ b/lucene/core/src/java/org/apache/lucene/search/BoostQuery.java
@@ -113,8 +113,8 @@ public final class BoostQuery extends Query {
   }
 
   @Override
-  public Weight createWeight(IndexSearcher searcher, boolean needsScores, float boost) throws IOException {
-    return query.createWeight(searcher, needsScores, BoostQuery.this.boost * boost);
+  public Weight createWeight(IndexSearcher searcher, ScoreMode scoreMode, float boost) throws IOException {
+    return query.createWeight(searcher, scoreMode, BoostQuery.this.boost * boost);
   }
 
 }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/4fc5a872/lucene/core/src/java/org/apache/lucene/search/CachingCollector.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/search/CachingCollector.java b/lucene/core/src/java/org/apache/lucene/search/CachingCollector.java
index 6a14240..9853772 100644
--- a/lucene/core/src/java/org/apache/lucene/search/CachingCollector.java
+++ b/lucene/core/src/java/org/apache/lucene/search/CachingCollector.java
@@ -68,6 +68,11 @@ public abstract class CachingCollector extends FilterCollector {
     public final float score() { return score; }
 
     @Override
+    public float maxScore() {
+      return Float.POSITIVE_INFINITY;
+    }
+
+    @Override
     public int docID() {
       return doc;
     }
@@ -175,8 +180,8 @@ public abstract class CachingCollector extends FilterCollector {
 
     /** Ensure the scores are collected so they can be replayed, even if the wrapped collector doesn't need them. */
     @Override
-    public boolean needsScores() {
-      return true;
+    public ScoreMode scoreMode() {
+      return ScoreMode.COMPLETE;
     }
 
     @Override
@@ -301,8 +306,8 @@ public abstract class CachingCollector extends FilterCollector {
       public void collect(int doc) {}
 
       @Override
-      public boolean needsScores() {
-        return true;
+      public ScoreMode scoreMode() {
+        return ScoreMode.COMPLETE;
       }
 
     };

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/4fc5a872/lucene/core/src/java/org/apache/lucene/search/Collector.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/search/Collector.java b/lucene/core/src/java/org/apache/lucene/search/Collector.java
index 0e66166..9818c67 100644
--- a/lucene/core/src/java/org/apache/lucene/search/Collector.java
+++ b/lucene/core/src/java/org/apache/lucene/search/Collector.java
@@ -74,9 +74,7 @@ public interface Collector {
   LeafCollector getLeafCollector(LeafReaderContext context) throws IOException;
   
   /**
-   * Indicates if document scores are needed by this collector.
-   * 
-   * @return {@code true} if scores are needed.
+   * Indicates what features are required from the scorer.
    */
-  boolean needsScores();
+  ScoreMode scoreMode();
 }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/4fc5a872/lucene/core/src/java/org/apache/lucene/search/ConjunctionScorer.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/search/ConjunctionScorer.java b/lucene/core/src/java/org/apache/lucene/search/ConjunctionScorer.java
index e32f126..2a8b6e6 100644
--- a/lucene/core/src/java/org/apache/lucene/search/ConjunctionScorer.java
+++ b/lucene/core/src/java/org/apache/lucene/search/ConjunctionScorer.java
@@ -62,6 +62,26 @@ class ConjunctionScorer extends Scorer {
   }
 
   @Override
+  public float maxScore() {
+    // We iterate in the same order as #score() so no need to worry
+    // about floating-point errors: we would do the same errors in
+    // #score()
+    double sum = 0d;
+    for (Scorer scorer : scorers) {
+      sum += scorer.maxScore();
+    }
+    return (float) sum;
+  }
+
+  @Override
+  public void setMinCompetitiveScore(float score) {
+    if (scorers.length == 1) {
+      scorers[0].setMinCompetitiveScore(score);
+    }
+    // TODO: handle the case when there are multiple scoring clauses too
+  }
+
+  @Override
   public Collection<ChildScorer> getChildren() {
     ArrayList<ChildScorer> children = new ArrayList<>();
     for (Scorer scorer : required) {

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/4fc5a872/lucene/core/src/java/org/apache/lucene/search/ConstantScoreQuery.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/search/ConstantScoreQuery.java b/lucene/core/src/java/org/apache/lucene/search/ConstantScoreQuery.java
index 90cc5b4..9334f66 100644
--- a/lucene/core/src/java/org/apache/lucene/search/ConstantScoreQuery.java
+++ b/lucene/core/src/java/org/apache/lucene/search/ConstantScoreQuery.java
@@ -94,6 +94,10 @@ public final class ConstantScoreQuery extends Query {
             public float score() throws IOException {
               return theScore;
             }
+            @Override
+            public float maxScore() {
+              return theScore;
+            }
           });
         }
       };
@@ -106,9 +110,9 @@ public final class ConstantScoreQuery extends Query {
   }
 
   @Override
-  public Weight createWeight(IndexSearcher searcher, boolean needsScores, float boost) throws IOException {
-    final Weight innerWeight = searcher.createWeight(query, false, 1f);
-    if (needsScores) {
+  public Weight createWeight(IndexSearcher searcher, ScoreMode scoreMode, float boost) throws IOException {
+    final Weight innerWeight = searcher.createWeight(query, ScoreMode.COMPLETE_NO_SCORES, 1f);
+    if (scoreMode.needsScores()) {
       return new ConstantScoreWeight(this, boost) {
 
         @Override
@@ -137,6 +141,10 @@ public final class ConstantScoreQuery extends Query {
                   return score;
                 }
                 @Override
+                public float maxScore() {
+                  return score;
+                }
+                @Override
                 public Collection<ChildScorer> getChildren() {
                   return Collections.singleton(new ChildScorer(innerScorer, "constant"));
                 }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/4fc5a872/lucene/core/src/java/org/apache/lucene/search/ConstantScoreScorer.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/search/ConstantScoreScorer.java b/lucene/core/src/java/org/apache/lucene/search/ConstantScoreScorer.java
index 2846388..5c57746 100644
--- a/lucene/core/src/java/org/apache/lucene/search/ConstantScoreScorer.java
+++ b/lucene/core/src/java/org/apache/lucene/search/ConstantScoreScorer.java
@@ -54,6 +54,11 @@ public final class ConstantScoreScorer extends Scorer {
   }
 
   @Override
+  public float maxScore() {
+    return score;
+  }
+
+  @Override
   public DocIdSetIterator iterator() {
     return disi;
   }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/4fc5a872/lucene/core/src/java/org/apache/lucene/search/DisiWrapper.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/search/DisiWrapper.java b/lucene/core/src/java/org/apache/lucene/search/DisiWrapper.java
index f254340..6412d41 100644
--- a/lucene/core/src/java/org/apache/lucene/search/DisiWrapper.java
+++ b/lucene/core/src/java/org/apache/lucene/search/DisiWrapper.java
@@ -38,6 +38,9 @@ public class DisiWrapper {
   // two-phase iteration
   public final TwoPhaseIterator twoPhaseView;
 
+  // For MaxScoreScorer
+  long maxScore;
+
   // FOR SPANS
   public final Spans spans;
   public int lastApproxMatchDoc; // last doc of approximation that did match

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/4fc5a872/lucene/core/src/java/org/apache/lucene/search/DisjunctionMaxQuery.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/search/DisjunctionMaxQuery.java b/lucene/core/src/java/org/apache/lucene/search/DisjunctionMaxQuery.java
index d3fee9d..97c02a6 100644
--- a/lucene/core/src/java/org/apache/lucene/search/DisjunctionMaxQuery.java
+++ b/lucene/core/src/java/org/apache/lucene/search/DisjunctionMaxQuery.java
@@ -97,15 +97,15 @@ public final class DisjunctionMaxQuery extends Query implements Iterable<Query>
 
     /** The Weights for our subqueries, in 1-1 correspondence with disjuncts */
     protected final ArrayList<Weight> weights = new ArrayList<>();  // The Weight's for our subqueries, in 1-1 correspondence with disjuncts
-    private final boolean needsScores;
+    private final ScoreMode scoreMode;
 
     /** Construct the Weight for this Query searched by searcher.  Recursively construct subquery weights. */
-    public DisjunctionMaxWeight(IndexSearcher searcher, boolean needsScores, float boost) throws IOException {
+    public DisjunctionMaxWeight(IndexSearcher searcher, ScoreMode scoreMode, float boost) throws IOException {
       super(DisjunctionMaxQuery.this);
       for (Query disjunctQuery : disjuncts) {
-        weights.add(searcher.createWeight(disjunctQuery, needsScores, boost));
+        weights.add(searcher.createWeight(disjunctQuery, scoreMode, boost));
       }
-      this.needsScores = needsScores;
+      this.scoreMode = scoreMode;
     }
 
     @Override
@@ -133,7 +133,7 @@ public final class DisjunctionMaxQuery extends Query implements Iterable<Query>
         // only one sub-scorer in this segment
         return scorers.get(0);
       } else {
-        return new DisjunctionMaxScorer(this, tieBreakerMultiplier, scorers, needsScores);
+        return new DisjunctionMaxScorer(this, tieBreakerMultiplier, scorers, scoreMode.needsScores());
       }
     }
 
@@ -181,8 +181,8 @@ public final class DisjunctionMaxQuery extends Query implements Iterable<Query>
 
   /** Create the Weight used to score us */
   @Override
-  public Weight createWeight(IndexSearcher searcher, boolean needsScores, float boost) throws IOException {
-    return new DisjunctionMaxWeight(searcher, needsScores, boost);
+  public Weight createWeight(IndexSearcher searcher, ScoreMode scoreMode, float boost) throws IOException {
+    return new DisjunctionMaxWeight(searcher, scoreMode, boost);
   }
 
   /** Optimize our representation and our subqueries representations

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/4fc5a872/lucene/core/src/java/org/apache/lucene/search/DisjunctionMaxScorer.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/search/DisjunctionMaxScorer.java b/lucene/core/src/java/org/apache/lucene/search/DisjunctionMaxScorer.java
index 961fd43..084de66 100644
--- a/lucene/core/src/java/org/apache/lucene/search/DisjunctionMaxScorer.java
+++ b/lucene/core/src/java/org/apache/lucene/search/DisjunctionMaxScorer.java
@@ -58,4 +58,10 @@ final class DisjunctionMaxScorer extends DisjunctionScorer {
     }
     return (float) (scoreMax + (scoreSum - scoreMax) * tieBreakerMultiplier); 
   }
+
+  @Override
+  public float maxScore() {
+    // TODO: implement but be careful about floating-point errors.
+    return Float.POSITIVE_INFINITY;
+  }
 }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/4fc5a872/lucene/core/src/java/org/apache/lucene/search/DisjunctionSumScorer.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/search/DisjunctionSumScorer.java b/lucene/core/src/java/org/apache/lucene/search/DisjunctionSumScorer.java
index 69858e3..729a298 100644
--- a/lucene/core/src/java/org/apache/lucene/search/DisjunctionSumScorer.java
+++ b/lucene/core/src/java/org/apache/lucene/search/DisjunctionSumScorer.java
@@ -40,4 +40,11 @@ final class DisjunctionSumScorer extends DisjunctionScorer {
     }
     return (float)score;
   }
+
+  @Override
+  public float maxScore() {
+    // TODO: implement it but be careful with floating-point errors
+    return Float.POSITIVE_INFINITY;
+  }
+
 }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/4fc5a872/lucene/core/src/java/org/apache/lucene/search/DocValuesFieldExistsQuery.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/search/DocValuesFieldExistsQuery.java b/lucene/core/src/java/org/apache/lucene/search/DocValuesFieldExistsQuery.java
index 2689e58..009f11c 100644
--- a/lucene/core/src/java/org/apache/lucene/search/DocValuesFieldExistsQuery.java
+++ b/lucene/core/src/java/org/apache/lucene/search/DocValuesFieldExistsQuery.java
@@ -62,7 +62,7 @@ public final class DocValuesFieldExistsQuery extends Query {
   }
 
   @Override
-  public Weight createWeight(IndexSearcher searcher, boolean needsScores, float boost) throws IOException {
+  public Weight createWeight(IndexSearcher searcher, ScoreMode scoreMode, float boost) throws IOException {
     return new ConstantScoreWeight(this, boost) {
       @Override
       public Scorer scorer(LeafReaderContext context) throws IOException {

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/4fc5a872/lucene/core/src/java/org/apache/lucene/search/DocValuesRewriteMethod.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/search/DocValuesRewriteMethod.java b/lucene/core/src/java/org/apache/lucene/search/DocValuesRewriteMethod.java
index 0086205..5d59198 100644
--- a/lucene/core/src/java/org/apache/lucene/search/DocValuesRewriteMethod.java
+++ b/lucene/core/src/java/org/apache/lucene/search/DocValuesRewriteMethod.java
@@ -72,7 +72,7 @@ public final class DocValuesRewriteMethod extends MultiTermQuery.RewriteMethod {
     public final String getField() { return query.getField(); }
     
     @Override
-    public Weight createWeight(IndexSearcher searcher, boolean needsScores, float boost) throws IOException {
+    public Weight createWeight(IndexSearcher searcher, ScoreMode scoreMode, float boost) throws IOException {
       return new ConstantScoreWeight(this, boost) {
         @Override
         public Scorer scorer(LeafReaderContext context) throws IOException {

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/4fc5a872/lucene/core/src/java/org/apache/lucene/search/DoubleValuesSource.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/search/DoubleValuesSource.java b/lucene/core/src/java/org/apache/lucene/search/DoubleValuesSource.java
index bdcd068..d5d19ec 100644
--- a/lucene/core/src/java/org/apache/lucene/search/DoubleValuesSource.java
+++ b/lucene/core/src/java/org/apache/lucene/search/DoubleValuesSource.java
@@ -81,7 +81,7 @@ public abstract class DoubleValuesSource implements SegmentCacheable {
    * IndexReader-independent implementations can just return {@code this}
    *
    * Queries that use DoubleValuesSource objects should call rewrite() during
-   * {@link Query#createWeight(IndexSearcher, boolean, float)} rather than during
+   * {@link Query#createWeight(IndexSearcher, ScoreMode, float)} rather than during
    * {@link Query#rewrite(IndexReader)} to avoid IndexReader reference leakage
    */
   public abstract DoubleValuesSource rewrite(IndexSearcher reader) throws IOException;
@@ -553,7 +553,7 @@ public abstract class DoubleValuesSource implements SegmentCacheable {
 
     @Override
     public DoubleValuesSource rewrite(IndexSearcher searcher) throws IOException {
-      return new WeightDoubleValuesSource(searcher.rewrite(query).createWeight(searcher, true, 1f));
+      return new WeightDoubleValuesSource(searcher.rewrite(query).createWeight(searcher, ScoreMode.COMPLETE, 1f));
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/4fc5a872/lucene/core/src/java/org/apache/lucene/search/ExactPhraseScorer.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/search/ExactPhraseScorer.java b/lucene/core/src/java/org/apache/lucene/search/ExactPhraseScorer.java
index f54f5f5..85a242e 100644
--- a/lucene/core/src/java/org/apache/lucene/search/ExactPhraseScorer.java
+++ b/lucene/core/src/java/org/apache/lucene/search/ExactPhraseScorer.java
@@ -104,6 +104,11 @@ final class ExactPhraseScorer extends Scorer {
     return docScorer.score(docID(), freq);
   }
 
+  @Override
+  public float maxScore() {
+    return docScorer.maxScore(Integer.MAX_VALUE);
+  }
+
   /** Advance the given pos enum to the first doc on or after {@code target}.
    *  Return {@code false} if the enum was exhausted before reaching
    *  {@code target} and {@code true} otherwise. */

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/4fc5a872/lucene/core/src/java/org/apache/lucene/search/FakeScorer.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/search/FakeScorer.java b/lucene/core/src/java/org/apache/lucene/search/FakeScorer.java
index 3a357c0..07b5048 100644
--- a/lucene/core/src/java/org/apache/lucene/search/FakeScorer.java
+++ b/lucene/core/src/java/org/apache/lucene/search/FakeScorer.java
@@ -40,6 +40,11 @@ final class FakeScorer extends Scorer {
   }
 
   @Override
+  public float maxScore() {
+    return Float.POSITIVE_INFINITY;
+  }
+
+  @Override
   public DocIdSetIterator iterator() {
     throw new UnsupportedOperationException();
   }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/4fc5a872/lucene/core/src/java/org/apache/lucene/search/FilterCollector.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/search/FilterCollector.java b/lucene/core/src/java/org/apache/lucene/search/FilterCollector.java
index d4ec914..f7ff0ce 100644
--- a/lucene/core/src/java/org/apache/lucene/search/FilterCollector.java
+++ b/lucene/core/src/java/org/apache/lucene/search/FilterCollector.java
@@ -46,7 +46,7 @@ public abstract class FilterCollector implements Collector {
   }
 
   @Override
-  public boolean needsScores() {
-    return in.needsScores();
+  public ScoreMode scoreMode() {
+    return in.scoreMode();
   }
 }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/4fc5a872/lucene/core/src/java/org/apache/lucene/search/FilterScorer.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/search/FilterScorer.java b/lucene/core/src/java/org/apache/lucene/search/FilterScorer.java
index 22c8ecb..7bcb1ce 100644
--- a/lucene/core/src/java/org/apache/lucene/search/FilterScorer.java
+++ b/lucene/core/src/java/org/apache/lucene/search/FilterScorer.java
@@ -59,6 +59,9 @@ public abstract class FilterScorer extends Scorer {
     return in.score();
   }
 
+  // Leave maxScore abstract on purpose since the goal of this Filter class is
+  // to change the way the score is computed.
+
   @Override
   public final int docID() {
     return in.docID();

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/4fc5a872/lucene/core/src/java/org/apache/lucene/search/FilterWeight.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/search/FilterWeight.java b/lucene/core/src/java/org/apache/lucene/search/FilterWeight.java
index 380ad40..925c953 100644
--- a/lucene/core/src/java/org/apache/lucene/search/FilterWeight.java
+++ b/lucene/core/src/java/org/apache/lucene/search/FilterWeight.java
@@ -47,7 +47,7 @@ public abstract class FilterWeight extends Weight {
   /**
    * Alternative constructor.
    * Use this variant only if the <code>weight</code> was not obtained
-   * via the {@link Query#createWeight(IndexSearcher, boolean, float)}
+   * via the {@link Query#createWeight(IndexSearcher, ScoreMode, float)}
    * method of the <code>query</code> object.
    */
   protected FilterWeight(Query query, Weight weight) {

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/4fc5a872/lucene/core/src/java/org/apache/lucene/search/IndexOrDocValuesQuery.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/search/IndexOrDocValuesQuery.java b/lucene/core/src/java/org/apache/lucene/search/IndexOrDocValuesQuery.java
index 8df5675..e679e81 100644
--- a/lucene/core/src/java/org/apache/lucene/search/IndexOrDocValuesQuery.java
+++ b/lucene/core/src/java/org/apache/lucene/search/IndexOrDocValuesQuery.java
@@ -110,9 +110,9 @@ public final class IndexOrDocValuesQuery extends Query {
   }
 
   @Override
-  public Weight createWeight(IndexSearcher searcher, boolean needsScores, float boost) throws IOException {
-    final Weight indexWeight = indexQuery.createWeight(searcher, needsScores, boost);
-    final Weight dvWeight = dvQuery.createWeight(searcher, needsScores, boost);
+  public Weight createWeight(IndexSearcher searcher, ScoreMode scoreMode, float boost) throws IOException {
+    final Weight indexWeight = indexQuery.createWeight(searcher, scoreMode, boost);
+    final Weight dvWeight = dvQuery.createWeight(searcher, scoreMode, boost);
     return new Weight(this) {
       @Override
       public void extractTerms(Set<Term> terms) {

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/4fc5a872/lucene/core/src/java/org/apache/lucene/search/IndexSearcher.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/search/IndexSearcher.java b/lucene/core/src/java/org/apache/lucene/search/IndexSearcher.java
index accb99e..5ee815c 100644
--- a/lucene/core/src/java/org/apache/lucene/search/IndexSearcher.java
+++ b/lucene/core/src/java/org/apache/lucene/search/IndexSearcher.java
@@ -96,6 +96,10 @@ public class IndexSearcher {
         public float score(int doc, float freq) {
           return 0f;
         }
+        @Override
+        public float maxScore(float maxFreq) {
+          return 0f;
+        }
       };
     }
 
@@ -407,7 +411,7 @@ public class IndexSearcher {
 
       @Override
       public TopScoreDocCollector newCollector() throws IOException {
-        return TopScoreDocCollector.create(cappedNumHits, after);
+        return TopScoreDocCollector.create(cappedNumHits, after, true);
       }
 
       @Override
@@ -445,7 +449,7 @@ public class IndexSearcher {
    */
   public void search(Query query, Collector results)
     throws IOException {
-    search(leafContexts, createNormalizedWeight(query, results.needsScores()), results);
+    search(leafContexts, createNormalizedWeight(query, results.scoreMode()), results);
   }
 
   /** Search implementation with arbitrary sorting, plus
@@ -570,14 +574,22 @@ public class IndexSearcher {
       return collectorManager.reduce(Collections.singletonList(collector));
     } else {
       final List<C> collectors = new ArrayList<>(leafSlices.length);
-      boolean needsScores = false;
+      ScoreMode scoreMode = null;
       for (int i = 0; i < leafSlices.length; ++i) {
         final C collector = collectorManager.newCollector();
         collectors.add(collector);
-        needsScores |= collector.needsScores();
+        if (scoreMode == null) {
+          scoreMode = collector.scoreMode();
+        } else if (scoreMode != collector.scoreMode()) {
+          throw new IllegalStateException("CollectorManager does not always produce collectors with the same score mode");
+        }
+      }
+      if (scoreMode == null) {
+        // no segments
+        scoreMode = ScoreMode.COMPLETE;
       }
 
-      final Weight weight = createNormalizedWeight(query, needsScores);
+      final Weight weight = createNormalizedWeight(query, scoreMode);
       final List<Future<C>> topDocsFutures = new ArrayList<>(leafSlices.length);
       for (int i = 0; i < leafSlices.length; ++i) {
         final LeafReaderContext[] leaves = leafSlices[i].leaves;
@@ -674,7 +686,7 @@ public class IndexSearcher {
    * entire index.
    */
   public Explanation explain(Query query, int doc) throws IOException {
-    return explain(createNormalizedWeight(query, true), doc);
+    return explain(createNormalizedWeight(query, ScoreMode.COMPLETE), doc);
   }
 
   /** Expert: low-level implementation method
@@ -707,9 +719,9 @@ public class IndexSearcher {
    * can then directly be used to get a {@link Scorer}.
    * @lucene.internal
    */
-  public Weight createNormalizedWeight(Query query, boolean needsScores) throws IOException {
+  public Weight createNormalizedWeight(Query query, ScoreMode scoreMode) throws IOException {
     query = rewrite(query);
-    return createWeight(query, needsScores, 1f);
+    return createWeight(query, scoreMode, 1f);
   }
 
   /**
@@ -717,10 +729,10 @@ public class IndexSearcher {
    * if possible and configured.
    * @lucene.experimental
    */
-  public Weight createWeight(Query query, boolean needsScores, float boost) throws IOException {
+  public Weight createWeight(Query query, ScoreMode scoreMode, float boost) throws IOException {
     final QueryCache queryCache = this.queryCache;
-    Weight weight = query.createWeight(this, needsScores, boost);
-    if (needsScores == false && queryCache != null) {
+    Weight weight = query.createWeight(this, scoreMode, boost);
+    if (scoreMode.needsScores() == false && queryCache != null) {
       weight = queryCache.doCache(weight, queryCachingPolicy);
     }
     return weight;

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/4fc5a872/lucene/core/src/java/org/apache/lucene/search/MatchAllDocsQuery.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/search/MatchAllDocsQuery.java b/lucene/core/src/java/org/apache/lucene/search/MatchAllDocsQuery.java
index 8d3ff03..89b2997 100644
--- a/lucene/core/src/java/org/apache/lucene/search/MatchAllDocsQuery.java
+++ b/lucene/core/src/java/org/apache/lucene/search/MatchAllDocsQuery.java
@@ -29,7 +29,7 @@ import org.apache.lucene.util.Bits;
 public final class MatchAllDocsQuery extends Query {
 
   @Override
-  public Weight createWeight(IndexSearcher searcher, boolean needsScores, float boost) {
+  public Weight createWeight(IndexSearcher searcher, ScoreMode scoreMode, float boost) {
     return new ConstantScoreWeight(this, boost) {
       @Override
       public String toString() {

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/4fc5a872/lucene/core/src/java/org/apache/lucene/search/MatchNoDocsQuery.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/search/MatchNoDocsQuery.java b/lucene/core/src/java/org/apache/lucene/search/MatchNoDocsQuery.java
index 5e2c127..525a183 100644
--- a/lucene/core/src/java/org/apache/lucene/search/MatchNoDocsQuery.java
+++ b/lucene/core/src/java/org/apache/lucene/search/MatchNoDocsQuery.java
@@ -42,7 +42,7 @@ public class MatchNoDocsQuery extends Query {
   }
   
   @Override
-  public Weight createWeight(IndexSearcher searcher, boolean needsScores, float boost) throws IOException {
+  public Weight createWeight(IndexSearcher searcher, ScoreMode scoreMode, float boost) throws IOException {
     return new Weight(this) {
       @Override
       public void extractTerms(Set<Term> terms) {

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/4fc5a872/lucene/core/src/java/org/apache/lucene/search/MinShouldMatchSumScorer.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/search/MinShouldMatchSumScorer.java b/lucene/core/src/java/org/apache/lucene/search/MinShouldMatchSumScorer.java
index 5b56b58..ead2604 100644
--- a/lucene/core/src/java/org/apache/lucene/search/MinShouldMatchSumScorer.java
+++ b/lucene/core/src/java/org/apache/lucene/search/MinShouldMatchSumScorer.java
@@ -326,6 +326,12 @@ final class MinShouldMatchSumScorer extends Scorer {
   }
 
   @Override
+  public float maxScore() {
+    // TODO: implement but be careful about floating-point errors.
+    return Float.POSITIVE_INFINITY;
+  }
+
+  @Override
   public int docID() {
     assert doc == lead.doc;
     return doc;

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/4fc5a872/lucene/core/src/java/org/apache/lucene/search/MultiCollector.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/search/MultiCollector.java b/lucene/core/src/java/org/apache/lucene/search/MultiCollector.java
index 81cd594..6339573 100644
--- a/lucene/core/src/java/org/apache/lucene/search/MultiCollector.java
+++ b/lucene/core/src/java/org/apache/lucene/search/MultiCollector.java
@@ -95,7 +95,7 @@ public class MultiCollector implements Collector {
     this.collectors = collectors;
     int numNeedsScores = 0;
     for (Collector collector : collectors) {
-      if (collector.needsScores()) {
+      if (collector.scoreMode().needsScores()) {
         numNeedsScores += 1;
       }
     }
@@ -103,13 +103,16 @@ public class MultiCollector implements Collector {
   }
 
   @Override
-  public boolean needsScores() {
+  public ScoreMode scoreMode() {
+    ScoreMode scoreMode = null;
     for (Collector collector : collectors) {
-      if (collector.needsScores()) {
-        return true;
+      if (scoreMode == null) {
+        scoreMode = collector.scoreMode();
+      } else if (scoreMode != collector.scoreMode()) {
+        return ScoreMode.COMPLETE;
       }
     }
-    return false;
+    return scoreMode;
   }
 
   @Override

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/4fc5a872/lucene/core/src/java/org/apache/lucene/search/MultiCollectorManager.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/search/MultiCollectorManager.java b/lucene/core/src/java/org/apache/lucene/search/MultiCollectorManager.java
index a8c6d1c..efb2864 100644
--- a/lucene/core/src/java/org/apache/lucene/search/MultiCollectorManager.java
+++ b/lucene/core/src/java/org/apache/lucene/search/MultiCollectorManager.java
@@ -34,6 +34,9 @@ public class MultiCollectorManager implements CollectorManager<MultiCollectorMan
   @SafeVarargs
   @SuppressWarnings({"varargs", "unchecked"})
   public MultiCollectorManager(final CollectorManager<? extends Collector, ?>... collectorManagers) {
+    if (collectorManagers.length < 1) {
+      throw new IllegalArgumentException("There must be at least one collector");
+    }
     this.collectorManagers = (CollectorManager[]) collectorManagers;
   }
 
@@ -71,11 +74,16 @@ public class MultiCollectorManager implements CollectorManager<MultiCollectorMan
     }
 
     @Override
-    final public boolean needsScores() {
-      for (Collector collector : collectors)
-        if (collector.needsScores())
-          return true;
-      return false;
+    final public ScoreMode scoreMode() {
+      ScoreMode scoreMode = null;
+      for (Collector collector : collectors) {
+        if (scoreMode == null) {
+          scoreMode = collector.scoreMode();
+        } else if (scoreMode != collector.scoreMode()) {
+          return ScoreMode.COMPLETE;
+        }
+      }
+      return scoreMode;
     }
 
     public class LeafCollectors implements LeafCollector {

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/4fc5a872/lucene/core/src/java/org/apache/lucene/search/MultiPhraseQuery.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/search/MultiPhraseQuery.java b/lucene/core/src/java/org/apache/lucene/search/MultiPhraseQuery.java
index 9f22621..44a5ad0 100644
--- a/lucene/core/src/java/org/apache/lucene/search/MultiPhraseQuery.java
+++ b/lucene/core/src/java/org/apache/lucene/search/MultiPhraseQuery.java
@@ -334,8 +334,8 @@ public class MultiPhraseQuery extends Query {
   }
 
   @Override
-  public Weight createWeight(IndexSearcher searcher, boolean needsScores, float boost) throws IOException {
-    return new MultiPhraseWeight(searcher, needsScores, boost);
+  public Weight createWeight(IndexSearcher searcher, ScoreMode scoreMode, float boost) throws IOException {
+    return new MultiPhraseWeight(searcher, scoreMode.needsScores(), boost);
   }
 
   /** Prints a user-readable version of this query. */

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/4fc5a872/lucene/core/src/java/org/apache/lucene/search/MultiTermQueryConstantScoreWrapper.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/search/MultiTermQueryConstantScoreWrapper.java b/lucene/core/src/java/org/apache/lucene/search/MultiTermQueryConstantScoreWrapper.java
index 705946f..f82316d 100644
--- a/lucene/core/src/java/org/apache/lucene/search/MultiTermQueryConstantScoreWrapper.java
+++ b/lucene/core/src/java/org/apache/lucene/search/MultiTermQueryConstantScoreWrapper.java
@@ -108,7 +108,7 @@ final class MultiTermQueryConstantScoreWrapper<Q extends MultiTermQuery> extends
   public final String getField() { return query.getField(); }
 
   @Override
-  public Weight createWeight(IndexSearcher searcher, boolean needsScores, float boost) throws IOException {
+  public Weight createWeight(IndexSearcher searcher, ScoreMode scoreMode, float boost) throws IOException {
     return new ConstantScoreWeight(this, boost) {
 
       /** Try to collect terms from the given terms enum and return true iff all
@@ -153,7 +153,7 @@ final class MultiTermQueryConstantScoreWrapper<Q extends MultiTermQuery> extends
             bq.add(new TermQuery(new Term(query.field, t.term), termContext), Occur.SHOULD);
           }
           Query q = new ConstantScoreQuery(bq.build());
-          final Weight weight = searcher.rewrite(q).createWeight(searcher, needsScores, score());
+          final Weight weight = searcher.rewrite(q).createWeight(searcher, scoreMode, score());
           return new WeightOrDocIdSet(weight);
         }
 

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/4fc5a872/lucene/core/src/java/org/apache/lucene/search/NormsFieldExistsQuery.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/search/NormsFieldExistsQuery.java b/lucene/core/src/java/org/apache/lucene/search/NormsFieldExistsQuery.java
index 17da9e8..74218b4 100644
--- a/lucene/core/src/java/org/apache/lucene/search/NormsFieldExistsQuery.java
+++ b/lucene/core/src/java/org/apache/lucene/search/NormsFieldExistsQuery.java
@@ -62,7 +62,7 @@ public final class NormsFieldExistsQuery extends Query {
   }
 
   @Override
-  public Weight createWeight(IndexSearcher searcher, boolean needsScores, float boost) throws IOException {
+  public Weight createWeight(IndexSearcher searcher, ScoreMode scoreMode, float boost) throws IOException {
     return new ConstantScoreWeight(this, boost) {
       @Override
       public Scorer scorer(LeafReaderContext context) throws IOException {

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/4fc5a872/lucene/core/src/java/org/apache/lucene/search/PhraseQuery.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/search/PhraseQuery.java b/lucene/core/src/java/org/apache/lucene/search/PhraseQuery.java
index cf1c646..e0b60be 100644
--- a/lucene/core/src/java/org/apache/lucene/search/PhraseQuery.java
+++ b/lucene/core/src/java/org/apache/lucene/search/PhraseQuery.java
@@ -509,8 +509,8 @@ public class PhraseQuery extends Query {
 
 
   @Override
-  public Weight createWeight(IndexSearcher searcher, boolean needsScores, float boost) throws IOException {
-    return new PhraseWeight(searcher, needsScores, boost);
+  public Weight createWeight(IndexSearcher searcher, ScoreMode scoreMode, float boost) throws IOException {
+    return new PhraseWeight(searcher, scoreMode.needsScores(), boost);
   }
 
   /** Prints a user-readable version of this query. */

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/4fc5a872/lucene/core/src/java/org/apache/lucene/search/PointInSetQuery.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/search/PointInSetQuery.java b/lucene/core/src/java/org/apache/lucene/search/PointInSetQuery.java
index c006ff8..689d64a 100644
--- a/lucene/core/src/java/org/apache/lucene/search/PointInSetQuery.java
+++ b/lucene/core/src/java/org/apache/lucene/search/PointInSetQuery.java
@@ -106,7 +106,7 @@ public abstract class PointInSetQuery extends Query {
   }
 
   @Override
-  public final Weight createWeight(IndexSearcher searcher, boolean needsScores, float boost) throws IOException {
+  public final Weight createWeight(IndexSearcher searcher, ScoreMode scoreMode, float boost) throws IOException {
 
     // We don't use RandomAccessWeight here: it's no good to approximate with "match all docs".
     // This is an inverted structure and should be used in the first pass:

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/4fc5a872/lucene/core/src/java/org/apache/lucene/search/PointRangeQuery.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/search/PointRangeQuery.java b/lucene/core/src/java/org/apache/lucene/search/PointRangeQuery.java
index 04135c8..7e48383 100644
--- a/lucene/core/src/java/org/apache/lucene/search/PointRangeQuery.java
+++ b/lucene/core/src/java/org/apache/lucene/search/PointRangeQuery.java
@@ -99,7 +99,7 @@ public abstract class PointRangeQuery extends Query {
   }
 
   @Override
-  public final Weight createWeight(IndexSearcher searcher, boolean needsScores, float boost) throws IOException {
+  public final Weight createWeight(IndexSearcher searcher, ScoreMode scoreMode, float boost) throws IOException {
 
     // We don't use RandomAccessWeight here: it's no good to approximate with "match all docs".
     // This is an inverted structure and should be used in the first pass:

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/4fc5a872/lucene/core/src/java/org/apache/lucene/search/Query.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/search/Query.java b/lucene/core/src/java/org/apache/lucene/search/Query.java
index 024aa3e..54de63f 100644
--- a/lucene/core/src/java/org/apache/lucene/search/Query.java
+++ b/lucene/core/src/java/org/apache/lucene/search/Query.java
@@ -59,10 +59,10 @@ public abstract class Query {
    * <p>
    * Only implemented by primitive queries, which re-write to themselves.
    *
-   * @param needsScores   True if document scores ({@link Scorer#score}) are needed.
+   * @param scoreMode     How the produced scorers will be consumed.
    * @param boost         The boost that is propagated by the parent queries.
    */
-  public Weight createWeight(IndexSearcher searcher, boolean needsScores, float boost) throws IOException {
+  public Weight createWeight(IndexSearcher searcher, ScoreMode scoreMode, float boost) throws IOException {
     throw new UnsupportedOperationException("Query " + this + " does not implement createWeight");
   }
 

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/4fc5a872/lucene/core/src/java/org/apache/lucene/search/QueryCache.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/search/QueryCache.java b/lucene/core/src/java/org/apache/lucene/search/QueryCache.java
index 25412b9..94e34ee 100644
--- a/lucene/core/src/java/org/apache/lucene/search/QueryCache.java
+++ b/lucene/core/src/java/org/apache/lucene/search/QueryCache.java
@@ -29,7 +29,7 @@ public interface QueryCache {
    * Return a wrapper around the provided <code>weight</code> that will cache
    * matching docs per-segment accordingly to the given <code>policy</code>.
    * NOTE: The returned weight will only be equivalent if scores are not needed.
-   * @see Collector#needsScores()
+   * @see Collector#scoreMode()
    */
   Weight doCache(Weight weight, QueryCachingPolicy policy);
 

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/4fc5a872/lucene/core/src/java/org/apache/lucene/search/QueryRescorer.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/search/QueryRescorer.java b/lucene/core/src/java/org/apache/lucene/search/QueryRescorer.java
index 73c37d2..b264d72 100644
--- a/lucene/core/src/java/org/apache/lucene/search/QueryRescorer.java
+++ b/lucene/core/src/java/org/apache/lucene/search/QueryRescorer.java
@@ -60,7 +60,7 @@ public abstract class QueryRescorer extends Rescorer {
 
     List<LeafReaderContext> leaves = searcher.getIndexReader().leaves();
 
-    Weight weight = searcher.createNormalizedWeight(query, true);
+    Weight weight = searcher.createNormalizedWeight(query, ScoreMode.COMPLETE);
 
     // Now merge sort docIDs from hits, with reader's leaves:
     int hitUpto = 0;

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/4fc5a872/lucene/core/src/java/org/apache/lucene/search/ReqExclScorer.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/search/ReqExclScorer.java b/lucene/core/src/java/org/apache/lucene/search/ReqExclScorer.java
index 38a3356..3714d49 100644
--- a/lucene/core/src/java/org/apache/lucene/search/ReqExclScorer.java
+++ b/lucene/core/src/java/org/apache/lucene/search/ReqExclScorer.java
@@ -77,6 +77,17 @@ class ReqExclScorer extends Scorer {
   }
 
   @Override
+  public float maxScore() {
+    return reqScorer.maxScore();
+  }
+
+  @Override
+  public void setMinCompetitiveScore(float score) {
+    // The score of this scorer is the same as the score of 'reqScorer'.
+    reqScorer.setMinCompetitiveScore(score);
+  }
+
+  @Override
   public Collection<ChildScorer> getChildren() {
     return Collections.singleton(new ChildScorer(reqScorer, "MUST"));
   }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/4fc5a872/lucene/core/src/java/org/apache/lucene/search/ReqOptSumScorer.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/search/ReqOptSumScorer.java b/lucene/core/src/java/org/apache/lucene/search/ReqOptSumScorer.java
index 61e2c7e..35de51a 100644
--- a/lucene/core/src/java/org/apache/lucene/search/ReqOptSumScorer.java
+++ b/lucene/core/src/java/org/apache/lucene/search/ReqOptSumScorer.java
@@ -86,6 +86,11 @@ class ReqOptSumScorer extends Scorer {
   }
 
   @Override
+  public float maxScore() {
+    return reqScorer.maxScore() + optScorer.maxScore();
+  }
+
+  @Override
   public Collection<ChildScorer> getChildren() {
     ArrayList<ChildScorer> children = new ArrayList<>(2);
     children.add(new ChildScorer(reqScorer, "MUST"));

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/4fc5a872/lucene/core/src/java/org/apache/lucene/search/ScoreCachingWrappingScorer.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/search/ScoreCachingWrappingScorer.java b/lucene/core/src/java/org/apache/lucene/search/ScoreCachingWrappingScorer.java
index 9a99d89..1384cbe 100644
--- a/lucene/core/src/java/org/apache/lucene/search/ScoreCachingWrappingScorer.java
+++ b/lucene/core/src/java/org/apache/lucene/search/ScoreCachingWrappingScorer.java
@@ -32,7 +32,7 @@ import java.util.Collections;
  * several places, however all they have in hand is a {@link Scorer} object, and
  * might end up computing the score of a document more than once.
  */
-public class ScoreCachingWrappingScorer extends FilterScorer {
+public final class ScoreCachingWrappingScorer extends FilterScorer {
 
   private int curDoc = -1;
   private float curScore;
@@ -54,6 +54,16 @@ public class ScoreCachingWrappingScorer extends FilterScorer {
   }
 
   @Override
+  public float maxScore() {
+    return in.maxScore();
+  }
+
+  @Override
+  public void setMinCompetitiveScore(float minScore) {
+    in.setMinCompetitiveScore(minScore);
+  }
+
+  @Override
   public Collection<ChildScorer> getChildren() {
     return Collections.singleton(new ChildScorer(in, "CACHED"));
   }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/4fc5a872/lucene/core/src/java/org/apache/lucene/search/ScoreMode.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/search/ScoreMode.java b/lucene/core/src/java/org/apache/lucene/search/ScoreMode.java
new file mode 100644
index 0000000..31a5d10
--- /dev/null
+++ b/lucene/core/src/java/org/apache/lucene/search/ScoreMode.java
@@ -0,0 +1,60 @@
+/*
+ * 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.search;
+
+/**
+ * Different modes of search.
+ */
+public enum ScoreMode {
+  
+  /**
+   * Produced scorers will allow visiting all matches and get their score.
+   */
+  COMPLETE {
+    @Override
+    public boolean needsScores() {
+      return true;
+    }
+  },
+
+  /**
+   * Produced scorers will allow visiting all matches but scores won't be
+   * available.
+   */
+  COMPLETE_NO_SCORES {
+    @Override
+    public boolean needsScores() {
+      return false;
+    }
+  },
+
+  /**
+   * Produced scorers will optionally allow skipping over non-competitive
+   * hits using the {@link Scorer#setMinCompetitiveScore(float)} API.
+   */
+  TOP_SCORES {
+    @Override
+    public boolean needsScores() {
+      return true;
+    }
+  };
+
+  /**
+   * Whether this {@link ScoreMode} needs to compute scores.
+   */
+  public abstract boolean needsScores();
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/4fc5a872/lucene/core/src/java/org/apache/lucene/search/Scorer.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/search/Scorer.java b/lucene/core/src/java/org/apache/lucene/search/Scorer.java
index a4d8b5e..2fb0d26 100644
--- a/lucene/core/src/java/org/apache/lucene/search/Scorer.java
+++ b/lucene/core/src/java/org/apache/lucene/search/Scorer.java
@@ -143,4 +143,21 @@ public abstract class Scorer {
   public TwoPhaseIterator twoPhaseIterator() {
     return null;
   }
+
+  /**
+   * Optional method: Tell the scorer that its iterator may safely ignore all
+   * documents whose score is less than the given {@code minScore}. This is a
+   * no-op by default.
+   *
+   * This method may only be called from collectors that use
+   * {@link ScoreMode#TOP_SCORES}, and successive calls may only set increasing
+   * values of {@code minScore}.
+   */
+  public void setMinCompetitiveScore(float minScore) {
+    // no-op by default
+  }
+
+  /** Return the maximum score that this scorer may produce. If scores are not
+   *  bounded, {@link Float#POSITIVE_INFINITY} must be returned. */
+  public abstract float maxScore();
 }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/4fc5a872/lucene/core/src/java/org/apache/lucene/search/SloppyPhraseScorer.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/search/SloppyPhraseScorer.java b/lucene/core/src/java/org/apache/lucene/search/SloppyPhraseScorer.java
index 75e4d86..dc5490a 100644
--- a/lucene/core/src/java/org/apache/lucene/search/SloppyPhraseScorer.java
+++ b/lucene/core/src/java/org/apache/lucene/search/SloppyPhraseScorer.java
@@ -557,6 +557,11 @@ final class SloppyPhraseScorer extends Scorer {
   }
 
   @Override
+  public float maxScore() {
+    return docScorer.maxScore(Float.POSITIVE_INFINITY);
+  }
+
+  @Override
   public String toString() { return "scorer(" + weight + ")"; }
 
   @Override

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/4fc5a872/lucene/core/src/java/org/apache/lucene/search/SynonymQuery.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/search/SynonymQuery.java b/lucene/core/src/java/org/apache/lucene/search/SynonymQuery.java
index 4e4d69f..ce9d6e0 100644
--- a/lucene/core/src/java/org/apache/lucene/search/SynonymQuery.java
+++ b/lucene/core/src/java/org/apache/lucene/search/SynonymQuery.java
@@ -25,6 +25,8 @@ import java.util.List;
 import java.util.Objects;
 import java.util.Set;
 
+import org.apache.lucene.index.FieldInfo;
+import org.apache.lucene.index.IndexOptions;
 import org.apache.lucene.index.IndexReader;
 import org.apache.lucene.index.LeafReaderContext;
 import org.apache.lucene.index.PostingsEnum;
@@ -111,8 +113,8 @@ public final class SynonymQuery extends Query {
   }
 
   @Override
-  public Weight createWeight(IndexSearcher searcher, boolean needsScores, float boost) throws IOException {
-    if (needsScores) {
+  public Weight createWeight(IndexSearcher searcher, ScoreMode scoreMode, float boost) throws IOException {
+    if (scoreMode.needsScores()) {
       return new SynonymWeight(this, searcher, boost);
     } else {
       // if scores are not needed, let BooleanWeight deal with optimizing that case.
@@ -120,7 +122,7 @@ public final class SynonymQuery extends Query {
       for (Term term : terms) {
         bq.add(new TermQuery(term), BooleanClause.Occur.SHOULD);
       }
-      return searcher.rewrite(bq.build()).createWeight(searcher, needsScores, boost);
+      return searcher.rewrite(bq.build()).createWeight(searcher, ScoreMode.COMPLETE_NO_SCORES, boost);
     }
   }
   
@@ -189,19 +191,32 @@ public final class SynonymQuery extends Query {
     @Override
     public Scorer scorer(LeafReaderContext context) throws IOException {
       Similarity.SimScorer simScorer = null;
+      IndexOptions indexOptions = IndexOptions.NONE;
+      if (terms.length > 0) {
+        FieldInfo info = context.reader()
+            .getFieldInfos()
+            .fieldInfo(terms[0].field());
+        if (info != null) {
+          indexOptions = info.getIndexOptions();
+        }
+      }
       // we use termscorers + disjunction as an impl detail
       List<Scorer> subScorers = new ArrayList<>();
+      long maxFreq = 0;
       for (int i = 0; i < terms.length; i++) {
         TermState state = termContexts[i].get(context.ord);
         if (state != null) {
           TermsEnum termsEnum = context.reader().terms(terms[i].field()).iterator();
           termsEnum.seekExact(terms[i].bytes(), state);
+
+          maxFreq += getMaxFreq(indexOptions, termsEnum.totalTermFreq(), termsEnum.docFreq());
+
           PostingsEnum postings = termsEnum.postings(null, PostingsEnum.FREQS);
           // lazy init sim, in case no terms exist
           if (simScorer == null) {
             simScorer = similarity.simScorer(simWeight, context);
           }
-          subScorers.add(new TermScorer(this, postings, simScorer));
+          subScorers.add(new TermScorer(this, postings, simScorer, Float.POSITIVE_INFINITY));
         }
       }
       if (subScorers.isEmpty()) {
@@ -210,7 +225,7 @@ public final class SynonymQuery extends Query {
         // we must optimize this case (term not in segment), disjunctionscorer requires >= 2 subs
         return subScorers.get(0);
       } else {
-        return new SynonymScorer(simScorer, this, subScorers);
+        return new SynonymScorer(simScorer, this, subScorers, maxFreq);
       }
     }
 
@@ -220,20 +235,38 @@ public final class SynonymQuery extends Query {
     }
 
   }
-  
+
+  private long getMaxFreq(IndexOptions indexOptions, long ttf, long df) {
+    // TODO: store the max term freq?
+    if (indexOptions.compareTo(IndexOptions.DOCS) <= 0) {
+      // omitTFAP field, tf values are implicitly 1.
+      return 1;
+    } else {
+      assert ttf >= 0;
+      return Math.min(Integer.MAX_VALUE, ttf - df + 1);
+    }
+  }
+
   static class SynonymScorer extends DisjunctionScorer {
     private final Similarity.SimScorer similarity;
+    private final float maxFreq;
     
-    SynonymScorer(Similarity.SimScorer similarity, Weight weight, List<Scorer> subScorers) {
+    SynonymScorer(Similarity.SimScorer similarity, Weight weight, List<Scorer> subScorers, float maxFreq) {
       super(weight, subScorers, true);
       this.similarity = similarity;
+      this.maxFreq = maxFreq;
     }
 
     @Override
     protected float score(DisiWrapper topList) throws IOException {
       return similarity.score(topList.doc, tf(topList));
     }
-    
+
+    @Override
+    public float maxScore() {
+      return similarity.maxScore(maxFreq);
+    }
+
     /** combines TF of all subs. */
     final int tf(DisiWrapper topList) throws IOException {
       int tf = 0;