xapian-core  2.1.0
mset.cc
Go to the documentation of this file.
1 
4 /* Copyright (C) 2017,2024,2025,2026 Olly Betts
5  * Copyright (C) 2018 Uppinder Chugh
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, see
19  * <https://www.gnu.org/licenses/>.
20  */
21 
22 #include <config.h>
23 
24 #include "msetinternal.h"
25 #include "xapian/mset.h"
26 
27 // FIXME: Clustering API needs work: #include "xapian/cluster.h"
28 
30 #include "clamp_cast.h"
31 #include "net/serialise.h"
32 #include "matcher/msetcmp.h"
33 #include "omassert.h"
34 #include "pack.h"
35 #include "roundestimate.h"
36 #include "serialise-double.h"
37 #include "str.h"
39 
40 #include <algorithm>
41 #include <cfloat>
42 #include <string>
43 #include <string_view>
44 #include <unordered_set>
45 
46 using namespace std;
47 
48 namespace Xapian {
49 
50 MSet::MSet(const MSet&) = default;
51 
52 MSet&
53 MSet::operator=(const MSet&) = default;
54 
55 MSet::MSet(MSet&&) = default;
56 
57 MSet&
58 MSet::operator=(MSet&&) = default;
59 
60 MSet::MSet() : internal(new MSet::Internal) {}
61 
62 MSet::MSet(Internal* internal_) : internal(internal_) {}
63 
65 
66 void
68 {
69  internal->fetch(first, last);
70 }
71 
72 void
74 {
75  internal->set_item_weight(i, weight);
76 }
77 
78 #if 0 // FIXME: Diversification API needs work.
87 static double
88 evaluate_dmset(const vector<Xapian::docid>& dmset,
89  const Xapian::ClusterSet& cset,
90  double factor1,
91  double factor2,
92  const Xapian::MSet& mset,
93  const vector<double>& dissimilarity)
94 {
95  double score_1 = 0, score_2 = 0;
96 
97  // FIXME: We could compute score_1 once then adjust for each candidate
98  // change.
99  // Seems hard to do similar for score_2 though.
100  for (auto mset_index : dmset)
101  score_1 += mset[mset_index].get_weight();
102 
103  auto cset_size = cset.size();
104  for (Xapian::doccount c = 0; c < cset_size; ++c) {
105  double min_dist = numeric_limits<double>::max();
106  unsigned int pos = 1;
107  for (auto mset_index : dmset) {
108  // FIXME: Pre-compute 1.0 / log(2.0 + i) for i = [0, dmset.size()) ?
109  double weight = dissimilarity[mset_index * cset_size + c];
110  weight /= log(1.0 + pos);
111  min_dist = min(min_dist, weight);
112  ++pos;
113  }
114  score_2 += min_dist;
115  }
116 
117  return factor2 * score_2 - factor1 * score_1;
118 }
119 
120 void
121 MSet::diversify_(Xapian::doccount k,
123  double factor1,
124  double factor2)
125 {
126  // Ensured by inlined caller.
127  AssertRel(k, >=, 2);
128 
129  auto mset_size = size();
130  if (mset_size <= k) {
131  // Picking k documents would pick the whole MSet so nothing to do.
132  //
133  // Since k >= 2, this means we don't try to diversify an MSet with
134  // 2 documents (for which reordering can't usefully improve diversity
135  // since the only possible change is to swap the order of the 2
136  // documents).
137  return;
138  }
139 
141  std::vector<Xapian::doccount> main_dmset;
142  main_dmset.reserve(k);
143 
144  Xapian::doccount count = 0;
145  TermListGroup tlg(*this);
146  std::vector<Xapian::Point> points;
147  points.reserve(mset_size);
148  for (MSetIterator it = begin(); it != end(); ++it) {
149  Xapian::Document doc = it.get_document();
150  doc.internal->set_index(count);
151  points.push_back(Xapian::Point(tlg, doc));
152  // Initial top-k diversified documents
153  if (count < k) {
154  // The initial diversified document set is the top-k documents from
155  // the MSet.
156  main_dmset.push_back(count);
157  }
158  ++count;
159  }
160 
161  // Cluster the MSet into k clusters.
163 
176  // Pre-compute all the dissimilarity values.
177  auto cset_size = cset.size();
178  std::vector<double> dissimilarity;
179  dissimilarity.reserve(cset_size * points.size());
180  {
182  for (const auto& point : points) {
183  for (unsigned int c = 0; c < cset_size; ++c) {
184  double dist = d.similarity(point, cset[c].get_centroid());
185  dissimilarity.push_back(1.0 - dist);
186  }
187  }
188  }
189 
190  // Build topc, which contains the union of the top-r relevant documents of
191  // each cluster.
192  vector<Xapian::docid> topc;
193  for (Xapian::doccount c = 0; c < cset_size; ++c) {
194  // FIXME: This is supposed to pick the `r` most relevant documents, but
195  // actually seems to pick those with the lowest docids.
196  auto documents = cset[c].get_documents();
197  auto limit = std::min(r, documents.size());
198  for (Xapian::doccount d = 0; d < limit; ++d) {
199  auto mset_index = documents[d].internal->get_index();
200  topc.push_back(mset_index);
201  }
202  }
203 
204  vector<Xapian::doccount> curr_dmset = main_dmset;
205 
206  while (true) {
207  bool found_better_dmset = false;
208  for (unsigned int i = 0; i < main_dmset.size(); ++i) {
209  auto curr_doc = main_dmset[i];
210  double best_score = evaluate_dmset(curr_dmset, cset,
211  factor1, factor2,
212  *this, dissimilarity);
213  bool found_better_doc = false;
214 
215  for (unsigned int j = 0; j < topc.size(); ++j) {
216  // Continue if candidate document from topc already
217  // exists in curr_dmset. FIXME: Linear search!
218  auto candidate_doc = find(curr_dmset.begin(), curr_dmset.end(),
219  topc[j]);
220  if (candidate_doc != curr_dmset.end()) {
221  continue;
222  }
223 
224  auto temp_doc = curr_dmset[i];
225  curr_dmset[i] = topc[j];
226  double score = evaluate_dmset(curr_dmset, cset,
227  factor1, factor2,
228  *this, dissimilarity);
229 
230  if (score < best_score) {
231  curr_doc = curr_dmset[i];
232  best_score = score;
233  found_better_doc = true;
234  }
235 
236  curr_dmset[i] = temp_doc;
237  }
238  if (found_better_doc) {
239  curr_dmset[i] = curr_doc;
240  found_better_dmset = true;
241  }
242  }
243 
244  // Terminate algorithm when there's no change in current
245  // document matchset
246  if (!found_better_dmset)
247  break;
248 
249  main_dmset = curr_dmset;
250  }
251 
252  // Reorder the results to reflect the diversification. To do this we need
253  // to partition the MSet so the promoted documents come first (in original
254  // MSet order), followed by the non-promoted documents (also in original
255  // MSet order).
256  unordered_set<Xapian::docid> promoted{k};
257  for (auto mset_index : main_dmset) {
258  promoted.insert(internal->items[mset_index].get_docid());
259  }
260 
261  stable_partition(internal->items.begin(), internal->items.end(),
262  [&](const Result& result) {
263  return promoted.count(result.get_docid());
264  });
265 }
266 #endif
267 
268 void
270 {
271  std::sort(internal->items.begin(), internal->items.end(),
273 }
274 
275 int
276 MSet::convert_to_percent(double weight) const
277 {
278  return internal->convert_to_percent(weight);
279 }
280 
282 MSet::get_termfreq(std::string_view term) const
283 {
284  // Check the cached data for query terms first.
285  Xapian::doccount termfreq;
286  if (usual(internal->stats && internal->stats->get_stats(term, termfreq))) {
287  return termfreq;
288  }
289 
290  if (rare(!internal->enquire)) {
291  // Consistent with get_termfreq() on an empty database which always
292  // returns 0.
293  return 0;
294  }
295 
296  // Fall back to asking the database via enquire.
297  return internal->enquire->get_termfreq(term);
298 }
299 
300 double
301 MSet::get_termweight(std::string_view term) const
302 {
303  // A term not in the query has no termweight, so 0.0 makes sense as the
304  // answer in such cases.
305  double weight = 0.0;
306  if (usual(internal->stats)) {
307  (void)internal->stats->get_termweight(term, weight);
308  }
309  return weight;
310 }
311 
314 {
315  return internal->first;
316 }
317 
320 {
321  return internal->matches_lower_bound;
322 }
323 
326 {
327  // Doing this here avoids calculating if the estimate is never looked at,
328  // though does mean we recalculate if this method is called more than once.
332 }
333 
336 {
337  return internal->matches_upper_bound;
338 }
339 
342 {
343  return internal->uncollapsed_lower_bound;
344 }
345 
348 {
349  // Doing this here avoids calculating if the estimate is never looked at,
350  // though does mean we recalculate if this method is called more than once.
354 }
355 
358 {
359  return internal->uncollapsed_upper_bound;
360 }
361 
362 double
364 {
365  return internal->max_attained;
366 }
367 
368 double
370 {
371  return internal->max_possible;
372 }
373 
375 MSet::size() const
376 {
377  return clamp_cast<Xapian::doccount>(internal->items.size());
378 }
379 
380 std::string
381 MSet::snippet(std::string_view text,
382  size_t length,
383  const Xapian::Stem& stemmer,
384  unsigned flags,
385  std::string_view hi_start,
386  std::string_view hi_end,
387  std::string_view omit) const
388 {
389  // The actual implementation is in queryparser/termgenerator_internal.cc.
390  return internal->snippet(text, length, stemmer, flags,
391  hi_start, hi_end, omit);
392 }
393 
394 std::string
396 {
397  return internal->get_description();
398 }
399 
400 Document
402 {
403  if (index >= items.size()) {
404  string msg = "Requested index ";
405  msg += str(index);
406  msg += " in MSet of size ";
407  msg += str(items.size());
408  throw Xapian::RangeError(msg);
409  }
410  Assert(enquire);
411  return enquire->get_document(items[index].get_docid());
412 }
413 
414 void
416 {
417  if (items.empty() || !enquire) {
418  return;
419  }
420  if (last > items.size() - 1) {
421  last = Xapian::doccount(items.size() - 1);
422  }
423  if (first_ <= last) {
424  Xapian::doccount n = last - first_;
425  for (Xapian::doccount i = 0; i <= n; ++i) {
426  enquire->request_document(items[i].get_docid());
427  }
428  }
429 }
430 
431 void
433 {
434  // max_attained is updated assuming that set_item_weight is called on every
435  // MSet item from 0 up. While assigning new weights max_attained is updated
436  // as the maximum of the new weights set till Xapian::doccount i.
437  if (i == 0)
438  max_attained = weight;
439  else
440  max_attained = max(max_attained, weight);
441  // Ideally the max_possible should be the maximum possible weight that
442  // can be assigned by the reranking algorithm, but since it is not always
443  // possible to calculate the max possible weight for a reranking algorithm
444  // we use this approach.
445  max_possible = max(max_possible, max_attained);
446  items[i].set_weight(weight);
447 }
448 
449 int
451 {
452  int percent;
453  if (percent_scale_factor == 0.0) {
454  // For an unweighted search, give all matches 100%.
455  percent = 100;
456  } else if (weight <= 0.0) {
457  // Some weighting schemes can return zero relevance while matching,
458  // so give such matches 0%.
459  percent = 0;
460  } else {
461  // Adding on 100 * DBL_EPSILON was a hack to work around excess
462  // precision (e.g. on x86 when not using SSE), but this code seems like
463  // it's generally asking for problems with floating point rounding
464  // issues - maybe we ought to carry through the matching and total
465  // number of subqueries and calculate using those instead.
466  //
467  // There are corresponding hacks in matcher/matcher.cc.
468  percent = int(weight * percent_scale_factor + 100.0 * DBL_EPSILON);
469  if (percent <= 0) {
470  // Make any non-zero weight give a non-zero percentage.
471  percent = 1;
472  } else if (percent > 100) {
473  // Make sure we don't ever exceed 100%.
474  percent = 100;
475  }
476  // FIXME: Ideally we should also make sure any non-exact match gives
477  // < 100%.
478  }
479  return percent;
480 }
481 
482 void
484  Xapian::doccount n_shards)
485 {
486  for (auto& result : items) {
487  result.unshard_docid(shard, n_shards);
488  }
489 }
490 
491 void
492 MSet::Internal::merge_stats(const Internal* o, bool collapsing)
493 {
494  if (snippet_bg_relevance.empty()) {
495  snippet_bg_relevance = o->snippet_bg_relevance;
496  } else {
497  Assert(snippet_bg_relevance == o->snippet_bg_relevance);
498  }
499  if (collapsing) {
500  matches_lower_bound = max(matches_lower_bound, o->matches_lower_bound);
501  // matches_estimated will get adjusted later in this case.
502  } else {
503  matches_lower_bound += o->matches_lower_bound;
504  }
505  matches_estimated += o->matches_estimated;
506  matches_upper_bound += o->matches_upper_bound;
507  uncollapsed_lower_bound += o->uncollapsed_lower_bound;
508  uncollapsed_estimated += o->uncollapsed_estimated;
509  uncollapsed_upper_bound += o->uncollapsed_upper_bound;
510  max_possible = max(max_possible, o->max_possible);
511  if (o->max_attained > max_attained) {
512  max_attained = o->max_attained;
513  percent_scale_factor = o->percent_scale_factor;
514  }
515 }
516 
517 string
519 {
520  string result;
521 
522  result += serialise_double(max_possible);
523  result += serialise_double(max_attained);
524 
525  result += serialise_double(percent_scale_factor);
526 
527  pack_uint(result, first);
528  // Send back the raw matches_* values. MSet::get_matches_estimated()
529  // rounds the estimate lazily, but when we merge MSet objects we really
530  // want to merge based on the raw estimates.
531  //
532  // It is also cleaner that a round-trip through serialisation gives you an
533  // object which is as close to the original as possible.
534  pack_uint(result, matches_lower_bound);
535  pack_uint(result, matches_estimated);
536  pack_uint(result, matches_upper_bound);
537  pack_uint(result, uncollapsed_lower_bound);
538  pack_uint(result, uncollapsed_estimated);
539  pack_uint(result, uncollapsed_upper_bound);
540 
541  pack_uint(result, items.size());
542  for (auto&& item : items) {
543  result += serialise_double(item.get_weight());
544  pack_uint(result, item.get_docid());
545  pack_string(result, item.get_sort_key());
546  pack_string(result, item.get_collapse_key());
547  pack_uint(result, item.get_collapse_count());
548  }
549 
550  if (stats)
551  result += serialise_stats(*stats);
552 
553  return result;
554 }
555 
556 void
557 MSet::Internal::unserialise(const char * p, const char * p_end)
558 {
559  items.clear();
560 
561  max_possible = unserialise_double(&p, p_end);
562  max_attained = unserialise_double(&p, p_end);
563 
564  percent_scale_factor = unserialise_double(&p, p_end);
565 
566  size_t msize;
567  if (!unpack_uint(&p, p_end, &first) ||
568  !unpack_uint(&p, p_end, &matches_lower_bound) ||
569  !unpack_uint(&p, p_end, &matches_estimated) ||
570  !unpack_uint(&p, p_end, &matches_upper_bound) ||
571  !unpack_uint(&p, p_end, &uncollapsed_lower_bound) ||
572  !unpack_uint(&p, p_end, &uncollapsed_estimated) ||
573  !unpack_uint(&p, p_end, &uncollapsed_upper_bound) ||
574  !unpack_uint(&p, p_end, &msize)) {
576  }
577  for ( ; msize; --msize) {
578  double wt = unserialise_double(&p, p_end);
579  Xapian::docid did;
580  string sort_key, key;
581  Xapian::doccount collapse_cnt;
582  if (!unpack_uint(&p, p_end, &did) ||
583  !unpack_string(&p, p_end, sort_key) ||
584  !unpack_string(&p, p_end, key) ||
585  !unpack_uint(&p, p_end, &collapse_cnt)) {
587  }
588  items.emplace_back(wt, did, std::move(key), collapse_cnt,
589  std::move(sort_key));
590  }
591 
592  if (p != p_end) {
593  stats.reset(new Xapian::Weight::Internal());
594  unserialise_stats(p, p_end, *stats);
595  }
596 }
597 
598 string
600 {
601  string desc = "MSet(matches_lower_bound=";
602  desc += str(matches_lower_bound);
603  desc += ", matches_estimated=";
604  desc += str(matches_estimated);
605  desc += ", matches_upper_bound=";
606  desc += str(matches_upper_bound);
607  if (uncollapsed_lower_bound != matches_lower_bound) {
608  desc += ", uncollapsed_lower_bound=";
609  desc += str(uncollapsed_lower_bound);
610  }
611  if (uncollapsed_estimated != matches_estimated) {
612  desc += ", uncollapsed_estimated=";
613  desc += str(uncollapsed_estimated);
614  }
615  if (uncollapsed_upper_bound != matches_upper_bound) {
616  desc += ", uncollapsed_upper_bound=";
617  desc += str(uncollapsed_upper_bound);
618  }
619  if (first != 0) {
620  desc += ", first=";
621  desc += str(first);
622  }
623  if (max_possible > 0) {
624  desc += ", max_possible=";
625  desc += str(max_possible);
626  }
627  if (max_attained > 0) {
628  desc += ", max_attained=";
629  desc += str(max_attained);
630  }
631  desc += ", [";
632  bool comma = false;
633  for (auto&& item : items) {
634  if (comma) {
635  desc += ", ";
636  } else {
637  comma = true;
638  }
639  desc += item.get_description();
640  }
641  desc += "])";
642  return desc;
643 }
644 
645 }
Cast a value to a type, clamping out of range values.
A result in an MSet.
Definition: result.h:30
Class for storing the results returned by the Clusterer.
Definition: cluster.h:454
Xapian::doccount size() const
Return the number of clusters.
Class for calculating the cosine distance between two documents.
Definition: cluster.h:540
double similarity(const PointType &a, const PointType &b) const override
Calculates and returns the cosine similarity using the formula cos(theta) = a.b/(|a|*|b|)
Class representing a document.
Definition: document.h:64
Xapian::Internal::intrusive_ptr_nonnull< Internal > internal
Definition: document.h:67
LCD clusterer: This clusterer implements the LCD clustering algorithm adapted from Modelling efficien...
Definition: cluster.h:664
ClusterSet cluster(const MSet &mset) override
Implements the LCD clustering algorithm.
Xapian::MSet internals.
Definition: msetinternal.h:44
Xapian::doccount uncollapsed_upper_bound
Definition: msetinternal.h:76
std::string serialise() const
Serialise this object.
Definition: mset.cc:518
int convert_to_percent(double weight) const
Definition: mset.cc:450
Xapian::Internal::intrusive_ptr< const Enquire::Internal > enquire
Definition: msetinternal.h:64
std::unordered_map< std::string, double > snippet_bg_relevance
Relevance weights for non-query terms for generating snippets.
Definition: msetinternal.h:56
std::string get_description() const
Return a string describing this object.
Definition: mset.cc:599
std::unique_ptr< Xapian::Weight::Internal > stats
For looking up query term frequencies and weights.
Definition: msetinternal.h:62
std::vector< Result > items
The items in the MSet.
Definition: msetinternal.h:59
Xapian::doccount uncollapsed_lower_bound
Definition: msetinternal.h:72
Xapian::doccount matches_estimated
Definition: msetinternal.h:68
void unshard_docids(Xapian::doccount shard, Xapian::doccount n_shards)
Definition: mset.cc:483
void unserialise(const char *p, const char *p_end)
Unserialise a serialised Xapian::MSet::Internal object.
Definition: mset.cc:557
Xapian::Document get_document(Xapian::doccount index) const
Definition: mset.cc:401
void merge_stats(const Internal *o, bool collapsing)
Definition: mset.cc:492
void fetch(Xapian::doccount first, Xapian::doccount last) const
Definition: mset.cc:415
void set_item_weight(Xapian::doccount i, double weight)
Definition: mset.cc:432
Xapian::doccount matches_lower_bound
Definition: msetinternal.h:66
double percent_scale_factor
Scale factor to convert weights to percentages.
Definition: msetinternal.h:85
Xapian::doccount matches_upper_bound
Definition: msetinternal.h:70
Xapian::doccount uncollapsed_estimated
Definition: msetinternal.h:74
Class representing a list of search results.
Definition: mset.h:46
Xapian::Internal::intrusive_ptr_nonnull< Internal > internal
Definition: mset.h:78
Xapian::doccount get_termfreq(std::string_view term) const
Get the termfreq of a term.
Definition: mset.cc:282
void sort_by_relevance()
Sorts the list of documents in MSet according to their weights.
Definition: mset.cc:269
void set_item_weight(Xapian::doccount i, double wt)
Update the weight corresponding to the document indexed at position i with wt.
Definition: mset.cc:73
Xapian::doccount size() const
Return number of items in this MSet object.
Definition: mset.cc:375
MSet()
Default constructor.
Definition: mset.cc:60
double get_max_possible() const
The maximum possible weight any document could achieve.
Definition: mset.cc:369
void fetch_(Xapian::doccount first, Xapian::doccount last) const
Definition: mset.cc:67
Xapian::doccount get_uncollapsed_matches_upper_bound() const
Upper bound on the total number of matching documents before collapsing.
Definition: mset.cc:357
friend class MSetIterator
Definition: mset.h:47
Xapian::doccount get_uncollapsed_matches_estimated() const
Estimate of the total number of matching documents before collapsing.
Definition: mset.cc:347
Xapian::doccount get_uncollapsed_matches_lower_bound() const
Lower bound on the total number of matching documents before collapsing.
Definition: mset.cc:341
int convert_to_percent(double weight) const
Convert a weight to a percentage.
Definition: mset.cc:276
std::string get_description() const
Return a string describing this object.
Definition: mset.cc:395
~MSet()
Destructor.
Definition: mset.cc:64
Xapian::doccount get_firstitem() const
Rank of first item in this MSet.
Definition: mset.cc:313
double get_termweight(std::string_view term) const
Get the term weight of a term.
Definition: mset.cc:301
Xapian::doccount get_matches_upper_bound() const
Upper bound on the total number of matching documents.
Definition: mset.cc:335
MSetIterator begin() const
Return iterator pointing to the first item in this MSet.
Definition: mset.h:790
std::string snippet(std::string_view text, size_t length=500, const Xapian::Stem &stemmer=Xapian::Stem(), unsigned flags=SNIPPET_BACKGROUND_MODEL|SNIPPET_EXHAUSTIVE, std::string_view hi_start="<b>", std::string_view hi_end="</b>", std::string_view omit="...") const
Generate a snippet.
Definition: mset.cc:381
double get_max_attained() const
The maximum weight attained by any document.
Definition: mset.cc:363
Xapian::doccount get_matches_lower_bound() const
Lower bound on the total number of matching documents.
Definition: mset.cc:319
MSetIterator end() const
Return iterator pointing to just after the last item in this MSet.
Definition: mset.h:795
Xapian::doccount get_matches_estimated() const
Estimate of the total number of matching documents.
Definition: mset.cc:325
Class to represent a document as a point in the Vector Space Model.
Definition: cluster.h:322
RangeError indicates an attempt to access outside the bounds of a container.
Definition: error.h:959
Class representing a stemming algorithm.
Definition: stem.h:74
Class to hold statistics for a given collection.
#define usual(COND)
Definition: config.h:617
#define rare(COND)
Definition: config.h:616
string term
PositionList * p
Xapian::termpos pos
Append a string to an object description, escaping invalid UTF-8.
Abstract base class for a document.
Class representing a list of search results.
MSetCmp get_msetcmp_function(Xapian::Enquire::Internal::sort_setting sort_by, bool sort_forward, bool sort_val_reverse)
Select the appropriate msetcmp function.
Definition: msetcmp.cc:100
Result comparison functions.
Xapian::MSet internals.
void sort(_RandomAccessIterator first, _RandomAccessIterator last, _Compare comp)
Definition: heap.h:277
string str(int value)
Convert int to std::string.
Definition: str.cc:91
The Xapian namespace contains public interfaces for the Xapian library.
Definition: compactor.cc:82
unsigned XAPIAN_DOCID_BASE_TYPE doccount
A count of documents.
Definition: types.h:37
unsigned XAPIAN_DOCID_BASE_TYPE docid
A unique identifier for a document.
Definition: types.h:51
Various assertion macros.
#define AssertRel(A, REL, B)
Definition: omassert.h:123
#define Assert(COND)
Definition: omassert.h:122
void unpack_throw_serialisation_error(const char *p)
Throw appropriate SerialisationError.
Definition: pack.cc:29
Pack types into strings and unpack them again.
bool unpack_string(const char **p, const char *end, std::string &result)
Decode a std::string from a string.
Definition: pack.h:468
bool unpack_uint(const char **p, const char *end, U *result)
Decode an unsigned integer from a string.
Definition: pack.h:346
void pack_uint(std::string &s, U value)
Append an encoded unsigned integer to a string.
Definition: pack.h:315
void pack_string(std::string &s, std::string_view value)
Append an encoded std::string to a string.
Definition: pack.h:442
Round a bounded estimate to an appropriate number of S.F.
Xapian::doccount round_estimate(T lb, T ub, T est)
Round a bounded estimate to an appropriate number of S.F.
Definition: roundestimate.h:37
string serialise_double(double v)
Serialise a double to a string.
double unserialise_double(const char **p, const char *end)
Unserialise a double serialised by serialise_double.
functions to serialise and unserialise a double
string serialise_stats(const Xapian::Weight::Internal &stats)
Serialise a stats object.
Definition: serialise.cc:42
void unserialise_stats(const char *p, const char *p_end, Xapian::Weight::Internal &stat)
Unserialise a serialised stats object.
Definition: serialise.cc:92
functions to convert classes to strings and back
static Xapian::Stem stemmer
Definition: stemtest.cc:42
Convert types to std::string.