1 /////////////////////////////////////////////////////////////////////////////
2 // Copyright (c) 2009-2014 Alan Wright. All rights reserved.
3 // Distributable under the terms of either the Apache License (Version 2.0)
4 // or the GNU Lesser General Public License.
5 /////////////////////////////////////////////////////////////////////////////
6 
7 #include "LuceneInc.h"
8 #include "TopScoreDocCollector.h"
9 #include "_TopScoreDocCollector.h"
10 #include "HitQueue.h"
11 #include "ScoreDoc.h"
12 #include "Scorer.h"
13 #include "TopDocs.h"
14 #include "MiscUtils.h"
15 
16 namespace Lucene {
17 
TopScoreDocCollector(int32_t numHits)18 TopScoreDocCollector::TopScoreDocCollector(int32_t numHits) : TopDocsCollector(newLucene<HitQueue>(numHits, true)) {
19     // HitQueue implements getSentinelObject to return a ScoreDoc, so we know that at this point top()
20     // is already initialized.
21     pqTop = pq->top();
22     docBase = 0;
23 }
24 
~TopScoreDocCollector()25 TopScoreDocCollector::~TopScoreDocCollector() {
26 }
27 
create(int32_t numHits,bool docsScoredInOrder)28 TopScoreDocCollectorPtr TopScoreDocCollector::create(int32_t numHits, bool docsScoredInOrder) {
29     if (docsScoredInOrder) {
30         return newLucene<InOrderTopScoreDocCollector>(numHits);
31     } else {
32         return newLucene<OutOfOrderTopScoreDocCollector>(numHits);
33     }
34 }
35 
newTopDocs(Collection<ScoreDocPtr> results,int32_t start)36 TopDocsPtr TopScoreDocCollector::newTopDocs(Collection<ScoreDocPtr> results, int32_t start) {
37     if (!results) {
38         return EMPTY_TOPDOCS();
39     }
40 
41     // We need to compute maxScore in order to set it in TopDocs. If start == 0, it means the largest element
42     // is already in results, use its score as maxScore.  Otherwise pop everything else, until the largest
43     // element is extracted and use its score as maxScore.
44     double maxScore = std::numeric_limits<double>::quiet_NaN();
45     if (start == 0) {
46         maxScore = results[0]->score;
47     } else {
48         for (int32_t i = pq->size(); i > 1; --i) {
49             pq->pop();
50         }
51         maxScore = pq->pop()->score;
52     }
53 
54     return newLucene<TopDocs>(totalHits, results, maxScore);
55 }
56 
setNextReader(const IndexReaderPtr & reader,int32_t docBase)57 void TopScoreDocCollector::setNextReader(const IndexReaderPtr& reader, int32_t docBase) {
58     this->docBase = docBase;
59 }
60 
setScorer(const ScorerPtr & scorer)61 void TopScoreDocCollector::setScorer(const ScorerPtr& scorer) {
62     this->_scorer = scorer;
63 }
64 
InOrderTopScoreDocCollector(int32_t numHits)65 InOrderTopScoreDocCollector::InOrderTopScoreDocCollector(int32_t numHits) : TopScoreDocCollector(numHits) {
66 }
67 
~InOrderTopScoreDocCollector()68 InOrderTopScoreDocCollector::~InOrderTopScoreDocCollector() {
69 }
70 
collect(int32_t doc)71 void InOrderTopScoreDocCollector::collect(int32_t doc) {
72     double score = ScorerPtr(_scorer)->score();
73 
74     // This collector cannot handle these scores
75     BOOST_ASSERT(score != -std::numeric_limits<double>::infinity());
76     BOOST_ASSERT(!MiscUtils::isNaN(score));
77 
78     ++totalHits;
79     if (score <= pqTop->score) {
80         // Since docs are returned in-order (ie., increasing doc Id), a document with equal score to
81         // pqTop.score cannot compete since HitQueue favours documents with lower doc Ids.  Therefore
82         // reject those docs too.
83         return;
84     }
85     pqTop->doc = doc + docBase;
86     pqTop->score = score;
87     pqTop = pq->updateTop();
88 }
89 
acceptsDocsOutOfOrder()90 bool InOrderTopScoreDocCollector::acceptsDocsOutOfOrder() {
91     return false;
92 }
93 
OutOfOrderTopScoreDocCollector(int32_t numHits)94 OutOfOrderTopScoreDocCollector::OutOfOrderTopScoreDocCollector(int32_t numHits) : TopScoreDocCollector(numHits) {
95 }
96 
~OutOfOrderTopScoreDocCollector()97 OutOfOrderTopScoreDocCollector::~OutOfOrderTopScoreDocCollector() {
98 }
99 
collect(int32_t doc)100 void OutOfOrderTopScoreDocCollector::collect(int32_t doc) {
101     double score = ScorerPtr(_scorer)->score();
102 
103     // This collector cannot handle NaN
104     BOOST_ASSERT(!MiscUtils::isNaN(score));
105 
106     ++totalHits;
107     doc += docBase;
108     if (score < pqTop->score || (score == pqTop->score && doc > pqTop->doc)) {
109         return;
110     }
111     pqTop->doc = doc;
112     pqTop->score = score;
113     pqTop = pq->updateTop();
114 }
115 
acceptsDocsOutOfOrder()116 bool OutOfOrderTopScoreDocCollector::acceptsDocsOutOfOrder() {
117     return true;
118 }
119 
120 }
121