/src/core/Search/TopFieldCollector.cs

https://bitbucket.org/jmblair/lucene.net · C# · 1137 lines · 799 code · 108 blank · 230 comment · 138 complexity · e06848df9aefaa04bd645446c7c3ced1 MD5 · raw file

  1. /*
  2. * Licensed to the Apache Software Foundation (ASF) under one or more
  3. * contributor license agreements. See the NOTICE file distributed with
  4. * this work for additional information regarding copyright ownership.
  5. * The ASF licenses this file to You under the Apache License, Version 2.0
  6. * (the "License"); you may not use this file except in compliance with
  7. * the License. You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. using System;
  18. using Lucene.Net.Util;
  19. using IndexReader = Lucene.Net.Index.IndexReader;
  20. using Entry = Lucene.Net.Search.FieldValueHitQueue.Entry;
  21. namespace Lucene.Net.Search
  22. {
  23. /// <summary> A <see cref="Collector" /> that sorts by <see cref="SortField" /> using
  24. /// <see cref="FieldComparator" />s.
  25. /// <p/>
  26. /// See the <see cref="Create" /> method
  27. /// for instantiating a TopFieldCollector.
  28. ///
  29. /// <p/><b>NOTE:</b> This API is experimental and might change in
  30. /// incompatible ways in the next release.<p/>
  31. /// </summary>
  32. public abstract class TopFieldCollector : TopDocsCollector<Entry>
  33. {
  34. // TODO: one optimization we could do is to pre-fill
  35. // the queue with sentinel value that guaranteed to
  36. // always compare lower than a real hit; this would
  37. // save having to check queueFull on each insert
  38. //
  39. // Implements a TopFieldCollector over one SortField criteria, without
  40. // tracking document scores and maxScore.
  41. //
  42. private class OneComparatorNonScoringCollector : TopFieldCollector
  43. {
  44. internal FieldComparator comparator;
  45. internal int reverseMul;
  46. public OneComparatorNonScoringCollector(FieldValueHitQueue queue, int numHits, bool fillFields):base(queue, numHits, fillFields)
  47. {
  48. comparator = queue.GetComparators()[0];
  49. reverseMul = queue.GetReverseMul()[0];
  50. }
  51. internal void UpdateBottom(int doc)
  52. {
  53. // bottom.score is already set to Float.NaN in add().
  54. bottom.Doc = docBase + doc;
  55. bottom = pq.UpdateTop();
  56. }
  57. public override void Collect(int doc)
  58. {
  59. ++internalTotalHits;
  60. if (queueFull)
  61. {
  62. if ((reverseMul * comparator.CompareBottom(doc)) <= 0)
  63. {
  64. // since docs are visited in doc Id order, if compare is 0, it means
  65. // this document is largest than anything else in the queue, and
  66. // therefore not competitive.
  67. return ;
  68. }
  69. // This hit is competitive - replace bottom element in queue & adjustTop
  70. comparator.Copy(bottom.slot, doc);
  71. UpdateBottom(doc);
  72. comparator.SetBottom(bottom.slot);
  73. }
  74. else
  75. {
  76. // Startup transient: queue hasn't gathered numHits yet
  77. int slot = internalTotalHits - 1;
  78. // Copy hit into queue
  79. comparator.Copy(slot, doc);
  80. Add(slot, doc, System.Single.NaN);
  81. if (queueFull)
  82. {
  83. comparator.SetBottom(bottom.slot);
  84. }
  85. }
  86. }
  87. public override void SetNextReader(IndexReader reader, int docBase)
  88. {
  89. this.docBase = docBase;
  90. comparator.SetNextReader(reader, docBase);
  91. }
  92. public override void SetScorer(Scorer scorer)
  93. {
  94. comparator.SetScorer(scorer);
  95. }
  96. }
  97. //
  98. // Implements a TopFieldCollector over one SortField criteria, without
  99. // tracking document scores and maxScore, and assumes out of orderness in doc
  100. // Ids collection.
  101. //
  102. private class OutOfOrderOneComparatorNonScoringCollector:OneComparatorNonScoringCollector
  103. {
  104. public OutOfOrderOneComparatorNonScoringCollector(FieldValueHitQueue queue, int numHits, bool fillFields):base(queue, numHits, fillFields)
  105. {
  106. }
  107. public override void Collect(int doc)
  108. {
  109. ++internalTotalHits;
  110. if (queueFull)
  111. {
  112. // Fastmatch: return if this hit is not competitive
  113. int cmp = reverseMul * comparator.CompareBottom(doc);
  114. if (cmp < 0 || (cmp == 0 && doc + docBase > bottom.Doc))
  115. {
  116. return ;
  117. }
  118. // This hit is competitive - replace bottom element in queue & adjustTop
  119. comparator.Copy(bottom.slot, doc);
  120. UpdateBottom(doc);
  121. comparator.SetBottom(bottom.slot);
  122. }
  123. else
  124. {
  125. // Startup transient: queue hasn't gathered numHits yet
  126. int slot = internalTotalHits - 1;
  127. // Copy hit into queue
  128. comparator.Copy(slot, doc);
  129. Add(slot, doc, System.Single.NaN);
  130. if (queueFull)
  131. {
  132. comparator.SetBottom(bottom.slot);
  133. }
  134. }
  135. }
  136. public override bool AcceptsDocsOutOfOrder
  137. {
  138. get { return true; }
  139. }
  140. }
  141. /*
  142. * Implements a TopFieldCollector over one SortField criteria, while tracking
  143. * document scores but no maxScore.
  144. */
  145. private class OneComparatorScoringNoMaxScoreCollector : OneComparatorNonScoringCollector
  146. {
  147. internal Scorer scorer;
  148. public OneComparatorScoringNoMaxScoreCollector(FieldValueHitQueue queue, int numHits, bool fillFields):base(queue, numHits, fillFields)
  149. {
  150. }
  151. internal void UpdateBottom(int doc, float score)
  152. {
  153. bottom.Doc = docBase + doc;
  154. bottom.Score = score;
  155. bottom = pq.UpdateTop();
  156. }
  157. public override void Collect(int doc)
  158. {
  159. ++internalTotalHits;
  160. if (queueFull)
  161. {
  162. if ((reverseMul * comparator.CompareBottom(doc)) <= 0)
  163. {
  164. // since docs are visited in doc Id order, if compare is 0, it means
  165. // this document is largest than anything else in the queue, and
  166. // therefore not competitive.
  167. return ;
  168. }
  169. // Compute the score only if the hit is competitive.
  170. float score = scorer.Score();
  171. // This hit is competitive - replace bottom element in queue & adjustTop
  172. comparator.Copy(bottom.slot, doc);
  173. UpdateBottom(doc, score);
  174. comparator.SetBottom(bottom.slot);
  175. }
  176. else
  177. {
  178. // Compute the score only if the hit is competitive.
  179. float score = scorer.Score();
  180. // Startup transient: queue hasn't gathered numHits yet
  181. int slot = internalTotalHits - 1;
  182. // Copy hit into queue
  183. comparator.Copy(slot, doc);
  184. Add(slot, doc, score);
  185. if (queueFull)
  186. {
  187. comparator.SetBottom(bottom.slot);
  188. }
  189. }
  190. }
  191. public override void SetScorer(Scorer scorer)
  192. {
  193. this.scorer = scorer;
  194. comparator.SetScorer(scorer);
  195. }
  196. }
  197. /*
  198. * Implements a TopFieldCollector over one SortField criteria, while tracking
  199. * document scores but no maxScore, and assumes out of orderness in doc Ids
  200. * collection.
  201. */
  202. private class OutOfOrderOneComparatorScoringNoMaxScoreCollector : OneComparatorScoringNoMaxScoreCollector
  203. {
  204. public OutOfOrderOneComparatorScoringNoMaxScoreCollector(FieldValueHitQueue queue, int numHits, bool fillFields):base(queue, numHits, fillFields)
  205. {
  206. }
  207. public override void Collect(int doc)
  208. {
  209. ++internalTotalHits;
  210. if (queueFull)
  211. {
  212. // Fastmatch: return if this hit is not competitive
  213. int cmp = reverseMul * comparator.CompareBottom(doc);
  214. if (cmp < 0 || (cmp == 0 && doc + docBase > bottom.Doc))
  215. {
  216. return ;
  217. }
  218. // Compute the score only if the hit is competitive.
  219. float score = scorer.Score();
  220. // This hit is competitive - replace bottom element in queue & adjustTop
  221. comparator.Copy(bottom.slot, doc);
  222. UpdateBottom(doc, score);
  223. comparator.SetBottom(bottom.slot);
  224. }
  225. else
  226. {
  227. // Compute the score only if the hit is competitive.
  228. float score = scorer.Score();
  229. // Startup transient: queue hasn't gathered numHits yet
  230. int slot = internalTotalHits - 1;
  231. // Copy hit into queue
  232. comparator.Copy(slot, doc);
  233. Add(slot, doc, score);
  234. if (queueFull)
  235. {
  236. comparator.SetBottom(bottom.slot);
  237. }
  238. }
  239. }
  240. public override bool AcceptsDocsOutOfOrder
  241. {
  242. get { return true; }
  243. }
  244. }
  245. //
  246. // Implements a TopFieldCollector over one SortField criteria, with tracking
  247. // document scores and maxScore.
  248. //
  249. private class OneComparatorScoringMaxScoreCollector:OneComparatorNonScoringCollector
  250. {
  251. internal Scorer scorer;
  252. public OneComparatorScoringMaxScoreCollector(FieldValueHitQueue queue, int numHits, bool fillFields):base(queue, numHits, fillFields)
  253. {
  254. // Must set maxScore to NEG_INF, or otherwise Math.max always returns NaN.
  255. maxScore = System.Single.NegativeInfinity;
  256. }
  257. internal void UpdateBottom(int doc, float score)
  258. {
  259. bottom.Doc = docBase + doc;
  260. bottom.Score = score;
  261. bottom = pq.UpdateTop();
  262. }
  263. public override void Collect(int doc)
  264. {
  265. float score = scorer.Score();
  266. if (score > maxScore)
  267. {
  268. maxScore = score;
  269. }
  270. ++internalTotalHits;
  271. if (queueFull)
  272. {
  273. if ((reverseMul * comparator.CompareBottom(doc)) <= 0)
  274. {
  275. // since docs are visited in doc Id order, if compare is 0, it means
  276. // this document is largest than anything else in the queue, and
  277. // therefore not competitive.
  278. return ;
  279. }
  280. // This hit is competitive - replace bottom element in queue & adjustTop
  281. comparator.Copy(bottom.slot, doc);
  282. UpdateBottom(doc, score);
  283. comparator.SetBottom(bottom.slot);
  284. }
  285. else
  286. {
  287. // Startup transient: queue hasn't gathered numHits yet
  288. int slot = internalTotalHits - 1;
  289. // Copy hit into queue
  290. comparator.Copy(slot, doc);
  291. Add(slot, doc, score);
  292. if (queueFull)
  293. {
  294. comparator.SetBottom(bottom.slot);
  295. }
  296. }
  297. }
  298. public override void SetScorer(Scorer scorer)
  299. {
  300. this.scorer = scorer;
  301. base.SetScorer(scorer);
  302. }
  303. }
  304. //
  305. // Implements a TopFieldCollector over one SortField criteria, with tracking
  306. // document scores and maxScore, and assumes out of orderness in doc Ids
  307. // collection.
  308. //
  309. private class OutOfOrderOneComparatorScoringMaxScoreCollector : OneComparatorScoringMaxScoreCollector
  310. {
  311. public OutOfOrderOneComparatorScoringMaxScoreCollector(FieldValueHitQueue queue, int numHits, bool fillFields):base(queue, numHits, fillFields)
  312. {
  313. }
  314. public override void Collect(int doc)
  315. {
  316. float score = scorer.Score();
  317. if (score > maxScore)
  318. {
  319. maxScore = score;
  320. }
  321. ++internalTotalHits;
  322. if (queueFull)
  323. {
  324. // Fastmatch: return if this hit is not competitive
  325. int cmp = reverseMul * comparator.CompareBottom(doc);
  326. if (cmp < 0 || (cmp == 0 && doc + docBase > bottom.Doc))
  327. {
  328. return ;
  329. }
  330. // This hit is competitive - replace bottom element in queue & adjustTop
  331. comparator.Copy(bottom.slot, doc);
  332. UpdateBottom(doc, score);
  333. comparator.SetBottom(bottom.slot);
  334. }
  335. else
  336. {
  337. // Startup transient: queue hasn't gathered numHits yet
  338. int slot = internalTotalHits - 1;
  339. // Copy hit into queue
  340. comparator.Copy(slot, doc);
  341. Add(slot, doc, score);
  342. if (queueFull)
  343. {
  344. comparator.SetBottom(bottom.slot);
  345. }
  346. }
  347. }
  348. public override bool AcceptsDocsOutOfOrder
  349. {
  350. get { return true; }
  351. }
  352. }
  353. /*
  354. * Implements a TopFieldCollector over multiple SortField criteria, without
  355. * tracking document scores and maxScore.
  356. */
  357. private class MultiComparatorNonScoringCollector:TopFieldCollector
  358. {
  359. internal FieldComparator[] comparators;
  360. internal int[] reverseMul;
  361. public MultiComparatorNonScoringCollector(FieldValueHitQueue queue, int numHits, bool fillFields):base(queue, numHits, fillFields)
  362. {
  363. comparators = queue.GetComparators();
  364. reverseMul = queue.GetReverseMul();
  365. }
  366. internal void UpdateBottom(int doc)
  367. {
  368. // bottom.score is already set to Float.NaN in add().
  369. bottom.Doc = docBase + doc;
  370. bottom = pq.UpdateTop();
  371. }
  372. public override void Collect(int doc)
  373. {
  374. ++internalTotalHits;
  375. if (queueFull)
  376. {
  377. // Fastmatch: return if this hit is not competitive
  378. for (int i = 0; ; i++)
  379. {
  380. int c = reverseMul[i] * comparators[i].CompareBottom(doc);
  381. if (c < 0)
  382. {
  383. // Definitely not competitive.
  384. return ;
  385. }
  386. else if (c > 0)
  387. {
  388. // Definitely competitive.
  389. break;
  390. }
  391. else if (i == comparators.Length - 1)
  392. {
  393. // Here c=0. If we're at the last comparator, this doc is not
  394. // competitive, since docs are visited in doc Id order, which means
  395. // this doc cannot compete with any other document in the queue.
  396. return ;
  397. }
  398. }
  399. // This hit is competitive - replace bottom element in queue & adjustTop
  400. for (int i = 0; i < comparators.Length; i++)
  401. {
  402. comparators[i].Copy(bottom.slot, doc);
  403. }
  404. UpdateBottom(doc);
  405. for (int i = 0; i < comparators.Length; i++)
  406. {
  407. comparators[i].SetBottom(bottom.slot);
  408. }
  409. }
  410. else
  411. {
  412. // Startup transient: queue hasn't gathered numHits yet
  413. int slot = internalTotalHits - 1;
  414. // Copy hit into queue
  415. for (int i = 0; i < comparators.Length; i++)
  416. {
  417. comparators[i].Copy(slot, doc);
  418. }
  419. Add(slot, doc, System.Single.NaN);
  420. if (queueFull)
  421. {
  422. for (int i = 0; i < comparators.Length; i++)
  423. {
  424. comparators[i].SetBottom(bottom.slot);
  425. }
  426. }
  427. }
  428. }
  429. public override void SetNextReader(IndexReader reader, int docBase)
  430. {
  431. this.docBase = docBase;
  432. for (int i = 0; i < comparators.Length; i++)
  433. {
  434. comparators[i].SetNextReader(reader, docBase);
  435. }
  436. }
  437. public override void SetScorer(Scorer scorer)
  438. {
  439. // set the scorer on all comparators
  440. for (int i = 0; i < comparators.Length; i++)
  441. {
  442. comparators[i].SetScorer(scorer);
  443. }
  444. }
  445. }
  446. /*
  447. * Implements a TopFieldCollector over multiple SortField criteria, without
  448. * tracking document scores and maxScore, and assumes out of orderness in doc
  449. * Ids collection.
  450. */
  451. private class OutOfOrderMultiComparatorNonScoringCollector:MultiComparatorNonScoringCollector
  452. {
  453. public OutOfOrderMultiComparatorNonScoringCollector(FieldValueHitQueue queue, int numHits, bool fillFields):base(queue, numHits, fillFields)
  454. {
  455. }
  456. public override void Collect(int doc)
  457. {
  458. ++internalTotalHits;
  459. if (queueFull)
  460. {
  461. // Fastmatch: return if this hit is not competitive
  462. for (int i = 0; ; i++)
  463. {
  464. int c = reverseMul[i] * comparators[i].CompareBottom(doc);
  465. if (c < 0)
  466. {
  467. // Definitely not competitive.
  468. return ;
  469. }
  470. else if (c > 0)
  471. {
  472. // Definitely competitive.
  473. break;
  474. }
  475. else if (i == comparators.Length - 1)
  476. {
  477. // This is the equals case.
  478. if (doc + docBase > bottom.Doc)
  479. {
  480. // Definitely not competitive
  481. return ;
  482. }
  483. break;
  484. }
  485. }
  486. // This hit is competitive - replace bottom element in queue & adjustTop
  487. for (int i = 0; i < comparators.Length; i++)
  488. {
  489. comparators[i].Copy(bottom.slot, doc);
  490. }
  491. UpdateBottom(doc);
  492. for (int i = 0; i < comparators.Length; i++)
  493. {
  494. comparators[i].SetBottom(bottom.slot);
  495. }
  496. }
  497. else
  498. {
  499. // Startup transient: queue hasn't gathered numHits yet
  500. int slot = internalTotalHits - 1;
  501. // Copy hit into queue
  502. for (int i = 0; i < comparators.Length; i++)
  503. {
  504. comparators[i].Copy(slot, doc);
  505. }
  506. Add(slot, doc, System.Single.NaN);
  507. if (queueFull)
  508. {
  509. for (int i = 0; i < comparators.Length; i++)
  510. {
  511. comparators[i].SetBottom(bottom.slot);
  512. }
  513. }
  514. }
  515. }
  516. public override bool AcceptsDocsOutOfOrder
  517. {
  518. get { return true; }
  519. }
  520. }
  521. /*
  522. * Implements a TopFieldCollector over multiple SortField criteria, with
  523. * tracking document scores and maxScore.
  524. */
  525. private class MultiComparatorScoringMaxScoreCollector : MultiComparatorNonScoringCollector
  526. {
  527. internal Scorer scorer;
  528. public MultiComparatorScoringMaxScoreCollector(FieldValueHitQueue queue, int numHits, bool fillFields):base(queue, numHits, fillFields)
  529. {
  530. // Must set maxScore to NEG_INF, or otherwise Math.max always returns NaN.
  531. maxScore = System.Single.NegativeInfinity;
  532. }
  533. internal void UpdateBottom(int doc, float score)
  534. {
  535. bottom.Doc = docBase + doc;
  536. bottom.Score = score;
  537. bottom = pq.UpdateTop();
  538. }
  539. public override void Collect(int doc)
  540. {
  541. float score = scorer.Score();
  542. if (score > maxScore)
  543. {
  544. maxScore = score;
  545. }
  546. ++internalTotalHits;
  547. if (queueFull)
  548. {
  549. // Fastmatch: return if this hit is not competitive
  550. for (int i = 0; ; i++)
  551. {
  552. int c = reverseMul[i] * comparators[i].CompareBottom(doc);
  553. if (c < 0)
  554. {
  555. // Definitely not competitive.
  556. return ;
  557. }
  558. else if (c > 0)
  559. {
  560. // Definitely competitive.
  561. break;
  562. }
  563. else if (i == comparators.Length - 1)
  564. {
  565. // Here c=0. If we're at the last comparator, this doc is not
  566. // competitive, since docs are visited in doc Id order, which means
  567. // this doc cannot compete with any other document in the queue.
  568. return ;
  569. }
  570. }
  571. // This hit is competitive - replace bottom element in queue & adjustTop
  572. for (int i = 0; i < comparators.Length; i++)
  573. {
  574. comparators[i].Copy(bottom.slot, doc);
  575. }
  576. UpdateBottom(doc, score);
  577. for (int i = 0; i < comparators.Length; i++)
  578. {
  579. comparators[i].SetBottom(bottom.slot);
  580. }
  581. }
  582. else
  583. {
  584. // Startup transient: queue hasn't gathered numHits yet
  585. int slot = internalTotalHits - 1;
  586. // Copy hit into queue
  587. for (int i = 0; i < comparators.Length; i++)
  588. {
  589. comparators[i].Copy(slot, doc);
  590. }
  591. Add(slot, doc, score);
  592. if (queueFull)
  593. {
  594. for (int i = 0; i < comparators.Length; i++)
  595. {
  596. comparators[i].SetBottom(bottom.slot);
  597. }
  598. }
  599. }
  600. }
  601. public override void SetScorer(Scorer scorer)
  602. {
  603. this.scorer = scorer;
  604. base.SetScorer(scorer);
  605. }
  606. }
  607. /*
  608. * Implements a TopFieldCollector over multiple SortField criteria, with
  609. * tracking document scores and maxScore, and assumes out of orderness in doc
  610. * Ids collection.
  611. */
  612. private sealed class OutOfOrderMultiComparatorScoringMaxScoreCollector:MultiComparatorScoringMaxScoreCollector
  613. {
  614. public OutOfOrderMultiComparatorScoringMaxScoreCollector(FieldValueHitQueue queue, int numHits, bool fillFields):base(queue, numHits, fillFields)
  615. {
  616. }
  617. public override void Collect(int doc)
  618. {
  619. float score = scorer.Score();
  620. if (score > maxScore)
  621. {
  622. maxScore = score;
  623. }
  624. ++internalTotalHits;
  625. if (queueFull)
  626. {
  627. // Fastmatch: return if this hit is not competitive
  628. for (int i = 0; ; i++)
  629. {
  630. int c = reverseMul[i] * comparators[i].CompareBottom(doc);
  631. if (c < 0)
  632. {
  633. // Definitely not competitive.
  634. return ;
  635. }
  636. else if (c > 0)
  637. {
  638. // Definitely competitive.
  639. break;
  640. }
  641. else if (i == comparators.Length - 1)
  642. {
  643. // This is the equals case.
  644. if (doc + docBase > bottom.Doc)
  645. {
  646. // Definitely not competitive
  647. return ;
  648. }
  649. break;
  650. }
  651. }
  652. // This hit is competitive - replace bottom element in queue & adjustTop
  653. for (int i = 0; i < comparators.Length; i++)
  654. {
  655. comparators[i].Copy(bottom.slot, doc);
  656. }
  657. UpdateBottom(doc, score);
  658. for (int i = 0; i < comparators.Length; i++)
  659. {
  660. comparators[i].SetBottom(bottom.slot);
  661. }
  662. }
  663. else
  664. {
  665. // Startup transient: queue hasn't gathered numHits yet
  666. int slot = internalTotalHits - 1;
  667. // Copy hit into queue
  668. for (int i = 0; i < comparators.Length; i++)
  669. {
  670. comparators[i].Copy(slot, doc);
  671. }
  672. Add(slot, doc, score);
  673. if (queueFull)
  674. {
  675. for (int i = 0; i < comparators.Length; i++)
  676. {
  677. comparators[i].SetBottom(bottom.slot);
  678. }
  679. }
  680. }
  681. }
  682. public override bool AcceptsDocsOutOfOrder
  683. {
  684. get { return true; }
  685. }
  686. }
  687. /*
  688. * Implements a TopFieldCollector over multiple SortField criteria, with
  689. * tracking document scores and maxScore.
  690. */
  691. private class MultiComparatorScoringNoMaxScoreCollector:MultiComparatorNonScoringCollector
  692. {
  693. internal Scorer scorer;
  694. public MultiComparatorScoringNoMaxScoreCollector(FieldValueHitQueue queue, int numHits, bool fillFields):base(queue, numHits, fillFields)
  695. {
  696. }
  697. internal void UpdateBottom(int doc, float score)
  698. {
  699. bottom.Doc = docBase + doc;
  700. bottom.Score = score;
  701. bottom = pq.UpdateTop();
  702. }
  703. public override void Collect(int doc)
  704. {
  705. ++internalTotalHits;
  706. if (queueFull)
  707. {
  708. // Fastmatch: return if this hit is not competitive
  709. for (int i = 0; ; i++)
  710. {
  711. int c = reverseMul[i] * comparators[i].CompareBottom(doc);
  712. if (c < 0)
  713. {
  714. // Definitely not competitive.
  715. return ;
  716. }
  717. else if (c > 0)
  718. {
  719. // Definitely competitive.
  720. break;
  721. }
  722. else if (i == comparators.Length - 1)
  723. {
  724. // Here c=0. If we're at the last comparator, this doc is not
  725. // competitive, since docs are visited in doc Id order, which means
  726. // this doc cannot compete with any other document in the queue.
  727. return ;
  728. }
  729. }
  730. // This hit is competitive - replace bottom element in queue & adjustTop
  731. for (int i = 0; i < comparators.Length; i++)
  732. {
  733. comparators[i].Copy(bottom.slot, doc);
  734. }
  735. // Compute score only if it is competitive.
  736. float score = scorer.Score();
  737. UpdateBottom(doc, score);
  738. for (int i = 0; i < comparators.Length; i++)
  739. {
  740. comparators[i].SetBottom(bottom.slot);
  741. }
  742. }
  743. else
  744. {
  745. // Startup transient: queue hasn't gathered numHits yet
  746. int slot = internalTotalHits - 1;
  747. // Copy hit into queue
  748. for (int i = 0; i < comparators.Length; i++)
  749. {
  750. comparators[i].Copy(slot, doc);
  751. }
  752. // Compute score only if it is competitive.
  753. float score = scorer.Score();
  754. Add(slot, doc, score);
  755. if (queueFull)
  756. {
  757. for (int i = 0; i < comparators.Length; i++)
  758. {
  759. comparators[i].SetBottom(bottom.slot);
  760. }
  761. }
  762. }
  763. }
  764. public override void SetScorer(Scorer scorer)
  765. {
  766. this.scorer = scorer;
  767. base.SetScorer(scorer);
  768. }
  769. }
  770. /*
  771. * Implements a TopFieldCollector over multiple SortField criteria, with
  772. * tracking document scores and maxScore, and assumes out of orderness in doc
  773. * Ids collection.
  774. */
  775. private sealed class OutOfOrderMultiComparatorScoringNoMaxScoreCollector:MultiComparatorScoringNoMaxScoreCollector
  776. {
  777. public OutOfOrderMultiComparatorScoringNoMaxScoreCollector(FieldValueHitQueue queue, int numHits, bool fillFields):base(queue, numHits, fillFields)
  778. {
  779. }
  780. public override void Collect(int doc)
  781. {
  782. ++internalTotalHits;
  783. if (queueFull)
  784. {
  785. // Fastmatch: return if this hit is not competitive
  786. for (int i = 0; ; i++)
  787. {
  788. int c = reverseMul[i] * comparators[i].CompareBottom(doc);
  789. if (c < 0)
  790. {
  791. // Definitely not competitive.
  792. return ;
  793. }
  794. else if (c > 0)
  795. {
  796. // Definitely competitive.
  797. break;
  798. }
  799. else if (i == comparators.Length - 1)
  800. {
  801. // This is the equals case.
  802. if (doc + docBase > bottom.Doc)
  803. {
  804. // Definitely not competitive
  805. return ;
  806. }
  807. break;
  808. }
  809. }
  810. // This hit is competitive - replace bottom element in queue & adjustTop
  811. for (int i = 0; i < comparators.Length; i++)
  812. {
  813. comparators[i].Copy(bottom.slot, doc);
  814. }
  815. // Compute score only if it is competitive.
  816. float score = scorer.Score();
  817. UpdateBottom(doc, score);
  818. for (int i = 0; i < comparators.Length; i++)
  819. {
  820. comparators[i].SetBottom(bottom.slot);
  821. }
  822. }
  823. else
  824. {
  825. // Startup transient: queue hasn't gathered numHits yet
  826. int slot = internalTotalHits - 1;
  827. // Copy hit into queue
  828. for (int i = 0; i < comparators.Length; i++)
  829. {
  830. comparators[i].Copy(slot, doc);
  831. }
  832. // Compute score only if it is competitive.
  833. float score = scorer.Score();
  834. Add(slot, doc, score);
  835. if (queueFull)
  836. {
  837. for (int i = 0; i < comparators.Length; i++)
  838. {
  839. comparators[i].SetBottom(bottom.slot);
  840. }
  841. }
  842. }
  843. }
  844. public override void SetScorer(Scorer scorer)
  845. {
  846. this.scorer = scorer;
  847. base.SetScorer(scorer);
  848. }
  849. public override bool AcceptsDocsOutOfOrder
  850. {
  851. get { return true; }
  852. }
  853. }
  854. private static readonly ScoreDoc[] EMPTY_SCOREDOCS = new ScoreDoc[0];
  855. private bool fillFields;
  856. /*
  857. * Stores the maximum score value encountered, needed for normalizing. If
  858. * document scores are not tracked, this value is initialized to NaN.
  859. */
  860. internal float maxScore = System.Single.NaN;
  861. internal int numHits;
  862. internal FieldValueHitQueue.Entry bottom = null;
  863. internal bool queueFull;
  864. internal int docBase;
  865. // Declaring the constructor private prevents extending this class by anyone
  866. // else. Note that the class cannot be final since it's extended by the
  867. // internal versions. If someone will define a constructor with any other
  868. // visibility, then anyone will be able to extend the class, which is not what
  869. // we want.
  870. private TopFieldCollector(PriorityQueue<Entry> pq, int numHits, bool fillFields)
  871. : base(pq)
  872. {
  873. this.numHits = numHits;
  874. this.fillFields = fillFields;
  875. }
  876. /// <summary> Creates a new <see cref="TopFieldCollector" /> from the given
  877. /// arguments.
  878. ///
  879. /// <p/><b>NOTE</b>: The instances returned by this method
  880. /// pre-allocate a full array of length
  881. /// <c>numHits</c>.
  882. ///
  883. /// </summary>
  884. /// <param name="sort">the sort criteria (SortFields).
  885. /// </param>
  886. /// <param name="numHits">the number of results to collect.
  887. /// </param>
  888. /// <param name="fillFields">specifies whether the actual field values should be returned on
  889. /// the results (FieldDoc).
  890. /// </param>
  891. /// <param name="trackDocScores">specifies whether document scores should be tracked and set on the
  892. /// results. Note that if set to false, then the results' scores will
  893. /// be set to Float.NaN. Setting this to true affects performance, as
  894. /// it incurs the score computation on each competitive result.
  895. /// Therefore if document scores are not required by the application,
  896. /// it is recommended to set it to false.
  897. /// </param>
  898. /// <param name="trackMaxScore">specifies whether the query's maxScore should be tracked and set
  899. /// on the resulting <see cref="TopDocs" />. Note that if set to false,
  900. /// <see cref="TopDocs.MaxScore" /> returns Float.NaN. Setting this to
  901. /// true affects performance as it incurs the score computation on
  902. /// each result. Also, setting this true automatically sets
  903. /// <c>trackDocScores</c> to true as well.
  904. /// </param>
  905. /// <param name="docsScoredInOrder">specifies whether documents are scored in doc Id order or not by
  906. /// the given <see cref="Scorer" /> in <see cref="Collector.SetScorer(Scorer)" />.
  907. /// </param>
  908. /// <returns> a <see cref="TopFieldCollector" /> instance which will sort the results by
  909. /// the sort criteria.
  910. /// </returns>
  911. /// <throws> IOException </throws>
  912. public static TopFieldCollector Create(Sort sort, int numHits, bool fillFields, bool trackDocScores, bool trackMaxScore, bool docsScoredInOrder)
  913. {
  914. if (sort.fields.Length == 0)
  915. {
  916. throw new System.ArgumentException("Sort must contain at least one field");
  917. }
  918. FieldValueHitQueue queue = FieldValueHitQueue.Create(sort.fields, numHits);
  919. if (queue.GetComparators().Length == 1)
  920. {
  921. if (docsScoredInOrder)
  922. {
  923. if (trackMaxScore)
  924. {
  925. return new OneComparatorScoringMaxScoreCollector(queue, numHits, fillFields);
  926. }
  927. else if (trackDocScores)
  928. {
  929. return new OneComparatorScoringNoMaxScoreCollector(queue, numHits, fillFields);
  930. }
  931. else
  932. {
  933. return new OneComparatorNonScoringCollector(queue, numHits, fillFields);
  934. }
  935. }
  936. else
  937. {
  938. if (trackMaxScore)
  939. {
  940. return new OutOfOrderOneComparatorScoringMaxScoreCollector(queue, numHits, fillFields);
  941. }
  942. else if (trackDocScores)
  943. {
  944. return new OutOfOrderOneComparatorScoringNoMaxScoreCollector(queue, numHits, fillFields);
  945. }
  946. else
  947. {
  948. return new OutOfOrderOneComparatorNonScoringCollector(queue, numHits, fillFields);
  949. }
  950. }
  951. }
  952. // multiple comparators.
  953. if (docsScoredInOrder)
  954. {
  955. if (trackMaxScore)
  956. {
  957. return new MultiComparatorScoringMaxScoreCollector(queue, numHits, fillFields);
  958. }
  959. else if (trackDocScores)
  960. {
  961. return new MultiComparatorScoringNoMaxScoreCollector(queue, numHits, fillFields);
  962. }
  963. else
  964. {
  965. return new MultiComparatorNonScoringCollector(queue, numHits, fillFields);
  966. }
  967. }
  968. else
  969. {
  970. if (trackMaxScore)
  971. {
  972. return new OutOfOrderMultiComparatorScoringMaxScoreCollector(queue, numHits, fillFields);
  973. }
  974. else if (trackDocScores)
  975. {
  976. return new OutOfOrderMultiComparatorScoringNoMaxScoreCollector(queue, numHits, fillFields);
  977. }
  978. else
  979. {
  980. return new OutOfOrderMultiComparatorNonScoringCollector(queue, numHits, fillFields);
  981. }
  982. }
  983. }
  984. internal void Add(int slot, int doc, float score)
  985. {
  986. bottom = pq.Add(new Entry(slot, docBase + doc, score));
  987. queueFull = internalTotalHits == numHits;
  988. }
  989. /*
  990. * Only the following callback methods need to be overridden since
  991. * topDocs(int, int) calls them to return the results.
  992. */
  993. protected internal override void PopulateResults(ScoreDoc[] results, int howMany)
  994. {
  995. if (fillFields)
  996. {
  997. // avoid casting if unnecessary.
  998. FieldValueHitQueue queue = (FieldValueHitQueue) pq;
  999. for (int i = howMany - 1; i >= 0; i--)
  1000. {
  1001. results[i] = queue.FillFields(queue.Pop());
  1002. }
  1003. }
  1004. else
  1005. {
  1006. for (int i = howMany - 1; i >= 0; i--)
  1007. {
  1008. Entry entry = pq.Pop();
  1009. results[i] = new FieldDoc(entry.Doc, entry.Score);
  1010. }
  1011. }
  1012. }
  1013. public /*protected internal*/ override TopDocs NewTopDocs(ScoreDoc[] results, int start)
  1014. {
  1015. if (results == null)
  1016. {
  1017. results = EMPTY_SCOREDOCS;
  1018. // Set maxScore to NaN, in case this is a maxScore tracking collector.
  1019. maxScore = System.Single.NaN;
  1020. }
  1021. // If this is a maxScoring tracking collector and there were no results,
  1022. return new TopFieldDocs(internalTotalHits, results, ((FieldValueHitQueue) pq).GetFields(), maxScore);
  1023. }
  1024. public override bool AcceptsDocsOutOfOrder
  1025. {
  1026. get { return false; }
  1027. }
  1028. }
  1029. }