xapian-core  2.1.0
protomset.h
Go to the documentation of this file.
1 
4 /* Copyright (C) 2004-2026 Olly Betts
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, see
18  * <https://www.gnu.org/licenses/>.
19  */
20 
21 #ifndef XAPIAN_INCLUDED_PROTOMSET_H
22 #define XAPIAN_INCLUDED_PROTOMSET_H
23 
24 #include "api/enquireinternal.h"
25 #include "api/result.h"
26 #include "api/smallvector.h"
27 #include "collapser.h"
28 #include "heap.h"
29 #include "matchtimeout.h"
30 #include "msetcmp.h"
31 #include "omassert.h"
32 #include "spymaster.h"
33 
34 #include <algorithm>
35 
37 
38 class ProtoMSet {
40  class MCmpAdaptor {
42 
43  public:
44  explicit MCmpAdaptor(ProtoMSet* protomset_) : protomset(protomset_) {}
45 
47  return protomset->mcmp(protomset->results[a],
48  protomset->results[b]);
49  }
50  };
51 
52  friend class MCmpAdaptor;
53 
59 
61 
63 
65 
77  double min_weight = 0.0;
78 
84  double max_weight = 0.0;
85 
86  bool min_weight_pending = false;
87 
93 
95  std::vector<Result> results;
96 
101  std::vector<Xapian::doccount> min_heap;
102 
105 
112 
115 
117 
119 
120  double percent_scale = 0.0;
121 
123 
125 
126  double max_possible;
127 
129 
131 
132  Xapian::doccount size() const { return Xapian::doccount(results.size()); }
133 
134  public:
136  Xapian::doccount max_items,
137  Xapian::doccount check_at_least_,
138  MSetCmp mcmp_,
140  Xapian::termcount total_subqs_,
141  PostListTree& pltree_,
142  Xapian::valueno collapse_key,
143  Xapian::doccount collapse_max,
144  int percent_threshold_,
145  double percent_threshold_factor_,
146  double max_possible_,
147  bool stop_once_full_,
148  double time_limit)
149  : check_at_least(check_at_least_),
150  sort_by(sort_by_),
151  mcmp(mcmp_),
152  first(first_),
153  total_subqs(total_subqs_),
154  percent_threshold(percent_threshold_),
155  percent_threshold_factor(percent_threshold_factor_),
156  pltree(pltree_),
157  collapser(collapse_key, collapse_max, results, mcmp),
158  max_possible(max_possible_),
159  stop_once_full(stop_once_full_),
160  timeout(time_limit)
161  {
162  // Always track at least one result so the matcher can rely on
163  // being able to look at it to see the best match so far.
164  max_size = std::max(first_ + max_items, Xapian::doccount{1});
165  results.reserve(max_size);
166  }
167 
168  ProtoMSet(const ProtoMSet&) = delete;
169 
170  ProtoMSet& operator=(const ProtoMSet&) = delete;
171 
173 
174  bool full() const { return size() == max_size; }
175 
176  double get_min_weight() const { return min_weight; }
177 
178  void update_max_weight(double weight) {
179  if (weight <= max_weight)
180  return;
181 
182  max_weight = weight;
184  if (percent_threshold) {
186  }
187  }
188 
189  bool checked_enough() {
191  return true;
192  }
195  return true;
196  }
197  return false;
198  }
199 
204  bool handle_min_weight_pending(bool finalising = false) {
205  // min_weight_pending shouldn't get set when unweighted.
207  min_weight_pending = false;
208  bool weight_first = (sort_by == Xapian::Enquire::Internal::REL ||
210  double new_min_weight = HUGE_VAL;
211  Xapian::doccount j = 0;
212  Xapian::doccount min_elt = 0;
213  for (Xapian::doccount i = 0; i != size(); ++i) {
214  if (results[i].get_weight() < min_weight) {
215  continue;
216  }
217  if (i != j) {
218  results[j] = std::move(results[i]);
219  if (collapser) {
221  }
222  }
223  if (weight_first && results[j].get_weight() < new_min_weight) {
224  new_min_weight = results[j].get_weight();
225  min_elt = j;
226  }
227  ++j;
228  }
229  if (weight_first) {
230  if (finalising) {
232  min_weight = new_min_weight;
233  } else {
234  if (checked_enough())
235  min_weight = new_min_weight;
236  }
237  }
238  if (j != size()) {
239  results.erase(results.begin() + j, results.end());
240  if (!finalising) {
241  return false;
242  }
243  }
244  if (!finalising && min_elt != 0 && !collapser) {
245  // Install the correct element at the tip of the heap, so
246  // that Heap::make() has less to do. NB Breaks collapsing.
247  std::swap(results[0], results[min_elt]);
248  }
249  return true;
250  }
251 
252  bool early_reject(Result& new_item,
253  bool calculated_weight,
254  SpyMaster& spymaster,
255  const Xapian::Document& doc) {
256  if (min_heap.empty())
257  return false;
258 
259  // We're sorting by value (in part at least), so compare the item
260  // against the lowest currently in the proto-mset. If sort_by is VAL,
261  // then new_item.get_weight() won't be set yet, but that doesn't matter
262  // since it's not used by the sort function.
263  Xapian::doccount worst_idx = min_heap.front();
264  if (mcmp(new_item, results[worst_idx]))
265  return false;
266 
267  // The candidate isn't good enough to make the proto-mset, but there
268  // are still things we may need to do with it.
269  if (!collapser) {
270  // We're not collapsing so we can perform an early reject.
272  double weight =
273  calculated_weight ? new_item.get_weight() : pltree.get_weight();
274  spymaster(doc, weight);
275  update_max_weight(weight);
276  return true;
277  }
278 
279  // We're collapsing - the question is should we increment
280  // known_matching_docs?
281 
282  if (checked_enough()) {
283  // We are collapsing but known_matching_docs has already reached
284  // check_at_least so we don't need to worry about whether we can
285  // increment it further.
286  double weight =
287  calculated_weight ? new_item.get_weight() : pltree.get_weight();
288  update_max_weight(weight);
289  return true;
290  }
291 
292  // We can't early reject but need to continue on and check if this item
293  // would be collapsed or not (and if not ProtoMSet::add() will get
294  // called and known_matching_docs incremented there.
295  return false;
296  }
297 
303  bool process(Result&& new_item,
304  ValueStreamDocument& vsdoc) {
305  update_max_weight(new_item.get_weight());
306 
307  if (!collapser) {
308  // No collapsing, so just add the item.
309  add(std::move(new_item));
310  } else {
311  auto res = collapser.check(new_item, vsdoc);
312  switch (res) {
313  case REJECT:
314  return true;
315 
316  case REPLACE:
317  // There was a previous item in the collapse tab so the
318  // MSet can't be empty.
319  Assert(!results.empty());
320 
321  // This is one of the best collapse_max potential MSet
322  // entries with this key which we've seen so far. The
323  // entry with this key which it displaced is still in the
324  // proto-MSet so replace it.
325  replace(collapser.old_item, std::move(new_item));
326  return true;
327 
328  default:
329  break;
330  }
331 
332  auto elt = add(std::move(new_item));
333  if (res != EMPTY && elt != Xapian::doccount(-1)) {
334  collapser.process(res, elt);
335  }
336  }
337 
338  if (stop_once_full) {
339  if (full() && checked_enough()) {
340  return false;
341  }
342  }
343 
344  return true;
345  }
346 
347  // Returns the new item's index, or Xapian::doccount(-1) if not added.
350 
351  if (item.get_weight() < min_weight) {
352  return Xapian::doccount(-1);
353  }
354 
355  if (item.get_weight() > max_weight) {
356  update_max_weight(item.get_weight());
357  }
358 
359  if (!full()) {
360  // We're still filling, or just about to become full.
361  results.push_back(std::move(item));
362  Assert(min_heap.empty());
363  return size() - 1;
364  }
365 
366  if (min_heap.empty()) {
367  // This breaks if we're collapsing because it moves elements around
368  // but can be used if we aren't (and could be for elements with
369  // no collapse key too - FIXME).
370  if (min_weight_pending) {
371  if (!handle_min_weight_pending()) {
372  results.push_back(std::move(item));
373  return size() - 1;
374  }
375  }
376 
377  if (size() == 0) {
378  // E.g. get_mset(0, 0, 10);
379  return Xapian::doccount(-1);
380  }
381  min_heap.reserve(size());
382  for (Xapian::doccount i = 0; i != size(); ++i)
383  min_heap.push_back(i);
384  Heap::make(min_heap.begin(), min_heap.end(), MCmpAdaptor(this));
387  if (checked_enough()) {
388  min_weight = results[min_heap.front()].get_weight();
389  }
390  }
391  }
392 
393  Xapian::doccount worst_idx = min_heap.front();
394  if (!mcmp(item, results[worst_idx])) {
395  // The new item is less than what we already had.
396  return Xapian::doccount(-1);
397  }
398 
399  results[worst_idx] = std::move(item);
400  Heap::replace(min_heap.begin(), min_heap.end(), MCmpAdaptor(this));
403  if (checked_enough()) {
404  min_weight = results[min_heap.front()].get_weight();
405  }
406  }
407  return worst_idx;
408  }
409 
410  void replace(Xapian::doccount old_item, Result&& b) {
411  results[old_item] = std::move(b);
412  if (min_heap.empty())
413  return;
414 
415  // We need to find the entry in min_heap corresponding to old_item.
416  // The simplest way is just to linear-scan for it, and that's actually
417  // fairly efficient as we're just searching for an integer in a
418  // vector of integers. The heap structure means that the lowest ranked
419  // entry is first and lower ranked entries will tend to be nearer the
420  // start, so intuitively scanning forwards for an entry which we're
421  // removing because we found a higher ranking one seems sensible, but
422  // I've not actually profiled this.
423  auto it = std::find(min_heap.begin(), min_heap.end(), old_item);
424  if (rare(it == min_heap.end())) {
425  // min_heap should contain all indices of results.
426  Assert(false);
427  return;
428  }
429 
430  // siftdown() here is correct (because it's on a min-heap).
431  Heap::siftdown(min_heap.begin(), min_heap.end(), it, MCmpAdaptor(this));
432  }
433 
434  void set_new_min_weight(double min_wt) {
435  if (min_wt <= min_weight)
436  return;
437 
438  min_weight = min_wt;
439 
440  if (results.empty()) {
441  // This method gets called before we start matching to set the
442  // fixed weight_threshold threshold.
443  return;
444  }
445 
446 #if 0
447  // FIXME: Is this possible? set_new_min_weight() from a percentage
448  // threshold can't do this...
449  if (min_wt > max_weight) {
450  // The new threshold invalidates all current entries.
451  results.resize(0);
452  min_heap.resize(0);
453  return;
454  }
455 #endif
456 
457  if (!min_heap.empty()) {
458  // If sorting primarily by weight, we could pop the heap while the
459  // tip's weight is < min_wt, but each pop needs 2*log(n)
460  // comparisons, and then pushing replacements for each of those
461  // items needs log(n) comparisons.
462  //
463  // Instead we just discard the heap - if we need to rebuild it,
464  // that'll require 3*n comparisons. The break even is about 3
465  // discarded items for n=10 or about 5 for n=100, but we may never
466  // need to rebuild the heap.
467  min_heap.clear();
468  }
469 
470  // Note that we need to check items against min_weight at some point.
471  min_weight_pending = true;
472  }
473 
475  if (results.empty() || max_weight == 0.0)
476  return;
477 
480  Assert(percent_scale > 0);
481  if (!percent_threshold) {
482  return;
483  }
484 
485  // Truncate the results if necessary.
487  if (min_weight_pending) {
489  }
490  }
491 
494  const std::vector<std::unique_ptr<LocalSubMatch>>& locals,
495  const Xapian::VecUniquePtr<EstimateOp>& estimates,
496  Xapian::doccount max_items) {
498 
499  Xapian::doccount matches_lower_bound;
500  Xapian::doccount matches_estimated;
501  Xapian::doccount matches_upper_bound;
502  Xapian::doccount uncollapsed_lower_bound;
503  Xapian::doccount uncollapsed_estimated;
504  Xapian::doccount uncollapsed_upper_bound;
505 
506  if (!collapser && (!full() || known_matching_docs < check_at_least)) {
507  // Under these conditions we know exactly how many matching docs
508  // there are for the full match so we don't need to resolve the
509  // EstimateOp stack.
511  if (!full()) {
512  // We didn't get all the results requested, so we know that
513  // we've got all there are, and the bounds and estimate are
514  // all equal to that number.
515  m = size();
516  // And that should equal known_matching_docs, unless a percentage
517  // threshold caused some matches to be excluded.
518  if (!percent_threshold) {
520  } else {
522  }
523  } else {
524  // Otherwise we didn't reach check_at_least, so
525  // known_matching_docs gives the exact size.
527  }
528 
529  matches_lower_bound = matches_estimated = matches_upper_bound = m;
530 
531  // When not collapsing the uncollapsed bounds are just the same.
532  uncollapsed_lower_bound = matches_lower_bound;
533  uncollapsed_estimated = matches_estimated;
534  uncollapsed_upper_bound = matches_upper_bound;
535  } else {
536  matches_lower_bound = 0;
537  matches_estimated = 0;
538  matches_upper_bound = 0;
539  for (size_t i = 0; i != estimates.size(); ++i) {
540  if (estimates[i]) {
541  Assert(locals[i].get());
542  Estimates e = locals[i]->resolve(estimates[i]);
543  matches_lower_bound += e.min;
544  matches_estimated += e.est;
545  matches_upper_bound += e.max;
546  }
547  }
548 
549  AssertRel(matches_estimated, >=, matches_lower_bound);
550  AssertRel(matches_estimated, <=, matches_upper_bound);
551 
552  uncollapsed_lower_bound = matches_lower_bound;
553  uncollapsed_estimated = matches_estimated;
554  uncollapsed_upper_bound = matches_upper_bound;
555 
556  if (!full()) {
557  // We didn't get all the results requested, so we know that we've
558  // got all there are, and the bounds and estimate are all equal to
559  // that number.
560  matches_lower_bound = size();
561  matches_estimated = matches_lower_bound;
562  matches_upper_bound = matches_lower_bound;
563 
564  // And that should equal known_matching_docs, unless a percentage
565  // threshold caused some matches to be excluded.
566  if (!percent_threshold) {
567  AssertEq(matches_estimated, known_matching_docs);
568  } else {
569  AssertRel(matches_estimated, <=, known_matching_docs);
570  }
571 
572  if (matches_lower_bound > uncollapsed_lower_bound) {
573  // Clamp the uncollapsed bound to be at least the collapsed
574  // one.
575  uncollapsed_lower_bound = matches_lower_bound;
576  }
577  } else {
578  // We can end up scaling the estimate more than once, so collect
579  // the scale factors and apply them in one go to avoid rounding
580  // more than once.
581  double estimate_scale = 1.0;
582  double unique_rate = 1.0;
583 
584  if (collapser) {
585  matches_lower_bound = collapser.get_matches_lower_bound();
586 
587  Xapian::doccount docs_considered =
590  if (docs_considered > 0) {
591  // Scale the estimate by the rate at which we've been
592  // finding unique documents.
593  double unique = double(docs_considered - dups_ignored);
594  unique_rate = unique / double(docs_considered);
595  }
596 
597  // We can safely reduce the upper bound by the number of
598  // duplicates we've ignored.
599  matches_upper_bound -= dups_ignored;
600  }
601 
602  if (mdecider) {
603  if (!percent_threshold && !collapser) {
604  if (known_matching_docs > matches_lower_bound) {
605  // We're not collapsing or doing a percentage
606  // threshold, so known_matching_docs is a lower bound
607  // on the total number of matches.
608  matches_lower_bound = known_matching_docs;
609  }
610  }
611  }
612 
613  if (percent_threshold) {
614  // Scale the estimate assuming that document weights are evenly
615  // distributed from 0 to the maximum weight seen.
616  estimate_scale *= (1.0 - percent_threshold_factor);
617 
618  // This is all we can be sure of without additional work.
619  matches_lower_bound = size();
620 
621  if (collapser) {
622  uncollapsed_lower_bound = matches_lower_bound;
623  }
624  }
625 
626  if (collapser && estimate_scale != 1.0) {
627  uncollapsed_estimated =
628  Xapian::doccount(uncollapsed_estimated * estimate_scale +
629  0.5);
630  }
631 
632  estimate_scale *= unique_rate;
633 
634  if (estimate_scale != 1.0) {
635  matches_estimated =
636  Xapian::doccount(matches_estimated * estimate_scale + 0.5);
637  if (matches_estimated < matches_lower_bound)
638  matches_estimated = matches_lower_bound;
639  }
640 
641  if (collapser || mdecider) {
642  // Clamp the estimate to the range given by the bounds.
643  AssertRel(matches_lower_bound, <=, matches_upper_bound);
644  matches_estimated = std::clamp(matches_estimated,
645  matches_lower_bound,
646  matches_upper_bound);
647  } else if (!percent_threshold) {
648  AssertRel(known_matching_docs, <=, matches_upper_bound);
649  if (known_matching_docs > matches_lower_bound)
650  matches_lower_bound = known_matching_docs;
651  if (known_matching_docs > matches_estimated)
652  matches_estimated = known_matching_docs;
653  }
654 
655  if (collapser) {
656  if (!mdecider && !percent_threshold) {
657  AssertRel(known_matching_docs, <=, uncollapsed_upper_bound);
658  if (known_matching_docs > uncollapsed_lower_bound)
659  uncollapsed_lower_bound = known_matching_docs;
660  }
661 
662  if (matches_lower_bound > uncollapsed_lower_bound) {
663  // Clamp the uncollapsed bound to be at least the collapsed
664  // one.
665  uncollapsed_lower_bound = matches_lower_bound;
666  }
667 
668  // Clamp the estimate to lie within the known bounds.
669  if (uncollapsed_estimated < uncollapsed_lower_bound) {
670  uncollapsed_estimated = uncollapsed_lower_bound;
671  } else if (uncollapsed_estimated > uncollapsed_upper_bound) {
672  uncollapsed_estimated = uncollapsed_upper_bound;
673  }
674  } else {
675  // When not collapsing the uncollapsed bounds are just the same.
676  uncollapsed_lower_bound = matches_lower_bound;
677  uncollapsed_estimated = matches_estimated;
678  uncollapsed_upper_bound = matches_upper_bound;
679  }
680  }
681  }
682 
683  // FIXME: Profile using min_heap here (when it's been created) to
684  // handle "first" and perform the sort.
685  if (first != 0) {
686  if (first > size()) {
687  results.clear();
688  goto no_results_to_sort;
689  }
690  // We perform nth_element() on reverse iterators so that the
691  // unwanted elements end up at the end of items, which means
692  // that the call to erase() to remove them doesn't have to copy
693  // any elements.
694  auto nth = results.rbegin() + first;
695  std::nth_element(results.rbegin(), nth, results.rend(), mcmp);
696  // Discard the unwanted elements.
697  results.erase(results.end() - first, results.end());
698  } else if (max_items == 0) {
699  results.clear();
700  goto no_results_to_sort;
701  }
702 
703  std::sort(results.begin(), results.end(), mcmp);
704 
705  // Note: finalise() is a no-op if results.empty() so no_results_to_sort
706  // can safely skip over this too.
708 
709 no_results_to_sort:
710 
711  // The estimates should lie between the bounds.
712  AssertRel(matches_lower_bound, <=, matches_estimated);
713  AssertRel(matches_estimated, <=, matches_upper_bound);
714  AssertRel(uncollapsed_lower_bound, <=, uncollapsed_estimated);
715  AssertRel(uncollapsed_estimated, <=, uncollapsed_upper_bound);
716 
717  // Collapsing should only reduce the bounds and estimate.
718  AssertRel(matches_lower_bound, <=, uncollapsed_lower_bound);
719  AssertRel(matches_estimated, <=, uncollapsed_estimated);
720  AssertRel(matches_upper_bound, <=, uncollapsed_upper_bound);
721 
723  matches_upper_bound,
724  matches_lower_bound,
725  matches_estimated,
726  uncollapsed_upper_bound,
727  uncollapsed_lower_bound,
728  uncollapsed_estimated,
729  max_possible,
730  max_weight,
731  std::move(results),
732  percent_scale * 100.0));
733  }
734 };
735 
736 #endif // XAPIAN_INCLUDED_PROTOMSET_H
The Collapser class tracks collapse keys and the documents they match.
Definition: collapser.h:135
Xapian::doccount get_docs_considered() const
Definition: collapser.h:243
collapse_result check(Result &result, Xapian::Document::Internal &vsdoc)
Check a new result.
Definition: collapser.cc:126
void finalise(double min_weight, int percent_threshold)
Definition: collapser.cc:237
Xapian::doccount old_item
Replaced item when REPLACE is returned by collapse().
Definition: collapser.h:182
Xapian::doccount get_dups_ignored() const
Definition: collapser.h:245
void process(collapse_result action, Xapian::doccount item)
Handle a new Result.
Definition: collapser.cc:164
Xapian::doccount get_matches_lower_bound() const
Definition: collapser.cc:212
void result_has_moved(Xapian::doccount from, Xapian::doccount to)
Process relocation of entry in results.
Definition: collapser.h:223
Xapian::termcount count_matching_subqs() const
Definition: postlisttree.h:231
double get_weight() const
Definition: postlisttree.h:166
Adapt MSetCmp to be usable with min_heap.
Definition: protomset.h:40
bool operator()(Xapian::doccount a, Xapian::doccount b) const
Definition: protomset.h:46
MCmpAdaptor(ProtoMSet *protomset_)
Definition: protomset.h:44
ProtoMSet * protomset
Definition: protomset.h:41
double max_weight
The highest document weight seen.
Definition: protomset.h:84
Collapser & get_collapser()
Definition: protomset.h:172
std::vector< Result > results
The items in the proto-MSet.
Definition: protomset.h:95
bool process(Result &&new_item, ValueStreamDocument &vsdoc)
Process new_item.
Definition: protomset.h:303
double percent_threshold_factor
Definition: protomset.h:118
void replace(Xapian::doccount old_item, Result &&b)
Definition: protomset.h:410
Xapian::doccount add(Result &&item)
Definition: protomset.h:348
ProtoMSet & operator=(const ProtoMSet &)=delete
Xapian::doccount first
First entry wanted in MSet.
Definition: protomset.h:104
double min_weight
Minimum threshold on the weight.
Definition: protomset.h:77
double max_possible
Definition: protomset.h:126
Xapian::termcount total_subqs
How many weighted leaf subqueries there are.
Definition: protomset.h:111
ProtoMSet(const ProtoMSet &)=delete
TimeOut timeout
Definition: protomset.h:130
bool stop_once_full
Definition: protomset.h:128
std::vector< Xapian::doccount > min_heap
A heap of offsets into results.
Definition: protomset.h:101
MSetCmp mcmp
Definition: protomset.h:64
bool early_reject(Result &new_item, bool calculated_weight, SpyMaster &spymaster, const Xapian::Document &doc)
Definition: protomset.h:252
Collapser collapser
Definition: protomset.h:124
void set_new_min_weight(double min_wt)
Definition: protomset.h:434
Xapian::termcount max_weight_subqs_matched
The number of subqueries which matched to give max_weight.
Definition: protomset.h:114
Xapian::doccount check_at_least
Definition: protomset.h:60
PostListTree & pltree
Definition: protomset.h:122
Xapian::Enquire::Internal::sort_setting sort_by
Definition: protomset.h:62
bool min_weight_pending
Definition: protomset.h:86
ProtoMSet(Xapian::doccount first_, Xapian::doccount max_items, Xapian::doccount check_at_least_, MSetCmp mcmp_, Xapian::Enquire::Internal::sort_setting sort_by_, Xapian::termcount total_subqs_, PostListTree &pltree_, Xapian::valueno collapse_key, Xapian::doccount collapse_max, int percent_threshold_, double percent_threshold_factor_, double max_possible_, bool stop_once_full_, double time_limit)
Definition: protomset.h:135
bool full() const
Definition: protomset.h:174
Xapian::doccount known_matching_docs
Count of how many known matching documents have been processed so far.
Definition: protomset.h:92
void update_max_weight(double weight)
Definition: protomset.h:178
double percent_scale
Definition: protomset.h:120
bool handle_min_weight_pending(bool finalising=false)
Resolve a pending min_weight change.
Definition: protomset.h:204
double get_min_weight() const
Definition: protomset.h:176
void finalise_percentages()
Definition: protomset.h:474
Xapian::MSet finalise(const Xapian::MatchDecider *mdecider, const std::vector< std::unique_ptr< LocalSubMatch >> &locals, const Xapian::VecUniquePtr< EstimateOp > &estimates, Xapian::doccount max_items)
Definition: protomset.h:493
int percent_threshold
Definition: protomset.h:116
Xapian::doccount size() const
Definition: protomset.h:132
Xapian::doccount max_size
Maximum size the ProtoMSet needs to grow to.
Definition: protomset.h:58
bool checked_enough()
Definition: protomset.h:189
A result in an MSet.
Definition: result.h:30
double get_weight() const
Definition: result.h:70
bool timed_out() const
Definition: matchtimeout.h:115
A document which gets its values from a ValueStreamManager.
Class representing a document.
Definition: document.h:64
A smart pointer that uses intrusive reference counting.
Definition: intrusive_ptr.h:83
Xapian::MSet internals.
Definition: msetinternal.h:44
Class representing a list of search results.
Definition: mset.h:46
Abstract base class for match deciders.
Definition: matchdecider.h:37
Suitable for "simple" type T.
Definition: smallvector.h:62
size_type size() const
Definition: smallvector.h:135
Collapse documents with the same collapse key during the match.
@ EMPTY
Definition: collapser.h:34
@ REPLACE
Definition: collapser.h:38
@ REJECT
Definition: collapser.h:37
#define rare(COND)
Definition: config.h:616
Xapian::Enquire internals.
C++ STL heap implementation with extensions.
Time limits for the matcher.
Result comparison functions.
bool(* MSetCmp)(const Result &, const Result &)
Definition: msetcmp.h:29
void replace(_RandomAccessIterator first, _RandomAccessIterator last, _Compare comp)
Definition: heap.h:230
void siftdown(_RandomAccessIterator first, _RandomAccessIterator last, _RandomAccessIterator elt, _Compare comp)
Definition: heap.h:249
void make(_RandomAccessIterator first, _RandomAccessIterator last, _Compare comp)
Definition: heap.h:259
void sort(_RandomAccessIterator first, _RandomAccessIterator last, _Compare comp)
Definition: heap.h:277
unsigned XAPIAN_TERMCOUNT_BASE_TYPE termcount
A counts of terms.
Definition: types.h:64
unsigned valueno
The number for a value slot in a document.
Definition: types.h:90
unsigned XAPIAN_DOCID_BASE_TYPE doccount
A count of documents.
Definition: types.h:37
Various assertion macros.
#define AssertEq(A, B)
Definition: omassert.h:124
#define AssertRel(A, REL, B)
Definition: omassert.h:123
#define Assert(COND)
Definition: omassert.h:122
A result in an MSet.
Custom vector implementations using small vector optimisation.
Class for managing MatchSpy objects during the match.
Xapian::doccount est
Definition: estimateop.h:33
Xapian::doccount min
Definition: estimateop.h:33
Xapian::doccount max
Definition: estimateop.h:33